diff options
702 files changed, 17421 insertions, 5085 deletions
diff --git a/Android.bp b/Android.bp index bd30de2c6749..8d3b4af82bc8 100644 --- a/Android.bp +++ b/Android.bp @@ -708,11 +708,9 @@ filegroup { name: "framework-services-net-module-wifi-shared-srcs", srcs: [ "core/java/android/net/DhcpResults.java", - "core/java/android/net/shared/Inet4AddressUtils.java", "core/java/android/net/shared/InetAddressUtils.java", "core/java/android/net/util/IpUtils.java", "core/java/android/util/LocalLog.java", - "core/java/com/android/internal/util/Preconditions.java", ], } @@ -1175,7 +1173,10 @@ java_library { "framework-annotations-lib", "unsupportedappusage", ], - visibility: ["//frameworks/base/wifi"], + visibility: [ + "//frameworks/base/wifi", + "//frameworks/base/services/net", + ], } filegroup { diff --git a/StubLibraries.bp b/StubLibraries.bp index 270c160dc5e9..bfc1367cceb9 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -97,9 +97,6 @@ stubs_defaults { droidstubs { name: "api-stubs-docs", defaults: ["metalava-full-api-stubs-default"], - api_filename: "public_api.txt", - private_api_filename: "private.txt", - removed_api_filename: "removed.txt", removed_dex_api_filename: "removed-dex.txt", arg_files: [ "core/res/AndroidManifest.xml", @@ -142,11 +139,6 @@ module_libs = " " + droidstubs { name: "system-api-stubs-docs", defaults: ["metalava-full-api-stubs-default"], - api_tag_name: "SYSTEM", - api_filename: "system-api.txt", - private_api_filename: "system-private.txt", - private_dex_api_filename: "system-private-dex.txt", - removed_api_filename: "system-removed.txt", removed_dex_api_filename: "system-removed-dex.txt", arg_files: [ "core/res/AndroidManifest.xml", @@ -179,9 +171,6 @@ droidstubs { droidstubs { name: "test-api-stubs-docs", defaults: ["metalava-full-api-stubs-default"], - api_tag_name: "TEST", - api_filename: "test-api.txt", - removed_api_filename: "test-removed.txt", arg_files: [ "core/res/AndroidManifest.xml", ], @@ -217,7 +206,6 @@ droidstubs { droidstubs { name: "module-lib-api", defaults: ["metalava-full-api-stubs-default"], - api_tag_name: "MODULE_LIB", arg_files: ["core/res/AndroidManifest.xml"], args: metalava_framework_docs_args + module_libs, check_api: { diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java index 6af1178b55f1..b4a7cd4de2ae 100644 --- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java +++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java @@ -131,6 +131,10 @@ class BlobStoreConfig { LEASE_ACQUISITION_WAIT_DURATION_MS = properties.getLong(key, DEFAULT_LEASE_ACQUISITION_WAIT_DURATION_MS); break; + case KEY_COMMIT_COOL_OFF_DURATION_MS: + COMMIT_COOL_OFF_DURATION_MS = properties.getLong(key, + DEFAULT_COMMIT_COOL_OFF_DURATION_MS); + break; default: Slog.wtf(TAG, "Unknown key in device config properties: " + key); } @@ -149,6 +153,9 @@ class BlobStoreConfig { fout.println(String.format(dumpFormat, KEY_LEASE_ACQUISITION_WAIT_DURATION_MS, TimeUtils.formatDuration(LEASE_ACQUISITION_WAIT_DURATION_MS), TimeUtils.formatDuration(DEFAULT_LEASE_ACQUISITION_WAIT_DURATION_MS))); + fout.println(String.format(dumpFormat, KEY_COMMIT_COOL_OFF_DURATION_MS, + TimeUtils.formatDuration(COMMIT_COOL_OFF_DURATION_MS), + TimeUtils.formatDuration(DEFAULT_COMMIT_COOL_OFF_DURATION_MS))); } } diff --git a/apex/statsd/aidl/android/os/IStatsd.aidl b/apex/statsd/aidl/android/os/IStatsd.aidl index 80308d26a430..0d3f4208a2ab 100644 --- a/apex/statsd/aidl/android/os/IStatsd.aidl +++ b/apex/statsd/aidl/android/os/IStatsd.aidl @@ -182,12 +182,6 @@ interface IStatsd { void unsetBroadcastSubscriber(long configId, long subscriberId, int callingUid); /** - * Apps can send an atom via this application breadcrumb with the specified label and state for - * this label. This allows building custom metrics and predicates. - */ - void sendAppBreadcrumbAtom(int label, int state); - - /** * Tell the stats daemon that all the pullers registered during boot have been sent. */ oneway void allPullersFromBootRegistered(); diff --git a/apex/statsd/framework/java/android/util/StatsLog.java b/apex/statsd/framework/java/android/util/StatsLog.java index 536b71a0e625..4eeae57fe195 100644 --- a/apex/statsd/framework/java/android/util/StatsLog.java +++ b/apex/statsd/framework/java/android/util/StatsLog.java @@ -25,8 +25,7 @@ import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.content.Context; import android.os.IStatsd; -import android.os.RemoteException; -import android.os.StatsFrameworkInitializer; +import android.os.Process; import android.util.proto.ProtoOutputStream; import com.android.internal.util.StatsdStatsLog; @@ -45,10 +44,6 @@ public final class StatsLog { private static final boolean DEBUG = false; private static final int EXPERIMENT_IDS_FIELD_ID = 1; - private static IStatsd sService; - - private static Object sLogLock = new Object(); - private StatsLog() { } @@ -59,26 +54,13 @@ public final class StatsLog { * @return True if the log request was sent to statsd. */ public static boolean logStart(int label) { - synchronized (sLogLock) { - try { - IStatsd service = getIStatsdLocked(); - if (service == null) { - if (DEBUG) { - Log.d(TAG, "Failed to find statsd when logging start"); - } - return false; - } - service.sendAppBreadcrumbAtom(label, - StatsdStatsLog.APP_BREADCRUMB_REPORTED__STATE__START); - return true; - } catch (RemoteException e) { - sService = null; - if (DEBUG) { - Log.d(TAG, "Failed to connect to statsd when logging start"); - } - return false; - } - } + int callingUid = Process.myUid(); + StatsdStatsLog.write( + StatsdStatsLog.APP_BREADCRUMB_REPORTED, + callingUid, + label, + StatsdStatsLog.APP_BREADCRUMB_REPORTED__STATE__START); + return true; } /** @@ -88,26 +70,13 @@ public final class StatsLog { * @return True if the log request was sent to statsd. */ public static boolean logStop(int label) { - synchronized (sLogLock) { - try { - IStatsd service = getIStatsdLocked(); - if (service == null) { - if (DEBUG) { - Log.d(TAG, "Failed to find statsd when logging stop"); - } - return false; - } - service.sendAppBreadcrumbAtom( - label, StatsdStatsLog.APP_BREADCRUMB_REPORTED__STATE__STOP); - return true; - } catch (RemoteException e) { - sService = null; - if (DEBUG) { - Log.d(TAG, "Failed to connect to statsd when logging stop"); - } - return false; - } - } + int callingUid = Process.myUid(); + StatsdStatsLog.write( + StatsdStatsLog.APP_BREADCRUMB_REPORTED, + callingUid, + label, + StatsdStatsLog.APP_BREADCRUMB_REPORTED__STATE__STOP); + return true; } /** @@ -117,26 +86,13 @@ public final class StatsLog { * @return True if the log request was sent to statsd. */ public static boolean logEvent(int label) { - synchronized (sLogLock) { - try { - IStatsd service = getIStatsdLocked(); - if (service == null) { - if (DEBUG) { - Log.d(TAG, "Failed to find statsd when logging event"); - } - return false; - } - service.sendAppBreadcrumbAtom( - label, StatsdStatsLog.APP_BREADCRUMB_REPORTED__STATE__UNSPECIFIED); - return true; - } catch (RemoteException e) { - sService = null; - if (DEBUG) { - Log.d(TAG, "Failed to connect to statsd when logging event"); - } - return false; - } - } + int callingUid = Process.myUid(); + StatsdStatsLog.write( + StatsdStatsLog.APP_BREADCRUMB_REPORTED, + callingUid, + label, + StatsdStatsLog.APP_BREADCRUMB_REPORTED__STATE__UNSPECIFIED); + return true; } /** @@ -181,17 +137,6 @@ public final class StatsLog { return true; } - private static IStatsd getIStatsdLocked() throws RemoteException { - if (sService != null) { - return sService; - } - sService = IStatsd.Stub.asInterface(StatsFrameworkInitializer - .getStatsServiceManager() - .getStatsdServiceRegisterer() - .get()); - return sService; - } - /** * Write an event to stats log using the raw format. * diff --git a/api/current.txt b/api/current.txt index 80e2d00b3e48..3f8bee9230a6 100644 --- a/api/current.txt +++ b/api/current.txt @@ -48759,12 +48759,8 @@ package android.telephony.ims { method public void onCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.MmTelFeature.MmTelCapabilities); } - public class ImsRcsManager implements android.telephony.ims.RegistrationManager { - method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); - method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); + public class ImsRcsManager { method @NonNull public android.telephony.ims.RcsUceAdapter getUceAdapter(); - method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException; - method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback); field public static final String ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN = "android.telephony.ims.action.SHOW_CAPABILITY_DISCOVERY_OPT_IN"; } diff --git a/api/test-current.txt b/api/test-current.txt index 755380e93b2a..abfd12bfcc2e 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -126,6 +126,7 @@ package android.app { public class ActivityTaskManager { method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void clearLaunchParamsForPackages(java.util.List<java.lang.String>); + method public static boolean currentUiModeSupportsErrorDialogs(@NonNull android.content.Context); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public String listAllStacks(); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void moveTaskToStack(int, int, boolean); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public boolean moveTopActivityToPinnedStack(int, android.graphics.Rect); @@ -504,6 +505,11 @@ package android.app { method public boolean isStatusBarExpansionDisabled(); } + public class TaskInfo { + method @NonNull public android.content.res.Configuration getConfiguration(); + method @NonNull public android.window.WindowContainerToken getToken(); + } + public class TimePickerDialog extends android.app.AlertDialog implements android.content.DialogInterface.OnClickListener android.widget.TimePicker.OnTimeChangedListener { method public android.widget.TimePicker getTimePicker(); } @@ -4978,6 +4984,7 @@ package android.view.accessibility { public class AccessibilityNodeInfo implements android.os.Parcelable { method public void addChild(@NonNull android.os.IBinder); + method public long getSourceNodeId(); method public void setLeashedParent(@Nullable android.os.IBinder, int); method public static void setNumInstancesInUseCounter(java.util.concurrent.atomic.AtomicInteger); method public void writeToParcelNoRecycle(android.os.Parcel, int); diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index acd9ec3be210..f30ed17c392f 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -104,7 +104,7 @@ cc_defaults { "src/subscriber/IncidentdReporter.cpp", "src/subscriber/SubscriberReporter.cpp", "src/uid_data.proto", - "src/utils/NamedLatch.cpp", + "src/utils/MultiConditionTrigger.cpp", ], local_include_dirs: [ @@ -295,7 +295,12 @@ cc_test { //TODO(b/153588990): Remove when the build system properly separates //32bit and 64bit architectures. - compile_multilib: "prefer32", + compile_multilib: "both", + multilib: { + lib64: { + suffix: "64", + } + }, cflags: [ "-Wall", @@ -366,7 +371,7 @@ cc_test { "tests/StatsService_test.cpp", "tests/storage/StorageManager_test.cpp", "tests/UidMap_test.cpp", - "tests/utils/NamedLatch_test.cpp", + "tests/utils/MultiConditionTrigger_test.cpp", ], static_libs: [ diff --git a/cmds/statsd/src/FieldValue.cpp b/cmds/statsd/src/FieldValue.cpp index cfc1de49e259..c9ccfb93c86d 100644 --- a/cmds/statsd/src/FieldValue.cpp +++ b/cmds/statsd/src/FieldValue.cpp @@ -18,7 +18,6 @@ #include "Log.h" #include "FieldValue.h" #include "HashableDimensionKey.h" -#include "atoms_info.h" #include "math.h" namespace android { diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h index e251399776fb..ba4cf11b84f1 100644 --- a/cmds/statsd/src/FieldValue.h +++ b/cmds/statsd/src/FieldValue.h @@ -382,10 +382,6 @@ public: inline void setUidField(bool isUid) { setBitmaskAtPos(UID_POS, isUid); } - inline void setResetState(int32_t resetState) { - mResetState = resetState; - } - // Default value = false inline bool isNested() const { return getValueFromBitmask(NESTED_POS); } @@ -398,12 +394,6 @@ public: // Default value = false inline bool isUidField() const { return getValueFromBitmask(UID_POS); } - // If a reset state is not sent in the StatsEvent, returns -1. Note that a - // reset satate is only sent if and only if a reset should be triggered. - inline int32_t getResetState() const { - return mResetState; - } - private: inline void setBitmaskAtPos(int pos, bool value) { mBooleanBitmask &= ~(1 << pos); // clear @@ -417,8 +407,6 @@ private: // This is a bitmask over all annotations stored in boolean form. Because // there are only 4 booleans, just one byte is required. uint8_t mBooleanBitmask = 0; - - int32_t mResetState = -1; }; /** diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index d914ab2436c7..062fcf9c9dae 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -25,7 +25,6 @@ #include <frameworks/base/cmds/statsd/src/experiment_ids.pb.h> #include "android-base/stringprintf.h" -#include "atoms_info.h" #include "external/StatsPullerManager.h" #include "guardrail/StatsdStats.h" #include "logd/LogEvent.h" @@ -389,15 +388,24 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event) { void StatsLogProcessor::OnLogEvent(LogEvent* event, int64_t elapsedRealtimeNs) { std::lock_guard<std::mutex> lock(mMetricsMutex); + // Tell StatsdStats about new event + const int64_t eventElapsedTimeNs = event->GetElapsedTimestampNs(); + int atomId = event->GetTagId(); + StatsdStats::getInstance().noteAtomLogged(atomId, eventElapsedTimeNs / NS_PER_SEC); + if (!event->isValid()) { + StatsdStats::getInstance().noteAtomError(atomId); + return; + } + // Hard-coded logic to update train info on disk and fill in any information // this log event may be missing. - if (event->GetTagId() == android::os::statsd::util::BINARY_PUSH_STATE_CHANGED) { + if (atomId == android::os::statsd::util::BINARY_PUSH_STATE_CHANGED) { onBinaryPushStateChangedEventLocked(event); } // Hard-coded logic to update experiment ids on disk for certain rollback // types and fill the rollback atom with experiment ids - if (event->GetTagId() == android::os::statsd::util::WATCHDOG_ROLLBACK_OCCURRED) { + if (atomId == android::os::statsd::util::WATCHDOG_ROLLBACK_OCCURRED) { onWatchdogRollbackOccurredLocked(event); } @@ -406,16 +414,11 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event, int64_t elapsedRealtimeNs) { ALOGI("%s", event->ToString().c_str()); } #endif - const int64_t eventElapsedTimeNs = event->GetElapsedTimestampNs(); - resetIfConfigTtlExpiredLocked(eventElapsedTimeNs); - StatsdStats::getInstance().noteAtomLogged( - event->GetTagId(), event->GetElapsedTimestampNs() / NS_PER_SEC); - // Hard-coded logic to update the isolated uid's in the uid-map. // The field numbers need to be currently updated by hand with atoms.proto - if (event->GetTagId() == android::os::statsd::util::ISOLATED_UID_CHANGED) { + if (atomId == android::os::statsd::util::ISOLATED_UID_CHANGED) { onIsolatedUidChangedEventLocked(*event); } @@ -432,7 +435,7 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event, int64_t elapsedRealtimeNs) { } - if (event->GetTagId() != android::os::statsd::util::ISOLATED_UID_CHANGED) { + if (atomId != android::os::statsd::util::ISOLATED_UID_CHANGED) { // Map the isolated uid to host uid if necessary. mapIsolatedUidToHostUidIfNecessaryLocked(event); } @@ -1051,8 +1054,8 @@ int64_t StatsLogProcessor::getLastReportTimeNs(const ConfigKey& key) { void StatsLogProcessor::notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid, const int64_t version) { std::lock_guard<std::mutex> lock(mMetricsMutex); - ALOGW("Received app upgrade"); - for (auto it : mMetricsManagers) { + VLOG("Received app upgrade"); + for (const auto& it : mMetricsManagers) { it.second->notifyAppUpgrade(eventTimeNs, apk, uid, version); } } @@ -1060,20 +1063,28 @@ void StatsLogProcessor::notifyAppUpgrade(const int64_t& eventTimeNs, const strin void StatsLogProcessor::notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, const int uid) { std::lock_guard<std::mutex> lock(mMetricsMutex); - ALOGW("Received app removed"); - for (auto it : mMetricsManagers) { + VLOG("Received app removed"); + for (const auto& it : mMetricsManagers) { it.second->notifyAppRemoved(eventTimeNs, apk, uid); } } void StatsLogProcessor::onUidMapReceived(const int64_t& eventTimeNs) { std::lock_guard<std::mutex> lock(mMetricsMutex); - ALOGW("Received uid map"); - for (auto it : mMetricsManagers) { + VLOG("Received uid map"); + for (const auto& it : mMetricsManagers) { it.second->onUidMapReceived(eventTimeNs); } } +void StatsLogProcessor::onStatsdInitCompleted(const int64_t& elapsedTimeNs) { + std::lock_guard<std::mutex> lock(mMetricsMutex); + VLOG("Received boot completed signal"); + for (const auto& it : mMetricsManagers) { + it.second->onStatsdInitCompleted(elapsedTimeNs); + } +} + void StatsLogProcessor::noteOnDiskData(const ConfigKey& key) { std::lock_guard<std::mutex> lock(mMetricsMutex); mOnDiskDataConfigs.insert(key); diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index 97512ed7595c..ffd83ba978f4 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -120,6 +120,11 @@ public: /* Notify all MetricsManagers of uid map snapshots received */ void onUidMapReceived(const int64_t& eventTimeNs) override; + /* Notify all metrics managers of boot completed + * This will force a bucket split when the boot is finished. + */ + void onStatsdInitCompleted(const int64_t& elapsedTimeNs); + // Reset all configs. void resetConfigs(); diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index ae7a8d0d30cc..bd9f7a59fcbd 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -118,7 +118,8 @@ StatsService::StatsService(const sp<Looper>& handlerLooper, shared_ptr<LogEventQ } })), mEventQueue(queue), - mBootCompleteLatch({kBootCompleteTag, kUidMapReceivedTag, kAllPullersRegisteredTag}), + mBootCompleteTrigger({kBootCompleteTag, kUidMapReceivedTag, kAllPullersRegisteredTag}, + [this]() { mProcessor->onStatsdInitCompleted(getElapsedRealtimeNs()); }), mStatsCompanionServiceDeathRecipient( AIBinder_DeathRecipient_new(StatsService::statsCompanionServiceDied)) { mUidMap = UidMap::getInstance(); @@ -165,12 +166,6 @@ StatsService::StatsService(const sp<Looper>& handlerLooper, shared_ptr<LogEventQ std::thread pushedEventThread([this] { readLogs(); }); pushedEventThread.detach(); } - - std::thread bootCompletedThread([this] { - mBootCompleteLatch.wait(); - VLOG("In the boot completed thread"); - }); - bootCompletedThread.detach(); } StatsService::~StatsService() { @@ -946,7 +941,7 @@ Status StatsService::informAllUidData(const ScopedFileDescriptor& fd) { packageNames, installers); - mBootCompleteLatch.countDown(kUidMapReceivedTag); + mBootCompleteTrigger.markComplete(kUidMapReceivedTag); VLOG("StatsService::informAllUidData UidData proto parsed successfully."); return Status::ok(); } @@ -1066,7 +1061,7 @@ Status StatsService::bootCompleted() { ENFORCE_UID(AID_SYSTEM); VLOG("StatsService::bootCompleted was called"); - mBootCompleteLatch.countDown(kBootCompleteTag); + mBootCompleteTrigger.markComplete(kBootCompleteTag); return Status::ok(); } @@ -1222,20 +1217,11 @@ Status StatsService::unsetBroadcastSubscriber(int64_t configId, return Status::ok(); } -Status StatsService::sendAppBreadcrumbAtom(int32_t label, int32_t state) { - // Permission check not necessary as it's meant for applications to write to - // statsd. - android::os::statsd::util::stats_write(android::os::statsd::util::APP_BREADCRUMB_REPORTED, - (int32_t) AIBinder_getCallingUid(), label, - state); - return Status::ok(); -} - Status StatsService::allPullersFromBootRegistered() { ENFORCE_UID(AID_SYSTEM); VLOG("StatsService::allPullersFromBootRegistered was called"); - mBootCompleteLatch.countDown(kAllPullersRegisteredTag); + mBootCompleteTrigger.markComplete(kAllPullersRegisteredTag); return Status::ok(); } diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index 79324d89d8e8..b49fa1d42e66 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -33,7 +33,7 @@ #include "packages/UidMap.h" #include "shell/ShellSubscriber.h" #include "statscompanion_util.h" -#include "utils/NamedLatch.h" +#include "utils/MultiConditionTrigger.h" using namespace android; using namespace android::os; @@ -162,11 +162,6 @@ public: virtual void sayHiToStatsCompanion(); /** - * Binder call to get AppBreadcrumbReported atom. - */ - virtual Status sendAppBreadcrumbAtom(int32_t label, int32_t state) override; - - /** * Binder call to notify statsd that all pullers from boot have been registered. */ virtual Status allPullersFromBootRegistered(); @@ -386,7 +381,7 @@ private: mutable mutex mShellSubscriberMutex; std::shared_ptr<LogEventQueue> mEventQueue; - NamedLatch mBootCompleteLatch; + MultiConditionTrigger mBootCompleteTrigger; static const inline string kBootCompleteTag = "BOOT_COMPLETE"; static const inline string kUidMapReceivedTag = "UID_MAP"; static const inline string kAllPullersRegisteredTag = "PULLERS_REGISTERED"; @@ -399,11 +394,14 @@ private: FRIEND_TEST(StatsServiceTest, TestAddConfig_invalid); FRIEND_TEST(StatsServiceTest, TestGetUidFromArgs); FRIEND_TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp); + FRIEND_TEST(PartialBucketE2eTest, TestCountMetricSplitOnBoot); FRIEND_TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade); FRIEND_TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval); FRIEND_TEST(PartialBucketE2eTest, TestCountMetricWithoutSplit); + FRIEND_TEST(PartialBucketE2eTest, TestValueMetricOnBootWithoutMinPartialBucket); FRIEND_TEST(PartialBucketE2eTest, TestValueMetricWithoutMinPartialBucket); FRIEND_TEST(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket); + FRIEND_TEST(PartialBucketE2eTest, TestGaugeMetricOnBootWithoutMinPartialBucket); FRIEND_TEST(PartialBucketE2eTest, TestGaugeMetricWithoutMinPartialBucket); FRIEND_TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket); }; diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 13e7ac1fd4a0..88824f0f8c1d 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -46,6 +46,7 @@ import "frameworks/base/core/proto/android/stats/dnsresolver/dns_resolver.proto" import "frameworks/base/core/proto/android/stats/devicepolicy/device_policy.proto"; import "frameworks/base/core/proto/android/stats/devicepolicy/device_policy_enums.proto"; import "frameworks/base/core/proto/android/stats/docsui/docsui_enums.proto"; +import "frameworks/base/core/proto/android/stats/accessibility/accessibility_enums.proto"; import "frameworks/base/core/proto/android/stats/enums.proto"; import "frameworks/base/core/proto/android/stats/intelligence/enums.proto"; import "frameworks/base/core/proto/android/stats/launcher/launcher.proto"; @@ -423,6 +424,9 @@ message Atom { PackageInstallerV2Reported package_installer_v2_reported = 263 [(module) = "framework"]; UserLifecycleJourneyReported user_lifecycle_journey_reported = 264 [(module) = "framework"]; UserLifecycleEventOccurred user_lifecycle_event_occurred = 265 [(module) = "framework"]; + AccessibilityShortcutReported accessibility_shortcut_reported = + 266 [(module) = "framework"]; + AccessibilityServiceReported accessibility_service_reported = 267 [(module) = "framework"]; SdkExtensionStatus sdk_extension_status = 354; // StatsdStats tracks platform atoms with ids upto 500. @@ -9441,3 +9445,38 @@ message UserLifecycleEventOccurred { } optional State state = 4; // Represents the state of an event (beginning/ending) } + +/** + * Logs when accessibility shortcut clicked. + * + * Logged from: + * frameworks/base/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java + */ +message AccessibilityShortcutReported { + // The accessibility feature(including installed a11y service, framework a11y feature, + // and installed a11y activity) package name that is assigned to the accessibility shortcut. + optional string package_name = 1; + + // The definition of the accessibility shortcut. + // From frameworks/base/core/proto/android/stats/accessibility/accessibility_enums.proto. + optional android.stats.accessibility.ShortcutType shortcut_type = 2; + + // The definition of the service status. + // From frameworks/base/core/proto/android/stats/accessibility/accessibility_enums.proto. + optional android.stats.accessibility.ServiceStatus service_status = 3; +} + +/** + * Logs when accessibility service status changed. + * + * Logged from: + * frameworks/base/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java + */ +message AccessibilityServiceReported { + // The accessibility service package name. + optional string package_name = 1; + + // The definition of the service status. + // From frameworks/base/core/proto/android/stats/accessibility/accessibility_enums.proto. + optional android.stats.accessibility.ServiceStatus service_status = 2; +} diff --git a/cmds/statsd/src/external/StatsCallbackPuller.cpp b/cmds/statsd/src/external/StatsCallbackPuller.cpp index 933f48d1714b..3618bb0dd08b 100644 --- a/cmds/statsd/src/external/StatsCallbackPuller.cpp +++ b/cmds/statsd/src/external/StatsCallbackPuller.cpp @@ -67,8 +67,14 @@ bool StatsCallbackPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) { lock_guard<mutex> lk(*cv_mutex); for (const StatsEventParcel& parcel: output) { shared_ptr<LogEvent> event = make_shared<LogEvent>(/*uid=*/-1, /*pid=*/-1); - event->parseBuffer((uint8_t*)parcel.buffer.data(), parcel.buffer.size()); - sharedData->push_back(event); + bool valid = event->parseBuffer((uint8_t*)parcel.buffer.data(), + parcel.buffer.size()); + if (valid) { + sharedData->push_back(event); + } else { + StatsdStats::getInstance().noteAtomError(event->GetTagId(), + /*pull=*/true); + } } *pullSuccess = success; *pullFinish = true; diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index ebe961014336..cfd5d14b0d3b 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -252,9 +252,13 @@ void StatsPullerManager::RegisterPullUidProvider(const ConfigKey& configKey, mPullUidProviders[configKey] = provider; } -void StatsPullerManager::UnregisterPullUidProvider(const ConfigKey& configKey) { +void StatsPullerManager::UnregisterPullUidProvider(const ConfigKey& configKey, + wp<PullUidProvider> provider) { std::lock_guard<std::mutex> _l(mLock); - mPullUidProviders.erase(configKey); + const auto& it = mPullUidProviders.find(configKey); + if (it != mPullUidProviders.end() && it->second == provider) { + mPullUidProviders.erase(it); + } } void StatsPullerManager::OnAlarmFired(int64_t elapsedTimeNs) { diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h index ab0cceeb112e..5e18aaa6ed61 100644 --- a/cmds/statsd/src/external/StatsPullerManager.h +++ b/cmds/statsd/src/external/StatsPullerManager.h @@ -78,11 +78,12 @@ public: wp<PullDataReceiver> receiver); // Registers a pull uid provider for the config key. When pulling atoms, it will be used to - // determine which atoms to pull from. + // determine which uids to pull from. virtual void RegisterPullUidProvider(const ConfigKey& configKey, wp<PullUidProvider> provider); // Unregister a pull uid provider. - virtual void UnregisterPullUidProvider(const ConfigKey& configKey); + virtual void UnregisterPullUidProvider(const ConfigKey& configKey, + wp<PullUidProvider> provider); // Verify if we know how to pull for this matcher bool PullerForMatcherExists(int tagId) const; @@ -180,6 +181,8 @@ private: FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents); FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm); FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation); + + FRIEND_TEST(StatsLogProcessorTest, TestPullUidProviderSetOnConfigUpdate); }; } // namespace statsd diff --git a/cmds/statsd/src/external/puller_util.cpp b/cmds/statsd/src/external/puller_util.cpp index 9e72a23d27f4..84bc68402723 100644 --- a/cmds/statsd/src/external/puller_util.cpp +++ b/cmds/statsd/src/external/puller_util.cpp @@ -17,7 +17,6 @@ #define DEBUG false // STOPSHIP if true #include "Log.h" -#include "atoms_info.h" #include "puller_util.h" namespace android { diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp index db637b15e969..46f5dbda5521 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.cpp +++ b/cmds/statsd/src/guardrail/StatsdStats.cpp @@ -54,6 +54,7 @@ const int FIELD_ID_ACTIVATION_BROADCAST_GUARDRAIL = 19; const int FIELD_ID_ATOM_STATS_TAG = 1; const int FIELD_ID_ATOM_STATS_COUNT = 2; +const int FIELD_ID_ATOM_STATS_ERROR_COUNT = 3; const int FIELD_ID_ANOMALY_ALARMS_REGISTERED = 1; const int FIELD_ID_PERIODIC_ALARMS_REGISTERED = 1; @@ -549,6 +550,20 @@ void StatsdStats::noteBucketBoundaryDelayNs(int64_t metricId, int64_t timeDelayN std::min(pullStats.minBucketBoundaryDelayNs, timeDelayNs); } +void StatsdStats::noteAtomError(int atomTag, bool pull) { + lock_guard<std::mutex> lock(mLock); + if (pull) { + mPulledAtomStats[atomTag].atomErrorCount++; + return; + } + + bool present = (mPushedAtomErrorStats.find(atomTag) != mPushedAtomErrorStats.end()); + bool full = (mPushedAtomErrorStats.size() >= (size_t)kMaxPushedAtomErrorStatsSize); + if (!full || present) { + mPushedAtomErrorStats[atomTag]++; + } +} + StatsdStats::AtomMetricStats& StatsdStats::getAtomMetricStats(int64_t metricId) { auto atomMetricStatsIter = mAtomMetricStats.find(metricId); if (atomMetricStatsIter != mAtomMetricStats.end()) { @@ -604,9 +619,11 @@ void StatsdStats::resetInternalLocked() { pullStats.second.pullExceedMaxDelay = 0; pullStats.second.registeredCount = 0; pullStats.second.unregisteredCount = 0; + pullStats.second.atomErrorCount = 0; } mAtomMetricStats.clear(); mActivationBroadcastGuardrailStats.clear(); + mPushedAtomErrorStats.clear(); } string buildTimeString(int64_t timeSec) { @@ -617,6 +634,15 @@ string buildTimeString(int64_t timeSec) { return string(timeBuffer); } +int StatsdStats::getPushedAtomErrors(int atomId) const { + const auto& it = mPushedAtomErrorStats.find(atomId); + if (it != mPushedAtomErrorStats.end()) { + return it->second; + } else { + return 0; + } +} + void StatsdStats::dumpStats(int out) const { lock_guard<std::mutex> lock(mLock); time_t t = mStartTimeSec; @@ -721,11 +747,13 @@ void StatsdStats::dumpStats(int out) const { const size_t atomCounts = mPushedAtomStats.size(); for (size_t i = 2; i < atomCounts; i++) { if (mPushedAtomStats[i] > 0) { - dprintf(out, "Atom %lu->%d\n", (unsigned long)i, mPushedAtomStats[i]); + dprintf(out, "Atom %zu->(total count)%d, (error count)%d\n", i, mPushedAtomStats[i], + getPushedAtomErrors((int)i)); } } for (const auto& pair : mNonPlatformPushedAtomStats) { - dprintf(out, "Atom %lu->%d\n", (unsigned long)pair.first, pair.second); + dprintf(out, "Atom %d->(total count)%d, (error count)%d\n", pair.first, pair.second, + getPushedAtomErrors(pair.first)); } dprintf(out, "********Pulled Atom stats***********\n"); @@ -737,13 +765,15 @@ void StatsdStats::dumpStats(int out) const { "nanos)%lld, " " (max pull delay nanos)%lld, (data error)%ld\n" " (pull timeout)%ld, (pull exceed max delay)%ld\n" - " (registered count) %ld, (unregistered count) %ld\n", + " (registered count) %ld, (unregistered count) %ld\n" + " (atom error count) %d\n", (int)pair.first, (long)pair.second.totalPull, (long)pair.second.totalPullFromCache, (long)pair.second.pullFailed, (long)pair.second.minPullIntervalSec, (long long)pair.second.avgPullTimeNs, (long long)pair.second.maxPullTimeNs, (long long)pair.second.avgPullDelayNs, (long long)pair.second.maxPullDelayNs, pair.second.dataError, pair.second.pullTimeout, pair.second.pullExceedMaxDelay, - pair.second.registeredCount, pair.second.unregisteredCount); + pair.second.registeredCount, pair.second.unregisteredCount, + pair.second.atomErrorCount); } if (mAnomalyAlarmRegisteredStats > 0) { @@ -919,6 +949,10 @@ void StatsdStats::dumpStats(std::vector<uint8_t>* output, bool reset) { proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOM_STATS | FIELD_COUNT_REPEATED); proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_TAG, (int32_t)i); proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_COUNT, mPushedAtomStats[i]); + int errors = getPushedAtomErrors(i); + if (errors > 0) { + proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_ERROR_COUNT, errors); + } proto.end(token); } } @@ -928,6 +962,10 @@ void StatsdStats::dumpStats(std::vector<uint8_t>* output, bool reset) { proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOM_STATS | FIELD_COUNT_REPEATED); proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_TAG, pair.first); proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_COUNT, pair.second); + int errors = getPushedAtomErrors(pair.first); + if (errors > 0) { + proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_ERROR_COUNT, errors); + } proto.end(token); } diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h index ff31e9e73fd0..805281ccd2d2 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.h +++ b/cmds/statsd/src/guardrail/StatsdStats.h @@ -16,7 +16,6 @@ #pragma once #include "config/ConfigKey.h" -#include "atoms_info.h" #include <gtest/gtest_prod.h> #include <log/log_time.h> @@ -461,6 +460,16 @@ public: */ void noteActivationBroadcastGuardrailHit(const int uid); + /** + * Reports that an atom is erroneous or cannot be parsed successfully by + * statsd. An atom tag of 0 indicates that the client did not supply the + * atom id within the encoding. + * + * For pushed atoms only, this call should be preceded by a call to + * noteAtomLogged. + */ + void noteAtomError(int atomTag, bool pull=false); + /** * Reset the historical stats. Including all stats in icebox, and the tracked stats about * metrics, matchers, and atoms. The active configs will be kept and StatsdStats will continue @@ -499,6 +508,7 @@ public: long emptyData = 0; long registeredCount = 0; long unregisteredCount = 0; + int32_t atomErrorCount = 0; } PulledAtomStats; typedef struct { @@ -546,6 +556,12 @@ private: // Maps PullAtomId to its stats. The size is capped by the puller atom counts. std::map<int, PulledAtomStats> mPulledAtomStats; + // Stores the number of times a pushed atom was logged erroneously. The + // corresponding counts for pulled atoms are stored in PulledAtomStats. + // The max size of this map is kMaxAtomErrorsStatsSize. + std::map<int, int> mPushedAtomErrorStats; + int kMaxPushedAtomErrorStatsSize = 100; + // Maps metric ID to its stats. The size is capped by the number of metrics. std::map<int64_t, AtomMetricStats> mAtomMetricStats; @@ -613,6 +629,8 @@ private: void addToIceBoxLocked(std::shared_ptr<ConfigStats>& stats); + int getPushedAtomErrors(int atomId) const; + /** * Get a reference to AtomMetricStats for a metric. If none exists, create it. The reference * will live as long as `this`. @@ -631,6 +649,7 @@ private: FRIEND_TEST(StatsdStatsTest, TestPullAtomStats); FRIEND_TEST(StatsdStatsTest, TestAtomMetricsStats); FRIEND_TEST(StatsdStatsTest, TestActivationBroadcastGuardrailHit); + FRIEND_TEST(StatsdStatsTest, TestAtomErrorStats); }; } // namespace statsd diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp index 61cd01728ab1..eb830e114b40 100644 --- a/cmds/statsd/src/logd/LogEvent.cpp +++ b/cmds/statsd/src/logd/LogEvent.cpp @@ -114,14 +114,6 @@ LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, mValues.push_back(FieldValue(Field(mTagId, getSimpleField(4)), Value(trainInfo.status))); } -LogEvent::~LogEvent() { - if (mContext) { - // This is for the case when LogEvent is created using the test interface - // but init() isn't called. - android_log_destroy(&mContext); - } -} - void LogEvent::parseInt32(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) { int32_t value = readNextValue<int32_t>(); addToValues(pos, depth, value, last); @@ -303,8 +295,7 @@ void LogEvent::parseTriggerStateResetAnnotation(uint8_t annotationType) { return; } - int32_t resetState = readNextValue<int32_t>(); - mValues[mValues.size() - 1].mAnnotations.setResetState(resetState); + mResetState = readNextValue<int32_t>(); } void LogEvent::parseStateNestedAnnotation(uint8_t annotationType) { @@ -386,7 +377,6 @@ bool LogEvent::parseBuffer(uint8_t* buf, size_t len) { typeInfo = readNextValue<uint8_t>(); uint8_t typeId = getTypeId(typeInfo); - // TODO(b/144373276): handle errors passed to the socket switch (typeId) { case BOOL_TYPE: parseBool(pos, /*depth=*/0, last, getNumAnnotations(typeInfo)); @@ -413,8 +403,13 @@ bool LogEvent::parseBuffer(uint8_t* buf, size_t len) { parseAttributionChain(pos, /*depth=*/0, last, getNumAnnotations(typeInfo)); if (mAttributionChainIndex == -1) mAttributionChainIndex = pos[0]; break; + case ERROR_TYPE: + mErrorBitmask = readNextValue<int32_t>(); + mValid = false; + break; default: mValid = false; + break; } } diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h index 41fdcc2cbe7a..dedcfaf6cd87 100644 --- a/cmds/statsd/src/logd/LogEvent.h +++ b/cmds/statsd/src/logd/LogEvent.h @@ -70,7 +70,7 @@ public: explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, const InstallTrainInfo& installTrainInfo); - ~LogEvent(); + ~LogEvent() {} /** * Get the timestamp associated with this event. @@ -184,6 +184,12 @@ public: return mExclusiveStateFieldIndex; } + // If a reset state is not sent in the StatsEvent, returns -1. Note that a + // reset state is sent if and only if a reset should be triggered. + inline int getResetState() const { + return mResetState; + } + inline LogEvent makeCopy() { return LogEvent(*this); } @@ -204,6 +210,14 @@ public: return BAD_INDEX; } + bool isValid() const { + return mValid; + } + + int32_t getErrorBitmask() const { + return mErrorBitmask; + } + private: /** * Only use this if copy is absolutely needed. @@ -230,12 +244,13 @@ private: bool checkPreviousValueType(Type expected); /** - * The below three variables are only valid during the execution of + * The below two variables are only valid during the execution of * parseBuffer. There are no guarantees about the state of these variables * before/after. */ uint8_t* mBuf; uint32_t mRemainingLen; // number of valid bytes left in the buffer being parsed + bool mValid = true; // stores whether the event we received from the socket is valid /** @@ -287,19 +302,15 @@ private: // matching. std::vector<FieldValue> mValues; - // This field is used when statsD wants to create log event object and write fields to it. After - // calling init() function, this object would be destroyed to save memory usage. - // When the log event is created from log msg, this field is never initiated. - android_log_context mContext = NULL; - // The timestamp set by the logd. int64_t mLogdTimestampNs; // The elapsed timestamp set by statsd log writer. int64_t mElapsedTimestampNs; - // The atom tag of the event. - int mTagId; + // The atom tag of the event (defaults to 0 if client does not + // appropriately set the atom id). + int mTagId = 0; // The uid of the logging client (defaults to -1). int32_t mLogUid = -1; @@ -307,11 +318,15 @@ private: // The pid of the logging client (defaults to -1). int32_t mLogPid = -1; + // Bitmask of errors sent by StatsEvent/AStatsEvent. + int32_t mErrorBitmask = 0; + // Annotations bool mTruncateTimestamp = false; int mUidFieldIndex = -1; int mAttributionChainIndex = -1; int mExclusiveStateFieldIndex = -1; + int mResetState = -1; }; void writeExperimentIdsToProto(const std::vector<int64_t>& experimentIds, std::vector<uint8_t>* protoOut); diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h index a4711e8357f2..f9a8842efc3d 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.h +++ b/cmds/statsd/src/metrics/CountMetricProducer.h @@ -109,10 +109,11 @@ private: FRIEND_TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition); FRIEND_TEST(CountMetricProducerTest, TestEventsWithSlicedCondition); FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced); - FRIEND_TEST(CountMetricProducerTest, TestEventWithAppUpgrade); - FRIEND_TEST(CountMetricProducerTest, TestEventWithAppUpgradeInNextBucket); FRIEND_TEST(CountMetricProducerTest, TestFirstBucket); FRIEND_TEST(CountMetricProducerTest, TestOneWeekTimeUnit); + + FRIEND_TEST(CountMetricProducerTest_PartialBucket, TestSplitInCurrentBucket); + FRIEND_TEST(CountMetricProducerTest_PartialBucket, TestSplitInNextBucket); }; } // namespace statsd diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h index cc48f99add01..6f84076ee6b5 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.h +++ b/cmds/statsd/src/metrics/DurationMetricProducer.h @@ -154,12 +154,14 @@ private: FRIEND_TEST(DurationMetricTrackerTest, TestNoCondition); FRIEND_TEST(DurationMetricTrackerTest, TestNonSlicedCondition); FRIEND_TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState); - FRIEND_TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade); - FRIEND_TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket); - FRIEND_TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgrade); - FRIEND_TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgradeInNextBucket); FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicates); FRIEND_TEST(DurationMetricTrackerTest, TestFirstBucket); + + FRIEND_TEST(DurationMetricProducerTest_PartialBucket, TestSumDuration); + FRIEND_TEST(DurationMetricProducerTest_PartialBucket, + TestSumDurationWithSplitInFollowingBucket); + FRIEND_TEST(DurationMetricProducerTest_PartialBucket, TestMaxDuration); + FRIEND_TEST(DurationMetricProducerTest_PartialBucket, TestMaxDurationWithSplitInNextBucket); }; } // namespace statsd diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h index aa0cae26080d..2eb584b097ea 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.h +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h @@ -73,18 +73,23 @@ public: bool pullSuccess, int64_t originalPullTimeNs) override; // GaugeMetric needs to immediately trigger another pull when we create the partial bucket. - void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid, - const int64_t version) override { + void notifyAppUpgrade(const int64_t& eventTimeNs) override { std::lock_guard<std::mutex> lock(mMutex); if (!mSplitBucketForAppUpgrade) { return; } - if (eventTimeNs > getCurrentBucketEndTimeNs()) { - // Flush full buckets on the normal path up to the latest bucket boundary. - flushIfNeededLocked(eventTimeNs); + flushLocked(eventTimeNs); + if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) { + pullAndMatchEventsLocked(eventTimeNs); } - flushCurrentBucketLocked(eventTimeNs, eventTimeNs); + }; + + // GaugeMetric needs to immediately trigger another pull when we create the partial bucket. + void onStatsdInitCompleted(const int64_t& eventTimeNs) override { + std::lock_guard<std::mutex> lock(mMutex); + + flushLocked(eventTimeNs); if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) { pullAndMatchEventsLocked(eventTimeNs); } @@ -190,13 +195,14 @@ private: FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition); FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition); FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition); - FRIEND_TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade); - FRIEND_TEST(GaugeMetricProducerTest, TestPulledWithUpgrade); FRIEND_TEST(GaugeMetricProducerTest, TestPulledWithAppUpgradeDisabled); FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection); FRIEND_TEST(GaugeMetricProducerTest, TestFirstBucket); FRIEND_TEST(GaugeMetricProducerTest, TestPullOnTrigger); FRIEND_TEST(GaugeMetricProducerTest, TestRemoveDimensionInOutput); + + FRIEND_TEST(GaugeMetricProducerTest_PartialBucket, TestPushedEvents); + FRIEND_TEST(GaugeMetricProducerTest_PartialBucket, TestPulled); }; } // namespace statsd diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index 6aba13ca7859..91c98ea27269 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -141,30 +141,25 @@ public: } /** - * Forces this metric to split into a partial bucket right now. If we're past a full bucket, we - * first call the standard flushing code to flush up to the latest full bucket. Then we call - * the flush again when the end timestamp is forced to be now, and then after flushing, update - * the start timestamp to be now. + * Force a partial bucket split on app upgrade */ - virtual void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid, - const int64_t version) { + virtual void notifyAppUpgrade(const int64_t& eventTimeNs) { std::lock_guard<std::mutex> lock(mMutex); - - if (eventTimeNs > getCurrentBucketEndTimeNs()) { - // Flush full buckets on the normal path up to the latest bucket boundary. - flushIfNeededLocked(eventTimeNs); - } - // Now flush a partial bucket. - flushCurrentBucketLocked(eventTimeNs, eventTimeNs); - // Don't update the current bucket number so that the anomaly tracker knows this bucket - // is a partial bucket and can merge it with the previous bucket. + flushLocked(eventTimeNs); }; - void notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, const int uid) { + void notifyAppRemoved(const int64_t& eventTimeNs) { // Force buckets to split on removal also. - notifyAppUpgrade(eventTimeNs, apk, uid, 0); + notifyAppUpgrade(eventTimeNs); }; + /** + * Force a partial bucket split on boot complete. + */ + virtual void onStatsdInitCompleted(const int64_t& eventTimeNs) { + std::lock_guard<std::mutex> lock(mMutex); + flushLocked(eventTimeNs); + } // Consume the parsed stats log entry that already matched the "what" of the metric. void onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event) { std::lock_guard<std::mutex> lock(mMutex); @@ -292,8 +287,7 @@ public: // End: getters/setters protected: /** - * Flushes the current bucket if the eventTime is after the current bucket's end time. This will - also flush the current partial bucket in memory. + * Flushes the current bucket if the eventTime is after the current bucket's end time. */ virtual void flushIfNeededLocked(const int64_t& eventTime){}; diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index d832ed86580d..e03de0b991cd 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -189,7 +189,7 @@ MetricsManager::~MetricsManager() { StateManager::getInstance().unregisterListener(atomId, it); } } - mPullerManager->UnregisterPullUidProvider(mConfigKey); + mPullerManager->UnregisterPullUidProvider(mConfigKey, this); VLOG("~MetricsManager()"); } @@ -231,8 +231,8 @@ bool MetricsManager::isConfigValid() const { void MetricsManager::notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid, const int64_t version) { // Inform all metric producers. - for (auto it : mAllMetricProducers) { - it->notifyAppUpgrade(eventTimeNs, apk, uid, version); + for (const auto& it : mAllMetricProducers) { + it->notifyAppUpgrade(eventTimeNs); } // check if we care this package if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) != mAllowedPkg.end()) { @@ -252,8 +252,8 @@ void MetricsManager::notifyAppUpgrade(const int64_t& eventTimeNs, const string& void MetricsManager::notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, const int uid) { // Inform all metric producers. - for (auto it : mAllMetricProducers) { - it->notifyAppRemoved(eventTimeNs, apk, uid); + for (const auto& it : mAllMetricProducers) { + it->notifyAppRemoved(eventTimeNs); } // check if we care this package if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) != mAllowedPkg.end()) { @@ -282,6 +282,13 @@ void MetricsManager::onUidMapReceived(const int64_t& eventTimeNs) { initLogSourceWhiteList(); } +void MetricsManager::onStatsdInitCompleted(const int64_t& eventTimeNs) { + // Inform all metric producers. + for (const auto& it : mAllMetricProducers) { + it->onStatsdInitCompleted(eventTimeNs); + } +} + void MetricsManager::init() { for (const auto& producer : mAllMetricProducers) { producer->prepareFirstBucket(); diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index 1fd6572cc760..ef03d2064ab0 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -70,6 +70,8 @@ public: void onUidMapReceived(const int64_t& eventTimeNs); + void onStatsdInitCompleted(const int64_t& elapsedTimeNs); + void init(); vector<int32_t> getPullAtomUids(int32_t atomId) override; diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h index e9273dc54424..c8dc8cc290c4 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.h +++ b/cmds/statsd/src/metrics/ValueMetricProducer.h @@ -69,8 +69,7 @@ public: bool pullSuccess, int64_t originalPullTimeNs) override; // ValueMetric needs special logic if it's a pulled atom. - void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid, - const int64_t version) override { + void notifyAppUpgrade(const int64_t& eventTimeNs) override { std::lock_guard<std::mutex> lock(mMutex); if (!mSplitBucketForAppUpgrade) { return; @@ -81,6 +80,15 @@ public: flushCurrentBucketLocked(eventTimeNs, eventTimeNs); }; + // ValueMetric needs special logic if it's a pulled atom. + void onStatsdInitCompleted(const int64_t& eventTimeNs) override { + std::lock_guard<std::mutex> lock(mMutex); + if (mIsPulled && mCondition) { + pullAndMatchEventsLocked(eventTimeNs); + } + flushCurrentBucketLocked(eventTimeNs, eventTimeNs); + }; + void onStateChanged(int64_t eventTimeNs, int32_t atomId, const HashableDimensionKey& primaryKey, int oldState, int newState) override; @@ -256,7 +264,6 @@ private: FRIEND_TEST(ValueMetricProducerTest, TestAnomalyDetection); FRIEND_TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange); - FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundariesOnAppUpgrade); FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundariesOnConditionChange); FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition); FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition); @@ -269,10 +276,8 @@ private: FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled); FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition); FRIEND_TEST(ValueMetricProducerTest, TestFirstBucket); - FRIEND_TEST(ValueMetricProducerTest, TestFullBucketResetWhenLastBucketInvalid); FRIEND_TEST(ValueMetricProducerTest, TestLateOnDataPulledWithDiff); FRIEND_TEST(ValueMetricProducerTest, TestLateOnDataPulledWithoutDiff); - FRIEND_TEST(ValueMetricProducerTest, TestPartialBucketCreated); FRIEND_TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries); FRIEND_TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryFalse); FRIEND_TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryTrue); @@ -283,15 +288,12 @@ private: FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset); FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset); FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering); - FRIEND_TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade); - FRIEND_TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse); FRIEND_TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled); FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateAvg); FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateMax); FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateMin); FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateSum); FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithCondition); - FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade); FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition); FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullDelayExceeded); FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange); @@ -313,6 +315,14 @@ private: FRIEND_TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenGuardRailHit); FRIEND_TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenAccumulateEventWrongBucket); + + FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestBucketBoundariesOnPartialBucket); + FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestFullBucketResetWhenLastBucketInvalid); + FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestPartialBucketCreated); + FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestPushedEvents); + FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestPulledValue); + FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestPulledValueWhileConditionFalse); + friend class ValueMetricProducerTestHelper; }; diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp index 88616dde61b7..3ab44f4a06af 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp @@ -21,7 +21,6 @@ #include <inttypes.h> -#include "atoms_info.h" #include "FieldValue.h" #include "MetricProducer.h" #include "condition/CombinationConditionTracker.h" diff --git a/cmds/statsd/src/state/StateTracker.cpp b/cmds/statsd/src/state/StateTracker.cpp index b7f314a819df..b63713b64c5d 100644 --- a/cmds/statsd/src/state/StateTracker.cpp +++ b/cmds/statsd/src/state/StateTracker.cpp @@ -51,7 +51,7 @@ void StateTracker::onLogEvent(const LogEvent& event) { return; } - const int32_t resetState = stateValue.mAnnotations.getResetState(); + const int32_t resetState = event.getResetState(); if (resetState != -1) { VLOG("StateTracker new reset state: %d", resetState); handleReset(eventTimeNs, resetState); diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto index f4247eca6e23..868247bc9d64 100644 --- a/cmds/statsd/src/stats_log.proto +++ b/cmds/statsd/src/stats_log.proto @@ -425,6 +425,7 @@ message StatsdStatsReport { message AtomStats { optional int32 tag = 1; optional int32 count = 2; + optional int32 error_count = 3; } repeated AtomStats atom_stats = 7; @@ -460,6 +461,7 @@ message StatsdStatsReport { optional int64 empty_data = 15; optional int64 registered_count = 16; optional int64 unregistered_count = 17; + optional int32 atom_error_count = 18; } repeated PulledAtomStats pulled_atom_stats = 10; diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp index 563531351fe0..2acffee0f443 100644 --- a/cmds/statsd/src/stats_log_util.cpp +++ b/cmds/statsd/src/stats_log_util.cpp @@ -24,7 +24,6 @@ #include "statscompanion_util.h" -using android::util::AtomsInfo; using android::util::FIELD_COUNT_REPEATED; using android::util::FIELD_TYPE_BOOL; using android::util::FIELD_TYPE_FIXED64; @@ -80,6 +79,8 @@ const int FIELD_ID_STATS_COMPANION_BINDER_TRANSACTION_FAILED = 14; const int FIELD_ID_EMPTY_DATA = 15; const int FIELD_ID_PULL_REGISTERED_COUNT = 16; const int FIELD_ID_PULL_UNREGISTERED_COUNT = 17; +const int FIELD_ID_ATOM_ERROR_COUNT = 18; + // for AtomMetricStats proto const int FIELD_ID_ATOM_METRIC_STATS = 17; const int FIELD_ID_METRIC_ID = 1; @@ -492,6 +493,7 @@ void writePullerStatsToStream(const std::pair<int, StatsdStats::PulledAtomStats> (long long) pair.second.registeredCount); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_UNREGISTERED_COUNT, (long long) pair.second.unregisteredCount); + protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_ERROR_COUNT, pair.second.atomErrorCount); protoOutput->end(token); } diff --git a/cmds/statsd/src/utils/NamedLatch.cpp b/cmds/statsd/src/utils/MultiConditionTrigger.cpp index 6e77977857cc..43a69337f368 100644 --- a/cmds/statsd/src/utils/NamedLatch.cpp +++ b/cmds/statsd/src/utils/MultiConditionTrigger.cpp @@ -15,7 +15,9 @@ */ #define DEBUG false // STOPSHIP if true -#include "NamedLatch.h" +#include "MultiConditionTrigger.h" + +#include <thread> using namespace std; @@ -23,26 +25,33 @@ namespace android { namespace os { namespace statsd { -NamedLatch::NamedLatch(const set<string>& eventNames) : mRemainingEventNames(eventNames) { +MultiConditionTrigger::MultiConditionTrigger(const set<string>& conditionNames, + function<void()> trigger) + : mRemainingConditionNames(conditionNames), + mTrigger(trigger), + mCompleted(mRemainingConditionNames.empty()) { + if (mCompleted) { + thread executorThread([this] { mTrigger(); }); + executorThread.detach(); + } } -void NamedLatch::countDown(const string& eventName) { - bool notify = false; +void MultiConditionTrigger::markComplete(const string& conditionName) { + bool doTrigger = false; { lock_guard<mutex> lg(mMutex); - mRemainingEventNames.erase(eventName); - notify = mRemainingEventNames.empty(); + if (mCompleted) { + return; + } + mRemainingConditionNames.erase(conditionName); + mCompleted = mRemainingConditionNames.empty(); + doTrigger = mCompleted; } - if (notify) { - mConditionVariable.notify_all(); + if (doTrigger) { + std::thread executorThread([this] { mTrigger(); }); + executorThread.detach(); } } - -void NamedLatch::wait() const { - unique_lock<mutex> unique_lk(mMutex); - mConditionVariable.wait(unique_lk, [this] { return mRemainingEventNames.empty(); }); -} - } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/utils/MultiConditionTrigger.h b/cmds/statsd/src/utils/MultiConditionTrigger.h new file mode 100644 index 000000000000..51f6029915be --- /dev/null +++ b/cmds/statsd/src/utils/MultiConditionTrigger.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <mutex> +#include <set> + +namespace android { +namespace os { +namespace statsd { + +/** + * This class provides a utility to wait for a set of named conditions to occur. + * + * It will execute the trigger runnable in a detached thread once all conditions have been marked + * true. + */ +class MultiConditionTrigger { +public: + explicit MultiConditionTrigger(const std::set<std::string>& conditionNames, + std::function<void()> trigger); + + MultiConditionTrigger(const MultiConditionTrigger&) = delete; + MultiConditionTrigger& operator=(const MultiConditionTrigger&) = delete; + + // Mark a specific condition as true. If this condition has called markComplete already or if + // the event was not specified in the constructor, the function is a no-op. + void markComplete(const std::string& eventName); + +private: + mutable std::mutex mMutex; + std::set<std::string> mRemainingConditionNames; + std::function<void()> mTrigger; + bool mCompleted; + + FRIEND_TEST(MultiConditionTriggerTest, TestCountDownCalledBySameEventName); +}; +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/utils/NamedLatch.h b/cmds/statsd/src/utils/NamedLatch.h deleted file mode 100644 index 70238370f647..000000000000 --- a/cmds/statsd/src/utils/NamedLatch.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT 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 <condition_variable> -#include <mutex> -#include <set> - -namespace android { -namespace os { -namespace statsd { - -/** - * This class provides a threading primitive similar to a latch. - * The primary difference is that it waits for named events to occur instead of waiting for - * N threads to reach a certain point. - * - * It uses a condition variable under the hood. - */ -class NamedLatch { -public: - explicit NamedLatch(const std::set<std::string>& eventNames); - - NamedLatch(const NamedLatch&) = delete; - NamedLatch& operator=(const NamedLatch&) = delete; - - // Mark a specific event as completed. If this event has called countDown already or if the - // event was not specified in the constructor, the function is a no-op. - void countDown(const std::string& eventName); - - // Blocks the calling thread until all events in eventNames have called countDown. - void wait() const; - -private: - mutable std::mutex mMutex; - mutable std::condition_variable mConditionVariable; - std::set<std::string> mRemainingEventNames; - - FRIEND_TEST(NamedLatchTest, TestCountDownCalledBySameEventName); -}; -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp index bb4578d9b701..e52e2d024e94 100644 --- a/cmds/statsd/tests/LogEvent_test.cpp +++ b/cmds/statsd/tests/LogEvent_test.cpp @@ -352,7 +352,7 @@ TEST(LogEventTest, TestResetStateAnnotation) { const vector<FieldValue>& values = event.getValues(); EXPECT_EQ(values.size(), 1); - EXPECT_EQ(values[0].mAnnotations.getResetState(), resetState); + EXPECT_EQ(event.getResetState(), resetState); } } // namespace statsd diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp index 1075fe4f9ce1..a44541dddf53 100644 --- a/cmds/statsd/tests/MetricsManager_test.cpp +++ b/cmds/statsd/tests/MetricsManager_test.cpp @@ -528,7 +528,7 @@ TEST(MetricsManagerTest, TestLogSources) { })); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); EXPECT_CALL(*pullerManager, RegisterPullUidProvider(kConfigKey, _)).Times(1); - EXPECT_CALL(*pullerManager, UnregisterPullUidProvider(kConfigKey)).Times(1); + EXPECT_CALL(*pullerManager, UnregisterPullUidProvider(kConfigKey, _)).Times(1); sp<AlarmMonitor> anomalyAlarmMonitor; sp<AlarmMonitor> periodicAlarmMonitor; diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp index d29394b1c5a6..b809286da5f4 100644 --- a/cmds/statsd/tests/StatsLogProcessor_test.cpp +++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp @@ -301,6 +301,29 @@ TEST(StatsLogProcessorTest, TestOnDumpReportEraseData) { EXPECT_TRUE(noData); } +TEST(StatsLogProcessorTest, TestPullUidProviderSetOnConfigUpdate) { + // Setup simple config key corresponding to empty config. + sp<UidMap> m = new UidMap(); + sp<StatsPullerManager> pullerManager = new StatsPullerManager(); + sp<AlarmMonitor> anomalyAlarmMonitor; + sp<AlarmMonitor> subscriberAlarmMonitor; + StatsLogProcessor p( + m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0, + [](const ConfigKey& key) { return true; }, + [](const int&, const vector<int64_t>&) { return true; }); + ConfigKey key(3, 4); + StatsdConfig config = MakeConfig(false); + p.OnConfigUpdated(0, key, config); + EXPECT_NE(pullerManager->mPullUidProviders.find(key), pullerManager->mPullUidProviders.end()); + + config.add_default_pull_packages("AID_STATSD"); + p.OnConfigUpdated(5, key, config); + EXPECT_NE(pullerManager->mPullUidProviders.find(key), pullerManager->mPullUidProviders.end()); + + p.OnConfigRemoved(key); + EXPECT_EQ(pullerManager->mPullUidProviders.find(key), pullerManager->mPullUidProviders.end()); +} + TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) { int uid = 1111; diff --git a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp index b173ee0334a7..911762339b70 100644 --- a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp @@ -89,6 +89,7 @@ StatsdConfig MakeValueMetricConfig(int64_t minTime) { valueMetric->set_bucket(FIVE_MINUTES); valueMetric->set_min_bucket_size_nanos(minTime); valueMetric->set_use_absolute_value_on_reset(true); + valueMetric->set_skip_zero_diff_output(false); return config; } @@ -217,6 +218,35 @@ TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval) { EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count()); } +TEST(PartialBucketE2eTest, TestCountMetricSplitOnBoot) { + shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); + SendConfig(service, MakeConfig()); + int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are + // initialized with. + + // Goes into the first bucket + service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + NS_PER_SEC, 100).get()); + int64_t bootCompleteTimeNs = start + 2 * NS_PER_SEC; + service->mProcessor->onStatsdInitCompleted(bootCompleteTimeNs); + // Goes into the second bucket. + service->mProcessor->OnLogEvent(CreateAppCrashEvent(start + 3 * NS_PER_SEC, 100).get()); + + ConfigMetricsReport report = GetReports(service->mProcessor, start + 4 * NS_PER_SEC); + backfillStartEndTimestamp(&report); + + ASSERT_EQ(1, report.metrics_size()); + ASSERT_EQ(1, report.metrics(0).count_metrics().data_size()); + ASSERT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info_size()); + EXPECT_TRUE(report.metrics(0) + .count_metrics() + .data(0) + .bucket_info(0) + .has_start_bucket_elapsed_nanos()); + EXPECT_EQ(MillisToNano(NanoToMillis(bootCompleteTimeNs)), + report.metrics(0).count_metrics().data(0).bucket_info(0).end_bucket_elapsed_nanos()); + EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count()); +} + TEST(PartialBucketE2eTest, TestValueMetricWithoutMinPartialBucket) { shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); service->mPullerManager->RegisterPullAtomCallback( @@ -229,13 +259,22 @@ TEST(PartialBucketE2eTest, TestValueMetricWithoutMinPartialBucket) { // initialized with. service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start); - service->mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2, - String16("v2"), String16("")); + int64_t appUpgradeTimeNs = 5 * 60 * NS_PER_SEC + start + 2 * NS_PER_SEC; + service->mUidMap->updateApp(appUpgradeTimeNs, String16(kApp1.c_str()), 1, 2, String16("v2"), + String16("")); ConfigMetricsReport report = - GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100, true); + GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC); + backfillStartEndTimestamp(&report); + EXPECT_EQ(1, report.metrics_size()); EXPECT_EQ(0, report.metrics(0).value_metrics().skipped_size()); + + // The fake subsystem state sleep puller returns two atoms. + ASSERT_EQ(2, report.metrics(0).value_metrics().data_size()); + ASSERT_EQ(2, report.metrics(0).value_metrics().data(0).bucket_info_size()); + EXPECT_EQ(MillisToNano(NanoToMillis(appUpgradeTimeNs)), + report.metrics(0).value_metrics().data(0).bucket_info(1).end_bucket_elapsed_nanos()); } TEST(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket) { @@ -249,13 +288,13 @@ TEST(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket) { int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are // initialized with. - const int64_t endSkipped = 5 * 60 * NS_PER_SEC + start + 2; + const int64_t endSkipped = 5 * 60 * NS_PER_SEC + start + 2 * NS_PER_SEC; service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start); service->mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2, String16("v2"), String16("")); ConfigMetricsReport report = - GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC, true); + GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC); backfillStartEndTimestamp(&report); ASSERT_EQ(1, report.metrics_size()); @@ -264,10 +303,49 @@ TEST(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket) { // Can't test the start time since it will be based on the actual time when the pulling occurs. EXPECT_EQ(MillisToNano(NanoToMillis(endSkipped)), report.metrics(0).value_metrics().skipped(0).end_bucket_elapsed_nanos()); + + ASSERT_EQ(2, report.metrics(0).value_metrics().data_size()); + EXPECT_EQ(1, report.metrics(0).value_metrics().data(0).bucket_info_size()); +} + +TEST(PartialBucketE2eTest, TestValueMetricOnBootWithoutMinPartialBucket) { + shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); + // Initial pull will fail since puller is not registered. + SendConfig(service, MakeValueMetricConfig(0)); + int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are + // initialized with. + + service->mPullerManager->RegisterPullAtomCallback( + /*uid=*/0, util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {}, + SharedRefBase::make<FakeSubsystemSleepCallback>()); + + int64_t bootCompleteTimeNs = start + NS_PER_SEC; + service->mProcessor->onStatsdInitCompleted(bootCompleteTimeNs); + + service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start); + + ConfigMetricsReport report = GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100); + backfillStartEndTimestamp(&report); + + // First bucket is dropped due to the initial pull failing + ASSERT_EQ(1, report.metrics_size()); + EXPECT_EQ(1, report.metrics(0).value_metrics().skipped_size()); + EXPECT_EQ(MillisToNano(NanoToMillis(bootCompleteTimeNs)), + report.metrics(0).value_metrics().skipped(0).end_bucket_elapsed_nanos()); + + // The fake subsystem state sleep puller returns two atoms. + ASSERT_EQ(2, report.metrics(0).value_metrics().data_size()); + ASSERT_EQ(1, report.metrics(0).value_metrics().data(0).bucket_info_size()); + EXPECT_EQ( + MillisToNano(NanoToMillis(bootCompleteTimeNs)), + report.metrics(0).value_metrics().data(0).bucket_info(0).start_bucket_elapsed_nanos()); } TEST(PartialBucketE2eTest, TestGaugeMetricWithoutMinPartialBucket) { shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); + service->mPullerManager->RegisterPullAtomCallback( + /*uid=*/0, util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {}, + SharedRefBase::make<FakeSubsystemSleepCallback>()); // Partial buckets don't occur when app is first installed. service->mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16("")); SendConfig(service, MakeGaugeMetricConfig(0)); @@ -278,16 +356,22 @@ TEST(PartialBucketE2eTest, TestGaugeMetricWithoutMinPartialBucket) { service->mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"), String16("")); - ConfigMetricsReport report = - GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100, true); - EXPECT_EQ(1, report.metrics_size()); + ConfigMetricsReport report = GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100); + backfillStartEndTimestamp(&report); + ASSERT_EQ(1, report.metrics_size()); EXPECT_EQ(0, report.metrics(0).gauge_metrics().skipped_size()); + // The fake subsystem state sleep puller returns two atoms. + ASSERT_EQ(2, report.metrics(0).gauge_metrics().data_size()); + EXPECT_EQ(2, report.metrics(0).gauge_metrics().data(0).bucket_info_size()); } TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket) { shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); // Partial buckets don't occur when app is first installed. service->mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16("")); + service->mPullerManager->RegisterPullAtomCallback( + /*uid=*/0, util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {}, + SharedRefBase::make<FakeSubsystemSleepCallback>()); SendConfig(service, MakeGaugeMetricConfig(60 * NS_PER_SEC /* One minute */)); int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are // initialized with. @@ -298,7 +382,7 @@ TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket) { String16("")); ConfigMetricsReport report = - GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC, true); + GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC); backfillStartEndTimestamp(&report); ASSERT_EQ(1, report.metrics_size()); ASSERT_EQ(1, report.metrics(0).gauge_metrics().skipped_size()); @@ -306,6 +390,38 @@ TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket) { EXPECT_TRUE(report.metrics(0).gauge_metrics().skipped(0).has_start_bucket_elapsed_nanos()); EXPECT_EQ(MillisToNano(NanoToMillis(endSkipped)), report.metrics(0).gauge_metrics().skipped(0).end_bucket_elapsed_nanos()); + ASSERT_EQ(2, report.metrics(0).gauge_metrics().data_size()); + EXPECT_EQ(1, report.metrics(0).gauge_metrics().data(0).bucket_info_size()); +} + +TEST(PartialBucketE2eTest, TestGaugeMetricOnBootWithoutMinPartialBucket) { + shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr); + // Initial pull will fail since puller hasn't been registered. + SendConfig(service, MakeGaugeMetricConfig(0)); + int64_t start = getElapsedRealtimeNs(); // This is the start-time the metrics producers are + // initialized with. + + service->mPullerManager->RegisterPullAtomCallback( + /*uid=*/0, util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {}, + SharedRefBase::make<FakeSubsystemSleepCallback>()); + + int64_t bootCompleteTimeNs = start + NS_PER_SEC; + service->mProcessor->onStatsdInitCompleted(bootCompleteTimeNs); + + service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start); + + ConfigMetricsReport report = GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100); + backfillStartEndTimestamp(&report); + + ASSERT_EQ(1, report.metrics_size()); + EXPECT_EQ(0, report.metrics(0).gauge_metrics().skipped_size()); + // The fake subsystem state sleep puller returns two atoms. + ASSERT_EQ(2, report.metrics(0).gauge_metrics().data_size()); + // No data in the first bucket, so nothing is reported + ASSERT_EQ(1, report.metrics(0).gauge_metrics().data(0).bucket_info_size()); + EXPECT_EQ( + MillisToNano(NanoToMillis(bootCompleteTimeNs)), + report.metrics(0).gauge_metrics().data(0).bucket_info(0).start_bucket_elapsed_nanos()); } #else diff --git a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp index 129fafa75b41..cdde603f4c0b 100644 --- a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp +++ b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp @@ -486,6 +486,41 @@ TEST(StatsdStatsTest, TestActivationBroadcastGuardrailHit) { EXPECT_TRUE(uid2Good); } +TEST(StatsdStatsTest, TestAtomErrorStats) { + StatsdStats stats; + + int pushAtomTag = 100; + int pullAtomTag = 1000; + int numErrors = 10; + + for (int i = 0; i < numErrors; i++) { + // We must call noteAtomLogged as well because only those pushed atoms + // that have been logged will have stats printed about them in the + // proto. + stats.noteAtomLogged(pushAtomTag, /*timeSec=*/0); + stats.noteAtomError(pushAtomTag, /*pull=*/false); + + stats.noteAtomError(pullAtomTag, /*pull=*/true); + } + + vector<uint8_t> output; + stats.dumpStats(&output, false); + StatsdStatsReport report; + EXPECT_TRUE(report.ParseFromArray(&output[0], output.size())); + + // Check error count = numErrors for push atom + EXPECT_EQ(1, report.atom_stats_size()); + const auto& pushedAtomStats = report.atom_stats(0); + EXPECT_EQ(pushAtomTag, pushedAtomStats.tag()); + EXPECT_EQ(numErrors, pushedAtomStats.error_count()); + + // Check error count = numErrors for pull atom + EXPECT_EQ(1, report.pulled_atom_stats_size()); + const auto& pulledAtomStats = report.pulled_atom_stats(0); + EXPECT_EQ(pullAtomTag, pulledAtomStats.atom_id()); + EXPECT_EQ(numErrors, pulledAtomStats.atom_error_count()); +} + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp index 65f8de69711d..8131725cd148 100644 --- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp @@ -38,9 +38,9 @@ namespace android { namespace os { namespace statsd { -const ConfigKey kConfigKey(0, 12345); namespace { +const ConfigKey kConfigKey(0, 12345); void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) { AStatsEvent* statsEvent = AStatsEvent_obtain(); @@ -61,6 +61,13 @@ void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId, string ui } // namespace +// Setup for parameterized tests. +class CountMetricProducerTest_PartialBucket : public TestWithParam<BucketSplitEvent> {}; + +INSTANTIATE_TEST_SUITE_P(CountMetricProducerTest_PartialBucket, + CountMetricProducerTest_PartialBucket, + testing::Values(APP_UPGRADE, BOOT_COMPLETE)); + TEST(CountMetricProducerTest, TestFirstBucket) { CountMetric metric; metric.set_id(1); @@ -237,11 +244,11 @@ TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) { EXPECT_EQ(1LL, bucketInfo.mCount); } -TEST(CountMetricProducerTest, TestEventWithAppUpgrade) { +TEST_P(CountMetricProducerTest_PartialBucket, TestSplitInCurrentBucket) { sp<AlarmMonitor> alarmMonitor; int64_t bucketStartTimeNs = 10000000000; int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; - int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; + int64_t eventTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; int tagId = 1; int conditionTagId = 2; @@ -260,22 +267,30 @@ TEST(CountMetricProducerTest, TestEventWithAppUpgrade) { sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor); EXPECT_TRUE(anomalyTracker != nullptr); - // Bucket is flushed yet. + // Bucket is not flushed yet. LogEvent event1(/*uid=*/0, /*pid=*/0); makeLogEvent(&event1, bucketStartTimeNs + 1, tagId, /*uid=*/"111"); countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); EXPECT_EQ(0UL, countProducer.mPastBuckets.size()); EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); - // App upgrade forces bucket flush. + // App upgrade or boot complete forces bucket flush. // Check that there's a past bucket and the bucket end is not adjusted. - countProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + switch (GetParam()) { + case APP_UPGRADE: + countProducer.notifyAppUpgrade(eventTimeNs); + break; + case BOOT_COMPLETE: + countProducer.onStatsdInitCompleted(eventTimeNs); + break; + } EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ((long long)bucketStartTimeNs, + EXPECT_EQ(bucketStartTimeNs, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs); - EXPECT_EQ((long long)eventUpgradeTimeNs, + EXPECT_EQ(eventTimeNs, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs); - EXPECT_EQ(eventUpgradeTimeNs, countProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(0, countProducer.getCurrentBucketNum()); + EXPECT_EQ(eventTimeNs, countProducer.mCurrentBucketStartTimeNs); // Anomaly tracker only contains full buckets. EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); @@ -285,7 +300,8 @@ TEST(CountMetricProducerTest, TestEventWithAppUpgrade) { makeLogEvent(&event2, bucketStartTimeNs + 59 * NS_PER_SEC + 10, tagId, /*uid=*/"222"); countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ(eventUpgradeTimeNs, countProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(eventTimeNs, countProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(0, countProducer.getCurrentBucketNum()); EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); // Third event in following bucket. @@ -294,13 +310,14 @@ TEST(CountMetricProducerTest, TestEventWithAppUpgrade) { countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); EXPECT_EQ(lastEndTimeNs, countProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(1, countProducer.getCurrentBucketNum()); EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); } -TEST(CountMetricProducerTest, TestEventWithAppUpgradeInNextBucket) { +TEST_P(CountMetricProducerTest_PartialBucket, TestSplitInNextBucket) { int64_t bucketStartTimeNs = 10000000000; int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; - int64_t eventUpgradeTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC; + int64_t eventTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC; int tagId = 1; int conditionTagId = 2; @@ -319,15 +336,23 @@ TEST(CountMetricProducerTest, TestEventWithAppUpgradeInNextBucket) { countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); EXPECT_EQ(0UL, countProducer.mPastBuckets.size()); - // App upgrade forces bucket flush. - // Check that there's a past bucket and the bucket end is not adjusted. - countProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + // App upgrade or boot complete forces bucket flush. + // Check that there's a past bucket and the bucket end is not adjusted since the upgrade + // occurred after the bucket end time. + switch (GetParam()) { + case APP_UPGRADE: + countProducer.notifyAppUpgrade(eventTimeNs); + break; + case BOOT_COMPLETE: + countProducer.onStatsdInitCompleted(eventTimeNs); + break; + } EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ((int64_t)bucketStartTimeNs, + EXPECT_EQ(bucketStartTimeNs, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs); EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs); - EXPECT_EQ(eventUpgradeTimeNs, countProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(eventTimeNs, countProducer.mCurrentBucketStartTimeNs); // Next event occurs in same bucket as partial bucket created. LogEvent event2(/*uid=*/0, /*pid=*/0); @@ -340,7 +365,7 @@ TEST(CountMetricProducerTest, TestEventWithAppUpgradeInNextBucket) { makeLogEvent(&event3, bucketStartTimeNs + 121 * NS_PER_SEC + 10, tagId, /*uid=*/"333"); countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ((int64_t)eventUpgradeTimeNs, + EXPECT_EQ((int64_t)eventTimeNs, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mBucketStartNs); EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mBucketEndNs); diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp index 30f815962160..8ef251952db7 100644 --- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp @@ -41,10 +41,10 @@ namespace android { namespace os { namespace statsd { -const ConfigKey kConfigKey(0, 12345); namespace { +const ConfigKey kConfigKey(0, 12345); void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) { AStatsEvent* statsEvent = AStatsEvent_obtain(); AStatsEvent_setAtomId(statsEvent, atomId); @@ -55,6 +55,13 @@ void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) { } // namespace +// Setup for parameterized tests. +class DurationMetricProducerTest_PartialBucket : public TestWithParam<BucketSplitEvent> {}; + +INSTANTIATE_TEST_SUITE_P(DurationMetricProducerTest_PartialBucket, + DurationMetricProducerTest_PartialBucket, + testing::Values(APP_UPGRADE, BOOT_COMPLETE)); + TEST(DurationMetricTrackerTest, TestFirstBucket) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); DurationMetric metric; @@ -205,7 +212,7 @@ TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState) { EXPECT_EQ(1LL, buckets2[0].mDuration); } -TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade) { +TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDuration) { /** * The duration starts from the first bucket, through the two partial buckets (10-70sec), * another bucket, and ends at the beginning of the next full bucket. @@ -217,15 +224,7 @@ TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade) { */ int64_t bucketStartTimeNs = 10000000000; int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; - int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; - int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC; - int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC; - int tagId = 1; - LogEvent event1(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event1, startTimeNs, tagId); - LogEvent event2(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event2, endTimeNs, tagId); DurationMetric metric; metric.set_id(1); @@ -238,32 +237,47 @@ TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade) { 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); + int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC; + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event1, startTimeNs, tagId); durationProducer.onMatchedLogEvent(1 /* start index*/, event1); EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); - durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; + switch (GetParam()) { + case APP_UPGRADE: + durationProducer.notifyAppUpgrade(partialBucketSplitTimeNs); + break; + case BOOT_COMPLETE: + durationProducer.onStatsdInitCompleted(partialBucketSplitTimeNs); + break; + } EXPECT_EQ(1UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); std::vector<DurationBucket> buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs); - EXPECT_EQ(eventUpgradeTimeNs, buckets[0].mBucketEndNs); - EXPECT_EQ(eventUpgradeTimeNs - startTimeNs, buckets[0].mDuration); - EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(partialBucketSplitTimeNs, buckets[0].mBucketEndNs); + EXPECT_EQ(partialBucketSplitTimeNs - startTimeNs, buckets[0].mDuration); + EXPECT_EQ(partialBucketSplitTimeNs, durationProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(0, durationProducer.getCurrentBucketNum()); // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. + int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC; + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event2, endTimeNs, tagId); durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; EXPECT_EQ(3UL, buckets.size()); - EXPECT_EQ(eventUpgradeTimeNs, buckets[1].mBucketStartNs); + EXPECT_EQ(partialBucketSplitTimeNs, buckets[1].mBucketStartNs); EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketEndNs); - EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - eventUpgradeTimeNs, buckets[1].mDuration); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - partialBucketSplitTimeNs, buckets[1].mDuration); EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[2].mBucketStartNs); EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[2].mBucketEndNs); EXPECT_EQ(bucketSizeNs, buckets[2].mDuration); } -TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket) { +TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDurationWithSplitInFollowingBucket) { /** * Expected buckets (start at 11s, upgrade at 75s, end at 135s): * - [10,70]: 59 secs @@ -272,15 +286,7 @@ TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket) { */ int64_t bucketStartTimeNs = 10000000000; int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; - int64_t eventUpgradeTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC; - int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC; - int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC; - int tagId = 1; - LogEvent event1(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event1, startTimeNs, tagId); - LogEvent event2(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event2, endTimeNs, tagId); DurationMetric metric; metric.set_id(1); @@ -293,11 +299,22 @@ TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket) { 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); + int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC; + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event1, startTimeNs, tagId); durationProducer.onMatchedLogEvent(1 /* start index*/, event1); EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); - durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC; + switch (GetParam()) { + case APP_UPGRADE: + durationProducer.notifyAppUpgrade(partialBucketSplitTimeNs); + break; + case BOOT_COMPLETE: + durationProducer.onStatsdInitCompleted(partialBucketSplitTimeNs); + break; + } EXPECT_EQ(2UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); std::vector<DurationBucket> buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; @@ -305,32 +322,29 @@ TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket) { EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs); EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs, buckets[0].mDuration); EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketStartNs); - EXPECT_EQ(eventUpgradeTimeNs, buckets[1].mBucketEndNs); - EXPECT_EQ(eventUpgradeTimeNs - (bucketStartTimeNs + bucketSizeNs), buckets[1].mDuration); - EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(partialBucketSplitTimeNs, buckets[1].mBucketEndNs); + EXPECT_EQ(partialBucketSplitTimeNs - (bucketStartTimeNs + bucketSizeNs), buckets[1].mDuration); + EXPECT_EQ(partialBucketSplitTimeNs, durationProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(1, durationProducer.getCurrentBucketNum()); // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. + int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC; + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event2, endTimeNs, tagId); durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; EXPECT_EQ(3UL, buckets.size()); - EXPECT_EQ(eventUpgradeTimeNs, buckets[2].mBucketStartNs); + EXPECT_EQ(partialBucketSplitTimeNs, buckets[2].mBucketStartNs); EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[2].mBucketEndNs); - EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs - eventUpgradeTimeNs, buckets[2].mDuration); + EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs - partialBucketSplitTimeNs, + buckets[2].mDuration); } -TEST(DurationMetricTrackerTest, TestSumDurationAnomalyWithUpgrade) { +TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDurationAnomaly) { sp<AlarmMonitor> alarmMonitor; int64_t bucketStartTimeNs = 10000000000; int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; - int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; - int64_t startTimeNs = bucketStartTimeNs + 1; - int64_t endTimeNs = startTimeNs + 65 * NS_PER_SEC; - int tagId = 1; - LogEvent event1(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event1, startTimeNs, tagId); - LogEvent event2(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event2, endTimeNs, tagId); // Setup metric with alert. DurationMetric metric; @@ -351,27 +365,35 @@ TEST(DurationMetricTrackerTest, TestSumDurationAnomalyWithUpgrade) { sp<AnomalyTracker> anomalyTracker = durationProducer.addAnomalyTracker(alert, alarmMonitor); EXPECT_TRUE(anomalyTracker != nullptr); + int64_t startTimeNs = bucketStartTimeNs + 1; + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event1, startTimeNs, tagId); durationProducer.onMatchedLogEvent(1 /* start index*/, event1); - durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + + int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; + switch (GetParam()) { + case APP_UPGRADE: + durationProducer.notifyAppUpgrade(partialBucketSplitTimeNs); + break; + case BOOT_COMPLETE: + durationProducer.onStatsdInitCompleted(partialBucketSplitTimeNs); + break; + } // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. + int64_t endTimeNs = startTimeNs + 65 * NS_PER_SEC; + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event2, endTimeNs, tagId); durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); + EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); } -TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgrade) { +TEST_P(DurationMetricProducerTest_PartialBucket, TestMaxDuration) { int64_t bucketStartTimeNs = 10000000000; int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; - int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; - int64_t startTimeNs = bucketStartTimeNs + 1; - int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC; - int tagId = 1; - LogEvent event1(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event1, startTimeNs, tagId); - LogEvent event2(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event2, endTimeNs, tagId); DurationMetric metric; metric.set_id(1); @@ -385,15 +407,30 @@ TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgrade) { 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); + int64_t startTimeNs = bucketStartTimeNs + 1; + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event1, startTimeNs, tagId); durationProducer.onMatchedLogEvent(1 /* start index*/, event1); EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); - durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; + switch (GetParam()) { + case APP_UPGRADE: + durationProducer.notifyAppUpgrade(partialBucketSplitTimeNs); + break; + case BOOT_COMPLETE: + durationProducer.onStatsdInitCompleted(partialBucketSplitTimeNs); + break; + } EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(partialBucketSplitTimeNs, durationProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(0, durationProducer.getCurrentBucketNum()); // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. + int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC; + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event2, endTimeNs, tagId); durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); @@ -406,18 +443,10 @@ TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgrade) { EXPECT_EQ(endTimeNs - startTimeNs, buckets[0].mDuration); } -TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgradeInNextBucket) { +TEST_P(DurationMetricProducerTest_PartialBucket, TestMaxDurationWithSplitInNextBucket) { int64_t bucketStartTimeNs = 10000000000; int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; - int64_t eventUpgradeTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC; - int64_t startTimeNs = bucketStartTimeNs + 1; - int64_t endTimeNs = startTimeNs + 115 * NS_PER_SEC; - int tagId = 1; - LogEvent event1(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event1, startTimeNs, tagId); - LogEvent event2(/*uid=*/0, /*pid=*/0); - makeLogEvent(&event2, endTimeNs, tagId); DurationMetric metric; metric.set_id(1); @@ -431,24 +460,39 @@ TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgradeInNextBucket) { 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs); + int64_t startTimeNs = bucketStartTimeNs + 1; + LogEvent event1(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event1, startTimeNs, tagId); durationProducer.onMatchedLogEvent(1 /* start index*/, event1); EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); - durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC; + switch (GetParam()) { + case APP_UPGRADE: + durationProducer.notifyAppUpgrade(partialBucketSplitTimeNs); + break; + case BOOT_COMPLETE: + durationProducer.onStatsdInitCompleted(partialBucketSplitTimeNs); + break; + } EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(partialBucketSplitTimeNs, durationProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(1, durationProducer.getCurrentBucketNum()); // Stop occurs in the same partial bucket as created for the app upgrade. + int64_t endTimeNs = startTimeNs + 115 * NS_PER_SEC; + LogEvent event2(/*uid=*/0, /*pid=*/0); + makeLogEvent(&event2, endTimeNs, tagId); durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(partialBucketSplitTimeNs, durationProducer.mCurrentBucketStartTimeNs); durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); std::vector<DurationBucket> buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; EXPECT_EQ(1UL, buckets.size()); - EXPECT_EQ(eventUpgradeTimeNs, buckets[0].mBucketStartNs); + EXPECT_EQ(partialBucketSplitTimeNs, buckets[0].mBucketStartNs); EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[0].mBucketEndNs); EXPECT_EQ(endTimeNs - startTimeNs, buckets[0].mDuration); } diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp index 42d0d5d8c530..9d2ec88e2f9b 100644 --- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp @@ -42,6 +42,8 @@ namespace android { namespace os { namespace statsd { +namespace { + const ConfigKey kConfigKey(0, 12345); const int tagId = 1; const int64_t metricId = 123; @@ -52,9 +54,8 @@ const int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000L const int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs; const int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs; const int64_t bucket4StartTimeNs = bucketStartTimeNs + 3 * bucketSizeNs; -const int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; +const int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; -namespace { shared_ptr<LogEvent> makeLogEvent(int32_t atomId, int64_t timestampNs, int32_t value1, string str1, int32_t value2) { AStatsEvent* statsEvent = AStatsEvent_obtain(); @@ -71,6 +72,13 @@ shared_ptr<LogEvent> makeLogEvent(int32_t atomId, int64_t timestampNs, int32_t v } } // anonymous namespace +// Setup for parameterized tests. +class GaugeMetricProducerTest_PartialBucket : public TestWithParam<BucketSplitEvent> {}; + +INSTANTIATE_TEST_SUITE_P(GaugeMetricProducerTest_PartialBucket, + GaugeMetricProducerTest_PartialBucket, + testing::Values(APP_UPGRADE, BOOT_COMPLETE)); + /* * Tests that the first bucket works correctly */ @@ -194,7 +202,7 @@ TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition) { EXPECT_EQ(25L, it->mValue.int_value); } -TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade) { +TEST_P(GaugeMetricProducerTest_PartialBucket, TestPushedEvents) { sp<AlarmMonitor> alarmMonitor; GaugeMetric metric; metric.set_id(metricId); @@ -230,11 +238,22 @@ TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade) { gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); EXPECT_EQ(1UL, (*gaugeProducer.mCurrentSlicedBucket).count(DEFAULT_METRIC_DIMENSION_KEY)); - gaugeProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + switch (GetParam()) { + case APP_UPGRADE: + gaugeProducer.notifyAppUpgrade(partialBucketSplitTimeNs); + break; + case BOOT_COMPLETE: + gaugeProducer.onStatsdInitCompleted(partialBucketSplitTimeNs); + break; + } EXPECT_EQ(0UL, (*gaugeProducer.mCurrentSlicedBucket).count(DEFAULT_METRIC_DIMENSION_KEY)); EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ(bucketStartTimeNs, + gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs); + EXPECT_EQ(partialBucketSplitTimeNs, + gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs); EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum); - EXPECT_EQ(eventUpgradeTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(partialBucketSplitTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); // Partial buckets are not sent to anomaly tracker. EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); @@ -244,7 +263,11 @@ TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade) { gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum); EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ((int64_t)eventUpgradeTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(bucketStartTimeNs, + gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs); + EXPECT_EQ(partialBucketSplitTimeNs, + gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs); + EXPECT_EQ((int64_t)partialBucketSplitTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); // Partial buckets are not sent to anomaly tracker. EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); @@ -267,7 +290,7 @@ TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade) { EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); } -TEST(GaugeMetricProducerTest, TestPulledWithUpgrade) { +TEST_P(GaugeMetricProducerTest_PartialBucket, TestPulled) { GaugeMetric metric; metric.set_id(metricId); metric.set_bucket(ONE_MINUTE); @@ -293,7 +316,8 @@ TEST(GaugeMetricProducerTest, TestPulledWithUpgrade) { .WillOnce(Invoke( [](int tagId, const ConfigKey&, vector<std::shared_ptr<LogEvent>>* data, bool) { data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, eventUpgradeTimeNs, 2)); + data->push_back( + CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 2)); return true; })); @@ -311,10 +335,21 @@ TEST(GaugeMetricProducerTest, TestPulledWithUpgrade) { .mFields->begin() ->mValue.int_value); - gaugeProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + switch (GetParam()) { + case APP_UPGRADE: + gaugeProducer.notifyAppUpgrade(partialBucketSplitTimeNs); + break; + case BOOT_COMPLETE: + gaugeProducer.onStatsdInitCompleted(partialBucketSplitTimeNs); + break; + } EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ(bucketStartTimeNs, + gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs); + EXPECT_EQ(partialBucketSplitTimeNs, + gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs); EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum); - EXPECT_EQ((int64_t)eventUpgradeTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(partialBucketSplitTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); EXPECT_EQ(2, gaugeProducer.mCurrentSlicedBucket->begin() ->second.front() @@ -370,7 +405,7 @@ TEST(GaugeMetricProducerTest, TestPulledWithAppUpgradeDisabled) { .mFields->begin() ->mValue.int_value); - gaugeProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); + gaugeProducer.notifyAppUpgrade(partialBucketSplitTimeNs); EXPECT_EQ(0UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum); EXPECT_EQ(bucketStartTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp index 3b4d646f0f2f..f493cc4033ad 100644 --- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp @@ -41,6 +41,8 @@ namespace android { namespace os { namespace statsd { +namespace { + const ConfigKey kConfigKey(0, 12345); const int tagId = 1; const int64_t metricId = 123; @@ -58,10 +60,18 @@ double epsilon = 0.001; static void assertPastBucketValuesSingleKey( const std::unordered_map<MetricDimensionKey, std::vector<ValueBucket>>& mPastBuckets, const std::initializer_list<int>& expectedValuesList, - const std::initializer_list<int64_t>& expectedDurationNsList) { - std::vector<int> expectedValues(expectedValuesList); - std::vector<int64_t> expectedDurationNs(expectedDurationNsList); + const std::initializer_list<int64_t>& expectedDurationNsList, + const std::initializer_list<int64_t>& expectedStartTimeNsList, + const std::initializer_list<int64_t>& expectedEndTimeNsList) { + vector<int> expectedValues(expectedValuesList); + vector<int64_t> expectedDurationNs(expectedDurationNsList); + vector<int64_t> expectedStartTimeNs(expectedStartTimeNsList); + vector<int64_t> expectedEndTimeNs(expectedEndTimeNsList); + ASSERT_EQ(expectedValues.size(), expectedDurationNs.size()); + ASSERT_EQ(expectedValues.size(), expectedStartTimeNs.size()); + ASSERT_EQ(expectedValues.size(), expectedEndTimeNs.size()); + if (expectedValues.size() == 0) { ASSERT_EQ(0, mPastBuckets.size()); return; @@ -70,15 +80,21 @@ static void assertPastBucketValuesSingleKey( ASSERT_EQ(1, mPastBuckets.size()); ASSERT_EQ(expectedValues.size(), mPastBuckets.begin()->second.size()); - auto buckets = mPastBuckets.begin()->second; + const vector<ValueBucket>& buckets = mPastBuckets.begin()->second; for (int i = 0; i < expectedValues.size(); i++) { EXPECT_EQ(expectedValues[i], buckets[i].values[0].long_value) << "Values differ at index " << i; EXPECT_EQ(expectedDurationNs[i], buckets[i].mConditionTrueNs) << "Condition duration value differ at index " << i; + EXPECT_EQ(expectedStartTimeNs[i], buckets[i].mBucketStartNs) + << "Start time differs at index " << i; + EXPECT_EQ(expectedEndTimeNs[i], buckets[i].mBucketEndNs) + << "End time differs at index " << i; } } +} // anonymous namespace + class ValueMetricProducerTestHelper { public: static sp<ValueMetricProducer> createValueProducerNoConditions( @@ -191,6 +207,13 @@ public: } }; +// Setup for parameterized tests. +class ValueMetricProducerTest_PartialBucket : public TestWithParam<BucketSplitEvent> {}; + +INSTANTIATE_TEST_SUITE_P(ValueMetricProducerTest_PartialBucket, + ValueMetricProducerTest_PartialBucket, + testing::Values(APP_UPGRADE, BOOT_COMPLETE)); + /* * Tests that the first bucket works correctly */ @@ -325,9 +348,10 @@ TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) { EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[2].mConditionTrueNs); } -TEST(ValueMetricProducerTest, TestPartialBucketCreated) { +TEST_P(ValueMetricProducerTest_PartialBucket, TestPartialBucketCreated) { ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + int64_t partialBucketSplitTimeNs = bucket2StartTimeNs + 2; EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) // Initialize bucket. .WillOnce(Invoke([](int tagId, const ConfigKey&, @@ -337,10 +361,12 @@ TEST(ValueMetricProducerTest, TestPartialBucketCreated) { return true; })) // Partial bucket. - .WillOnce(Invoke([](int tagId, const ConfigKey&, - vector<std::shared_ptr<LogEvent>>* data, bool) { + .WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&, + vector<std::shared_ptr<LogEvent>>* data, + bool) { data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 10, 5)); + data->push_back( + CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs + 8, 5)); return true; })); @@ -354,19 +380,21 @@ TEST(ValueMetricProducerTest, TestPartialBucketCreated) { valueProducer->onDataPulled(allData, /** success */ true, bucket2StartTimeNs); // Partial buckets created in 2nd bucket. - valueProducer->notifyAppUpgrade(bucket2StartTimeNs + 2, "com.foo", 10000, 1); + switch (GetParam()) { + case APP_UPGRADE: + valueProducer->notifyAppUpgrade(partialBucketSplitTimeNs); + break; + case BOOT_COMPLETE: + valueProducer->onStatsdInitCompleted(partialBucketSplitTimeNs); + break; + } + EXPECT_EQ(partialBucketSplitTimeNs, valueProducer->mCurrentBucketStartTimeNs); + EXPECT_EQ(1, valueProducer->getCurrentBucketNum()); - // One full bucket and one partial bucket. - EXPECT_EQ(1UL, valueProducer->mPastBuckets.size()); - vector<ValueBucket> buckets = valueProducer->mPastBuckets.begin()->second; - EXPECT_EQ(2UL, buckets.size()); - // Full bucket (2 - 1) - EXPECT_EQ(1, buckets[0].values[0].long_value); - EXPECT_EQ(bucketSizeNs, buckets[0].mConditionTrueNs); - // Full bucket (5 - 3) - EXPECT_EQ(3, buckets[1].values[0].long_value); - // partial bucket [bucket2StartTimeNs, bucket2StartTimeNs + 2] - EXPECT_EQ(2, buckets[1].mConditionTrueNs); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {1, 3}, + {bucketSizeNs, partialBucketSplitTimeNs - bucket2StartTimeNs}, + {bucketStartTimeNs, bucket2StartTimeNs}, + {bucket2StartTimeNs, partialBucketSplitTimeNs}); } /* @@ -613,7 +641,8 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) { allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 110)); valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs - 8}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs - 8}, + {bucketStartTimeNs}, {bucket2StartTimeNs}); // has one slice EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); @@ -625,7 +654,8 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) { EXPECT_EQ(10, curInterval.value.long_value); valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs - 8}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs - 8}, + {bucketStartTimeNs}, {bucket2StartTimeNs}); // has one slice EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); @@ -636,10 +666,12 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) { EXPECT_EQ(false, curBaseInfo.hasBase); valueProducer->onConditionChanged(true, bucket3StartTimeNs + 1); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10, 20}, {bucketSizeNs - 8, 1}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10, 20}, {bucketSizeNs - 8, 1}, + {bucketStartTimeNs, bucket2StartTimeNs}, + {bucket2StartTimeNs, bucket3StartTimeNs}); } -TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade) { +TEST_P(ValueMetricProducerTest_PartialBucket, TestPushedEvents) { ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); UidMap uidMap; @@ -660,25 +692,46 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade) { valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - valueProducer.notifyAppUpgrade(bucketStartTimeNs + 150, "ANY.APP", 1, 1); - EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ(bucketStartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs); + int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 150; + switch (GetParam()) { + case APP_UPGRADE: + valueProducer.notifyAppUpgrade(partialBucketSplitTimeNs); + break; + case BOOT_COMPLETE: + valueProducer.onStatsdInitCompleted(partialBucketSplitTimeNs); + break; + } + assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10}, + {partialBucketSplitTimeNs - bucketStartTimeNs}, + {bucketStartTimeNs}, {partialBucketSplitTimeNs}); + EXPECT_EQ(partialBucketSplitTimeNs, valueProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(0, valueProducer.getCurrentBucketNum()); + // Event arrives after the bucket split. LogEvent event2(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 59 * NS_PER_SEC, 10); + CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 59 * NS_PER_SEC, 20); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); - EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ(bucketStartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs); + + assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10}, + {partialBucketSplitTimeNs - bucketStartTimeNs}, + {bucketStartTimeNs}, {partialBucketSplitTimeNs}); + EXPECT_EQ(partialBucketSplitTimeNs, valueProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(0, valueProducer.getCurrentBucketNum()); // Next value should create a new bucket. LogEvent event3(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event3, tagId, bucketStartTimeNs + 65 * NS_PER_SEC, 10); + CreateRepeatedValueLogEvent(&event3, tagId, bucket2StartTimeNs + 5 * NS_PER_SEC, 10); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); - EXPECT_EQ(2UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10, 20}, + {partialBucketSplitTimeNs - bucketStartTimeNs, + bucket2StartTimeNs - partialBucketSplitTimeNs}, + {bucketStartTimeNs, partialBucketSplitTimeNs}, + {partialBucketSplitTimeNs, bucket2StartTimeNs}); EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, valueProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(1, valueProducer.getCurrentBucketNum()); } -TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade) { +TEST_P(ValueMetricProducerTest_PartialBucket, TestPulledValue) { ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); UidMap uidMap; @@ -689,14 +742,16 @@ TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade) { atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + int64_t partialBucketSplitTimeNs = bucket2StartTimeNs + 150; EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return()); EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return()); EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) .WillOnce(Return(true)) - .WillOnce(Invoke([](int tagId, const ConfigKey&, - vector<std::shared_ptr<LogEvent>>* data, bool) { + .WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&, + vector<std::shared_ptr<LogEvent>>* data, + bool) { data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 149, 120)); + data->push_back(CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 120)); return true; })); ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex, @@ -711,20 +766,27 @@ TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade) { valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - valueProducer.notifyAppUpgrade(bucket2StartTimeNs + 150, "ANY.APP", 1, 1); - EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ(bucket2StartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs); - assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {20}, {150}); + switch (GetParam()) { + case APP_UPGRADE: + valueProducer.notifyAppUpgrade(partialBucketSplitTimeNs); + break; + case BOOT_COMPLETE: + valueProducer.onStatsdInitCompleted(partialBucketSplitTimeNs); + break; + } + EXPECT_EQ(partialBucketSplitTimeNs, valueProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(1, valueProducer.getCurrentBucketNum()); + assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {20}, {150}, {bucket2StartTimeNs}, + {partialBucketSplitTimeNs}); allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 150)); valueProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); - EXPECT_EQ(2UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); EXPECT_EQ(bucket3StartTimeNs, valueProducer.mCurrentBucketStartTimeNs); - EXPECT_EQ(20L, - valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].values[0].long_value); - assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {20, 30}, - {150, bucketSizeNs - 150}); + EXPECT_EQ(2, valueProducer.getCurrentBucketNum()); + assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {20, 30}, {150, bucketSizeNs - 150}, + {bucket2StartTimeNs, partialBucketSplitTimeNs}, + {partialBucketSplitTimeNs, bucket3StartTimeNs}); } TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled) { @@ -754,12 +816,12 @@ TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled) { valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - valueProducer.notifyAppUpgrade(bucket2StartTimeNs + 150, "ANY.APP", 1, 1); + valueProducer.notifyAppUpgrade(bucket2StartTimeNs + 150); EXPECT_EQ(0UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); EXPECT_EQ(bucket2StartTimeNs, valueProducer.mCurrentBucketStartTimeNs); } -TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse) { +TEST_P(ValueMetricProducerTest_PartialBucket, TestPulledValueWhileConditionFalse) { ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); @@ -784,14 +846,21 @@ TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse) { valueProducer->onConditionChanged(false, bucket2StartTimeNs - 100); EXPECT_FALSE(valueProducer->mCondition); - valueProducer->notifyAppUpgrade(bucket2StartTimeNs - 50, "ANY.APP", 1, 1); + int64_t partialBucketSplitTimeNs = bucket2StartTimeNs - 50; + switch (GetParam()) { + case APP_UPGRADE: + valueProducer->notifyAppUpgrade(partialBucketSplitTimeNs); + break; + case BOOT_COMPLETE: + valueProducer->onStatsdInitCompleted(partialBucketSplitTimeNs); + break; + } // Expect one full buckets already done and starting a partial bucket. - EXPECT_EQ(bucket2StartTimeNs - 50, valueProducer->mCurrentBucketStartTimeNs); - EXPECT_EQ(1UL, valueProducer->mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ(bucketStartTimeNs, - valueProducer->mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs); + EXPECT_EQ(partialBucketSplitTimeNs, valueProducer->mCurrentBucketStartTimeNs); + EXPECT_EQ(0, valueProducer->getCurrentBucketNum()); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, - {(bucket2StartTimeNs - 100) - (bucketStartTimeNs + 1)}); + {(bucket2StartTimeNs - 100) - (bucketStartTimeNs + 1)}, + {bucketStartTimeNs}, {partialBucketSplitTimeNs}); EXPECT_FALSE(valueProducer->mCondition); } TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) { @@ -834,7 +903,8 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) { EXPECT_EQ(30, curInterval.value.long_value); valueProducer.flushIfNeededLocked(bucket2StartTimeNs); - assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {30}, {bucketSizeNs}); + assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {30}, {bucketSizeNs}, + {bucketStartTimeNs}, {bucket2StartTimeNs}); } TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) { @@ -895,7 +965,8 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) { EXPECT_EQ(50, curInterval.value.long_value); valueProducer.flushIfNeededLocked(bucket2StartTimeNs); - assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {50}, {20}); + assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {50}, {20}, {bucketStartTimeNs}, + {bucket2StartTimeNs}); } TEST(ValueMetricProducerTest, TestAnomalyDetection) { @@ -1012,7 +1083,8 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) { EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(23, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}, {bucketSizeNs}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}, {bucketSizeNs}, + {bucket2StartTimeNs}, {bucket3StartTimeNs}); // pull 3 come late. // The previous bucket gets closed with error. (Has start value 23, no ending) @@ -1028,7 +1100,15 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) { EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(36, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}, {bucketSizeNs}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}, {bucketSizeNs}, + {bucket2StartTimeNs}, {bucket3StartTimeNs}); + // The 3rd bucket is dropped due to multiple buckets being skipped. + ASSERT_EQ(1, valueProducer->mSkippedBuckets.size()); + EXPECT_EQ(bucket3StartTimeNs, valueProducer->mSkippedBuckets[0].bucketStartTimeNs); + EXPECT_EQ(bucket4StartTimeNs, valueProducer->mSkippedBuckets[0].bucketEndTimeNs); + ASSERT_EQ(1, valueProducer->mSkippedBuckets[0].dropEvents.size()); + EXPECT_EQ(MULTIPLE_BUCKETS_SKIPPED, valueProducer->mSkippedBuckets[0].dropEvents[0].reason); + EXPECT_EQ(bucket6StartTimeNs, valueProducer->mSkippedBuckets[0].dropEvents[0].dropTimeNs); } /* @@ -1073,7 +1153,8 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) { valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1); curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}, + {bucketStartTimeNs}, {bucket2StartTimeNs}); EXPECT_EQ(false, curBaseInfo.hasBase); // Now the alarm is delivered. @@ -1082,7 +1163,8 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) { allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 110)); valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}, + {bucketStartTimeNs}, {bucket2StartTimeNs}); curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(false, curBaseInfo.hasBase); @@ -1090,10 +1172,9 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) { } /* -* Test pulled event with non sliced condition. The pull on boundary come late, after the -condition -* change to false, and then true again. This is due to alarm delivered late. -*/ + * Test pulled event with non sliced condition. The pull on boundary come late, after the condition + * change to false, and then true again. This is due to alarm delivered late. + */ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) { ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); @@ -1139,7 +1220,8 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) { // pull on bucket boundary come late, condition change happens before it valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}, + {bucketStartTimeNs}, {bucket2StartTimeNs}); EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; @@ -1148,7 +1230,8 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) { // condition changed to true again, before the pull alarm is delivered valueProducer->onConditionChanged(true, bucket2StartTimeNs + 25); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}, + {bucketStartTimeNs}, {bucket2StartTimeNs}); curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(true, curBaseInfo.hasBase); @@ -1167,13 +1250,15 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) { EXPECT_EQ(140, curBaseInfo.base.long_value); EXPECT_EQ(true, curInterval.hasValue); EXPECT_EQ(10, curInterval.value.long_value); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}, + {bucketStartTimeNs}, {bucket2StartTimeNs}); allData.clear(); allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs, 160)); valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20, 30}, - {bucketSizeNs - 8, bucketSizeNs - 24}); + assertPastBucketValuesSingleKey( + valueProducer->mPastBuckets, {20, 30}, {bucketSizeNs - 8, bucketSizeNs - 24}, + {bucketStartTimeNs, bucket2StartTimeNs}, {bucket2StartTimeNs, bucket3StartTimeNs}); } TEST(ValueMetricProducerTest, TestPushedAggregateMin) { @@ -1216,7 +1301,8 @@ TEST(ValueMetricProducerTest, TestPushedAggregateMin) { EXPECT_EQ(10, curInterval.value.long_value); valueProducer.flushIfNeededLocked(bucket2StartTimeNs); - assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10}, {bucketSizeNs}); + assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10}, {bucketSizeNs}, + {bucketStartTimeNs}, {bucket2StartTimeNs}); } TEST(ValueMetricProducerTest, TestPushedAggregateMax) { @@ -1239,9 +1325,6 @@ TEST(ValueMetricProducerTest, TestPushedAggregateMax) { LogEvent event1(/*uid=*/0, /*pid=*/0); CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10); - - LogEvent event2(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 20); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); // has one slice @@ -1251,6 +1334,8 @@ TEST(ValueMetricProducerTest, TestPushedAggregateMax) { EXPECT_EQ(10, curInterval.value.long_value); EXPECT_EQ(true, curInterval.hasValue); + LogEvent event2(/*uid=*/0, /*pid=*/0); + CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 20); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); // has one slice @@ -1258,10 +1343,9 @@ TEST(ValueMetricProducerTest, TestPushedAggregateMax) { curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(20, curInterval.value.long_value); - valueProducer.flushIfNeededLocked(bucket3StartTimeNs); - /* EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); */ - /* EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size()); */ - /* EXPECT_EQ(20, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value); */ + valueProducer.flushIfNeededLocked(bucket2StartTimeNs); + assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {20}, {bucketSizeNs}, + {bucketStartTimeNs}, {bucket2StartTimeNs}); } TEST(ValueMetricProducerTest, TestPushedAggregateAvg) { @@ -1351,7 +1435,8 @@ TEST(ValueMetricProducerTest, TestPushedAggregateSum) { EXPECT_EQ(25, curInterval.value.long_value); valueProducer.flushIfNeededLocked(bucket2StartTimeNs); - assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {25}, {bucketSizeNs}); + assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {25}, {bucketSizeNs}, + {bucketStartTimeNs}, {bucket2StartTimeNs}); } TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) { @@ -1375,10 +1460,8 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) { LogEvent event1(/*uid=*/0, /*pid=*/0); CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10); - - LogEvent event2(/*uid=*/0, /*pid=*/0); - CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 15, 15); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); + // has one slice EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); ValueMetricProducer::Interval curInterval = @@ -1388,6 +1471,8 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) { EXPECT_EQ(10, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); + LogEvent event2(/*uid=*/0, /*pid=*/0); + CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 15, 15); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); // has one slice @@ -1400,12 +1485,14 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) { LogEvent event3(/*uid=*/0, /*pid=*/0); CreateRepeatedValueLogEvent(&event3, tagId, bucket2StartTimeNs + 10, 15); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); + EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(15, curBaseInfo.base.long_value); EXPECT_EQ(true, curInterval.hasValue); + EXPECT_EQ(0, curInterval.value.long_value); LogEvent event4(/*uid=*/0, /*pid=*/0); CreateRepeatedValueLogEvent(&event4, tagId, bucket2StartTimeNs + 15, 15); @@ -1416,11 +1503,11 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) { EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(15, curBaseInfo.base.long_value); EXPECT_EQ(true, curInterval.hasValue); + EXPECT_EQ(0, curInterval.value.long_value); valueProducer.flushIfNeededLocked(bucket3StartTimeNs); - EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); - EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size()); - assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {5}, {bucketSizeNs}); + assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {5}, {bucketSizeNs}, + {bucketStartTimeNs}, {bucket2StartTimeNs}); } TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) { @@ -1740,8 +1827,8 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) { EXPECT_EQ(3, baseInfo1.base.long_value); EXPECT_EQ(false, interval1.hasValue); EXPECT_EQ(0UL, valueProducer->mPastBuckets.size()); - vector<shared_ptr<LogEvent>> allData; + vector<shared_ptr<LogEvent>> allData; allData.clear(); allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 2, 4)); allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1, 11)); @@ -1753,7 +1840,8 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) { EXPECT_EQ(false, interval1.hasValue); EXPECT_EQ(8, interval1.value.long_value); EXPECT_FALSE(interval1.seenNewData); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs}, + {bucketStartTimeNs}, {bucket2StartTimeNs}); auto it = valueProducer->mCurrentSlicedBucket.begin(); for (; it != valueProducer->mCurrentSlicedBucket.end(); it++) { @@ -1769,14 +1857,13 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) { } EXPECT_TRUE(it != iter); EXPECT_TRUE(itBase != iterBase); - auto& interval2 = it->second[0]; - auto& baseInfo2 = itBase->second[0]; + auto interval2 = it->second[0]; + auto baseInfo2 = itBase->second[0]; EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); EXPECT_EQ(true, baseInfo2.hasBase); EXPECT_EQ(4, baseInfo2.base.long_value); EXPECT_EQ(false, interval2.hasValue); EXPECT_FALSE(interval2.seenNewData); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs}); // next pull somehow did not happen, skip to end of bucket 3 allData.clear(); @@ -1791,7 +1878,8 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) { EXPECT_EQ(5, baseInfo2.base.long_value); EXPECT_EQ(false, interval2.hasValue); EXPECT_FALSE(interval2.seenNewData); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs}, + {bucketStartTimeNs}, {bucket2StartTimeNs}); allData.clear(); allData.push_back(CreateTwoValueLogEvent(tagId, bucket5StartTimeNs + 1, 2, 14)); @@ -1805,9 +1893,13 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) { EXPECT_FALSE(interval2.seenNewData); ASSERT_EQ(2UL, valueProducer->mPastBuckets.size()); auto iterator = valueProducer->mPastBuckets.begin(); + EXPECT_EQ(bucket4StartTimeNs, iterator->second[0].mBucketStartNs); + EXPECT_EQ(bucket5StartTimeNs, iterator->second[0].mBucketEndNs); EXPECT_EQ(9, iterator->second[0].values[0].long_value); EXPECT_EQ(bucketSizeNs, iterator->second[0].mConditionTrueNs); iterator++; + EXPECT_EQ(bucketStartTimeNs, iterator->second[0].mBucketStartNs); + EXPECT_EQ(bucket2StartTimeNs, iterator->second[0].mBucketEndNs); EXPECT_EQ(8, iterator->second[0].values[0].long_value); EXPECT_EQ(bucketSizeNs, iterator->second[0].mConditionTrueNs); } @@ -2414,7 +2506,8 @@ TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary) { EXPECT_EQ(true, valueProducer->mHasGlobalBase); EXPECT_EQ(1UL, valueProducer->mPastBuckets.size()); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {1}, {bucketSizeNs - 12 + 1}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {1}, {bucketSizeNs - 12 + 1}, + {bucketStartTimeNs}, {bucket2StartTimeNs}); } TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries) { @@ -2461,10 +2554,11 @@ TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries) { EXPECT_EQ(true, valueProducer->mHasGlobalBase); } -TEST(ValueMetricProducerTest, TestFullBucketResetWhenLastBucketInvalid) { +TEST_P(ValueMetricProducerTest_PartialBucket, TestFullBucketResetWhenLastBucketInvalid) { ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + int64_t partialBucketSplitTimeNs = bucketStartTimeNs + bucketSizeNs / 2; EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) // Initialization. .WillOnce(Invoke( @@ -2474,23 +2568,41 @@ TEST(ValueMetricProducerTest, TestFullBucketResetWhenLastBucketInvalid) { return true; })) // notifyAppUpgrade. - .WillOnce(Invoke([](int tagId, const ConfigKey&, - vector<std::shared_ptr<LogEvent>>* data, bool) { + .WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&, + vector<std::shared_ptr<LogEvent>>* data, + bool) { data->clear(); - data->push_back(CreateRepeatedValueLogEvent( - tagId, bucketStartTimeNs + bucketSizeNs / 2, 10)); + data->push_back(CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 10)); return true; })); sp<ValueMetricProducer> valueProducer = ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); ASSERT_EQ(0UL, valueProducer->mCurrentFullBucket.size()); - valueProducer->notifyAppUpgrade(bucketStartTimeNs + bucketSizeNs / 2, "com.foo", 10000, 1); + switch (GetParam()) { + case APP_UPGRADE: + valueProducer->notifyAppUpgrade(partialBucketSplitTimeNs); + break; + case BOOT_COMPLETE: + valueProducer->onStatsdInitCompleted(partialBucketSplitTimeNs); + break; + } + EXPECT_EQ(partialBucketSplitTimeNs, valueProducer->mCurrentBucketStartTimeNs); + EXPECT_EQ(0, valueProducer->getCurrentBucketNum()); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {9}, + {partialBucketSplitTimeNs - bucketStartTimeNs}, + {bucketStartTimeNs}, {partialBucketSplitTimeNs}); ASSERT_EQ(1UL, valueProducer->mCurrentFullBucket.size()); vector<shared_ptr<LogEvent>> allData; allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 4)); valueProducer->onDataPulled(allData, /** fails */ false, bucket3StartTimeNs + 1); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {9}, + {partialBucketSplitTimeNs - bucketStartTimeNs}, + {bucketStartTimeNs}, {partialBucketSplitTimeNs}); + ASSERT_EQ(1, valueProducer->mSkippedBuckets.size()); + EXPECT_EQ(partialBucketSplitTimeNs, valueProducer->mSkippedBuckets[0].bucketStartTimeNs); + EXPECT_EQ(bucket2StartTimeNs, valueProducer->mSkippedBuckets[0].bucketEndTimeNs); ASSERT_EQ(0UL, valueProducer->mCurrentFullBucket.size()); } @@ -2537,7 +2649,8 @@ TEST(ValueMetricProducerTest, TestBucketBoundariesOnConditionChange) { valueProducer->onConditionChanged(false, bucket3StartTimeNs + 10); // Bucket should have been completed. - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {2}, {bucketSizeNs - 10}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {2}, {bucketSizeNs - 10}, + {bucket2StartTimeNs}, {bucket3StartTimeNs}); } TEST(ValueMetricProducerTest, TestLateOnDataPulledWithoutDiff) { @@ -2557,7 +2670,8 @@ TEST(ValueMetricProducerTest, TestLateOnDataPulledWithoutDiff) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); // Bucket should have been completed. - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs}, + {bucketStartTimeNs}, {bucket2StartTimeNs}); } TEST(ValueMetricProducerTest, TestLateOnDataPulledWithDiff) { @@ -2585,12 +2699,14 @@ TEST(ValueMetricProducerTest, TestLateOnDataPulledWithDiff) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); // Bucket should have been completed. - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {19}, {bucketSizeNs}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {19}, {bucketSizeNs}, + {bucketStartTimeNs}, {bucket2StartTimeNs}); } -TEST(ValueMetricProducerTest, TestBucketBoundariesOnAppUpgrade) { +TEST_P(ValueMetricProducerTest_PartialBucket, TestBucketBoundariesOnPartialBucket) { ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); + int64_t partialBucketSplitTimeNs = bucket2StartTimeNs + 2; sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _)) // Initialization. @@ -2601,20 +2717,29 @@ TEST(ValueMetricProducerTest, TestBucketBoundariesOnAppUpgrade) { return true; })) // notifyAppUpgrade. - .WillOnce(Invoke([](int tagId, const ConfigKey&, - vector<std::shared_ptr<LogEvent>>* data, bool) { + .WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&, + vector<std::shared_ptr<LogEvent>>* data, + bool) { data->clear(); - data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 2, 10)); + data->push_back(CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 10)); return true; })); sp<ValueMetricProducer> valueProducer = ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); - valueProducer->notifyAppUpgrade(bucket2StartTimeNs + 2, "com.foo", 10000, 1); + switch (GetParam()) { + case APP_UPGRADE: + valueProducer->notifyAppUpgrade(partialBucketSplitTimeNs); + break; + case BOOT_COMPLETE: + valueProducer->onStatsdInitCompleted(partialBucketSplitTimeNs); + break; + } // Bucket should have been completed. - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {9}, {bucketSizeNs}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {9}, {bucketSizeNs}, + {bucketStartTimeNs}, {bucket2StartTimeNs}); } TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged) { @@ -2642,7 +2767,7 @@ TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged) { valueProducer->onConditionChanged(true, bucketStartTimeNs + 8); valueProducer->onConditionChanged(false, bucketStartTimeNs + 10); - valueProducer->onConditionChanged(false, bucketStartTimeNs + 10); + valueProducer->onConditionChanged(false, bucketStartTimeNs + 12); EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; @@ -2654,7 +2779,8 @@ TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged) { allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 10)); valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 1); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {2}, {2}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {2}, {2}, {bucketStartTimeNs}, + {bucket2StartTimeNs}); } // TODO: b/145705635 fix or delete this test @@ -2705,7 +2831,7 @@ TEST(ValueMetricProducerTest, TestBucketInvalidIfGlobalBaseIsNotSet) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); // There was not global base available so all buckets are invalid. - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {}, {}, {}); } TEST(ValueMetricProducerTest, TestPullNeededFastDump) { @@ -2849,7 +2975,8 @@ TEST(ValueMetricProducerTest, TestPulledData_noDiff_withoutCondition) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 30); // Bucket should have been completed. - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs}, + {bucketStartTimeNs}, {bucket2StartTimeNs}); } TEST(ValueMetricProducerTest, TestPulledData_noDiff_withMultipleConditionChanges) { @@ -2892,7 +3019,8 @@ TEST(ValueMetricProducerTest, TestPulledData_noDiff_withMultipleConditionChanges allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 110)); valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {50 - 8}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {50 - 8}, + {bucketStartTimeNs}, {bucket2StartTimeNs}); curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(false, curBaseInfo.hasBase); @@ -2923,7 +3051,8 @@ TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryTrue) { allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 30)); valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs - 8}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs - 8}, + {bucketStartTimeNs}, {bucket2StartTimeNs}); ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; @@ -2946,7 +3075,7 @@ TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryFalse) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); // Condition was always false. - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {}, {}, {}); } TEST(ValueMetricProducerTest, TestPulledData_noDiff_withFailure) { @@ -2976,7 +3105,7 @@ TEST(ValueMetricProducerTest, TestPulledData_noDiff_withFailure) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); // No buckets, we had a failure. - assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {}); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {}, {}, {}); } /* diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.h b/cmds/statsd/tests/metrics/metrics_test_helper.h index 69f7e3f49792..be410b10d43b 100644 --- a/cmds/statsd/tests/metrics/metrics_test_helper.h +++ b/cmds/statsd/tests/metrics/metrics_test_helper.h @@ -44,7 +44,8 @@ public: vector<std::shared_ptr<LogEvent>>* data, bool useUids)); MOCK_METHOD2(RegisterPullUidProvider, void(const ConfigKey& configKey, wp<PullUidProvider> provider)); - MOCK_METHOD1(UnregisterPullUidProvider, void(const ConfigKey& configKey)); + MOCK_METHOD2(UnregisterPullUidProvider, + void(const ConfigKey& configKey, wp<PullUidProvider> provider)); }; class MockUidMap : public UidMap { diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h index 37b98891797a..4d68ea2ecb79 100644 --- a/cmds/statsd/tests/statsd_test_util.h +++ b/cmds/statsd/tests/statsd_test_util.h @@ -42,6 +42,8 @@ using Status = ::ndk::ScopedAStatus; const int SCREEN_STATE_ATOM_ID = util::SCREEN_STATE_CHANGED; const int UID_PROCESS_STATE_ATOM_ID = util::UID_PROCESS_STATE_CHANGED; +enum BucketSplitEvent { APP_UPGRADE, BOOT_COMPLETE }; + // Converts a ProtoOutputStream to a StatsLogReport proto. StatsLogReport outputStreamToProto(ProtoOutputStream* proto); diff --git a/cmds/statsd/tests/utils/MultiConditionTrigger_test.cpp b/cmds/statsd/tests/utils/MultiConditionTrigger_test.cpp new file mode 100644 index 000000000000..db402a0dd658 --- /dev/null +++ b/cmds/statsd/tests/utils/MultiConditionTrigger_test.cpp @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "utils/MultiConditionTrigger.h" + +#include <gtest/gtest.h> + +#include <chrono> +#include <set> +#include <thread> +#include <vector> + +#ifdef __ANDROID__ + +using namespace std; +using std::this_thread::sleep_for; + +namespace android { +namespace os { +namespace statsd { + +TEST(MultiConditionTrigger, TestMultipleConditions) { + int numConditions = 5; + string t1 = "t1", t2 = "t2", t3 = "t3", t4 = "t4", t5 = "t5"; + set<string> conditionNames = {t1, t2, t3, t4, t5}; + + mutex lock; + condition_variable cv; + bool triggerCalled = false; + + // Mark done as true and notify in the done. + MultiConditionTrigger trigger(conditionNames, [&lock, &cv, &triggerCalled] { + { + lock_guard lg(lock); + triggerCalled = true; + } + cv.notify_all(); + }); + + vector<thread> threads; + vector<bool> done(numConditions, false); + + int i = 0; + for (const string& conditionName : conditionNames) { + threads.emplace_back([&done, &conditionName, &trigger, i] { + sleep_for(chrono::milliseconds(3)); + done[i] = true; + trigger.markComplete(conditionName); + }); + i++; + } + + unique_lock<mutex> unique_lk(lock); + cv.wait(unique_lk, [&triggerCalled] { + return triggerCalled; + }); + + for (i = 0; i < numConditions; i++) { + EXPECT_EQ(done[i], 1); + } + + for (i = 0; i < numConditions; i++) { + threads[i].join(); + } +} + +TEST(MultiConditionTrigger, TestNoConditions) { + mutex lock; + condition_variable cv; + bool triggerCalled = false; + + MultiConditionTrigger trigger({}, [&lock, &cv, &triggerCalled] { + { + lock_guard lg(lock); + triggerCalled = true; + } + cv.notify_all(); + }); + + unique_lock<mutex> unique_lk(lock); + cv.wait(unique_lk, [&triggerCalled] { return triggerCalled; }); + EXPECT_TRUE(triggerCalled); + // Ensure that trigger occurs immediately if no events need to be completed. +} + +TEST(MultiConditionTrigger, TestMarkCompleteCalledBySameCondition) { + string t1 = "t1", t2 = "t2"; + set<string> conditionNames = {t1, t2}; + + mutex lock; + condition_variable cv; + bool triggerCalled = false; + + MultiConditionTrigger trigger(conditionNames, [&lock, &cv, &triggerCalled] { + { + lock_guard lg(lock); + triggerCalled = true; + } + cv.notify_all(); + }); + + trigger.markComplete(t1); + trigger.markComplete(t1); + + // Ensure that the trigger still hasn't fired. + { + lock_guard lg(lock); + EXPECT_FALSE(triggerCalled); + } + + trigger.markComplete(t2); + unique_lock<mutex> unique_lk(lock); + cv.wait(unique_lk, [&triggerCalled] { return triggerCalled; }); + EXPECT_TRUE(triggerCalled); +} + +TEST(MultiConditionTrigger, TestTriggerOnlyCalledOnce) { + string t1 = "t1"; + set<string> conditionNames = {t1}; + + mutex lock; + condition_variable cv; + bool triggerCalled = false; + int triggerCount = 0; + + MultiConditionTrigger trigger(conditionNames, [&lock, &cv, &triggerCalled, &triggerCount] { + { + lock_guard lg(lock); + triggerCount++; + triggerCalled = true; + } + cv.notify_all(); + }); + + trigger.markComplete(t1); + + // Ensure that the trigger fired. + { + unique_lock<mutex> unique_lk(lock); + cv.wait(unique_lk, [&triggerCalled] { return triggerCalled; }); + EXPECT_TRUE(triggerCalled); + EXPECT_EQ(triggerCount, 1); + triggerCalled = false; + } + + trigger.markComplete(t1); + + // Ensure that the trigger does not fire again. + { + unique_lock<mutex> unique_lk(lock); + cv.wait_for(unique_lk, chrono::milliseconds(5), [&triggerCalled] { return triggerCalled; }); + EXPECT_FALSE(triggerCalled); + EXPECT_EQ(triggerCount, 1); + } +} + +} // namespace statsd +} // namespace os +} // namespace android +#else +GTEST_LOG_(INFO) << "This test does nothing.\n"; +#endif diff --git a/cmds/statsd/tests/utils/NamedLatch_test.cpp b/cmds/statsd/tests/utils/NamedLatch_test.cpp deleted file mode 100644 index de48a133823e..000000000000 --- a/cmds/statsd/tests/utils/NamedLatch_test.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT 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 "utils/NamedLatch.h" - -#include <gtest/gtest.h> - -#include <chrono> -#include <set> -#include <thread> -#include <vector> - -#ifdef __ANDROID__ - -using namespace std; -using std::this_thread::sleep_for; - -namespace android { -namespace os { -namespace statsd { - -TEST(NamedLatchTest, TestWait) { - int numEvents = 5; - string t1 = "t1", t2 = "t2", t3 = "t3", t4 = "t4", t5 = "t5"; - set<string> eventNames = {t1, t2, t3, t4, t5}; - - NamedLatch latch(eventNames); - vector<thread> threads; - vector<bool> done(numEvents, false); - - int i = 0; - for (const string& eventName : eventNames) { - threads.emplace_back([&done, &eventName, &latch, i] { - sleep_for(chrono::milliseconds(3)); - done[i] = true; - latch.countDown(eventName); - }); - i++; - } - - latch.wait(); - - for (i = 0; i < numEvents; i++) { - EXPECT_EQ(done[i], 1); - } - - for (i = 0; i < numEvents; i++) { - threads[i].join(); - } -} - -TEST(NamedLatchTest, TestNoWorkers) { - NamedLatch latch({}); - latch.wait(); - // Ensure that latch does not wait if no events need to countDown. -} - -TEST(NamedLatchTest, TestCountDownCalledBySameEventName) { - string t1 = "t1", t2 = "t2"; - set<string> eventNames = {t1, t2}; - - NamedLatch latch(eventNames); - - thread waiterThread([&latch] { latch.wait(); }); - - latch.countDown(t1); - latch.countDown(t1); - - // Ensure that the latch's remaining threads still has t2. - latch.mMutex.lock(); - ASSERT_EQ(latch.mRemainingEventNames.size(), 1); - EXPECT_NE(latch.mRemainingEventNames.find(t2), latch.mRemainingEventNames.end()); - latch.mMutex.unlock(); - - latch.countDown(t2); - waiterThread.join(); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/cmds/uiautomator/library/Android.bp b/cmds/uiautomator/library/Android.bp index 3a260639de0f..c33d31f576a3 100644 --- a/cmds/uiautomator/library/Android.bp +++ b/cmds/uiautomator/library/Android.bp @@ -28,9 +28,6 @@ droiddoc { installable: false, args: "-stubpackages com.android.uiautomator.core:" + "com.android.uiautomator.testrunner", - api_tag_name: "UIAUTOMATOR", - api_filename: "uiautomator_api.txt", - removed_api_filename: "uiautomator_removed_api.txt", check_api: { current: { diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java index 1b941defba42..1cc63da3db0a 100644 --- a/core/java/android/app/ActivityTaskManager.java +++ b/core/java/android/app/ActivityTaskManager.java @@ -23,8 +23,10 @@ import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.pm.PackageManager; +import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Rect; +import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; @@ -430,4 +432,14 @@ public class ActivityTaskManager { throw e.rethrowFromSystemServer(); } } + + /** Returns whether the current UI mode supports error dialogs (ANR, crash, etc). */ + public static boolean currentUiModeSupportsErrorDialogs(@NonNull Context context) { + final Configuration config = context.getResources().getConfiguration(); + int modeType = config.uiMode & Configuration.UI_MODE_TYPE_MASK; + return (modeType != Configuration.UI_MODE_TYPE_CAR + && !(modeType == Configuration.UI_MODE_TYPE_WATCH && Build.IS_USER) + && modeType != Configuration.UI_MODE_TYPE_TELEVISION + && modeType != Configuration.UI_MODE_TYPE_VR_HEADSET); + } } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 21b56d3e337f..7d6ce41763d7 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -375,7 +375,6 @@ public final class ActivityThread extends ClientTransactionHandler { String mInstrumentedLibDir = null; boolean mSystemThread = false; boolean mSomeActivitiesChanged = false; - boolean mUpdatingSystemConfig = false; /* package */ boolean mHiddenApiWarningShown = false; // These can be accessed by multiple threads; mResourcesManager is the lock. @@ -587,8 +586,11 @@ public final class ActivityThread extends ClientTransactionHandler { throw new IllegalStateException( "Received config update for non-existing activity"); } + // Given alwaysReportChange=false because the configuration is from view root, the + // activity may not be able to handle the changes. In that case the activity will be + // relaunched immediately, then Activity#onConfigurationChanged shouldn't be called. activity.mMainThread.handleActivityConfigurationChanged(token, overrideConfig, - newDisplayId); + newDisplayId, false /* alwaysReportChange */); }; } @@ -2029,12 +2031,7 @@ public final class ActivityThread extends ClientTransactionHandler { break; } case APPLICATION_INFO_CHANGED: - mUpdatingSystemConfig = true; - try { - handleApplicationInfoChanged((ApplicationInfo) msg.obj); - } finally { - mUpdatingSystemConfig = false; - } + handleApplicationInfoChanged((ApplicationInfo) msg.obj); break; case RUN_ISOLATED_ENTRY_POINT: handleRunIsolatedEntryPoint((String) ((SomeArgs) msg.obj).arg1, @@ -2238,7 +2235,9 @@ public final class ActivityThread extends ClientTransactionHandler { LoadedApk packageInfo = ref != null ? ref.get() : null; if (ai != null && packageInfo != null) { if (!isLoadedApkResourceDirsUpToDate(packageInfo, ai)) { - packageInfo.updateApplicationInfo(ai, null); + List<String> oldPaths = new ArrayList<>(); + LoadedApk.makePaths(this, ai, oldPaths); + packageInfo.updateApplicationInfo(ai, oldPaths); } if (packageInfo.isSecurityViolation() @@ -2326,7 +2325,9 @@ public final class ActivityThread extends ClientTransactionHandler { if (packageInfo != null) { if (!isLoadedApkResourceDirsUpToDate(packageInfo, aInfo)) { - packageInfo.updateApplicationInfo(aInfo, null); + List<String> oldPaths = new ArrayList<>(); + LoadedApk.makePaths(this, aInfo, oldPaths); + packageInfo.updateApplicationInfo(aInfo, oldPaths); } return packageInfo; @@ -4467,7 +4468,8 @@ public final class ActivityThread extends ClientTransactionHandler { // simply finishing, and we are not starting another activity. if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) { if (r.newConfig != null) { - performConfigurationChangedForActivity(r, r.newConfig); + performConfigurationChangedForActivity(r, r.newConfig, + false /* alwaysReportChange */); if (DEBUG_CONFIGURATION) { Slog.v(TAG, "Resuming activity " + r.activityInfo.name + " with newConfig " + r.activity.mCurrentConfig); @@ -4787,7 +4789,8 @@ public final class ActivityThread extends ClientTransactionHandler { } } if (r.newConfig != null) { - performConfigurationChangedForActivity(r, r.newConfig); + performConfigurationChangedForActivity(r, r.newConfig, + false /* alwaysReportChange */); if (DEBUG_CONFIGURATION) Slog.v(TAG, "Updating activity vis " + r.activityInfo.name + " with new config " + r.activity.mCurrentConfig); @@ -5447,11 +5450,12 @@ public final class ActivityThread extends ClientTransactionHandler { * @param r ActivityClientRecord representing the Activity. * @param newBaseConfig The new configuration to use. This may be augmented with * {@link ActivityClientRecord#overrideConfig}. + * @param alwaysReportChange If the configuration is changed, always report to activity. */ private void performConfigurationChangedForActivity(ActivityClientRecord r, - Configuration newBaseConfig) { - performConfigurationChangedForActivity(r, newBaseConfig, - r.activity.getDisplayId(), false /* movedToDifferentDisplay */); + Configuration newBaseConfig, boolean alwaysReportChange) { + performConfigurationChangedForActivity(r, newBaseConfig, r.activity.getDisplayId(), + false /* movedToDifferentDisplay */, alwaysReportChange); } /** @@ -5464,16 +5468,19 @@ public final class ActivityThread extends ClientTransactionHandler { * {@link ActivityClientRecord#overrideConfig}. * @param displayId The id of the display where the Activity currently resides. * @param movedToDifferentDisplay Indicates if the activity was moved to different display. + * @param alwaysReportChange If the configuration is changed, always report to activity. * @return {@link Configuration} instance sent to client, null if not sent. */ private Configuration performConfigurationChangedForActivity(ActivityClientRecord r, - Configuration newBaseConfig, int displayId, boolean movedToDifferentDisplay) { + Configuration newBaseConfig, int displayId, boolean movedToDifferentDisplay, + boolean alwaysReportChange) { r.tmpConfig.setTo(newBaseConfig); if (r.overrideConfig != null) { r.tmpConfig.updateFrom(r.overrideConfig); } final Configuration reportedConfig = performActivityConfigurationChanged(r.activity, - r.tmpConfig, r.overrideConfig, displayId, movedToDifferentDisplay); + r.tmpConfig, r.overrideConfig, displayId, movedToDifferentDisplay, + alwaysReportChange); freeTextLayoutCachesIfNeeded(r.activity.mCurrentConfig.diff(r.tmpConfig)); return reportedConfig; } @@ -5529,11 +5536,12 @@ public final class ActivityThread extends ClientTransactionHandler { * ActivityManager. * @param displayId Id of the display where activity currently resides. * @param movedToDifferentDisplay Indicates if the activity was moved to different display. + * @param alwaysReportChange If the configuration is changed, always report to activity. * @return Configuration sent to client, null if no changes and not moved to different display. */ private Configuration performActivityConfigurationChanged(Activity activity, Configuration newConfig, Configuration amOverrideConfig, int displayId, - boolean movedToDifferentDisplay) { + boolean movedToDifferentDisplay, boolean alwaysReportChange) { if (activity == null) { throw new IllegalArgumentException("No activity provided."); } @@ -5556,7 +5564,7 @@ public final class ActivityThread extends ClientTransactionHandler { // Always send the task-level config changes. For system-level configuration, if // this activity doesn't handle any of the config changes, then don't bother // calling onConfigurationChanged as we're going to destroy it. - if (!mUpdatingSystemConfig + if (alwaysReportChange || (~activity.mActivityInfo.getRealConfigChanged() & diff) == 0 || !REPORT_TO_ACTIVITY) { shouldChangeConfig = true; @@ -5638,12 +5646,7 @@ public final class ActivityThread extends ClientTransactionHandler { public void handleConfigurationChanged(Configuration config) { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "configChanged"); mCurDefaultDisplayDpi = config.densityDpi; - mUpdatingSystemConfig = true; - try { - handleConfigurationChanged(config, null /* compat */); - } finally { - mUpdatingSystemConfig = false; - } + handleConfigurationChanged(config, null /* compat */); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } @@ -5725,7 +5728,7 @@ public final class ActivityThread extends ClientTransactionHandler { // config and avoid onConfigurationChanged if it hasn't changed. Activity a = (Activity) cb; performConfigurationChangedForActivity(mActivities.get(a.getActivityToken()), - config); + config, false /* alwaysReportChange */); } else if (!equivalent) { performConfigurationChanged(cb, config); } else { @@ -5832,16 +5835,28 @@ public final class ActivityThread extends ClientTransactionHandler { } } + @Override + public void handleActivityConfigurationChanged(IBinder activityToken, + Configuration overrideConfig, int displayId) { + handleActivityConfigurationChanged(activityToken, overrideConfig, displayId, + // This is the only place that uses alwaysReportChange=true. The entry point should + // be from ActivityConfigurationChangeItem or MoveToDisplayItem, so the server side + // has confirmed the activity should handle the configuration instead of relaunch. + // If Activity#onConfigurationChanged is called unexpectedly, then we can know it is + // something wrong from server side. + true /* alwaysReportChange */); + } + /** * Handle new activity configuration and/or move to a different display. * @param activityToken Target activity token. * @param overrideConfig Activity override config. * @param displayId Id of the display where activity was moved to, -1 if there was no move and * value didn't change. + * @param alwaysReportChange If the configuration is changed, always report to activity. */ - @Override - public void handleActivityConfigurationChanged(IBinder activityToken, - Configuration overrideConfig, int displayId) { + void handleActivityConfigurationChanged(IBinder activityToken, Configuration overrideConfig, + int displayId, boolean alwaysReportChange) { ActivityClientRecord r = mActivities.get(activityToken); // Check input params. if (r == null || r.activity == null) { @@ -5880,14 +5895,15 @@ public final class ActivityThread extends ClientTransactionHandler { + ", config=" + overrideConfig); final Configuration reportedConfig = performConfigurationChangedForActivity(r, - mCompatConfiguration, displayId, true /* movedToDifferentDisplay */); + mCompatConfiguration, displayId, true /* movedToDifferentDisplay */, + alwaysReportChange); if (viewRoot != null) { viewRoot.onMovedToDisplay(displayId, reportedConfig); } } else { if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle activity config changed: " + r.activityInfo.name + ", config=" + overrideConfig); - performConfigurationChangedForActivity(r, mCompatConfiguration); + performConfigurationChangedForActivity(r, mCompatConfiguration, alwaysReportChange); } // Notify the ViewRootImpl instance about configuration changes. It may have initiated this // update to make sure that resources are updated before updating itself. diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 8edf03d033f8..af027837ec91 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -2301,11 +2301,11 @@ public class Notification implements Parcelable priority = parcel.readInt(); - category = parcel.readString(); + category = parcel.readString8(); - mGroupKey = parcel.readString(); + mGroupKey = parcel.readString8(); - mSortKey = parcel.readString(); + mSortKey = parcel.readString8(); extras = Bundle.setDefusable(parcel.readBundle(), true); // may be null fixDuplicateExtras(); @@ -2329,12 +2329,12 @@ public class Notification implements Parcelable color = parcel.readInt(); if (parcel.readInt() != 0) { - mChannelId = parcel.readString(); + mChannelId = parcel.readString8(); } mTimeout = parcel.readLong(); if (parcel.readInt() != 0) { - mShortcutId = parcel.readString(); + mShortcutId = parcel.readString8(); } if (parcel.readInt() != 0) { @@ -2766,11 +2766,11 @@ public class Notification implements Parcelable parcel.writeInt(priority); - parcel.writeString(category); + parcel.writeString8(category); - parcel.writeString(mGroupKey); + parcel.writeString8(mGroupKey); - parcel.writeString(mSortKey); + parcel.writeString8(mSortKey); parcel.writeBundle(extras); // null ok @@ -2803,7 +2803,7 @@ public class Notification implements Parcelable if (mChannelId != null) { parcel.writeInt(1); - parcel.writeString(mChannelId); + parcel.writeString8(mChannelId); } else { parcel.writeInt(0); } @@ -2811,7 +2811,7 @@ public class Notification implements Parcelable if (mShortcutId != null) { parcel.writeInt(1); - parcel.writeString(mShortcutId); + parcel.writeString8(mShortcutId); } else { parcel.writeInt(0); } @@ -8873,7 +8873,7 @@ public class Notification implements Parcelable } mDesiredHeightResId = in.readInt(); if (in.readInt() != 0) { - mShortcutId = in.readString(); + mShortcutId = in.readString8(); } } @@ -9029,7 +9029,7 @@ public class Notification implements Parcelable out.writeInt(mDesiredHeightResId); out.writeInt(TextUtils.isEmpty(mShortcutId) ? 0 : 1); if (!TextUtils.isEmpty(mShortcutId)) { - out.writeString(mShortcutId); + out.writeString8(mShortcutId); } } diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java index 2feb9277775c..9f8d3c4090d6 100644 --- a/core/java/android/app/NotificationChannel.java +++ b/core/java/android/app/NotificationChannel.java @@ -102,7 +102,7 @@ public final class NotificationChannel implements Parcelable { private static final String ATT_FG_SERVICE_SHOWN = "fgservice"; private static final String ATT_GROUP = "group"; private static final String ATT_BLOCKABLE_SYSTEM = "blockable_system"; - private static final String ATT_ALLOW_BUBBLE = "allow_bubble"; + private static final String ATT_ALLOW_BUBBLE = "allow_bubbles"; private static final String ATT_ORIG_IMP = "orig_imp"; private static final String ATT_PARENT_CHANNEL = "parent"; private static final String ATT_CONVERSATION_ID = "conv_id"; @@ -168,7 +168,12 @@ public final class NotificationChannel implements Parcelable { NotificationManager.IMPORTANCE_UNSPECIFIED; private static final boolean DEFAULT_DELETED = false; private static final boolean DEFAULT_SHOW_BADGE = true; - private static final boolean DEFAULT_ALLOW_BUBBLE = false; + /** + * @hide + */ + public static final int DEFAULT_ALLOW_BUBBLE = -1; + private static final int ALLOW_BUBBLE_ON = 1; + private static final int ALLOW_BUBBLE_OFF = 0; @UnsupportedAppUsage private String mId; @@ -193,7 +198,7 @@ public final class NotificationChannel implements Parcelable { private AudioAttributes mAudioAttributes = Notification.AUDIO_ATTRIBUTES_DEFAULT; // If this is a blockable system notification channel. private boolean mBlockableSystem = false; - private boolean mAllowBubbles = DEFAULT_ALLOW_BUBBLE; + private int mAllowBubbles = DEFAULT_ALLOW_BUBBLE; private boolean mImportanceLockedByOEM; private boolean mImportanceLockedDefaultApp; private String mParentId = null; @@ -261,7 +266,7 @@ public final class NotificationChannel implements Parcelable { mAudioAttributes = in.readInt() > 0 ? AudioAttributes.CREATOR.createFromParcel(in) : null; mLightColor = in.readInt(); mBlockableSystem = in.readBoolean(); - mAllowBubbles = in.readBoolean(); + mAllowBubbles = in.readInt(); mImportanceLockedByOEM = in.readBoolean(); mOriginalImportance = in.readInt(); mParentId = in.readString(); @@ -320,7 +325,7 @@ public final class NotificationChannel implements Parcelable { } dest.writeInt(mLightColor); dest.writeBoolean(mBlockableSystem); - dest.writeBoolean(mAllowBubbles); + dest.writeInt(mAllowBubbles); dest.writeBoolean(mImportanceLockedByOEM); dest.writeInt(mOriginalImportance); dest.writeString(mParentId); @@ -550,7 +555,14 @@ public final class NotificationChannel implements Parcelable { * @see Notification#getBubbleMetadata() */ public void setAllowBubbles(boolean allowBubbles) { - mAllowBubbles = allowBubbles; + mAllowBubbles = allowBubbles ? ALLOW_BUBBLE_ON : ALLOW_BUBBLE_OFF; + } + + /** + * @hide + */ + public void setAllowBubbles(int allowed) { + mAllowBubbles = allowed; } /** @@ -701,6 +713,13 @@ public final class NotificationChannel implements Parcelable { * @see Notification#getBubbleMetadata() */ public boolean canBubble() { + return mAllowBubbles == ALLOW_BUBBLE_ON; + } + + /** + * @hide + */ + public int getAllowBubbles() { return mAllowBubbles; } @@ -872,7 +891,7 @@ public final class NotificationChannel implements Parcelable { lockFields(safeInt(parser, ATT_USER_LOCKED, 0)); setFgServiceShown(safeBool(parser, ATT_FG_SERVICE_SHOWN, false)); setBlockable(safeBool(parser, ATT_BLOCKABLE_SYSTEM, false)); - setAllowBubbles(safeBool(parser, ATT_ALLOW_BUBBLE, DEFAULT_ALLOW_BUBBLE)); + setAllowBubbles(safeInt(parser, ATT_ALLOW_BUBBLE, DEFAULT_ALLOW_BUBBLE)); setOriginalImportance(safeInt(parser, ATT_ORIG_IMP, DEFAULT_IMPORTANCE)); setConversationId(parser.getAttributeValue(null, ATT_PARENT_CHANNEL), parser.getAttributeValue(null, ATT_CONVERSATION_ID)); @@ -996,8 +1015,8 @@ public final class NotificationChannel implements Parcelable { if (isBlockable()) { out.attribute(null, ATT_BLOCKABLE_SYSTEM, Boolean.toString(isBlockable())); } - if (canBubble() != DEFAULT_ALLOW_BUBBLE) { - out.attribute(null, ATT_ALLOW_BUBBLE, Boolean.toString(canBubble())); + if (getAllowBubbles() != DEFAULT_ALLOW_BUBBLE) { + out.attribute(null, ATT_ALLOW_BUBBLE, Integer.toString(getAllowBubbles())); } if (getOriginalImportance() != DEFAULT_IMPORTANCE) { out.attribute(null, ATT_ORIG_IMP, Integer.toString(getOriginalImportance())); @@ -1059,7 +1078,7 @@ public final class NotificationChannel implements Parcelable { record.put(ATT_DELETED, Boolean.toString(isDeleted())); record.put(ATT_GROUP, getGroup()); record.put(ATT_BLOCKABLE_SYSTEM, isBlockable()); - record.put(ATT_ALLOW_BUBBLE, canBubble()); + record.put(ATT_ALLOW_BUBBLE, getAllowBubbles()); // TODO: original importance return record; } diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java index 0173731995dd..c7a2a1e11c9e 100644 --- a/core/java/android/app/TaskInfo.java +++ b/core/java/android/app/TaskInfo.java @@ -20,6 +20,7 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Intent; @@ -196,6 +197,20 @@ public class TaskInfo { return resizeMode != RESIZE_MODE_UNRESIZEABLE; } + /** @hide */ + @NonNull + @TestApi + public WindowContainerToken getToken() { + return token; + } + + /** @hide */ + @NonNull + @TestApi + public Configuration getConfiguration() { + return configuration; + } + /** * Reads the TaskInfo from a parcel. */ diff --git a/core/java/android/app/WindowContext.java b/core/java/android/app/WindowContext.java index 3a06c9d79fee..cb416c923c60 100644 --- a/core/java/android/app/WindowContext.java +++ b/core/java/android/app/WindowContext.java @@ -16,6 +16,7 @@ package android.app; import static android.view.WindowManagerGlobal.ADD_OKAY; +import static android.view.WindowManagerGlobal.ADD_TOO_MANY_TOKENS; import android.annotation.NonNull; import android.annotation.Nullable; @@ -81,6 +82,11 @@ public class WindowContext extends ContextWrapper { mOwnsToken = false; throw e.rethrowFromSystemServer(); } + if (result == ADD_TOO_MANY_TOKENS) { + throw new UnsupportedOperationException("createWindowContext failed! Too many unused " + + "window contexts. Please see Context#createWindowContext documentation for " + + "detail."); + } mOwnsToken = result == ADD_OKAY; Reference.reachabilityFence(this); } diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java index 9c806fa286bc..1923bf3c5a23 100644 --- a/core/java/android/content/ClipData.java +++ b/core/java/android/content/ClipData.java @@ -1206,7 +1206,7 @@ public class ClipData implements Parcelable { } } else { dest.writeInt(PARCEL_TYPE_STRING); - dest.writeString(text); + dest.writeString8(text); } } @@ -1215,7 +1215,7 @@ public class ClipData implements Parcelable { */ private static String readHtmlTextFromParcel(Parcel in) { if (in.readInt() == PARCEL_TYPE_STRING) { - return in.readString(); + return in.readString8(); } ParcelFileDescriptor pfd = in.readParcelable(ParcelFileDescriptor.class.getClassLoader()); diff --git a/core/java/android/content/ContentProviderOperation.java b/core/java/android/content/ContentProviderOperation.java index 494d2aeaf42a..1fb426eb3cfc 100644 --- a/core/java/android/content/ContentProviderOperation.java +++ b/core/java/android/content/ContentProviderOperation.java @@ -91,8 +91,8 @@ public class ContentProviderOperation implements Parcelable { private ContentProviderOperation(Parcel source) { mType = source.readInt(); mUri = Uri.CREATOR.createFromParcel(source); - mMethod = source.readInt() != 0 ? source.readString() : null; - mArg = source.readInt() != 0 ? source.readString() : null; + mMethod = source.readInt() != 0 ? source.readString8() : null; + mArg = source.readInt() != 0 ? source.readString8() : null; final int valuesSize = source.readInt(); if (valuesSize != -1) { mValues = new ArrayMap<>(valuesSize); @@ -107,7 +107,7 @@ public class ContentProviderOperation implements Parcelable { } else { mExtras = null; } - mSelection = source.readInt() != 0 ? source.readString() : null; + mSelection = source.readInt() != 0 ? source.readString8() : null; mSelectionArgs = source.readSparseArray(null); mExpectedCount = source.readInt() != 0 ? source.readInt() : null; mYieldAllowed = source.readInt() != 0; @@ -135,13 +135,13 @@ public class ContentProviderOperation implements Parcelable { Uri.writeToParcel(dest, mUri); if (mMethod != null) { dest.writeInt(1); - dest.writeString(mMethod); + dest.writeString8(mMethod); } else { dest.writeInt(0); } if (mArg != null) { dest.writeInt(1); - dest.writeString(mArg); + dest.writeString8(mArg); } else { dest.writeInt(0); } @@ -159,7 +159,7 @@ public class ContentProviderOperation implements Parcelable { } if (mSelection != null) { dest.writeInt(1); - dest.writeString(mSelection); + dest.writeString8(mSelection); } else { dest.writeInt(0); } @@ -591,7 +591,7 @@ public class ContentProviderOperation implements Parcelable { public BackReference(Parcel src) { this.fromIndex = src.readInt(); if (src.readInt() != 0) { - this.fromKey = src.readString(); + this.fromKey = src.readString8(); } else { this.fromKey = null; } @@ -620,7 +620,7 @@ public class ContentProviderOperation implements Parcelable { dest.writeInt(fromIndex); if (fromKey != null) { dest.writeInt(1); - dest.writeString(fromKey); + dest.writeString8(fromKey); } else { dest.writeInt(0); } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 7c1b62fc9b8e..09c684971549 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -5812,6 +5812,12 @@ public abstract class Context { * display.</b> If there is a need to add different window types, or non-associated windows, * separate Contexts should be used. * </p> + * <p> + * Creating a window context is an expensive operation. Misuse of this API may lead to a huge + * performance drop. The best practice is to use the same window context when possible. + * An approach is to create one window context with specific window type and display and + * use it everywhere it's needed.. + * </p> * * @param type Window type in {@link WindowManager.LayoutParams} * @param options Bundle used to pass window-related options. @@ -5824,7 +5830,9 @@ public abstract class Context { * @see #WINDOW_SERVICE * @see #LAYOUT_INFLATER_SERVICE * @see #WALLPAPER_SERVICE - * @throws IllegalArgumentException if token is invalid + * @throws UnsupportedOperationException if this {@link Context} does not attach to a display or + * the current number of window contexts without adding any view by + * {@link WindowManager#addView} <b>exceeds five</b>. */ public @NonNull Context createWindowContext(@WindowType int type, @Nullable Bundle options) { throw new RuntimeException("Not implemented. Must override in a subclass."); diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index b1d6c830d3b7..def150ab49e5 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -888,8 +888,8 @@ public class Intent implements Parcelable, Cloneable { public ShortcutIconResource createFromParcel(Parcel source) { ShortcutIconResource icon = new ShortcutIconResource(); - icon.packageName = source.readString(); - icon.resourceName = source.readString(); + icon.packageName = source.readString8(); + icon.resourceName = source.readString8(); return icon; } @@ -906,8 +906,8 @@ public class Intent implements Parcelable, Cloneable { } public void writeToParcel(Parcel dest, int flags) { - dest.writeString(packageName); - dest.writeString(resourceName); + dest.writeString8(packageName); + dest.writeString8(resourceName); } @Override @@ -10807,12 +10807,12 @@ public class Intent implements Parcelable, Cloneable { } public void writeToParcel(Parcel out, int flags) { - out.writeString(mAction); + out.writeString8(mAction); Uri.writeToParcel(out, mData); - out.writeString(mType); - out.writeString(mIdentifier); + out.writeString8(mType); + out.writeString8(mIdentifier); out.writeInt(mFlags); - out.writeString(mPackage); + out.writeString8(mPackage); ComponentName.writeToParcel(mComponent, out); if (mSourceBounds != null) { @@ -10826,7 +10826,7 @@ public class Intent implements Parcelable, Cloneable { final int N = mCategories.size(); out.writeInt(N); for (int i=0; i<N; i++) { - out.writeString(mCategories.valueAt(i)); + out.writeString8(mCategories.valueAt(i)); } } else { out.writeInt(0); @@ -10865,12 +10865,12 @@ public class Intent implements Parcelable, Cloneable { } public void readFromParcel(Parcel in) { - setAction(in.readString()); + setAction(in.readString8()); mData = Uri.CREATOR.createFromParcel(in); - mType = in.readString(); - mIdentifier = in.readString(); + mType = in.readString8(); + mIdentifier = in.readString8(); mFlags = in.readInt(); - mPackage = in.readString(); + mPackage = in.readString8(); mComponent = ComponentName.readFromParcel(in); if (in.readInt() != 0) { @@ -10882,7 +10882,7 @@ public class Intent implements Parcelable, Cloneable { mCategories = new ArraySet<String>(); int i; for (i=0; i<N; i++) { - mCategories.add(in.readString().intern()); + mCategories.add(in.readString8().intern()); } } else { mCategories = null; diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index f25ce76d9365..b1f88693d9c0 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -1206,17 +1206,17 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { dest.writeInt(theme); dest.writeInt(launchMode); dest.writeInt(documentLaunchMode); - dest.writeString(permission); - dest.writeString(taskAffinity); - dest.writeString(targetActivity); - dest.writeString(launchToken); + dest.writeString8(permission); + dest.writeString8(taskAffinity); + dest.writeString8(targetActivity); + dest.writeString8(launchToken); dest.writeInt(flags); dest.writeInt(privateFlags); dest.writeInt(screenOrientation); dest.writeInt(configChanges); dest.writeInt(softInputMode); dest.writeInt(uiOptions); - dest.writeString(parentActivityName); + dest.writeString8(parentActivityName); dest.writeInt(persistableMode); dest.writeInt(maxRecents); dest.writeInt(lockTaskLaunchMode); @@ -1227,7 +1227,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { dest.writeInt(0); } dest.writeInt(resizeMode); - dest.writeString(requestedVrComponent); + dest.writeString8(requestedVrComponent); dest.writeInt(rotationAnimation); dest.writeInt(colorMode); dest.writeFloat(maxAspectRatio); @@ -1327,17 +1327,17 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { theme = source.readInt(); launchMode = source.readInt(); documentLaunchMode = source.readInt(); - permission = source.readString(); - taskAffinity = source.readString(); - targetActivity = source.readString(); - launchToken = source.readString(); + permission = source.readString8(); + taskAffinity = source.readString8(); + targetActivity = source.readString8(); + launchToken = source.readString8(); flags = source.readInt(); privateFlags = source.readInt(); screenOrientation = source.readInt(); configChanges = source.readInt(); softInputMode = source.readInt(); uiOptions = source.readInt(); - parentActivityName = source.readString(); + parentActivityName = source.readString8(); persistableMode = source.readInt(); maxRecents = source.readInt(); lockTaskLaunchMode = source.readInt(); @@ -1345,7 +1345,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { windowLayout = new WindowLayout(source); } resizeMode = source.readInt(); - requestedVrComponent = source.readString(); + requestedVrComponent = source.readString8(); rotationAnimation = source.readInt(); colorMode = source.readInt(); maxAspectRatio = source.readFloat(); @@ -1386,7 +1386,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { gravity = source.readInt(); minWidth = source.readInt(); minHeight = source.readInt(); - windowLayoutAffinity = source.readString(); + windowLayoutAffinity = source.readString8(); } /** @@ -1476,7 +1476,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { dest.writeInt(gravity); dest.writeInt(minWidth); dest.writeInt(minHeight); - dest.writeString(windowLayoutAffinity); + dest.writeString8(windowLayoutAffinity); } } } diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index b67060111785..b37521b1189b 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -1704,10 +1704,10 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { return; } super.writeToParcel(dest, parcelableFlags); - dest.writeString(taskAffinity); - dest.writeString(permission); - dest.writeString(processName); - dest.writeString(className); + dest.writeString8(taskAffinity); + dest.writeString8(permission); + dest.writeString8(processName); + dest.writeString8(className); dest.writeInt(theme); dest.writeInt(flags); dest.writeInt(privateFlags); @@ -1721,28 +1721,28 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { } else { dest.writeInt(0); } - dest.writeString(scanSourceDir); - dest.writeString(scanPublicSourceDir); - dest.writeString(sourceDir); - dest.writeString(publicSourceDir); + dest.writeString8(scanSourceDir); + dest.writeString8(scanPublicSourceDir); + dest.writeString8(sourceDir); + dest.writeString8(publicSourceDir); dest.writeStringArray(splitNames); dest.writeStringArray(splitSourceDirs); dest.writeStringArray(splitPublicSourceDirs); dest.writeSparseArray((SparseArray) splitDependencies); - dest.writeString(nativeLibraryDir); - dest.writeString(secondaryNativeLibraryDir); - dest.writeString(nativeLibraryRootDir); + dest.writeString8(nativeLibraryDir); + dest.writeString8(secondaryNativeLibraryDir); + dest.writeString8(nativeLibraryRootDir); dest.writeInt(nativeLibraryRootRequiresIsa ? 1 : 0); - dest.writeString(primaryCpuAbi); - dest.writeString(secondaryCpuAbi); + dest.writeString8(primaryCpuAbi); + dest.writeString8(secondaryCpuAbi); dest.writeStringArray(resourceDirs); - dest.writeString(seInfo); - dest.writeString(seInfoUser); + dest.writeString8(seInfo); + dest.writeString8(seInfoUser); dest.writeStringArray(sharedLibraryFiles); dest.writeTypedList(sharedLibraryInfos); - dest.writeString(dataDir); - dest.writeString(deviceProtectedDataDir); - dest.writeString(credentialProtectedDataDir); + dest.writeString8(dataDir); + dest.writeString8(deviceProtectedDataDir); + dest.writeString8(credentialProtectedDataDir); dest.writeInt(uid); dest.writeInt(minSdkVersion); dest.writeInt(targetSdkVersion); @@ -1750,8 +1750,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { dest.writeInt(enabled ? 1 : 0); dest.writeInt(enabledSetting); dest.writeInt(installLocation); - dest.writeString(manageSpaceActivityName); - dest.writeString(backupAgentName); + dest.writeString8(manageSpaceActivityName); + dest.writeString8(backupAgentName); dest.writeInt(descriptionRes); dest.writeInt(uiOptions); dest.writeInt(fullBackupContent); @@ -1759,16 +1759,16 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { dest.writeInt(networkSecurityConfigRes); dest.writeInt(category); dest.writeInt(targetSandboxVersion); - dest.writeString(classLoaderName); + dest.writeString8(classLoaderName); dest.writeStringArray(splitClassLoaderNames); dest.writeInt(compileSdkVersion); - dest.writeString(compileSdkVersionCodename); - dest.writeString(appComponentFactory); + dest.writeString8(compileSdkVersionCodename); + dest.writeString8(appComponentFactory); dest.writeInt(iconRes); dest.writeInt(roundIconRes); dest.writeInt(mHiddenApiPolicy); dest.writeInt(hiddenUntilInstalled ? 1 : 0); - dest.writeString(zygotePreloadName); + dest.writeString8(zygotePreloadName); dest.writeInt(gwpAsanMode); } @@ -1788,10 +1788,10 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { @SuppressWarnings("unchecked") private ApplicationInfo(Parcel source) { super(source); - taskAffinity = source.readString(); - permission = source.readString(); - processName = source.readString(); - className = source.readString(); + taskAffinity = source.readString8(); + permission = source.readString8(); + processName = source.readString8(); + className = source.readString8(); theme = source.readInt(); flags = source.readInt(); privateFlags = source.readInt(); @@ -1802,28 +1802,28 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { storageUuid = new UUID(source.readLong(), source.readLong()); volumeUuid = StorageManager.convert(storageUuid); } - scanSourceDir = source.readString(); - scanPublicSourceDir = source.readString(); - sourceDir = source.readString(); - publicSourceDir = source.readString(); + scanSourceDir = source.readString8(); + scanPublicSourceDir = source.readString8(); + sourceDir = source.readString8(); + publicSourceDir = source.readString8(); splitNames = source.readStringArray(); splitSourceDirs = source.readStringArray(); splitPublicSourceDirs = source.readStringArray(); splitDependencies = source.readSparseArray(null); - nativeLibraryDir = source.readString(); - secondaryNativeLibraryDir = source.readString(); - nativeLibraryRootDir = source.readString(); + nativeLibraryDir = source.readString8(); + secondaryNativeLibraryDir = source.readString8(); + nativeLibraryRootDir = source.readString8(); nativeLibraryRootRequiresIsa = source.readInt() != 0; - primaryCpuAbi = source.readString(); - secondaryCpuAbi = source.readString(); + primaryCpuAbi = source.readString8(); + secondaryCpuAbi = source.readString8(); resourceDirs = source.readStringArray(); - seInfo = source.readString(); - seInfoUser = source.readString(); + seInfo = source.readString8(); + seInfoUser = source.readString8(); sharedLibraryFiles = source.readStringArray(); sharedLibraryInfos = source.createTypedArrayList(SharedLibraryInfo.CREATOR); - dataDir = source.readString(); - deviceProtectedDataDir = source.readString(); - credentialProtectedDataDir = source.readString(); + dataDir = source.readString8(); + deviceProtectedDataDir = source.readString8(); + credentialProtectedDataDir = source.readString8(); uid = source.readInt(); minSdkVersion = source.readInt(); targetSdkVersion = source.readInt(); @@ -1831,8 +1831,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { enabled = source.readInt() != 0; enabledSetting = source.readInt(); installLocation = source.readInt(); - manageSpaceActivityName = source.readString(); - backupAgentName = source.readString(); + manageSpaceActivityName = source.readString8(); + backupAgentName = source.readString8(); descriptionRes = source.readInt(); uiOptions = source.readInt(); fullBackupContent = source.readInt(); @@ -1840,16 +1840,16 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { networkSecurityConfigRes = source.readInt(); category = source.readInt(); targetSandboxVersion = source.readInt(); - classLoaderName = source.readString(); + classLoaderName = source.readString8(); splitClassLoaderNames = source.readStringArray(); compileSdkVersion = source.readInt(); - compileSdkVersionCodename = source.readString(); - appComponentFactory = source.readString(); + compileSdkVersionCodename = source.readString8(); + appComponentFactory = source.readString8(); iconRes = source.readInt(); roundIconRes = source.readInt(); mHiddenApiPolicy = source.readInt(); hiddenUntilInstalled = source.readInt() != 0; - zygotePreloadName = source.readString(); + zygotePreloadName = source.readString8(); gwpAsanMode = source.readInt(); } diff --git a/core/java/android/content/pm/ComponentInfo.java b/core/java/android/content/pm/ComponentInfo.java index 362098c447ce..628bcd70cdf6 100644 --- a/core/java/android/content/pm/ComponentInfo.java +++ b/core/java/android/content/pm/ComponentInfo.java @@ -197,8 +197,8 @@ public class ComponentInfo extends PackageItemInfo { public void writeToParcel(Parcel dest, int parcelableFlags) { super.writeToParcel(dest, parcelableFlags); applicationInfo.writeToParcel(dest, parcelableFlags); - dest.writeString(processName); - dest.writeString(splitName); + dest.writeString8(processName); + dest.writeString8(splitName); dest.writeInt(descriptionRes); dest.writeInt(enabled ? 1 : 0); dest.writeInt(exported ? 1 : 0); @@ -208,8 +208,8 @@ public class ComponentInfo extends PackageItemInfo { protected ComponentInfo(Parcel source) { super(source); applicationInfo = ApplicationInfo.CREATOR.createFromParcel(source); - processName = source.readString(); - splitName = source.readString(); + processName = source.readString8(); + splitName = source.readString8(); descriptionRes = source.readInt(); enabled = (source.readInt() != 0); exported = (source.readInt() != 0); diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java index 144a07eb4ea3..99e6d91a61ae 100644 --- a/core/java/android/content/pm/CrossProfileApps.java +++ b/core/java/android/content/pm/CrossProfileApps.java @@ -279,12 +279,8 @@ public class CrossProfileApps { * <ul> * <li>{@code UserManager#getEnabledProfileIds(int)} ()} returns at least one other profile for * the calling user.</li> - * <li>The calling app has requested</li> - * {@code android.Manifest.permission.INTERACT_ACROSS_PROFILES} in its manifest. - * <li>The calling package has either been whitelisted by default by the OEM or has been - * explicitly whitelisted by the admin via - * {@link android.app.admin.DevicePolicyManager#setCrossProfilePackages(ComponentName, Set)}. - * </li> + * <li>The calling app has requested + * {@code android.Manifest.permission.INTERACT_ACROSS_PROFILES} in its manifest.</li> * </ul> * * <p>Note that in order for the user to be able to grant the consent, the requesting package diff --git a/core/java/android/content/pm/FeatureInfo.java b/core/java/android/content/pm/FeatureInfo.java index 9f3ab77062ef..89269e0d7aa5 100644 --- a/core/java/android/content/pm/FeatureInfo.java +++ b/core/java/android/content/pm/FeatureInfo.java @@ -108,7 +108,7 @@ public class FeatureInfo implements Parcelable { @Override public void writeToParcel(Parcel dest, int parcelableFlags) { - dest.writeString(name); + dest.writeString8(name); dest.writeInt(version); dest.writeInt(reqGlEsVersion); dest.writeInt(flags); @@ -138,7 +138,7 @@ public class FeatureInfo implements Parcelable { }; private FeatureInfo(Parcel source) { - name = source.readString(); + name = source.readString8(); version = source.readInt(); reqGlEsVersion = source.readInt(); flags = source.readInt(); diff --git a/core/java/android/content/pm/IDataLoaderStatusListener.aidl b/core/java/android/content/pm/IDataLoaderStatusListener.aidl index 9819b5d4eeb9..ffe8b183e926 100644 --- a/core/java/android/content/pm/IDataLoaderStatusListener.aidl +++ b/core/java/android/content/pm/IDataLoaderStatusListener.aidl @@ -21,16 +21,29 @@ package android.content.pm; * @hide */ oneway interface IDataLoaderStatusListener { - /** Data loader status */ + /** When this status is returned from DataLoader, it means that the DataLoader + * process is running, bound to and has handled onCreate(). */ const int DATA_LOADER_CREATED = 0; + /** Listener will receive this status when the DataLoader process died, + * binder disconnected or class destroyed. */ const int DATA_LOADER_DESTROYED = 1; + /** DataLoader can receive missing pages and read pages notifications, + * and ready to provide data. */ const int DATA_LOADER_STARTED = 2; + /** DataLoader no longer ready to provide data and is not receiving + * any notifications from IncFS. */ const int DATA_LOADER_STOPPED = 3; + /** DataLoader streamed everything necessary to continue installation. */ const int DATA_LOADER_IMAGE_READY = 4; + /** Installation can't continue as DataLoader failed to stream necessary data. */ const int DATA_LOADER_IMAGE_NOT_READY = 5; + /** DataLoader reports that this instance is invalid and can never be restored. + * Warning: this is a terminal status that data loader should use carefully and + * the system should almost never use - e.g. only if all recovery attempts + * fail and all retry limits are exceeded. */ const int DATA_LOADER_UNRECOVERABLE = 6; /** Data loader status callback */ diff --git a/core/java/android/content/pm/InstrumentationInfo.java b/core/java/android/content/pm/InstrumentationInfo.java index 574a1ee2d0ce..745a6c1a0dff 100644 --- a/core/java/android/content/pm/InstrumentationInfo.java +++ b/core/java/android/content/pm/InstrumentationInfo.java @@ -157,21 +157,21 @@ public class InstrumentationInfo extends PackageItemInfo implements Parcelable { public void writeToParcel(Parcel dest, int parcelableFlags) { super.writeToParcel(dest, parcelableFlags); - dest.writeString(targetPackage); - dest.writeString(targetProcesses); - dest.writeString(sourceDir); - dest.writeString(publicSourceDir); + dest.writeString8(targetPackage); + dest.writeString8(targetProcesses); + dest.writeString8(sourceDir); + dest.writeString8(publicSourceDir); dest.writeStringArray(splitNames); dest.writeStringArray(splitSourceDirs); dest.writeStringArray(splitPublicSourceDirs); dest.writeSparseArray((SparseArray) splitDependencies); - dest.writeString(dataDir); - dest.writeString(deviceProtectedDataDir); - dest.writeString(credentialProtectedDataDir); - dest.writeString(primaryCpuAbi); - dest.writeString(secondaryCpuAbi); - dest.writeString(nativeLibraryDir); - dest.writeString(secondaryNativeLibraryDir); + dest.writeString8(dataDir); + dest.writeString8(deviceProtectedDataDir); + dest.writeString8(credentialProtectedDataDir); + dest.writeString8(primaryCpuAbi); + dest.writeString8(secondaryCpuAbi); + dest.writeString8(nativeLibraryDir); + dest.writeString8(secondaryNativeLibraryDir); dest.writeInt((handleProfiling == false) ? 0 : 1); dest.writeInt((functionalTest == false) ? 0 : 1); } @@ -189,21 +189,21 @@ public class InstrumentationInfo extends PackageItemInfo implements Parcelable { @SuppressWarnings("unchecked") private InstrumentationInfo(Parcel source) { super(source); - targetPackage = source.readString(); - targetProcesses = source.readString(); - sourceDir = source.readString(); - publicSourceDir = source.readString(); + targetPackage = source.readString8(); + targetProcesses = source.readString8(); + sourceDir = source.readString8(); + publicSourceDir = source.readString8(); splitNames = source.readStringArray(); splitSourceDirs = source.readStringArray(); splitPublicSourceDirs = source.readStringArray(); splitDependencies = source.readSparseArray(null); - dataDir = source.readString(); - deviceProtectedDataDir = source.readString(); - credentialProtectedDataDir = source.readString(); - primaryCpuAbi = source.readString(); - secondaryCpuAbi = source.readString(); - nativeLibraryDir = source.readString(); - secondaryNativeLibraryDir = source.readString(); + dataDir = source.readString8(); + deviceProtectedDataDir = source.readString8(); + credentialProtectedDataDir = source.readString8(); + primaryCpuAbi = source.readString8(); + secondaryCpuAbi = source.readString8(); + nativeLibraryDir = source.readString8(); + secondaryNativeLibraryDir = source.readString8(); handleProfiling = source.readInt() != 0; functionalTest = source.readInt() != 0; } diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java index 85c698f3fb0c..bb56ef7fd3a0 100644 --- a/core/java/android/content/pm/PackageInfo.java +++ b/core/java/android/content/pm/PackageInfo.java @@ -441,14 +441,14 @@ public class PackageInfo implements Parcelable { public void writeToParcel(Parcel dest, int parcelableFlags) { // Allow ApplicationInfo to be squashed. final boolean prevAllowSquashing = dest.allowSquashing(); - dest.writeString(packageName); + dest.writeString8(packageName); dest.writeStringArray(splitNames); dest.writeInt(versionCode); dest.writeInt(versionCodeMajor); - dest.writeString(versionName); + dest.writeString8(versionName); dest.writeInt(baseRevisionCode); dest.writeIntArray(splitRevisionCodes); - dest.writeString(sharedUserId); + dest.writeString8(sharedUserId); dest.writeInt(sharedUserLabel); if (applicationInfo != null) { dest.writeInt(1); @@ -475,14 +475,14 @@ public class PackageInfo implements Parcelable { dest.writeInt(isStub ? 1 : 0); dest.writeInt(coreApp ? 1 : 0); dest.writeInt(requiredForAllUsers ? 1 : 0); - dest.writeString(restrictedAccountType); - dest.writeString(requiredAccountType); - dest.writeString(overlayTarget); - dest.writeString(overlayCategory); + dest.writeString8(restrictedAccountType); + dest.writeString8(requiredAccountType); + dest.writeString8(overlayTarget); + dest.writeString8(overlayCategory); dest.writeInt(overlayPriority); dest.writeBoolean(mOverlayIsStatic); dest.writeInt(compileSdkVersion); - dest.writeString(compileSdkVersionCodename); + dest.writeString8(compileSdkVersionCodename); if (signingInfo != null) { dest.writeInt(1); signingInfo.writeToParcel(dest, parcelableFlags); @@ -508,14 +508,14 @@ public class PackageInfo implements Parcelable { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private PackageInfo(Parcel source) { - packageName = source.readString(); + packageName = source.readString8(); splitNames = source.createStringArray(); versionCode = source.readInt(); versionCodeMajor = source.readInt(); - versionName = source.readString(); + versionName = source.readString8(); baseRevisionCode = source.readInt(); splitRevisionCodes = source.createIntArray(); - sharedUserId = source.readString(); + sharedUserId = source.readString8(); sharedUserLabel = source.readInt(); int hasApp = source.readInt(); if (hasApp != 0) { @@ -540,14 +540,14 @@ public class PackageInfo implements Parcelable { isStub = source.readInt() != 0; coreApp = source.readInt() != 0; requiredForAllUsers = source.readInt() != 0; - restrictedAccountType = source.readString(); - requiredAccountType = source.readString(); - overlayTarget = source.readString(); - overlayCategory = source.readString(); + restrictedAccountType = source.readString8(); + requiredAccountType = source.readString8(); + overlayTarget = source.readString8(); + overlayCategory = source.readString8(); overlayPriority = source.readInt(); mOverlayIsStatic = source.readBoolean(); compileSdkVersion = source.readInt(); - compileSdkVersionCodename = source.readString(); + compileSdkVersionCodename = source.readString8(); int hasSigningInfo = source.readInt(); if (hasSigningInfo != 0) { signingInfo = SigningInfo.CREATOR.createFromParcel(source); diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java index 7fd5531bf20d..d41ace5bcf62 100644 --- a/core/java/android/content/pm/PackageItemInfo.java +++ b/core/java/android/content/pm/PackageItemInfo.java @@ -422,8 +422,8 @@ public class PackageItemInfo { } public void writeToParcel(Parcel dest, int parcelableFlags) { - dest.writeString(name); - dest.writeString(packageName); + dest.writeString8(name); + dest.writeString8(packageName); dest.writeInt(labelRes); TextUtils.writeToParcel(nonLocalizedLabel, dest, parcelableFlags); dest.writeInt(icon); @@ -452,8 +452,8 @@ public class PackageItemInfo { } protected PackageItemInfo(Parcel source) { - name = source.readString(); - packageName = source.readString(); + name = source.readString8(); + packageName = source.readString8(); labelRes = source.readInt(); nonLocalizedLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 85bafd9d37e2..8a57f826ad2e 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -6873,9 +6873,9 @@ public class PackageParser { /** @hide */ public boolean canHaveOatDir() { - // The following app types CANNOT have oat directory - // - non-updated system apps - return !isSystem() || isUpdatedSystemApp(); + // Nobody should be calling this method ever, but we can't rely on this. + // Thus no logic here and a reasonable return value. + return true; } public boolean isMatch(int flags) { diff --git a/core/java/android/content/pm/PackageParserCacheHelper.java b/core/java/android/content/pm/PackageParserCacheHelper.java index 44def3321c34..8212224e114c 100644 --- a/core/java/android/content/pm/PackageParserCacheHelper.java +++ b/core/java/android/content/pm/PackageParserCacheHelper.java @@ -78,10 +78,19 @@ public class PackageParserCacheHelper { /** * Read an string index from a parcel, and returns the corresponding string from the pool. */ - @Override public String readString(Parcel p) { return mStrings.get(p.readInt()); } + + @Override + public String readString8(Parcel p) { + return readString(p); + } + + @Override + public String readString16(Parcel p) { + return readString(p); + } } /** @@ -110,7 +119,6 @@ public class PackageParserCacheHelper { * Instead of writing a string directly to a parcel, this method adds it to the pool, * and write the index in the pool to the parcel. */ - @Override public void writeString(Parcel p, String s) { final Integer cur = mIndexes.get(s); if (cur != null) { @@ -133,6 +141,16 @@ public class PackageParserCacheHelper { } } + @Override + public void writeString8(Parcel p, String s) { + writeString(p, s); + } + + @Override + public void writeString16(Parcel p, String s) { + writeString(p, s); + } + /** * Closes a parcel by appending the string pool at the end and updating the pool offset, * which it assumes is at the first byte. It also uninstalls itself as a read-write helper. diff --git a/core/java/android/content/pm/ProviderInfo.java b/core/java/android/content/pm/ProviderInfo.java index 07d42dc823c4..3984ade73d6c 100644 --- a/core/java/android/content/pm/ProviderInfo.java +++ b/core/java/android/content/pm/ProviderInfo.java @@ -145,9 +145,9 @@ public final class ProviderInfo extends ComponentInfo @Override public void writeToParcel(Parcel out, int parcelableFlags) { super.writeToParcel(out, parcelableFlags); - out.writeString(authority); - out.writeString(readPermission); - out.writeString(writePermission); + out.writeString8(authority); + out.writeString8(readPermission); + out.writeString8(writePermission); out.writeInt(grantUriPermissions ? 1 : 0); out.writeInt(forceUriPermissions ? 1 : 0); out.writeTypedArray(uriPermissionPatterns, parcelableFlags); @@ -175,9 +175,9 @@ public final class ProviderInfo extends ComponentInfo private ProviderInfo(Parcel in) { super(in); - authority = in.readString(); - readPermission = in.readString(); - writePermission = in.readString(); + authority = in.readString8(); + readPermission = in.readString8(); + writePermission = in.readString8(); grantUriPermissions = in.readInt() != 0; forceUriPermissions = in.readInt() != 0; uriPermissionPatterns = in.createTypedArray(PatternMatcher.CREATOR); diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java index 5f90b6c43c60..d3f9e2486a64 100644 --- a/core/java/android/content/pm/ServiceInfo.java +++ b/core/java/android/content/pm/ServiceInfo.java @@ -244,7 +244,7 @@ public class ServiceInfo extends ComponentInfo public void writeToParcel(Parcel dest, int parcelableFlags) { super.writeToParcel(dest, parcelableFlags); - dest.writeString(permission); + dest.writeString8(permission); dest.writeInt(flags); dest.writeInt(mForegroundServiceType); } @@ -261,7 +261,7 @@ public class ServiceInfo extends ComponentInfo private ServiceInfo(Parcel source) { super(source); - permission = source.readString(); + permission = source.readString8(); flags = source.readInt(); mForegroundServiceType = source.readInt(); } diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java index f7c96a3a02c1..2f67f6ddc082 100644 --- a/core/java/android/database/sqlite/SQLiteConnection.java +++ b/core/java/android/database/sqlite/SQLiteConnection.java @@ -228,19 +228,26 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen } catch (SQLiteCantOpenDatabaseException e) { String message = String.format("Cannot open database '%s'", file); - final Path path = FileSystems.getDefault().getPath(file); - final Path dir = path.getParent(); - - if (!Files.isDirectory(dir)) { - message += ": Directory " + dir + " doesn't exist"; - } else if (!Files.exists(path)) { - message += ": File " + path + " doesn't exist"; - } else if (!Files.isReadable(path)) { - message += ": File " + path + " is not readable"; - } else if (Files.isDirectory(path)) { - message += ": Path " + path + " is a directory"; - } else { - message += ": Unknown reason"; + try { + // Try to diagnose for common reasons. If something fails in here, that's fine; + // just swallow the exception. + + final Path path = FileSystems.getDefault().getPath(file); + final Path dir = path.getParent(); + + if (!Files.isDirectory(dir)) { + message += ": Directory " + dir + " doesn't exist"; + } else if (!Files.exists(path)) { + message += ": File " + path + " doesn't exist"; + } else if (!Files.isReadable(path)) { + message += ": File " + path + " is not readable"; + } else if (Files.isDirectory(path)) { + message += ": Path " + path + " is a directory"; + } else { + message += ": Unknown reason"; + } + } catch (Throwable th) { + message += ": Unknown reason; cannot examine filesystem: " + th.getMessage(); } throw new SQLiteCantOpenDatabaseException(message, e); } finally { diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index 462110f5a795..ad9bf0745779 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -65,21 +65,23 @@ public abstract class DisplayManagerInternal { public abstract boolean isProximitySensorAvailable(); /** - * Take a screenshot of the specified display and return a buffer. + * Screenshot for internal system-only use such as rotation, etc. This method includes + * secure layers and the result should never be exposed to non-system applications. + * This method does not apply any rotation and provides the output in natural orientation. * * @param displayId The display id to take the screenshot of. * @return The buffer or null if we have failed. */ - public abstract SurfaceControl.ScreenshotGraphicBuffer screenshot(int displayId); + public abstract SurfaceControl.ScreenshotGraphicBuffer systemScreenshot(int displayId); /** - * Take a screenshot without secure layer of the specified display and return a buffer. + * General screenshot functionality that excludes secure layers and applies appropriate + * rotation that the device is currently in. * * @param displayId The display id to take the screenshot of. * @return The buffer or null if we have failed. */ - public abstract SurfaceControl.ScreenshotGraphicBuffer screenshotWithoutSecureLayer( - int displayId); + public abstract SurfaceControl.ScreenshotGraphicBuffer userScreenshot(int displayId); /** * Returns information about the specified logical display. diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java index 525bbfdbb06d..1cb4fe8cf4e7 100644 --- a/core/java/android/net/Uri.java +++ b/core/java/android/net/Uri.java @@ -500,7 +500,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { } static Uri readFrom(Parcel parcel) { - return new StringUri(parcel.readString()); + return new StringUri(parcel.readString8()); } public int describeContents() { @@ -509,7 +509,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { public void writeToParcel(Parcel parcel, int flags) { parcel.writeInt(TYPE_ID); - parcel.writeString(uriString); + parcel.writeString8(uriString); } /** Cached scheme separator index. */ @@ -875,7 +875,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { static Uri readFrom(Parcel parcel) { return new OpaqueUri( - parcel.readString(), + parcel.readString8(), Part.readFrom(parcel), Part.readFrom(parcel) ); @@ -887,7 +887,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { public void writeToParcel(Parcel parcel, int flags) { parcel.writeInt(TYPE_ID); - parcel.writeString(scheme); + parcel.writeString8(scheme); ssp.writeTo(parcel); fragment.writeTo(parcel); } @@ -1195,7 +1195,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { static Uri readFrom(Parcel parcel) { return new HierarchicalUri( - parcel.readString(), + parcel.readString8(), Part.readFrom(parcel), PathPart.readFrom(parcel), Part.readFrom(parcel), @@ -1209,7 +1209,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { public void writeToParcel(Parcel parcel, int flags) { parcel.writeInt(TYPE_ID); - parcel.writeString(scheme); + parcel.writeString8(scheme); authority.writeTo(parcel); path.writeTo(parcel); query.writeTo(parcel); @@ -2028,7 +2028,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { + mCanonicalRepresentation + ")"); } parcel.writeInt(mCanonicalRepresentation); - parcel.writeString(canonicalValue); + parcel.writeString8(canonicalValue); } } @@ -2060,7 +2060,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { static Part readFrom(Parcel parcel) { int representation = parcel.readInt(); - String value = parcel.readString(); + String value = parcel.readString8(); switch (representation) { case REPRESENTATION_ENCODED: return fromEncoded(value); @@ -2251,9 +2251,9 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { int representation = parcel.readInt(); switch (representation) { case REPRESENTATION_ENCODED: - return fromEncoded(parcel.readString()); + return fromEncoded(parcel.readString8()); case REPRESENTATION_DECODED: - return fromDecoded(parcel.readString()); + return fromDecoded(parcel.readString8()); default: throw new IllegalArgumentException("Unknown representation: " + representation); } diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index f0b7b5fa5a1a..93f6607ff9d4 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -307,7 +307,9 @@ public final class Parcel { @FastNative private static native void nativeWriteDouble(long nativePtr, double val); @FastNative - static native void nativeWriteString(long nativePtr, String val); + private static native void nativeWriteString8(long nativePtr, String val); + @FastNative + private static native void nativeWriteString16(long nativePtr, String val); @FastNative private static native void nativeWriteStrongBinder(long nativePtr, IBinder val); @FastNative @@ -325,7 +327,9 @@ public final class Parcel { @CriticalNative private static native double nativeReadDouble(long nativePtr); @FastNative - static native String nativeReadString(long nativePtr); + private static native String nativeReadString8(long nativePtr); + @FastNative + private static native String nativeReadString16(long nativePtr); @FastNative private static native IBinder nativeReadStrongBinder(long nativePtr); @FastNative @@ -386,8 +390,12 @@ public final class Parcel { * must use {@link #writeStringNoHelper(String)} to avoid * infinity recursive calls. */ - public void writeString(Parcel p, String s) { - nativeWriteString(p.mNativePtr, s); + public void writeString8(Parcel p, String s) { + p.writeString8NoHelper(s); + } + + public void writeString16(Parcel p, String s) { + p.writeString16NoHelper(s); } /** @@ -395,8 +403,12 @@ public final class Parcel { * must use {@link #readStringNoHelper()} to avoid * infinity recursive calls. */ - public String readString(Parcel p) { - return nativeReadString(p.mNativePtr); + public String readString8(Parcel p) { + return p.readString8NoHelper(); + } + + public String readString16(Parcel p) { + return p.readString16NoHelper(); } } @@ -759,7 +771,17 @@ public final class Parcel { * growing dataCapacity() if needed. */ public final void writeString(@Nullable String val) { - mReadWriteHelper.writeString(this, val); + writeString16(val); + } + + /** {@hide} */ + public final void writeString8(@Nullable String val) { + mReadWriteHelper.writeString8(this, val); + } + + /** {@hide} */ + public final void writeString16(@Nullable String val) { + mReadWriteHelper.writeString16(this, val); } /** @@ -770,7 +792,17 @@ public final class Parcel { * @hide */ public void writeStringNoHelper(@Nullable String val) { - nativeWriteString(mNativePtr, val); + writeString16NoHelper(val); + } + + /** {@hide} */ + public void writeString8NoHelper(@Nullable String val) { + nativeWriteString8(mNativePtr, val); + } + + /** {@hide} */ + public void writeString16NoHelper(@Nullable String val) { + nativeWriteString16(mNativePtr, val); } /** @@ -2337,7 +2369,17 @@ public final class Parcel { */ @Nullable public final String readString() { - return mReadWriteHelper.readString(this); + return readString16(); + } + + /** {@hide} */ + public final @Nullable String readString8() { + return mReadWriteHelper.readString8(this); + } + + /** {@hide} */ + public final @Nullable String readString16() { + return mReadWriteHelper.readString16(this); } /** @@ -2347,9 +2389,18 @@ public final class Parcel { * * @hide */ - @Nullable - public String readStringNoHelper() { - return nativeReadString(mNativePtr); + public @Nullable String readStringNoHelper() { + return readString16NoHelper(); + } + + /** {@hide} */ + public @Nullable String readString8NoHelper() { + return nativeReadString8(mNativePtr); + } + + /** {@hide} */ + public @Nullable String readString16NoHelper() { + return nativeReadString16(mNativePtr); } /** diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java index 251995a14090..321dc9e2246e 100644 --- a/core/java/android/os/incremental/IncrementalFileStorages.java +++ b/core/java/android/os/incremental/IncrementalFileStorages.java @@ -38,16 +38,10 @@ import android.content.pm.DataLoaderParams; import android.content.pm.IDataLoaderStatusListener; import android.content.pm.InstallationFileParcel; import android.text.TextUtils; -import android.util.Slog; import java.io.File; import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; import java.util.List; -import java.util.Objects; -import java.util.Random; /** * This class manages storage instances used during a package installation session. @@ -56,13 +50,9 @@ import java.util.Random; public final class IncrementalFileStorages { private static final String TAG = "IncrementalFileStorages"; - private static final String TMP_DIR_ROOT = "/data/incremental/tmp"; - private static final Random TMP_DIR_RANDOM = new Random(); - + private @NonNull final IncrementalManager mIncrementalManager; + private @NonNull final File mStageDir; private @Nullable IncrementalStorage mDefaultStorage; - private @Nullable String mDefaultDir; - private @NonNull IncrementalManager mIncrementalManager; - private @NonNull File mStageDir; /** * Set up files and directories used in an installation session. Only used by Incremental. @@ -85,72 +75,63 @@ public final class IncrementalFileStorages { throw new IOException("Failed to obtain incrementalManager."); } - IncrementalFileStorages result = null; - try { - result = new IncrementalFileStorages(stageDir, incrementalManager, dataLoaderParams, - dataLoaderStatusListener); - - if (!addedFiles.isEmpty()) { - result.mDefaultStorage.bind(stageDir.getAbsolutePath()); - } - - for (InstallationFileParcel file : addedFiles) { - if (file.location == LOCATION_DATA_APP) { - try { - result.addApkFile(file); - } catch (IOException e) { - // TODO(b/146080380): add incremental-specific error code - throw new IOException( - "Failed to add file to IncFS: " + file.name + ", reason: ", e); - } - } else { - throw new IOException("Unknown file location: " + file.location); + final IncrementalFileStorages result = + new IncrementalFileStorages(stageDir, incrementalManager, dataLoaderParams, + dataLoaderStatusListener); + for (InstallationFileParcel file : addedFiles) { + if (file.location == LOCATION_DATA_APP) { + try { + result.addApkFile(file); + } catch (IOException e) { + // TODO(b/146080380): add incremental-specific error code + throw new IOException( + "Failed to add file to IncFS: " + file.name + ", reason: ", e); } + } else { + throw new IOException("Unknown file location: " + file.location); } + } - // TODO(b/146080380): remove 5 secs wait in startLoading - if (!result.mDefaultStorage.startLoading()) { - // TODO(b/146080380): add incremental-specific error code - throw new IOException("Failed to start loading data for Incremental installation."); - } - - return result; - } catch (IOException e) { - Slog.e(TAG, "Failed to initialize Incremental file storages. Cleaning up...", e); - if (result != null) { - result.cleanUp(); - } - throw e; + if (!result.mDefaultStorage.startLoading()) { + // TODO(b/146080380): add incremental-specific error code + throw new IOException("Failed to start loading data for Incremental installation."); } + + return result; } private IncrementalFileStorages(@NonNull File stageDir, @NonNull IncrementalManager incrementalManager, @NonNull DataLoaderParams dataLoaderParams, @Nullable IDataLoaderStatusListener dataLoaderStatusListener) throws IOException { - mStageDir = stageDir; - mIncrementalManager = incrementalManager; - if (dataLoaderParams.getComponentName().getPackageName().equals("local")) { - final String incrementalPath = dataLoaderParams.getArguments(); - mDefaultDir = incrementalPath; - if (TextUtils.isEmpty(mDefaultDir)) { - throw new IOException("Failed to create storage: incrementalPath is empty"); - } - mDefaultStorage = mIncrementalManager.openStorage(incrementalPath); - } else { - mDefaultDir = getTempDir(); - if (mDefaultDir == null) { - throw new IOException("Failed to create storage: tempDir is empty"); + try { + mStageDir = stageDir; + mIncrementalManager = incrementalManager; + if (dataLoaderParams.getComponentName().getPackageName().equals("local")) { + final String incrementalPath = dataLoaderParams.getArguments(); + if (TextUtils.isEmpty(incrementalPath)) { + throw new IOException("Failed to create storage: incrementalPath is empty"); + } + mDefaultStorage = mIncrementalManager.openStorage(incrementalPath); + if (mDefaultStorage == null) { + throw new IOException( + "Couldn't open incremental storage at " + incrementalPath); + } + mDefaultStorage.bind(stageDir.getAbsolutePath()); + } else { + mDefaultStorage = mIncrementalManager.createStorage(stageDir.getAbsolutePath(), + dataLoaderParams, + dataLoaderStatusListener, + IncrementalManager.CREATE_MODE_CREATE + | IncrementalManager.CREATE_MODE_TEMPORARY_BIND, false); + if (mDefaultStorage == null) { + throw new IOException( + "Couldn't create incremental storage at " + stageDir); + } } - mDefaultStorage = mIncrementalManager.createStorage(mDefaultDir, - dataLoaderParams, - dataLoaderStatusListener, - IncrementalManager.CREATE_MODE_CREATE - | IncrementalManager.CREATE_MODE_TEMPORARY_BIND, false); - } - - if (mDefaultStorage == null) { - throw new IOException("Failed to create storage"); + } catch (IOException e) { + cleanUp(); + throw e; } } @@ -167,27 +148,14 @@ public final class IncrementalFileStorages { * TODO(b/136132412): make sure unnecessary binds are removed but useful storages are kept */ public void cleanUp() { - Objects.requireNonNull(mDefaultStorage); + if (mDefaultStorage == null) { + return; + } try { - mDefaultStorage.unBind(mDefaultDir); mDefaultStorage.unBind(mStageDir.getAbsolutePath()); } catch (IOException ignored) { } - - mDefaultDir = null; mDefaultStorage = null; } - - private static String getTempDir() { - final Path tmpDir = Paths.get(TMP_DIR_ROOT, - String.valueOf(TMP_DIR_RANDOM.nextInt(Integer.MAX_VALUE - 1))); - try { - Files.createDirectories(tmpDir); - } catch (Exception ex) { - Slog.e(TAG, "Failed to create dir", ex); - return null; - } - return tmpDir.toAbsolutePath().toString(); - } } diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java index 35518db32829..916edfae679f 100644 --- a/core/java/android/os/incremental/IncrementalManager.java +++ b/core/java/android/os/incremental/IncrementalManager.java @@ -32,8 +32,12 @@ import java.io.File; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; /** * Provides operations to open or create an IncrementalStorage, using IIncrementalService @@ -176,25 +180,6 @@ public final class IncrementalManager { } /** - * Iterates through path parents to find the base dir of an Incremental Storage. - * - * @param file Target file to search storage for. - * @return Absolute path which is a bind-mount point of Incremental File System. - */ - @Nullable - private Path getStoragePathForFile(File file) { - File currentPath = new File(file.getParent()); - while (currentPath.getParent() != null) { - IncrementalStorage storage = openStorage(currentPath.getAbsolutePath()); - if (storage != null) { - return currentPath.toPath(); - } - currentPath = new File(currentPath.getParent()); - } - return null; - } - - /** * Set up an app's code path. The expected outcome of this method is: * 1) The actual apk directory under /data/incremental is bind-mounted to the parent directory * of {@code afterCodeFile}. @@ -212,29 +197,27 @@ public final class IncrementalManager { */ public void renameCodePath(File beforeCodeFile, File afterCodeFile) throws IllegalArgumentException, IOException { - final String beforeCodePath = beforeCodeFile.getAbsolutePath(); - final String afterCodePathParent = afterCodeFile.getParentFile().getAbsolutePath(); - if (!isIncrementalPath(beforeCodePath)) { - throw new IllegalArgumentException("Not an Incremental path: " + beforeCodePath); - } - final String afterCodePathName = afterCodeFile.getName(); - final Path apkStoragePath = Paths.get(beforeCodePath); - if (apkStoragePath == null || apkStoragePath.toAbsolutePath() == null) { - throw new IOException("Invalid source storage path for: " + beforeCodePath); - } - final IncrementalStorage apkStorage = - openStorage(apkStoragePath.toAbsolutePath().toString()); + final File beforeCodeAbsolute = beforeCodeFile.getAbsoluteFile(); + final IncrementalStorage apkStorage = openStorage(beforeCodeAbsolute.toString()); if (apkStorage == null) { - throw new IOException("Failed to retrieve storage from Incremental Service."); + throw new IllegalArgumentException("Not an Incremental path: " + beforeCodeAbsolute); } - final IncrementalStorage linkedApkStorage = createStorage(afterCodePathParent, apkStorage, - IncrementalManager.CREATE_MODE_CREATE - | IncrementalManager.CREATE_MODE_PERMANENT_BIND); + final String targetStorageDir = afterCodeFile.getAbsoluteFile().getParent(); + final IncrementalStorage linkedApkStorage = + createStorage(targetStorageDir, apkStorage, + IncrementalManager.CREATE_MODE_CREATE + | IncrementalManager.CREATE_MODE_PERMANENT_BIND); if (linkedApkStorage == null) { - throw new IOException("Failed to create linked storage at dir: " + afterCodePathParent); + throw new IOException("Failed to create linked storage at dir: " + targetStorageDir); + } + try { + final String afterCodePathName = afterCodeFile.getName(); + linkFiles(apkStorage, beforeCodeAbsolute, "", linkedApkStorage, afterCodePathName); + apkStorage.unBind(beforeCodeAbsolute.toString()); + } catch (Exception e) { + linkedApkStorage.unBind(targetStorageDir); + throw e; } - linkFiles(apkStorage, beforeCodeFile, "", linkedApkStorage, afterCodePathName); - apkStorage.unBind(beforeCodePath); } /** @@ -252,22 +235,27 @@ public final class IncrementalManager { private void linkFiles(IncrementalStorage sourceStorage, File sourceAbsolutePath, String sourceRelativePath, IncrementalStorage targetStorage, String targetRelativePath) throws IOException { - targetStorage.makeDirectory(targetRelativePath); - final File[] entryList = sourceAbsolutePath.listFiles(); - for (int i = 0; i < entryList.length; i++) { - final File entry = entryList[i]; - final String entryName = entryList[i].getName(); - final String sourceEntryRelativePath = - sourceRelativePath.isEmpty() ? entryName : sourceRelativePath + "/" + entryName; - final String targetEntryRelativePath = targetRelativePath + "/" + entryName; - if (entry.isFile()) { + final Path sourceBase = sourceAbsolutePath.toPath().resolve(sourceRelativePath); + final Path targetRelative = Paths.get(targetRelativePath); + Files.walkFileTree(sourceAbsolutePath.toPath(), new SimpleFileVisitor<Path>() { + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) + throws IOException { + final Path relativeDir = sourceBase.relativize(dir); + targetStorage.makeDirectory(targetRelative.resolve(relativeDir).toString()); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) + throws IOException { + final Path relativeFile = sourceBase.relativize(file); sourceStorage.makeLink( - sourceEntryRelativePath, targetStorage, targetEntryRelativePath); - } else if (entry.isDirectory()) { - linkFiles(sourceStorage, entry, sourceEntryRelativePath, targetStorage, - targetEntryRelativePath); + file.toAbsolutePath().toString(), targetStorage, + targetRelative.resolve(relativeFile).toString()); + return FileVisitResult.CONTINUE; } - } + }); } /** diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java index df4ead13d5cb..28639b3ea2f7 100644 --- a/core/java/android/text/TextUtils.java +++ b/core/java/android/text/TextUtils.java @@ -748,7 +748,7 @@ public class TextUtils { int parcelableFlags) { if (cs instanceof Spanned) { p.writeInt(0); - p.writeString(cs.toString()); + p.writeString8(cs.toString()); Spanned sp = (Spanned) cs; Object[] os = sp.getSpans(0, cs.length(), Object.class); @@ -785,9 +785,9 @@ public class TextUtils { } else { p.writeInt(1); if (cs != null) { - p.writeString(cs.toString()); + p.writeString8(cs.toString()); } else { - p.writeString(null); + p.writeString8(null); } } } @@ -807,7 +807,7 @@ public class TextUtils { public CharSequence createFromParcel(Parcel p) { int kind = p.readInt(); - String string = p.readString(); + String string = p.readString8(); if (string == null) { return null; } diff --git a/core/java/android/text/style/DynamicDrawableSpan.java b/core/java/android/text/style/DynamicDrawableSpan.java index f37e4238a1c6..d6d99f846e16 100644 --- a/core/java/android/text/style/DynamicDrawableSpan.java +++ b/core/java/android/text/style/DynamicDrawableSpan.java @@ -166,7 +166,7 @@ public abstract class DynamicDrawableSpan extends ReplacementSpan { if (mVerticalAlignment == ALIGN_BASELINE) { transY -= paint.getFontMetricsInt().descent; } else if (mVerticalAlignment == ALIGN_CENTER) { - transY = (bottom - top) / 2 - b.getBounds().height() / 2; + transY = top + (bottom - top) / 2 - b.getBounds().height() / 2; } canvas.translate(x, transY); diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java index bdc8ba835fbe..df891303bb1d 100644 --- a/core/java/android/view/ImeInsetsSourceConsumer.java +++ b/core/java/android/view/ImeInsetsSourceConsumer.java @@ -153,7 +153,7 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { @Override protected boolean isRequestedVisibleAwaitingControl() { - return mIsRequestedVisibleAwaitingControl; + return mIsRequestedVisibleAwaitingControl || isRequestedVisible(); } private boolean isDummyOrEmptyEditor(EditorInfo info) { diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java index a72383913420..2dcfd899adf4 100644 --- a/core/java/android/view/InsetsSourceConsumer.java +++ b/core/java/android/view/InsetsSourceConsumer.java @@ -69,6 +69,13 @@ public class InsetsSourceConsumer { private Rect mPendingFrame; private Rect mPendingVisibleFrame; + /** + * Indicates if we have the pending animation. When we have the control, we need to play the + * animation if the requested visibility is different from the current state. But if we haven't + * had a leash yet, we will set this flag, and play the animation once we get the leash. + */ + private boolean mIsAnimationPending; + public InsetsSourceConsumer(@InternalInsetsType int type, InsetsState state, Supplier<Transaction> transactionSupplier, InsetsController controller) { mType = type; @@ -107,13 +114,21 @@ public class InsetsSourceConsumer { } else { // We are gaining control, and need to run an animation since previous state // didn't match - if (isRequestedVisibleAwaitingControl() != mState.getSource(mType).isVisible()) { - if (isRequestedVisibleAwaitingControl()) { + final boolean requestedVisible = isRequestedVisibleAwaitingControl(); + final boolean needAnimation = requestedVisible != mState.getSource(mType).isVisible(); + if (control.getLeash() != null && (needAnimation || mIsAnimationPending)) { + if (requestedVisible) { showTypes[0] |= toPublicType(getType()); } else { hideTypes[0] |= toPublicType(getType()); } + mIsAnimationPending = false; } else { + if (needAnimation) { + // We need animation but we haven't had a leash yet. Set this flag that when we + // get the leash we can play the deferred animation. + mIsAnimationPending = true; + } // We are gaining control, but don't need to run an animation. // However make sure that the leash visibility is still up to date. if (applyLocalVisibilityOverride()) { @@ -274,7 +289,10 @@ public class InsetsSourceConsumer { * the moment. */ protected void setRequestedVisible(boolean requestedVisible) { - mRequestedVisible = requestedVisible; + if (mRequestedVisible != requestedVisible) { + mRequestedVisible = requestedVisible; + mIsAnimationPending = false; + } if (applyLocalVisibilityOverride()) { mController.notifyVisibilityChanged(); } diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index cc380f32297e..e4dbd63765b6 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -514,33 +514,24 @@ public interface WindowManager extends ViewManager { int TAKE_SCREENSHOT_PROVIDED_IMAGE = 3; /** - * Parcel key for the screen shot bitmap sent with messages of type - * {@link #TAKE_SCREENSHOT_PROVIDED_IMAGE}, type {@link android.graphics.Bitmap} - * @hide - */ - String PARCEL_KEY_SCREENSHOT_BITMAP = "screenshot_screen_bitmap"; - - /** - * Parcel key for the screen bounds of the image sent with messages of type - * [@link {@link #TAKE_SCREENSHOT_PROVIDED_IMAGE}], type {@link Rect} in screen coordinates. - * @hide - */ - String PARCEL_KEY_SCREENSHOT_BOUNDS = "screenshot_screen_bounds"; - - /** - * Parcel key for the task id of the task that the screen shot was taken of, sent with messages - * of type [@link {@link #TAKE_SCREENSHOT_PROVIDED_IMAGE}], type int. - * @hide - */ - String PARCEL_KEY_SCREENSHOT_TASK_ID = "screenshot_task_id"; - - /** - * Parcel key for the visible insets of the image sent with messages of type - * [@link {@link #TAKE_SCREENSHOT_PROVIDED_IMAGE}], type {@link android.graphics.Insets} in - * screen coordinates. + * Enum listing the possible sources from which a screenshot was originated. Used for logging. + * * @hide */ - String PARCEL_KEY_SCREENSHOT_INSETS = "screenshot_insets"; + @IntDef({ScreenshotSource.SCREENSHOT_GLOBAL_ACTIONS, + ScreenshotSource.SCREENSHOT_KEY_CHORD, + ScreenshotSource.SCREENSHOT_KEY_OTHER, + ScreenshotSource.SCREENSHOT_OVERVIEW, + ScreenshotSource.SCREENSHOT_ACCESSIBILITY_ACTIONS, + ScreenshotSource.SCREENSHOT_OTHER}) + @interface ScreenshotSource { + int SCREENSHOT_GLOBAL_ACTIONS = 0; + int SCREENSHOT_KEY_CHORD = 1; + int SCREENSHOT_KEY_OTHER = 2; + int SCREENSHOT_OVERVIEW = 3; + int SCREENSHOT_ACCESSIBILITY_ACTIONS = 4; + int SCREENSHOT_OTHER = 5; + } /** * @hide diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java index fba6a55ef6db..94591eafe72d 100644 --- a/core/java/android/view/WindowManagerGlobal.java +++ b/core/java/android/view/WindowManagerGlobal.java @@ -145,6 +145,7 @@ public final class WindowManagerGlobal { public static final int ADD_INVALID_DISPLAY = -9; public static final int ADD_INVALID_TYPE = -10; public static final int ADD_INVALID_USER = -11; + public static final int ADD_TOO_MANY_TOKENS = -12; @UnsupportedAppUsage private static WindowManagerGlobal sDefaultWindowManager; diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index 4980b335646b..6646c3188fc7 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -3437,6 +3437,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @hide */ @UnsupportedAppUsage + @TestApi public long getSourceNodeId() { return mSourceNodeId; } diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 9f03d956f22c..16e87f8bca9a 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -5863,6 +5863,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te case KeyEvent.KEYCODE_DPAD_RIGHT: case KeyEvent.KEYCODE_DPAD_CENTER: case KeyEvent.KEYCODE_ENTER: + case KeyEvent.KEYCODE_NUMPAD_ENTER: okToSend = false; break; case KeyEvent.KEYCODE_BACK: diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java index 8d9ae58be290..00526d9f8a2c 100644 --- a/core/java/android/widget/AutoCompleteTextView.java +++ b/core/java/android/widget/AutoCompleteTextView.java @@ -821,6 +821,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe // was a click, the text view gets the selected item // from the drop down as its content case KeyEvent.KEYCODE_ENTER: + case KeyEvent.KEYCODE_NUMPAD_ENTER: case KeyEvent.KEYCODE_DPAD_CENTER: case KeyEvent.KEYCODE_TAB: if (event.hasNoModifiers()) { diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java index 8595fece4278..6425cf11ccb3 100644..100755 --- a/core/java/android/widget/ListPopupWindow.java +++ b/core/java/android/widget/ListPopupWindow.java @@ -1005,6 +1005,7 @@ public class ListPopupWindow implements ShowableListMenu { case KeyEvent.KEYCODE_DPAD_CENTER: case KeyEvent.KEYCODE_DPAD_DOWN: case KeyEvent.KEYCODE_DPAD_UP: + case KeyEvent.KEYCODE_NUMPAD_ENTER: return true; } } else { diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java index e9e0c1498034..baaf2a763487 100644 --- a/core/java/android/widget/NumberPicker.java +++ b/core/java/android/widget/NumberPicker.java @@ -1033,6 +1033,7 @@ public class NumberPicker extends LinearLayout { switch (keyCode) { case KeyEvent.KEYCODE_DPAD_CENTER: case KeyEvent.KEYCODE_ENTER: + case KeyEvent.KEYCODE_NUMPAD_ENTER: removeAllCallbacks(); break; case KeyEvent.KEYCODE_DPAD_DOWN: diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 7f6c0d2077f1..7016c5cf0de6 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -1213,7 +1213,7 @@ public class RemoteViews implements Parcelable, Filter { BitmapReflectionAction(Parcel in) { viewId = in.readInt(); - methodName = in.readString(); + methodName = in.readString8(); bitmapId = in.readInt(); bitmap = mBitmapCache.getBitmapForId(bitmapId); } @@ -1221,7 +1221,7 @@ public class RemoteViews implements Parcelable, Filter { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(viewId); - dest.writeString(methodName); + dest.writeString8(methodName); dest.writeInt(bitmapId); } @@ -1282,7 +1282,7 @@ public class RemoteViews implements Parcelable, Filter { ReflectionAction(Parcel in) { this.viewId = in.readInt(); - this.methodName = in.readString(); + this.methodName = in.readString8(); this.type = in.readInt(); //noinspection ConstantIfStatement if (false) { @@ -1318,7 +1318,7 @@ public class RemoteViews implements Parcelable, Filter { this.value = (char)in.readInt(); break; case STRING: - this.value = in.readString(); + this.value = in.readString8(); break; case CHAR_SEQUENCE: this.value = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); @@ -1347,7 +1347,7 @@ public class RemoteViews implements Parcelable, Filter { public void writeToParcel(Parcel out, int flags) { out.writeInt(this.viewId); - out.writeString(this.methodName); + out.writeString8(this.methodName); out.writeInt(this.type); //noinspection ConstantIfStatement if (false) { @@ -1383,7 +1383,7 @@ public class RemoteViews implements Parcelable, Filter { out.writeInt((int)((Character)this.value).charValue()); break; case STRING: - out.writeString((String)this.value); + out.writeString8((String)this.value); break; case CHAR_SEQUENCE: TextUtils.writeToParcel((CharSequence)this.value, out, flags); diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java index 15959c221ffc..6ef570cdc784 100644..100755 --- a/core/java/android/widget/SearchView.java +++ b/core/java/android/widget/SearchView.java @@ -1076,7 +1076,8 @@ public class SearchView extends LinearLayout implements CollapsibleActionView { // The search key is handled by the dialog's onKeyDown(). if (!mSearchSrcTextView.isEmpty() && event.hasNoModifiers()) { if (event.getAction() == KeyEvent.ACTION_UP) { - if (keyCode == KeyEvent.KEYCODE_ENTER) { + if (keyCode == KeyEvent.KEYCODE_ENTER + || keyCode == KeyEvent.KEYCODE_NUMPAD_ENTER) { v.cancelLongPress(); // Launch as a regular search. diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java index 217693eed686..61c77bc2f90e 100644 --- a/core/java/android/widget/SimpleMonthView.java +++ b/core/java/android/widget/SimpleMonthView.java @@ -420,6 +420,7 @@ class SimpleMonthView extends View { break; case KeyEvent.KEYCODE_DPAD_CENTER: case KeyEvent.KEYCODE_ENTER: + case KeyEvent.KEYCODE_NUMPAD_ENTER: if (mHighlightedDay != -1) { onDayClicked(mHighlightedDay); return true; diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index e933f18af979..ec07574f141f 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -8359,6 +8359,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener switch (keyCode) { case KeyEvent.KEYCODE_ENTER: + case KeyEvent.KEYCODE_NUMPAD_ENTER: if (event.hasNoModifiers()) { // When mInputContentType is set, we know that we are // running in a "modern" cupcake environment, so don't need @@ -8586,6 +8587,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return super.onKeyUp(keyCode, event); case KeyEvent.KEYCODE_ENTER: + case KeyEvent.KEYCODE_NUMPAD_ENTER: if (event.hasNoModifiers()) { if (mEditor != null && mEditor.mInputContentType != null && mEditor.mInputContentType.onEditorActionListener != null diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java index b1e356d258ee..bcb32fb60f47 100644 --- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java +++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java @@ -437,6 +437,9 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter { resetViewVisibilitiesForWorkProfileEmptyState(emptyStateView); emptyStateView.setVisibility(View.VISIBLE); + View container = emptyStateView.findViewById(R.id.resolver_empty_state_container); + setupContainerPadding(container); + TextView title = emptyStateView.findViewById(R.id.resolver_empty_state_title); title.setText(titleRes); @@ -463,6 +466,12 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter { activeListAdapter.markTabLoaded(); } + /** + * Sets up the padding of the view containing the empty state screens. + * <p>This method is meant to be overridden so that subclasses can customize the padding. + */ + protected void setupContainerPadding(View container) {} + private void showConsumerUserNoAppsAvailableEmptyState(ResolverListAdapter activeListAdapter) { ProfileDescriptor descriptor = getItem( userHandleToPageIndex(activeListAdapter.getUserHandle())); diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 970bab9bc53b..b671fa74bf9a 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -2721,11 +2721,12 @@ public class ChooserActivity extends ResolverActivity implements } private void setupScrollListener() { - if (mResolverDrawerLayout == null || shouldShowTabs()) { + if (mResolverDrawerLayout == null) { return; } - final View chooserHeader = mResolverDrawerLayout.findViewById(R.id.chooser_header); - final float defaultElevation = chooserHeader.getElevation(); + int elevatedViewResId = shouldShowTabs() ? R.id.resolver_tab_divider : R.id.chooser_header; + final View elevatedView = mResolverDrawerLayout.findViewById(elevatedViewResId); + final float defaultElevation = elevatedView.getElevation(); final float chooserHeaderScrollElevation = getResources().getDimensionPixelSize(R.dimen.chooser_header_scroll_elevation); @@ -2738,12 +2739,12 @@ public class ChooserActivity extends ResolverActivity implements if (view.getChildCount() > 0) { View child = view.getLayoutManager().findViewByPosition(0); if (child == null || child.getTop() < 0) { - chooserHeader.setElevation(chooserHeaderScrollElevation); + elevatedView.setElevation(chooserHeaderScrollElevation); return; } } - chooserHeader.setElevation(defaultElevation); + elevatedView.setElevation(defaultElevation); } }); } @@ -2885,6 +2886,13 @@ public class ChooserActivity extends ResolverActivity implements return METRICS_CATEGORY_CHOOSER; } + @Override + protected void onProfileTabSelected() { + ChooserGridAdapter currentRootAdapter = + mChooserMultiProfilePagerAdapter.getCurrentRootAdapter(); + currentRootAdapter.updateDirectShareExpansion(); + } + /** * Adapter for all types of items and targets in ShareSheet. * Note that ranked sections like Direct Share - while appearing grid-like - are handled on the @@ -3357,15 +3365,7 @@ public class ChooserActivity extends ResolverActivity implements } public void handleScroll(View v, int y, int oldy) { - // Only expand direct share area if there is a minimum number of shortcuts, - // which will help reduce the amount of visible shuffling due to older-style - // direct share targets. - int orientation = getResources().getConfiguration().orientation; - boolean canExpandDirectShare = - mChooserListAdapter.getNumShortcutResults() > getMaxTargetsPerRow() - && orientation == Configuration.ORIENTATION_PORTRAIT - && !isInMultiWindowMode(); - + boolean canExpandDirectShare = canExpandDirectShare(); if (mDirectShareViewHolder != null && canExpandDirectShare) { mDirectShareViewHolder.handleScroll( mChooserMultiProfilePagerAdapter.getActiveAdapterView(), y, oldy, @@ -3373,6 +3373,18 @@ public class ChooserActivity extends ResolverActivity implements } } + /** + * Only expand direct share area if there is a minimum number of shortcuts, + * which will help reduce the amount of visible shuffling due to older-style + * direct share targets. + */ + private boolean canExpandDirectShare() { + int orientation = getResources().getConfiguration().orientation; + return mChooserListAdapter.getNumShortcutResults() > getMaxTargetsPerRow() + && orientation == Configuration.ORIENTATION_PORTRAIT + && !isInMultiWindowMode(); + } + public ChooserListAdapter getListAdapter() { return mChooserListAdapter; } @@ -3380,6 +3392,19 @@ public class ChooserActivity extends ResolverActivity implements boolean shouldCellSpan(int position) { return getItemViewType(position) == VIEW_TYPE_NORMAL; } + + void updateDirectShareExpansion() { + if (mDirectShareViewHolder == null || !canExpandDirectShare()) { + return; + } + RecyclerView activeAdapterView = + mChooserMultiProfilePagerAdapter.getActiveAdapterView(); + if (mResolverDrawerLayout.isCollapsed()) { + mDirectShareViewHolder.collapse(activeAdapterView); + } else { + mDirectShareViewHolder.expand(activeAdapterView); + } + } } /** @@ -3577,6 +3602,20 @@ public class ChooserActivity extends ResolverActivity implements newHeight = Math.max(newHeight, mDirectShareMinHeight); yDiff = newHeight - prevHeight; + updateDirectShareRowHeight(view, yDiff, newHeight); + } + + void expand(RecyclerView view) { + updateDirectShareRowHeight(view, mDirectShareMaxHeight - mDirectShareCurrHeight, + mDirectShareMaxHeight); + } + + void collapse(RecyclerView view) { + updateDirectShareRowHeight(view, mDirectShareMinHeight - mDirectShareCurrHeight, + mDirectShareMinHeight); + } + + private void updateDirectShareRowHeight(RecyclerView view, int yDiff, int newHeight) { if (view == null || view.getChildCount() == 0 || yDiff == 0) { return; } diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java index 3eb0923363f2..2c4892561ddd 100644 --- a/core/java/com/android/internal/app/IntentForwarderActivity.java +++ b/core/java/com/android/internal/app/IntentForwarderActivity.java @@ -50,6 +50,9 @@ import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; /** * This is used in conjunction with @@ -74,11 +77,13 @@ public class IntentForwarderActivity extends Activity { private Injector mInjector; private MetricsLogger mMetricsLogger; + protected ExecutorService mExecutorService; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mInjector = createInjector(); + mExecutorService = Executors.newSingleThreadExecutor(); Intent intentReceived = getIntent(); String className = intentReceived.getComponent().getClassName(); @@ -118,30 +123,9 @@ public class IntentForwarderActivity extends Activity { mInjector.getIPackageManager(), getContentResolver()); if (newIntent != null) { newIntent.prepareToLeaveUser(callingUserId); - - final ResolveInfo ri = mInjector.resolveActivityAsUser(newIntent, MATCH_DEFAULT_ONLY, - targetUserId); - try { - startActivityAsCaller(newIntent, null, null, false, targetUserId); - } catch (RuntimeException e) { - int launchedFromUid = -1; - String launchedFromPackage = "?"; - try { - launchedFromUid = ActivityTaskManager.getService().getLaunchedFromUid( - getActivityToken()); - launchedFromPackage = ActivityTaskManager.getService().getLaunchedFromPackage( - getActivityToken()); - } catch (RemoteException ignored) { - } - - Slog.wtf(TAG, "Unable to launch as UID " + launchedFromUid + " package " - + launchedFromPackage + ", while running in " - + ActivityThread.currentProcessName(), e); - } - - if (shouldShowDisclosure(ri, intentReceived)) { - mInjector.showToast(userMessageId, Toast.LENGTH_LONG); - } + maybeShowDisclosureAsync(intentReceived, newIntent, targetUserId, userMessageId); + CompletableFuture.runAsync(() -> startActivityAsCaller( + newIntent, targetUserId), mExecutorService); } else { Slog.wtf(TAG, "the intent: " + intentReceived + " cannot be forwarded from user " + callingUserId + " to user " + targetUserId); @@ -149,6 +133,44 @@ public class IntentForwarderActivity extends Activity { finish(); } + private void maybeShowDisclosureAsync( + Intent intentReceived, Intent newIntent, int userId, int messageId) { + final CompletableFuture<ResolveInfo> resolveInfoFuture = + mInjector.resolveActivityAsUser(newIntent, MATCH_DEFAULT_ONLY, userId); + resolveInfoFuture.thenAcceptAsync(ri -> { + if (shouldShowDisclosure(ri, intentReceived)) { + mInjector.showToast(messageId, Toast.LENGTH_LONG); + } + }, getApplicationContext().getMainExecutor()); + } + + private void startActivityAsCaller(Intent newIntent, int userId) { + try { + startActivityAsCaller( + newIntent, + /* options= */ null, + /* permissionToken= */ null, + /* ignoreTargetSecurity= */ false, + userId); + } catch (RuntimeException e) { + int launchedFromUid = -1; + String launchedFromPackage = "?"; + try { + launchedFromUid = ActivityTaskManager.getService().getLaunchedFromUid( + getActivityToken()); + launchedFromPackage = ActivityTaskManager.getService() + .getLaunchedFromPackage(getActivityToken()); + } catch (RemoteException ignored) { + } + + Slog.wtf(TAG, "Unable to launch as UID " + launchedFromUid + " package " + + launchedFromPackage + ", while running in " + + ActivityThread.currentProcessName(), e); + } finally { + mExecutorService.shutdown(); + } + } + private void launchChooserActivityWithCorrectTab(Intent intentReceived, String className) { // When showing the sharesheet, instead of forwarding to the other profile, // we launch the sharesheet in the current user and select the other tab. @@ -322,8 +344,11 @@ public class IntentForwarderActivity extends Activity { } @Override - public ResolveInfo resolveActivityAsUser(Intent intent, int flags, int userId) { - return getPackageManager().resolveActivityAsUser(intent, flags, userId); + @Nullable + public CompletableFuture<ResolveInfo> resolveActivityAsUser( + Intent intent, int flags, int userId) { + return CompletableFuture.supplyAsync( + () -> getPackageManager().resolveActivityAsUser(intent, flags, userId)); } @Override @@ -339,7 +364,7 @@ public class IntentForwarderActivity extends Activity { PackageManager getPackageManager(); - ResolveInfo resolveActivityAsUser(Intent intent, int flags, int userId); + CompletableFuture<ResolveInfo> resolveActivityAsUser(Intent intent, int flags, int userId); void showToast(@StringRes int messageId, int duration); } diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 1bc982cdb42b..00faa3b3d21e 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -146,6 +146,7 @@ public class ResolverActivity extends Activity implements private static final String TAG = "ResolverActivity"; private static final boolean DEBUG = false; + private static final String LAST_SHOWN_TAB_KEY = "last_shown_tab_key"; private boolean mRegistered; @@ -844,9 +845,19 @@ public class ResolverActivity extends Activity implements } @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + ViewPager viewPager = findViewById(R.id.profile_pager); + outState.putInt(LAST_SHOWN_TAB_KEY, viewPager.getCurrentItem()); + } + + @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); resetButtonBar(); + ViewPager viewPager = findViewById(R.id.profile_pager); + viewPager.setCurrentItem(savedInstanceState.getInt(LAST_SHOWN_TAB_KEY)); + mMultiProfilePagerAdapter.clearInactiveProfileCache(); } private boolean isHttpSchemeAndViewAction(Intent intent) { @@ -1585,6 +1596,7 @@ public class ResolverActivity extends Activity implements TabHost tabHost = findViewById(R.id.profile_tabhost); tabHost.setup(); ViewPager viewPager = findViewById(R.id.profile_pager); + viewPager.setSaveEnabled(false); TabHost.TabSpec tabSpec = tabHost.newTabSpec(TAB_TAG_PERSONAL) .setContent(R.id.profile_pager) .setIndicator(getString(R.string.resolver_personal_tab)); @@ -1610,6 +1622,7 @@ public class ResolverActivity extends Activity implements } setupViewVisibilities(); maybeLogProfileChange(); + onProfileTabSelected(); DevicePolicyEventLogger .createEvent(DevicePolicyEnums.RESOLVER_SWITCH_TABS) .setInt(viewPager.getCurrentItem()) @@ -1628,6 +1641,12 @@ public class ResolverActivity extends Activity implements findViewById(R.id.resolver_tab_divider).setVisibility(View.VISIBLE); } + /** + * Callback called when user changes the profile tab. + * <p>This method is intended to be overridden by subclasses. + */ + protected void onProfileTabSelected() { } + private void resetCheckedItem() { if (!isIntentPicker()) { return; @@ -1745,22 +1764,33 @@ public class ResolverActivity extends Activity implements return; } final ViewGroup buttonLayout = findViewById(R.id.button_bar); - if (buttonLayout != null) { - buttonLayout.setVisibility(View.VISIBLE); - - if (!useLayoutWithDefault()) { - int inset = mSystemWindowInsets != null ? mSystemWindowInsets.bottom : 0; - buttonLayout.setPadding(buttonLayout.getPaddingLeft(), buttonLayout.getPaddingTop(), - buttonLayout.getPaddingRight(), getResources().getDimensionPixelSize( - R.dimen.resolver_button_bar_spacing) + inset); - } - mOnceButton = (Button) buttonLayout.findViewById(R.id.button_once); - mAlwaysButton = (Button) buttonLayout.findViewById(R.id.button_always); - - resetAlwaysOrOnceButtonBar(); - } else { + if (buttonLayout == null) { Log.e(TAG, "Layout unexpectedly does not have a button bar"); + return; + } + ResolverListAdapter activeListAdapter = + mMultiProfilePagerAdapter.getActiveListAdapter(); + View buttonBarDivider = findViewById(R.id.resolver_button_bar_divider); + if (activeListAdapter.isTabLoaded() + && mMultiProfilePagerAdapter.shouldShowEmptyStateScreen(activeListAdapter)) { + buttonLayout.setVisibility(View.INVISIBLE); + buttonBarDivider.setVisibility(View.INVISIBLE); + return; } + + buttonBarDivider.setVisibility(View.VISIBLE); + buttonLayout.setVisibility(View.VISIBLE); + + if (!useLayoutWithDefault()) { + int inset = mSystemWindowInsets != null ? mSystemWindowInsets.bottom : 0; + buttonLayout.setPadding(buttonLayout.getPaddingLeft(), buttonLayout.getPaddingTop(), + buttonLayout.getPaddingRight(), getResources().getDimensionPixelSize( + R.dimen.resolver_button_bar_spacing) + inset); + } + mOnceButton = (Button) buttonLayout.findViewById(R.id.button_once); + mAlwaysButton = (Button) buttonLayout.findViewById(R.id.button_always); + + resetAlwaysOrOnceButtonBar(); } private void resetAlwaysOrOnceButtonBar() { diff --git a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java index ad31d8b2e49a..885d1bbc5b77 100644 --- a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java +++ b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java @@ -213,6 +213,12 @@ public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerA /* subtitleRes */ 0); } + @Override + protected void setupContainerPadding(View container) { + container.setPadding(container.getPaddingLeft(), container.getPaddingTop(), + container.getPaddingRight(), /* bottom */ 0); + } + class ResolverProfileDescriptor extends ProfileDescriptor { private ResolverListAdapter resolverListAdapter; final ListView listView; diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 43bd4a610910..64324756796a 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -6093,7 +6093,8 @@ public class BatteryStatsImpl extends BatteryStats { return array; } - public void noteNetworkInterfaceTypeLocked(String iface, int networkType) { + /** @hide */ + public void noteNetworkInterfaceType(String iface, int networkType) { if (TextUtils.isEmpty(iface)) return; synchronized (mModemNetworkLock) { diff --git a/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java b/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java index ebfea450af88..56a6db95badc 100644 --- a/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java +++ b/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java @@ -46,6 +46,9 @@ public class GestureNavigationSettingsObserver extends ContentObserver { r.registerContentObserver( Settings.Secure.getUriFor(Settings.Secure.BACK_GESTURE_INSET_SCALE_RIGHT), false, this, UserHandle.USER_ALL); + r.registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), + false, this, UserHandle.USER_ALL); } public void unregister() { @@ -68,6 +71,11 @@ public class GestureNavigationSettingsObserver extends ContentObserver { return getSensitivity(userRes, Settings.Secure.BACK_GESTURE_INSET_SCALE_RIGHT); } + public boolean areNavigationButtonForcedVisible() { + return Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) == 0; + } + private int getSensitivity(Resources userRes, String side) { final int inset = userRes.getDimensionPixelSize( com.android.internal.R.dimen.config_backGestureInset); diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java index 7cff90bbf437..adadc5e20549 100644 --- a/core/java/com/android/internal/util/ScreenshotHelper.java +++ b/core/java/com/android/internal/util/ScreenshotHelper.java @@ -1,5 +1,7 @@ package com.android.internal.util; +import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_OTHER; + import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; @@ -10,11 +12,12 @@ import android.graphics.Bitmap; import android.graphics.Insets; import android.graphics.Rect; import android.net.Uri; -import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; +import android.os.Parcel; +import android.os.Parcelable; import android.os.RemoteException; import android.os.UserHandle; import android.util.Log; @@ -23,6 +26,109 @@ import android.view.WindowManager; import java.util.function.Consumer; public class ScreenshotHelper { + + /** + * Describes a screenshot request (to make it easier to pass data through to the handler). + */ + public static class ScreenshotRequest implements Parcelable { + private int mSource; + private boolean mHasStatusBar; + private boolean mHasNavBar; + private Bitmap mBitmap; + private Rect mBoundsInScreen; + private Insets mInsets; + private int mTaskId; + + ScreenshotRequest(int source, boolean hasStatus, boolean hasNav) { + mSource = source; + mHasStatusBar = hasStatus; + mHasNavBar = hasNav; + } + + ScreenshotRequest( + int source, Bitmap bitmap, Rect boundsInScreen, Insets insets, int taskId) { + mSource = source; + mBitmap = bitmap; + mBoundsInScreen = boundsInScreen; + mInsets = insets; + mTaskId = taskId; + } + + ScreenshotRequest(Parcel in) { + mSource = in.readInt(); + mHasStatusBar = in.readBoolean(); + mHasNavBar = in.readBoolean(); + if (in.readInt() == 1) { + mBitmap = in.readParcelable(Bitmap.class.getClassLoader()); + mBoundsInScreen = in.readParcelable(Rect.class.getClassLoader()); + mInsets = in.readParcelable(Insets.class.getClassLoader()); + mTaskId = in.readInt(); + } + } + + public int getSource() { + return mSource; + } + + public boolean getHasStatusBar() { + return mHasStatusBar; + } + + public boolean getHasNavBar() { + return mHasNavBar; + } + + public Bitmap getBitmap() { + return mBitmap; + } + + public Rect getBoundsInScreen() { + return mBoundsInScreen; + } + + public Insets getInsets() { + return mInsets; + } + + public int getTaskId() { + return mTaskId; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mSource); + dest.writeBoolean(mHasStatusBar); + dest.writeBoolean(mHasNavBar); + if (mBitmap == null) { + dest.writeInt(0); + } else { + dest.writeInt(1); + dest.writeParcelable(mBitmap, 0); + dest.writeParcelable(mBoundsInScreen, 0); + dest.writeParcelable(mInsets, 0); + dest.writeInt(mTaskId); + } + } + + public static final @NonNull Parcelable.Creator<ScreenshotRequest> CREATOR = + new Parcelable.Creator<ScreenshotRequest>() { + + @Override + public ScreenshotRequest createFromParcel(Parcel source) { + return new ScreenshotRequest(source); + } + + @Override + public ScreenshotRequest[] newArray(int size) { + return new ScreenshotRequest[size]; + } + }; + } private static final String TAG = "ScreenshotHelper"; // Time until we give up on the screenshot & show an error instead. @@ -36,8 +142,10 @@ public class ScreenshotHelper { mContext = context; } + + /** - * Request a screenshot be taken with a specific timeout. + * Request a screenshot be taken. * * Added to support reducing unit test duration; the method variant without a timeout argument * is recommended for general use. @@ -47,6 +155,32 @@ public class ScreenshotHelper { * or * {@link android.view.WindowManager#TAKE_SCREENSHOT_SELECTED_REGION} * @param hasStatus {@code true} if the status bar is currently showing. {@code false} + * if not. + * @param hasNav {@code true} if the navigation bar is currently showing. {@code + * false} if not. + * @param source The source of the screenshot request. One of + * {SCREENSHOT_GLOBAL_ACTIONS, SCREENSHOT_KEY_CHORD, + * SCREENSHOT_OVERVIEW, SCREENSHOT_OTHER} + * @param handler A handler used in case the screenshot times out + * @param completionConsumer Consumes `false` if a screenshot was not taken, and `true` if the + * screenshot was taken. + */ + public void takeScreenshot(final int screenshotType, final boolean hasStatus, + final boolean hasNav, int source, @NonNull Handler handler, + @Nullable Consumer<Uri> completionConsumer) { + ScreenshotRequest screenshotRequest = new ScreenshotRequest(source, hasStatus, hasNav); + takeScreenshot(screenshotType, SCREENSHOT_TIMEOUT_MS, handler, screenshotRequest, + completionConsumer); + } + + /** + * Request a screenshot be taken, with provided reason. + * + * @param screenshotType The type of screenshot, for example either + * {@link android.view.WindowManager#TAKE_SCREENSHOT_FULLSCREEN} + * or + * {@link android.view.WindowManager#TAKE_SCREENSHOT_SELECTED_REGION} + * @param hasStatus {@code true} if the status bar is currently showing. {@code false} * if * not. * @param hasNav {@code true} if the navigation bar is currently showing. {@code @@ -64,7 +198,7 @@ public class ScreenshotHelper { } /** - * Request a screenshot be taken. + * Request a screenshot be taken with a specific timeout. * * Added to support reducing unit test duration; the method variant without a timeout argument * is recommended for general use. @@ -89,9 +223,9 @@ public class ScreenshotHelper { public void takeScreenshot(final int screenshotType, final boolean hasStatus, final boolean hasNav, long timeoutMs, @NonNull Handler handler, @Nullable Consumer<Uri> completionConsumer) { - takeScreenshot(screenshotType, hasStatus, hasNav, timeoutMs, handler, null, - completionConsumer - ); + ScreenshotRequest screenshotRequest = new ScreenshotRequest(SCREENSHOT_OTHER, hasStatus, + hasNav); + takeScreenshot(screenshotType, timeoutMs, handler, screenshotRequest, completionConsumer); } /** @@ -106,23 +240,16 @@ public class ScreenshotHelper { * screenshot was taken. */ public void provideScreenshot(@NonNull Bitmap screenshot, @NonNull Rect boundsInScreen, - @NonNull Insets insets, int taskId, @NonNull Handler handler, - @Nullable Consumer<Uri> completionConsumer) { - Bundle imageBundle = new Bundle(); - imageBundle.putParcelable(WindowManager.PARCEL_KEY_SCREENSHOT_BITMAP, screenshot); - imageBundle.putParcelable(WindowManager.PARCEL_KEY_SCREENSHOT_BOUNDS, boundsInScreen); - imageBundle.putParcelable(WindowManager.PARCEL_KEY_SCREENSHOT_INSETS, insets); - imageBundle.putInt(WindowManager.PARCEL_KEY_SCREENSHOT_TASK_ID, taskId); - - takeScreenshot( - WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE, - false, false, // ignored when image bundle is set - SCREENSHOT_TIMEOUT_MS, handler, imageBundle, completionConsumer); + @NonNull Insets insets, int taskId, int source, + @NonNull Handler handler, @Nullable Consumer<Uri> completionConsumer) { + ScreenshotRequest screenshotRequest = + new ScreenshotRequest(source, screenshot, boundsInScreen, insets, taskId); + takeScreenshot(WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE, SCREENSHOT_TIMEOUT_MS, + handler, screenshotRequest, completionConsumer); } - private void takeScreenshot(final int screenshotType, final boolean hasStatus, - final boolean hasNav, long timeoutMs, @NonNull Handler handler, - @Nullable Bundle providedImage, @Nullable Consumer<Uri> completionConsumer) { + private void takeScreenshot(final int screenshotType, long timeoutMs, @NonNull Handler handler, + ScreenshotRequest screenshotRequest, @Nullable Consumer<Uri> completionConsumer) { synchronized (mScreenshotLock) { if (mScreenshotConnection != null) { return; @@ -157,7 +284,7 @@ public class ScreenshotHelper { return; } Messenger messenger = new Messenger(service); - Message msg = Message.obtain(null, screenshotType); + Message msg = Message.obtain(null, screenshotType, screenshotRequest); final ServiceConnection myConn = this; Handler h = new Handler(handler.getLooper()) { @Override @@ -175,12 +302,6 @@ public class ScreenshotHelper { } }; msg.replyTo = new Messenger(h); - msg.arg1 = hasStatus ? 1 : 0; - msg.arg2 = hasNav ? 1 : 0; - - if (screenshotType == WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE) { - msg.setData(providedImage); - } try { messenger.send(msg); diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java index 31527e8dbe5d..fb2ecf3a478f 100644 --- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java +++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java @@ -1084,6 +1084,7 @@ public class ResolverDrawerLayout extends ViewGroup { protected Parcelable onSaveInstanceState() { final SavedState ss = new SavedState(super.onSaveInstanceState()); ss.open = mCollapsibleHeight > 0 && mCollapseOffset == 0; + ss.mCollapsibleHeightReserved = mCollapsibleHeightReserved; return ss; } @@ -1092,6 +1093,7 @@ public class ResolverDrawerLayout extends ViewGroup { final SavedState ss = (SavedState) state; super.onRestoreInstanceState(ss.getSuperState()); mOpenOnLayout = ss.open; + mCollapsibleHeightReserved = ss.mCollapsibleHeightReserved; } public static class LayoutParams extends MarginLayoutParams { @@ -1142,6 +1144,7 @@ public class ResolverDrawerLayout extends ViewGroup { static class SavedState extends BaseSavedState { boolean open; + private int mCollapsibleHeightReserved; SavedState(Parcelable superState) { super(superState); @@ -1150,12 +1153,14 @@ public class ResolverDrawerLayout extends ViewGroup { private SavedState(Parcel in) { super(in); open = in.readInt() != 0; + mCollapsibleHeightReserved = in.readInt(); } @Override public void writeToParcel(Parcel out, int flags) { super.writeToParcel(out, flags); out.writeInt(open ? 1 : 0); + out.writeInt(mCollapsibleHeightReserved); } public static final Parcelable.Creator<SavedState> CREATOR = diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index b51d4f509f38..4cb2e975a58b 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -301,6 +301,8 @@ AndroidRuntime::~AndroidRuntime() } void AndroidRuntime::setArgv0(const char* argv0, bool setProcName) { + // Set the kernel's task name, for as much of the name as we can fit. + // The kernel's TASK_COMM_LEN minus one for the terminating NUL == 15. if (setProcName) { int len = strlen(argv0); if (len < 15) { @@ -309,8 +311,14 @@ void AndroidRuntime::setArgv0(const char* argv0, bool setProcName) { pthread_setname_np(pthread_self(), argv0 + len - 15); } } + + // Directly change the memory pointed to by argv[0]. memset(mArgBlockStart, 0, mArgBlockLength); strlcpy(mArgBlockStart, argv0, mArgBlockLength); + + // Let bionic know that we just did that, because __progname points + // into argv[0] (https://issuetracker.google.com/152893281). + setprogname(mArgBlockStart); } status_t AndroidRuntime::callMain(const String8& className, jclass clazz, diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp index 483b455965f7..e7a2fb428b50 100644 --- a/core/jni/android_os_Parcel.cpp +++ b/core/jni/android_os_Parcel.cpp @@ -273,7 +273,28 @@ static void android_os_Parcel_writeDouble(JNIEnv* env, jclass clazz, jlong nativ } } -static void android_os_Parcel_writeString(JNIEnv* env, jclass clazz, jlong nativePtr, jstring val) +static void android_os_Parcel_writeString8(JNIEnv* env, jclass clazz, jlong nativePtr, jstring val) +{ + Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr); + if (parcel != NULL) { + status_t err = NO_MEMORY; + if (val) { + const size_t len = env->GetStringUTFLength(val); + const char* str = env->GetStringUTFChars(val, 0); + if (str) { + err = parcel->writeString8(str, len); + env->ReleaseStringUTFChars(val, str); + } + } else { + err = parcel->writeString8(NULL, 0); + } + if (err != NO_ERROR) { + signalExceptionForError(env, clazz, err); + } + } +} + +static void android_os_Parcel_writeString16(JNIEnv* env, jclass clazz, jlong nativePtr, jstring val) { Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr); if (parcel != NULL) { @@ -444,7 +465,21 @@ static jdouble android_os_Parcel_readDouble(jlong nativePtr) return 0; } -static jstring android_os_Parcel_readString(JNIEnv* env, jclass clazz, jlong nativePtr) +static jstring android_os_Parcel_readString8(JNIEnv* env, jclass clazz, jlong nativePtr) +{ + Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr); + if (parcel != NULL) { + size_t len; + const char* str = parcel->readString8Inplace(&len); + if (str) { + return env->NewStringUTF(str); + } + return NULL; + } + return NULL; +} + +static jstring android_os_Parcel_readString16(JNIEnv* env, jclass clazz, jlong nativePtr) { Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr); if (parcel != NULL) { @@ -722,7 +757,9 @@ static const JNINativeMethod gParcelMethods[] = { // @FastNative {"nativeWriteDouble", "(JD)V", (void*)android_os_Parcel_writeDouble}, // @FastNative - {"nativeWriteString", "(JLjava/lang/String;)V", (void*)android_os_Parcel_writeString}, + {"nativeWriteString8", "(JLjava/lang/String;)V", (void*)android_os_Parcel_writeString8}, + // @FastNative + {"nativeWriteString16", "(JLjava/lang/String;)V", (void*)android_os_Parcel_writeString16}, // @FastNative {"nativeWriteStrongBinder", "(JLandroid/os/IBinder;)V", (void*)android_os_Parcel_writeStrongBinder}, // @FastNative @@ -740,7 +777,9 @@ static const JNINativeMethod gParcelMethods[] = { // @CriticalNative {"nativeReadDouble", "(J)D", (void*)android_os_Parcel_readDouble}, // @FastNative - {"nativeReadString", "(J)Ljava/lang/String;", (void*)android_os_Parcel_readString}, + {"nativeReadString8", "(J)Ljava/lang/String;", (void*)android_os_Parcel_readString8}, + // @FastNative + {"nativeReadString16", "(J)Ljava/lang/String;", (void*)android_os_Parcel_readString16}, // @FastNative {"nativeReadStrongBinder", "(J)Landroid/os/IBinder;", (void*)android_os_Parcel_readStrongBinder}, // @FastNative diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 21985f0bb4bb..3a5720fd8c4c 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -1555,22 +1555,15 @@ static void isolateJitProfile(JNIEnv* env, jobjectArray pkg_data_info_list, static void BindMountStorageToLowerFs(const userid_t user_id, const char* dir_name, const char* package, fail_fn_t fail_fn) { - bool hasPackage = (package != nullptr); bool hasSdcardFs = IsFilesystemSupported("sdcardfs"); std::string source; if (hasSdcardFs) { - source = hasPackage ? - StringPrintf("/mnt/runtime/default/emulated/%d/%s/%s", user_id, dir_name, package) : - StringPrintf("/mnt/runtime/default/emulated/%d/%s", user_id, dir_name); + source = StringPrintf("/mnt/runtime/default/emulated/%d/%s/%s", user_id, dir_name, package); } else { - source = hasPackage ? - StringPrintf("/mnt/pass_through/%d/emulated/%d/%s/%s", - user_id, user_id, dir_name, package) : - StringPrintf("/mnt/pass_through/%d/emulated/%d/%s", user_id, user_id, dir_name); + source = StringPrintf("/mnt/pass_through/%d/emulated/%d/%s/%s", + user_id, user_id, dir_name, package); } - std::string target = hasPackage ? - StringPrintf("/storage/emulated/%d/%s/%s", user_id, dir_name, package) : - StringPrintf("/storage/emulated/%d/%s", user_id, dir_name); + std::string target = StringPrintf("/storage/emulated/%d/%s/%s", user_id, dir_name, package); if (access(source.c_str(), F_OK) != 0) { fail_fn(CREATE_ERROR("Error accessing %s: %s", source.c_str(), strerror(errno))); @@ -1594,10 +1587,7 @@ static void BindMountStorageDirs(JNIEnv* env, jobjectArray pkg_data_info_list, int size = (pkg_data_info_list != nullptr) ? env->GetArrayLength(pkg_data_info_list) : 0; if (size == 0) { - // App data isolation is not enabled for this process, so we bind mount to whole obb/ dir. - BindMountStorageToLowerFs(user_id, "Android/obb", /* package */ nullptr, fail_fn); - BindMountStorageToLowerFs(user_id, "Android/data", /* package */ nullptr, fail_fn); - return; + fail_fn(CREATE_ERROR("Data package list cannot be empty")); } // Bind mount each package obb directory diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto index 3e007e4704a4..997829eacf96 100644 --- a/core/proto/android/app/settings_enums.proto +++ b/core/proto/android/app/settings_enums.proto @@ -2523,16 +2523,10 @@ enum PageId { // OS: R PANEL_ADD_WIFI_NETWORKS = 1809; - // OPEN: Settings > Accessibility > Enable accessibility service > Show tutorial dialog + // OPEN: Settings > Accessibility > Enable the feature or shortcut > Show tutorial dialog // CATEGORY: SETTINGS // OS: R - DIALOG_TOGGLE_SCREEN_ACCESSIBILITY_BUTTON = 1810; - - // OPEN: Settings > Accessibility > Enable accessibility service > Show tutorial dialog in - // gesture mode - // CATEGORY: SETTINGS - // OS: R - DIALOG_TOGGLE_SCREEN_GESTURE_NAVIGATION = 1811; + DIALOG_ACCESSIBILITY_TUTORIAL = 1810; // OPEN: Settings > Accessibility > Edit shortcut dialog // CATEGORY: SETTINGS diff --git a/core/proto/android/app/tvsettings_enums.proto b/core/proto/android/app/tvsettings_enums.proto index 6804d3f07736..30d365c71308 100644 --- a/core/proto/android/app/tvsettings_enums.proto +++ b/core/proto/android/app/tvsettings_enums.proto @@ -298,6 +298,12 @@ enum ItemId { // TvSettings > Apps > See all apps > [An app entry] > Permissions APPS_ALL_APPS_APP_ENTRY_PERMISSIONS = 0x1611A000; + // TvSettings > Apps > See all apps > [An app entry] > Enable + APPS_ALL_APPS_APP_ENTRY_ENABLE = 0x1611B000; + + // TvSettings > Apps > See all apps > [An app entry] > Open source licenses + APPS_ALL_APPS_APP_ENTRY_LICENSES = 0x1611C000; + // TvSettings > Apps > See all apps > Show system apps APPS_ALL_APPS_SHOW_SYSTEM_APPS = 0x16120000; diff --git a/core/proto/android/stats/accessibility/accessibility_enums.proto b/core/proto/android/stats/accessibility/accessibility_enums.proto new file mode 100644 index 000000000000..5118ad5a322c --- /dev/null +++ b/core/proto/android/stats/accessibility/accessibility_enums.proto @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; +package android.stats.accessibility; +option java_multiple_files = true; + +// The entry point of the accessibility shortcut. +enum ShortcutType { + UNKNOWN_TYPE = 0; + A11Y_BUTTON = 1; + VOLUME_KEY = 2; + TRIPLE_TAP = 3; + A11Y_BUTTON_LONG_PRESS = 4; +} + +// The service status code. +enum ServiceStatus { + UNKNOWN = 0; + ENABLED = 1; + DISABLED = 2; +}
\ No newline at end of file diff --git a/core/proto/android/stats/dnsresolver/dns_resolver.proto b/core/proto/android/stats/dnsresolver/dns_resolver.proto index 61b9b25fe7cf..b17d12c9c315 100644 --- a/core/proto/android/stats/dnsresolver/dns_resolver.proto +++ b/core/proto/android/stats/dnsresolver/dns_resolver.proto @@ -211,7 +211,7 @@ enum CacheStatus{ // 1. bionic/libc/kernel/uapi/asm-generic/errno-base.h // 2. bionic/libc/kernel/uapi/asm-generic/errno.h enum LinuxErrno { - SYS_UNKNOWN = 0; + SYS_NO_ERROR = 0; SYS_EPERM = 1; // Not super-user SYS_ENOENT = 2; // No such file or directory SYS_ESRCH = 3; // No such process diff --git a/core/res/res/layout/resolver_empty_states.xml b/core/res/res/layout/resolver_empty_states.xml index 25615d2dc304..fe11769e8613 100644 --- a/core/res/res/layout/resolver_empty_states.xml +++ b/core/res/res/layout/resolver_empty_states.xml @@ -24,6 +24,7 @@ android:paddingStart="24dp" android:paddingEnd="24dp"> <RelativeLayout + android:id="@+id/resolver_empty_state_container" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingTop="48dp" diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml index b754e0cfc022..76ecefc67c22 100644 --- a/core/res/res/layout/resolver_list.xml +++ b/core/res/res/layout/resolver_list.xml @@ -112,59 +112,61 @@ </FrameLayout> </LinearLayout> </TabHost> - - <View - android:layout_alwaysShow="true" - android:layout_width="match_parent" - android:layout_height="1dp" - android:background="?attr/colorBackgroundFloating" - android:foreground="?attr/dividerVertical" /> - <LinearLayout - android:id="@+id/button_bar" - android:visibility="gone" - style="?attr/buttonBarStyle" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_ignoreOffset="true" android:layout_alwaysShow="true" - android:layout_hasNestedScrollIndicator="true" - android:gravity="end|center_vertical" - android:orientation="horizontal" - android:layoutDirection="locale" - android:measureWithLargestChild="true" - android:background="?attr/colorBackgroundFloating" - android:paddingTop="@dimen/resolver_button_bar_spacing" - android:paddingBottom="@dimen/resolver_button_bar_spacing" - android:paddingStart="@dimen/resolver_edge_margin" - android:paddingEnd="@dimen/resolver_small_margin" - android:elevation="@dimen/resolver_elevation"> - - <Button - android:id="@+id/button_once" - android:layout_width="wrap_content" - android:layout_gravity="start" - android:maxLines="2" - style="?attr/buttonBarButtonStyle" - android:fontFamily="@android:string/config_headlineFontFamilyMedium" + android:orientation="vertical" + android:background="?attr/colorBackgroundFloating"> + <View + android:id="@+id/resolver_button_bar_divider" + android:layout_width="match_parent" + android:layout_height="1dp" + android:background="?attr/colorBackgroundFloating" + android:foreground="?attr/dividerVertical" /> + <LinearLayout + android:id="@+id/button_bar" + android:visibility="gone" + style="?attr/buttonBarStyle" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:textAllCaps="false" - android:enabled="false" - android:text="@string/activity_resolver_use_once" - android:onClick="onButtonClick" /> + android:layout_ignoreOffset="true" + android:layout_hasNestedScrollIndicator="true" + android:gravity="end|center_vertical" + android:orientation="horizontal" + android:layoutDirection="locale" + android:measureWithLargestChild="true" + android:paddingTop="@dimen/resolver_button_bar_spacing" + android:paddingBottom="@dimen/resolver_button_bar_spacing" + android:paddingStart="@dimen/resolver_edge_margin" + android:paddingEnd="@dimen/resolver_small_margin" + android:elevation="@dimen/resolver_elevation"> - <Button - android:id="@+id/button_always" - android:layout_width="wrap_content" - android:layout_gravity="end" - android:maxLines="2" - style="?attr/buttonBarButtonStyle" - android:fontFamily="@android:string/config_headlineFontFamilyMedium" - android:textAllCaps="false" - android:layout_height="wrap_content" - android:enabled="false" - android:text="@string/activity_resolver_use_always" - android:onClick="onButtonClick" /> - </LinearLayout> + <Button + android:id="@+id/button_once" + android:layout_width="wrap_content" + android:layout_gravity="start" + android:maxLines="2" + style="?attr/buttonBarButtonStyle" + android:fontFamily="@android:string/config_headlineFontFamilyMedium" + android:layout_height="wrap_content" + android:textAllCaps="false" + android:enabled="false" + android:text="@string/activity_resolver_use_once" + android:onClick="onButtonClick" /> + <Button + android:id="@+id/button_always" + android:layout_width="wrap_content" + android:layout_gravity="end" + android:maxLines="2" + style="?attr/buttonBarButtonStyle" + android:fontFamily="@android:string/config_headlineFontFamilyMedium" + android:textAllCaps="false" + android:layout_height="wrap_content" + android:enabled="false" + android:text="@string/activity_resolver_use_always" + android:onClick="onButtonClick" /> + </LinearLayout> + </LinearLayout> </com.android.internal.widget.ResolverDrawerLayout> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 1a9855311518..6fac46e834f2 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1908,7 +1908,7 @@ <!-- The name of the package that will hold the call screening role by default. --> <string name="config_defaultCallScreening" translatable="false"></string> <!-- The name of the package that will hold the system gallery role. --> - <string name="config_systemGallery" translatable="false">com.android.gallery</string> + <string name="config_systemGallery" translatable="false">com.android.gallery3d</string> <!-- The name of the package that will be allowed to change its components' label/icon. --> <string name="config_overrideComponentUiPackage" translatable="false"></string> @@ -3496,10 +3496,9 @@ <!-- Do not translate. Mcc codes whose existence trigger the presence of emergency affordances--> - <integer-array name="config_emergency_mcc_codes" translatable="false"> - <item>404</item> - <item>405</item> - </integer-array> + <string-array name="config_emergency_iso_country_codes" translatable="false"> + <item>in</item> + </string-array> <!-- Package name for the device provisioning package. --> <string name="config_deviceProvisioningPackage"></string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 05c00ce51ee3..4efd7ff3207e 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3085,7 +3085,7 @@ <java-symbol type="string" name="global_action_emergency" /> <java-symbol type="string" name="config_emergency_call_number" /> <java-symbol type="string" name="config_emergency_dialer_package" /> - <java-symbol type="array" name="config_emergency_mcc_codes" /> + <java-symbol type="array" name="config_emergency_iso_country_codes" /> <java-symbol type="string" name="config_dozeDoubleTapSensorType" /> <java-symbol type="string" name="config_dozeTapSensorType" /> @@ -3921,6 +3921,8 @@ <java-symbol type="id" name="resolver_empty_state_button" /> <java-symbol type="id" name="resolver_empty_state_progress" /> <java-symbol type="id" name="resolver_tab_divider" /> + <java-symbol type="id" name="resolver_button_bar_divider" /> + <java-symbol type="id" name="resolver_empty_state_container" /> <java-symbol type="string" name="resolver_cant_share_with_work_apps" /> <java-symbol type="string" name="resolver_cant_share_with_work_apps_explanation" /> <java-symbol type="string" name="resolver_cant_share_with_personal_apps" /> diff --git a/core/tests/benchmarks/src/android/os/ParcelStringBenchmark.java b/core/tests/benchmarks/src/android/os/ParcelStringBenchmark.java new file mode 100644 index 000000000000..daa90c28e0c4 --- /dev/null +++ b/core/tests/benchmarks/src/android/os/ParcelStringBenchmark.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.os; + +import com.google.caliper.AfterExperiment; +import com.google.caliper.BeforeExperiment; +import com.google.caliper.Param; + +public class ParcelStringBenchmark { + + @Param({"com.example.typical_package_name", "從不喜歡孤單一個 - 蘇永康/吳雨霏"}) + String mValue; + + private Parcel mParcel; + + @BeforeExperiment + protected void setUp() { + mParcel = Parcel.obtain(); + } + + @AfterExperiment + protected void tearDown() { + mParcel.recycle(); + mParcel = null; + } + + public void timeWriteString8(int reps) { + for (int i = 0; i < reps; i++) { + mParcel.setDataPosition(0); + mParcel.writeString8(mValue); + } + } + + public void timeReadString8(int reps) { + mParcel.writeString8(mValue); + + for (int i = 0; i < reps; i++) { + mParcel.setDataPosition(0); + mParcel.readString8(); + } + } + + public void timeWriteString16(int reps) { + for (int i = 0; i < reps; i++) { + mParcel.setDataPosition(0); + mParcel.writeString16(mValue); + } + } + + public void timeReadString16(int reps) { + mParcel.writeString16(mValue); + + for (int i = 0; i < reps; i++) { + mParcel.setDataPosition(0); + mParcel.readString16(); + } + } +} diff --git a/core/tests/coretests/src/android/os/ParcelTest.java b/core/tests/coretests/src/android/os/ParcelTest.java index 46873b9eb70b..dcb3e2f23da8 100644 --- a/core/tests/coretests/src/android/os/ParcelTest.java +++ b/core/tests/coretests/src/android/os/ParcelTest.java @@ -87,4 +87,27 @@ public class ParcelTest { p.recycle(); } + + /** + * Verify that writing/reading UTF-8 and UTF-16 strings works well. + */ + @Test + public void testStrings() { + final String[] strings = { + null, "", "abc\0def", "com.example.typical_package_name", + "從不喜歡孤單一個 - 蘇永康/吳雨霏", "example" + }; + + final Parcel p = Parcel.obtain(); + for (String string : strings) { + p.writeString8(string); + p.writeString16(string); + } + + p.setDataPosition(0); + for (String string : strings) { + assertEquals(string, p.readString8()); + assertEquals(string, p.readString16()); + } + } } diff --git a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java index 5424b6f19038..43590bae6770 100644 --- a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java @@ -68,6 +68,8 @@ import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; @RunWith(AndroidJUnit4.class) public class IntentForwarderActivityTest { @@ -633,6 +635,11 @@ public class IntentForwarderActivityTest { public void onCreate(@Nullable Bundle savedInstanceState) { getIntent().setComponent(sComponentName); super.onCreate(savedInstanceState); + try { + mExecutorService.awaitTermination(/* timeout= */ 30, TimeUnit.SECONDS); + } catch (InterruptedException e) { + e.printStackTrace(); + } } @Override @@ -671,7 +678,8 @@ public class IntentForwarderActivityTest { } @Override - public ResolveInfo resolveActivityAsUser(Intent intent, int flags, int userId) { + public CompletableFuture<ResolveInfo> resolveActivityAsUser( + Intent intent, int flags, int userId) { ActivityInfo activityInfo = new ActivityInfo(); activityInfo.packageName = sPackageName; activityInfo.name = sActivityName; @@ -680,7 +688,7 @@ public class IntentForwarderActivityTest { ResolveInfo resolveInfo = new ResolveInfo(); resolveInfo.activityInfo = activityInfo; - return resolveInfo; + return CompletableFuture.completedFuture(resolveInfo); } @Override diff --git a/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java b/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java index cd6b3af5fa6d..fe33cd80f735 100644 --- a/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java +++ b/core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotHelperTest.java @@ -36,6 +36,7 @@ import android.graphics.Insets; import android.graphics.Rect; import android.os.Handler; import android.os.Looper; +import android.view.WindowManager; import androidx.test.runner.AndroidJUnit4; @@ -91,7 +92,8 @@ public final class ScreenshotHelperTest { public void testProvidedImageScreenshot() { mScreenshotHelper.provideScreenshot( Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888), new Rect(), - Insets.of(0, 0, 0, 0), 1, mHandler, null); + Insets.of(0, 0, 0, 0), 1, + WindowManager.ScreenshotSource.SCREENSHOT_OTHER, mHandler, null); } @Test diff --git a/identity/java/android/security/identity/IdentityCredential.java b/identity/java/android/security/identity/IdentityCredential.java index 1db2f6357308..b351b3d77430 100644 --- a/identity/java/android/security/identity/IdentityCredential.java +++ b/identity/java/android/security/identity/IdentityCredential.java @@ -95,9 +95,7 @@ public abstract class IdentityCredential { /** * Sets whether to allow using an authentication key which use count has been exceeded if no * other key is available. This must be called prior to calling - * {@link #getEntries(byte[], Map, byte[], byte[])} or using a - * {@link android.hardware.biometrics.BiometricPrompt.CryptoObject} which references this - * object. + * {@link #getEntries(byte[], Map, byte[], byte[])}. * * By default this is set to true. * @@ -123,13 +121,14 @@ public abstract class IdentityCredential { * entries. * * <p>It is the responsibility of the calling application to know if authentication is needed - * and use e.g. {@link android.hardware.biometrics.BiometricPrompt}) to make the user + * and use e.g. {@link android.hardware.biometrics.BiometricPrompt} to make the user * authenticate using a {@link android.hardware.biometrics.BiometricPrompt.CryptoObject} which * references this object. If needed, this must be done before calling * {@link #getEntries(byte[], Map, byte[], byte[])}. * - * <p>If this method returns successfully (i.e. without throwing an exception), it must not be - * called again on this instance. + * <p>It is permissible to call this method multiple times using the same instance but if this + * is done, the {@code sessionTranscript} parameter must be identical for each call. If this is + * not the case, the {@link SessionTranscriptMismatchException} exception is thrown. * * <p>If not {@code null} the {@code requestMessage} parameter must contain data for the request * from the verifier. The content can be defined in the way appropriate for the credential, byt @@ -141,6 +140,9 @@ public abstract class IdentityCredential { * the example below.</li> * </ul> * + * <p>If these requirements are not met the {@link InvalidRequestMessageException} exception + * is thrown. + * * <p>Here's an example of CBOR which conforms to this requirement: * <pre> * ItemsRequest = { @@ -149,6 +151,8 @@ public abstract class IdentityCredential { * ? "RequestInfo" : {* tstr => any} ; Additional info the reader wants to provide * } * + * DocType = tstr + * * NameSpaces = { * + NameSpace => DataElements ; Requested data elements for each NameSpace * } @@ -172,16 +176,18 @@ public abstract class IdentityCredential { * EReaderKeyBytes * ] * - * DeviceEngagementBytes = #6.24(bstr .cbor DeviceEngagement) - * EReaderKeyBytes = #6.24(bstr .cbor EReaderKey.Pub) + * DeviceEngagementBytes = #6.24(bstr .cbor DeviceEngagement) ; Bytes of DeviceEngagement + * EReaderKeyBytes = #6.24(bstr .cbor EReaderKey.Pub) ; Bytes of EReaderKey.pub + * + * EReaderKey.Pub = COSE_Key ; Ephemeral public key provided by reader * </pre> * - * <p>If the SessionTranscript is not empty, a COSE_Key structure for the public part - * of the key-pair previously generated by {@link #createEphemeralKeyPair()} must appear - * somewhere in {@code DeviceEngagement} and the X and Y coordinates must both be present + * <p>where a {@code COSE_Key} structure for the public part of the key-pair previously + * generated by {@link #createEphemeralKeyPair()} must appear somewhere in + * {@code DeviceEngagement} and the X and Y coordinates must both be present * in uncompressed form. * - * <p>If {@code readerAuth} is not {@code null} it must be the bytes of a COSE_Sign1 + * <p>If {@code readerAuth} is not {@code null} it must be the bytes of a {@code COSE_Sign1} * structure as defined in RFC 8152. For the payload nil shall be used and the * detached payload is the ReaderAuthentication CBOR described below. * <pre> @@ -194,20 +200,23 @@ public abstract class IdentityCredential { * ItemsRequestBytes = #6.24(bstr .cbor ItemsRequest) ; Bytes of ItemsRequest * </pre> * - * <p>The public key corresponding to the key used to made signature, can be - * found in the {@code x5chain} unprotected header element of the COSE_Sign1 - * structure (as as described in 'draft-ietf-cose-x509-04'). There will be at - * least one certificate in said element and there may be more (and if so, + * <p>where {@code ItemsRequestBytes} are the bytes in the {@code requestMessage} parameter. + * + * <p>The public key corresponding to the key used to make the signature, can be found in the + * {@code x5chain} unprotected header element of the {@code COSE_Sign1} structure (as as + * described in + * <a href="https://tools.ietf.org/html/draft-ietf-cose-x509-04">draft-ietf-cose-x509-04</a>). + * There will be at least one certificate in said element and there may be more (and if so, * each certificate must be signed by its successor). * - * <p>Data elements protected by reader authentication is returned if, and only if, they are + * <p>Data elements protected by reader authentication are returned if, and only if, they are * mentioned in {@code requestMessage}, {@code requestMessage} is signed by the top-most - * certificate in {@code readerCertificateChain}, and the data element is configured - * with an {@link AccessControlProfile} with a {@link X509Certificate} in - * {@code readerCertificateChain}. + * certificate in the reader's certificate chain, and the data element is configured + * with an {@link AccessControlProfile} configured with an X.509 certificate which appears + * in the certificate chain. * * <p>Note that only items referenced in {@code entriesToRequest} are returned - the - * {@code requestMessage} parameter is only used to for enforcing reader authentication. + * {@code requestMessage} parameter is used only for enforcing reader authentication. * * <p>The reason for having {@code requestMessage} and {@code entriesToRequest} as separate * parameters is that the former represents a request from the remote verifier device @@ -219,13 +228,12 @@ public abstract class IdentityCredential { * @param entriesToRequest The entries to request, organized as a map of namespace * names with each value being a collection of data elements * in the given namespace. - * @param readerSignature COSE_Sign1 structure as described above or {@code null} - * if reader authentication is not being used. + * @param readerSignature A {@code COSE_Sign1} structure as described above or + * {@code null} if reader authentication is not being used. * @return A {@link ResultData} object containing entry data organized by namespace and a * cryptographically authenticated representation of the same data. * @throws SessionTranscriptMismatchException Thrown when trying use multiple different - * session transcripts in the same presentation - * session. + * session transcripts. * @throws NoAuthenticationKeyAvailableException if authentication keys were never * provisioned, the method * {@link #setAvailableAuthenticationKeys(int, int)} @@ -255,8 +263,8 @@ public abstract class IdentityCredential { * Sets the number of dynamic authentication keys the {@code IdentityCredential} will maintain, * and the number of times each should be used. * - * <p>{@code IdentityCredential}s will select the least-used dynamic authentication key each - * time {@link #getEntries(byte[], Map, byte[], byte[])} is called. {@code IdentityCredential}s + * <p>The Identity Credential system will select the least-used dynamic authentication key each + * time {@link #getEntries(byte[], Map, byte[], byte[])} is called. Identity Credentials * for which this method has not been called behave as though it had been called wit * {@code keyCount} 0 and {@code maxUsesPerKey} 1. * @@ -274,9 +282,10 @@ public abstract class IdentityCredential { * <p>When there aren't enough certified dynamic authentication keys, either because the key * count has been increased or because one or more keys have reached their usage count, this * method will generate replacement keys and certificates and return them for issuer - * certification. The issuer certificates and associated static authentication data must then - * be provided back to the {@code IdentityCredential} using - * {@link #storeStaticAuthenticationData(X509Certificate, byte[])}. + * certification. The issuer certificates and associated static authentication data must then + * be provided back to the Identity Credential using + * {@link #storeStaticAuthenticationData(X509Certificate, byte[])}. The private part of + * each authentication key never leaves secure hardware. * * <p>Each X.509 certificate is signed by CredentialKey. The certificate chain for CredentialKey * can be obtained using the {@link #getCredentialKeyCertificateChain()} method. diff --git a/identity/java/android/security/identity/IdentityCredentialStore.java b/identity/java/android/security/identity/IdentityCredentialStore.java index a1dfc77adb29..4f834d2b87b5 100644 --- a/identity/java/android/security/identity/IdentityCredentialStore.java +++ b/identity/java/android/security/identity/IdentityCredentialStore.java @@ -78,17 +78,21 @@ public abstract class IdentityCredentialStore { /** * Specifies that the cipher suite that will be used to secure communications between the reader - * is: + * and the prover is using the following primitives * * <ul> - * <li>ECDHE with HKDF-SHA-256 for key agreement.</li> - * <li>AES-256 with GCM block mode for authenticated encryption (nonces are incremented by one - * for every message).</li> - * <li>ECDSA with SHA-256 for signing (used for signing session transcripts to defeat - * man-in-the-middle attacks), signing keys are not ephemeral. See {@link IdentityCredential} - * for details on reader and prover signing keys.</li> + * <li>ECKA-DH (Elliptic Curve Key Agreement Algorithm - Diffie-Hellman, see BSI TR-03111).</li> + * + * <li>HKDF-SHA-256 (see RFC 5869).</li> + * + * <li>AES-256-GCM (see NIST SP 800-38D).</li> + * + * <li>HMAC-SHA-256 (see RFC 2104).</li> * </ul> * + * <p>The exact way these primitives are combined to derive the session key is specified in + * section 9.2.1.4 of ISO/IEC 18013-5 (see description of cipher suite '1').<p> + * * <p> * At present this is the only supported cipher suite. */ @@ -135,9 +139,20 @@ public abstract class IdentityCredentialStore { /** * Creates a new credential. * + * <p>When a credential is created, a cryptographic key-pair - CredentialKey - is created which + * is used to authenticate the store to the Issuing Authority. The private part of this + * key-pair never leaves secure hardware and the public part can be obtained using + * {@link WritableIdentityCredential#getCredentialKeyCertificateChain(byte[])} on the + * returned object. + * + * <p>In addition, all of the Credential data content is imported and a certificate for the + * CredentialKey and a signature produced with the CredentialKey are created. These latter + * values may be checked by an issuing authority to verify that the data was imported into + * secure hardware and that it was imported unmodified. + * * @param credentialName The name used to identify the credential. * @param docType The document type for the credential. - * @return A @{link WritableIdentityCredential} that can be used to create a new credential. + * @return A {@link WritableIdentityCredential} that can be used to create a new credential. * @throws AlreadyPersonalizedException if a credential with the given name already exists. * @throws DocTypeNotSupportedException if the given document type isn't supported by the store. */ @@ -148,6 +163,10 @@ public abstract class IdentityCredentialStore { /** * Retrieve a named credential. * + * <p>The cipher suite used to communicate with the remote verifier must also be specified. + * Currently only a single cipher-suite is supported. Support for other cipher suites may be + * added in a future version of this API. + * * @param credentialName the name of the credential to retrieve. * @param cipherSuite the cipher suite to use for communicating with the verifier. * @return The named credential, or null if not found. diff --git a/identity/java/android/security/identity/ResultData.java b/identity/java/android/security/identity/ResultData.java index 13552d619e05..37de2c4a50ea 100644 --- a/identity/java/android/security/identity/ResultData.java +++ b/identity/java/android/security/identity/ResultData.java @@ -34,23 +34,23 @@ public abstract class ResultData { /** Value was successfully retrieved. */ public static final int STATUS_OK = 0; - /** Requested entry does not exist. */ + /** The entry does not exist. */ public static final int STATUS_NO_SUCH_ENTRY = 1; - /** Requested entry was not requested. */ + /** The entry was not requested. */ public static final int STATUS_NOT_REQUESTED = 2; - /** Requested entry wasn't in the request message. */ + /** The entry wasn't in the request message. */ public static final int STATUS_NOT_IN_REQUEST_MESSAGE = 3; - /** The requested entry was not retrieved because user authentication wasn't performed. */ + /** The entry was not retrieved because user authentication failed. */ public static final int STATUS_USER_AUTHENTICATION_FAILED = 4; - /** The requested entry was not retrieved because reader authentication wasn't performed. */ + /** The entry was not retrieved because reader authentication failed. */ public static final int STATUS_READER_AUTHENTICATION_FAILED = 5; /** - * The requested entry was not retrieved because it was configured without any access + * The entry was not retrieved because it was configured without any access * control profile. */ public static final int STATUS_NO_ACCESS_CONTROL_PROFILES = 6; @@ -88,11 +88,10 @@ public abstract class ResultData { * * DeviceEngagementBytes = #6.24(bstr .cbor DeviceEngagement) * EReaderKeyBytes = #6.24(bstr .cbor EReaderKey.Pub) - * * DeviceNameSpacesBytes = #6.24(bstr .cbor DeviceNameSpaces) * </pre> * - * where + * <p>where * * <pre> * DeviceNameSpaces = { @@ -116,15 +115,16 @@ public abstract class ResultData { public abstract @NonNull byte[] getAuthenticatedData(); /** - * Returns a message authentication code over the data returned by - * {@link #getAuthenticatedData}, to prove to the reader that the data is from a trusted - * credential. + * Returns a message authentication code over the {@code DeviceAuthentication} CBOR + * specified in {@link #getAuthenticatedData()}, to prove to the reader that the data + * is from a trusted credential. * * <p>The MAC proves to the reader that the data is from a trusted credential. This code is * produced by using the key agreement and key derivation function from the ciphersuite * with the authentication private key and the reader ephemeral public key to compute a * shared message authentication code (MAC) key, then using the MAC function from the - * ciphersuite to compute a MAC of the authenticated data. + * ciphersuite to compute a MAC of the authenticated data. See section 9.2.3.5 of + * ISO/IEC 18013-5 for details of this operation. * * <p>If the {@code sessionTranscript} parameter passed to * {@link IdentityCredential#getEntries(byte[], Map, byte[], byte[])} was {@code null} @@ -157,7 +157,7 @@ public abstract class ResultData { /** * Get the names of all entries. * - * This includes the name of entries that wasn't successfully retrieved. + * <p>This includes the name of entries that wasn't successfully retrieved. * * @param namespaceName the namespace name to get entries for. * @return A collection of names or {@code null} if there are no entries for the given @@ -168,7 +168,7 @@ public abstract class ResultData { /** * Get the names of all entries that was successfully retrieved. * - * This only return entries for which {@link #getStatus(String, String)} will return + * <p>This only return entries for which {@link #getStatus(String, String)} will return * {@link #STATUS_OK}. * * @param namespaceName the namespace name to get entries for. @@ -181,16 +181,15 @@ public abstract class ResultData { /** * Gets the status of an entry. * - * This returns {@link #STATUS_OK} if the value was retrieved, {@link #STATUS_NO_SUCH_ENTRY} + * <p>This returns {@link #STATUS_OK} if the value was retrieved, {@link #STATUS_NO_SUCH_ENTRY} * if the given entry wasn't retrieved, {@link #STATUS_NOT_REQUESTED} if it wasn't requested, * {@link #STATUS_NOT_IN_REQUEST_MESSAGE} if the request message was set but the entry wasn't - * present in the request message, - * {@link #STATUS_USER_AUTHENTICATION_FAILED} if the value + * present in the request message, {@link #STATUS_USER_AUTHENTICATION_FAILED} if the value * wasn't retrieved because the necessary user authentication wasn't performed, - * {@link #STATUS_READER_AUTHENTICATION_FAILED} if the supplied reader certificate chain - * didn't match the set of certificates the entry was provisioned with, or - * {@link #STATUS_NO_ACCESS_CONTROL_PROFILES} if the entry was configured without any - * access control profiles. + * {@link #STATUS_READER_AUTHENTICATION_FAILED} if the supplied reader certificate chain didn't + * match the set of certificates the entry was provisioned with, or + * {@link #STATUS_NO_ACCESS_CONTROL_PROFILES} if the entry was configured without any access + * control profiles. * * @param namespaceName the namespace name of the entry. * @param name the name of the entry to get the value for. @@ -201,7 +200,7 @@ public abstract class ResultData { /** * Gets the raw CBOR data for the value of an entry. * - * This should only be called on an entry for which the {@link #getStatus(String, String)} + * <p>This should only be called on an entry for which the {@link #getStatus(String, String)} * method returns {@link #STATUS_OK}. * * @param namespaceName the namespace name of the entry. diff --git a/identity/java/android/security/identity/WritableIdentityCredential.java b/identity/java/android/security/identity/WritableIdentityCredential.java index e2a389bfd4da..c7aa32855abc 100644 --- a/identity/java/android/security/identity/WritableIdentityCredential.java +++ b/identity/java/android/security/identity/WritableIdentityCredential.java @@ -41,15 +41,16 @@ public abstract class WritableIdentityCredential { * <a href="https://source.android.com/security/keystore/attestation">Android Keystore</a> * attestation extension which describes the key and the security hardware in which it lives. * - * <p>Additionally, the attestation extension will contain the tag TODO_IC_KEY which indicates - * it is an Identity Credential key (which can only sign/MAC very specific messages) and not - * an Android Keystore key (which can be used to sign/MAC anything). + * <p>Additionally, the attestation extension will contain the tag Tag::IDENTITY_CREDENTIAL_KEY + * which indicates it is an Identity Credential key (which can only sign/MAC very specific + * messages) and not an Android Keystore key (which can be used to sign/MAC anything). * * <p>The issuer <b>MUST</b> carefully examine this certificate chain including (but not - * limited to) checking that the root certificate is well-known, the tag TODO_IC_KEY is - * present, the passed in challenge is present, the device has verified boot enabled, that each - * certificate in the chain is signed by its successor, that none of the certificates have been - * revoked and so on. + * limited to) checking that the root certificate is well-known, the tag + * Tag::IDENTITY_CREDENTIAL_KEY present, the passed in challenge is present, the tag + * Tag::ATTESTATION_APPLICATION_ID is set to the expected Android application, the device + * has verified boot enabled, each certificate in the chain is signed by its successor, + * none of the certificates have been revoked, and so on. * * <p>It is not strictly necessary to use this method to provision a credential if the issuing * authority doesn't care about the nature of the security hardware. If called, however, this diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 3e1f72da8731..8ea68833e20d 100755 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -2713,6 +2713,32 @@ public class AudioManager { } /** + * @hide + */ + public static String audioFocusToString(int focus) { + switch (focus) { + case AUDIOFOCUS_NONE: + return "AUDIOFOCUS_NONE"; + case AUDIOFOCUS_GAIN: + return "AUDIOFOCUS_GAIN"; + case AUDIOFOCUS_GAIN_TRANSIENT: + return "AUDIOFOCUS_GAIN_TRANSIENT"; + case AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK: + return "AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK"; + case AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE: + return "AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE"; + case AUDIOFOCUS_LOSS: + return "AUDIOFOCUS_LOSS"; + case AUDIOFOCUS_LOSS_TRANSIENT: + return "AUDIOFOCUS_LOSS_TRANSIENT"; + case AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: // Note CAN_DUCK not MAY_DUCK. + return "AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK"; + default: + return "AUDIO_FOCUS_UNKNOWN(" + focus + ")"; + } + } + + /** * Used to indicate no audio focus has been gained or lost, or requested. */ public static final int AUDIOFOCUS_NONE = 0; diff --git a/media/java/android/media/AudioPortEventHandler.java b/media/java/android/media/AudioPortEventHandler.java index 14249cbe8945..8e8dfaf24b9a 100644 --- a/media/java/android/media/AudioPortEventHandler.java +++ b/media/java/android/media/AudioPortEventHandler.java @@ -78,7 +78,8 @@ class AudioPortEventHandler { listeners.add((AudioManager.OnAudioPortUpdateListener)msg.obj); } } else { - listeners = mListeners; + listeners = (ArrayList<AudioManager.OnAudioPortUpdateListener>) + mListeners.clone(); } } // reset audio port cache if the event corresponds to a change coming diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index c11762bcdb40..373f6e126924 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -214,6 +214,175 @@ public class AudioSystem } } + /** + * @hide + * Convert a native audio format integer constant to a string. + */ + public static String audioFormatToString(int audioFormat) { + switch (audioFormat) { + case /* AUDIO_FORMAT_INVALID */ 0xFFFFFFFF: + return "AUDIO_FORMAT_INVALID"; + case /* AUDIO_FORMAT_DEFAULT */ 0: + return "AUDIO_FORMAT_DEFAULT"; + case /* AUDIO_FORMAT_MP3 */ 0x01000000: + return "AUDIO_FORMAT_MP3"; + case /* AUDIO_FORMAT_AMR_NB */ 0x02000000: + return "AUDIO_FORMAT_AMR_NB"; + case /* AUDIO_FORMAT_AMR_WB */ 0x03000000: + return "AUDIO_FORMAT_AMR_WB"; + case /* AUDIO_FORMAT_AAC */ 0x04000000: + return "AUDIO_FORMAT_AAC"; + case /* AUDIO_FORMAT_HE_AAC_V1 */ 0x05000000: + return "AUDIO_FORMAT_HE_AAC_V1"; + case /* AUDIO_FORMAT_HE_AAC_V2 */ 0x06000000: + return "AUDIO_FORMAT_HE_AAC_V2"; + case /* AUDIO_FORMAT_VORBIS */ 0x07000000: + return "AUDIO_FORMAT_VORBIS"; + case /* AUDIO_FORMAT_OPUS */ 0x08000000: + return "AUDIO_FORMAT_OPUS"; + case /* AUDIO_FORMAT_AC3 */ 0x09000000: + return "AUDIO_FORMAT_AC3"; + case /* AUDIO_FORMAT_E_AC3 */ 0x0A000000: + return "AUDIO_FORMAT_E_AC3"; + case /* AUDIO_FORMAT_DTS */ 0x0B000000: + return "AUDIO_FORMAT_DTS"; + case /* AUDIO_FORMAT_DTS_HD */ 0x0C000000: + return "AUDIO_FORMAT_DTS_HD"; + case /* AUDIO_FORMAT_IEC61937 */ 0x0D000000: + return "AUDIO_FORMAT_IEC61937"; + case /* AUDIO_FORMAT_DOLBY_TRUEHD */ 0x0E000000: + return "AUDIO_FORMAT_DOLBY_TRUEHD"; + case /* AUDIO_FORMAT_EVRC */ 0x10000000: + return "AUDIO_FORMAT_EVRC"; + case /* AUDIO_FORMAT_EVRCB */ 0x11000000: + return "AUDIO_FORMAT_EVRCB"; + case /* AUDIO_FORMAT_EVRCWB */ 0x12000000: + return "AUDIO_FORMAT_EVRCWB"; + case /* AUDIO_FORMAT_EVRCNW */ 0x13000000: + return "AUDIO_FORMAT_EVRCNW"; + case /* AUDIO_FORMAT_AAC_ADIF */ 0x14000000: + return "AUDIO_FORMAT_AAC_ADIF"; + case /* AUDIO_FORMAT_WMA */ 0x15000000: + return "AUDIO_FORMAT_WMA"; + case /* AUDIO_FORMAT_WMA_PRO */ 0x16000000: + return "AUDIO_FORMAT_WMA_PRO"; + case /* AUDIO_FORMAT_AMR_WB_PLUS */ 0x17000000: + return "AUDIO_FORMAT_AMR_WB_PLUS"; + case /* AUDIO_FORMAT_MP2 */ 0x18000000: + return "AUDIO_FORMAT_MP2"; + case /* AUDIO_FORMAT_QCELP */ 0x19000000: + return "AUDIO_FORMAT_QCELP"; + case /* AUDIO_FORMAT_DSD */ 0x1A000000: + return "AUDIO_FORMAT_DSD"; + case /* AUDIO_FORMAT_FLAC */ 0x1B000000: + return "AUDIO_FORMAT_FLAC"; + case /* AUDIO_FORMAT_ALAC */ 0x1C000000: + return "AUDIO_FORMAT_ALAC"; + case /* AUDIO_FORMAT_APE */ 0x1D000000: + return "AUDIO_FORMAT_APE"; + case /* AUDIO_FORMAT_AAC_ADTS */ 0x1E000000: + return "AUDIO_FORMAT_AAC_ADTS"; + case /* AUDIO_FORMAT_SBC */ 0x1F000000: + return "AUDIO_FORMAT_SBC"; + case /* AUDIO_FORMAT_APTX */ 0x20000000: + return "AUDIO_FORMAT_APTX"; + case /* AUDIO_FORMAT_APTX_HD */ 0x21000000: + return "AUDIO_FORMAT_APTX_HD"; + case /* AUDIO_FORMAT_AC4 */ 0x22000000: + return "AUDIO_FORMAT_AC4"; + case /* AUDIO_FORMAT_LDAC */ 0x23000000: + return "AUDIO_FORMAT_LDAC"; + case /* AUDIO_FORMAT_MAT */ 0x24000000: + return "AUDIO_FORMAT_MAT"; + case /* AUDIO_FORMAT_AAC_LATM */ 0x25000000: + return "AUDIO_FORMAT_AAC_LATM"; + case /* AUDIO_FORMAT_CELT */ 0x26000000: + return "AUDIO_FORMAT_CELT"; + case /* AUDIO_FORMAT_APTX_ADAPTIVE */ 0x27000000: + return "AUDIO_FORMAT_APTX_ADAPTIVE"; + case /* AUDIO_FORMAT_LHDC */ 0x28000000: + return "AUDIO_FORMAT_LHDC"; + case /* AUDIO_FORMAT_LHDC_LL */ 0x29000000: + return "AUDIO_FORMAT_LHDC_LL"; + case /* AUDIO_FORMAT_APTX_TWSP */ 0x2A000000: + return "AUDIO_FORMAT_APTX_TWSP"; + + /* Aliases */ + case /* AUDIO_FORMAT_PCM_16_BIT */ 0x1: + return "AUDIO_FORMAT_PCM_16_BIT"; // (PCM | PCM_SUB_16_BIT) + case /* AUDIO_FORMAT_PCM_8_BIT */ 0x2: + return "AUDIO_FORMAT_PCM_8_BIT"; // (PCM | PCM_SUB_8_BIT) + case /* AUDIO_FORMAT_PCM_32_BIT */ 0x3: + return "AUDIO_FORMAT_PCM_32_BIT"; // (PCM | PCM_SUB_32_BIT) + case /* AUDIO_FORMAT_PCM_8_24_BIT */ 0x4: + return "AUDIO_FORMAT_PCM_8_24_BIT"; // (PCM | PCM_SUB_8_24_BIT) + case /* AUDIO_FORMAT_PCM_FLOAT */ 0x5: + return "AUDIO_FORMAT_PCM_FLOAT"; // (PCM | PCM_SUB_FLOAT) + case /* AUDIO_FORMAT_PCM_24_BIT_PACKED */ 0x6: + return "AUDIO_FORMAT_PCM_24_BIT_PACKED"; // (PCM | PCM_SUB_24_BIT_PACKED) + case /* AUDIO_FORMAT_AAC_MAIN */ 0x4000001: + return "AUDIO_FORMAT_AAC_MAIN"; // (AAC | AAC_SUB_MAIN) + case /* AUDIO_FORMAT_AAC_LC */ 0x4000002: + return "AUDIO_FORMAT_AAC_LC"; // (AAC | AAC_SUB_LC) + case /* AUDIO_FORMAT_AAC_SSR */ 0x4000004: + return "AUDIO_FORMAT_AAC_SSR"; // (AAC | AAC_SUB_SSR) + case /* AUDIO_FORMAT_AAC_LTP */ 0x4000008: + return "AUDIO_FORMAT_AAC_LTP"; // (AAC | AAC_SUB_LTP) + case /* AUDIO_FORMAT_AAC_HE_V1 */ 0x4000010: + return "AUDIO_FORMAT_AAC_HE_V1"; // (AAC | AAC_SUB_HE_V1) + case /* AUDIO_FORMAT_AAC_SCALABLE */ 0x4000020: + return "AUDIO_FORMAT_AAC_SCALABLE"; // (AAC | AAC_SUB_SCALABLE) + case /* AUDIO_FORMAT_AAC_ERLC */ 0x4000040: + return "AUDIO_FORMAT_AAC_ERLC"; // (AAC | AAC_SUB_ERLC) + case /* AUDIO_FORMAT_AAC_LD */ 0x4000080: + return "AUDIO_FORMAT_AAC_LD"; // (AAC | AAC_SUB_LD) + case /* AUDIO_FORMAT_AAC_HE_V2 */ 0x4000100: + return "AUDIO_FORMAT_AAC_HE_V2"; // (AAC | AAC_SUB_HE_V2) + case /* AUDIO_FORMAT_AAC_ELD */ 0x4000200: + return "AUDIO_FORMAT_AAC_ELD"; // (AAC | AAC_SUB_ELD) + case /* AUDIO_FORMAT_AAC_XHE */ 0x4000300: + return "AUDIO_FORMAT_AAC_XHE"; // (AAC | AAC_SUB_XHE) + case /* AUDIO_FORMAT_AAC_ADTS_MAIN */ 0x1e000001: + return "AUDIO_FORMAT_AAC_ADTS_MAIN"; // (AAC_ADTS | AAC_SUB_MAIN) + case /* AUDIO_FORMAT_AAC_ADTS_LC */ 0x1e000002: + return "AUDIO_FORMAT_AAC_ADTS_LC"; // (AAC_ADTS | AAC_SUB_LC) + case /* AUDIO_FORMAT_AAC_ADTS_SSR */ 0x1e000004: + return "AUDIO_FORMAT_AAC_ADTS_SSR"; // (AAC_ADTS | AAC_SUB_SSR) + case /* AUDIO_FORMAT_AAC_ADTS_LTP */ 0x1e000008: + return "AUDIO_FORMAT_AAC_ADTS_LTP"; // (AAC_ADTS | AAC_SUB_LTP) + case /* AUDIO_FORMAT_AAC_ADTS_HE_V1 */ 0x1e000010: + return "AUDIO_FORMAT_AAC_ADTS_HE_V1"; // (AAC_ADTS | AAC_SUB_HE_V1) + case /* AUDIO_FORMAT_AAC_ADTS_SCALABLE */ 0x1e000020: + return "AUDIO_FORMAT_AAC_ADTS_SCALABLE"; // (AAC_ADTS | AAC_SUB_SCALABLE) + case /* AUDIO_FORMAT_AAC_ADTS_ERLC */ 0x1e000040: + return "AUDIO_FORMAT_AAC_ADTS_ERLC"; // (AAC_ADTS | AAC_SUB_ERLC) + case /* AUDIO_FORMAT_AAC_ADTS_LD */ 0x1e000080: + return "AUDIO_FORMAT_AAC_ADTS_LD"; // (AAC_ADTS | AAC_SUB_LD) + case /* AUDIO_FORMAT_AAC_ADTS_HE_V2 */ 0x1e000100: + return "AUDIO_FORMAT_AAC_ADTS_HE_V2"; // (AAC_ADTS | AAC_SUB_HE_V2) + case /* AUDIO_FORMAT_AAC_ADTS_ELD */ 0x1e000200: + return "AUDIO_FORMAT_AAC_ADTS_ELD"; // (AAC_ADTS | AAC_SUB_ELD) + case /* AUDIO_FORMAT_AAC_ADTS_XHE */ 0x1e000300: + return "AUDIO_FORMAT_AAC_ADTS_XHE"; // (AAC_ADTS | AAC_SUB_XHE) + case /* AUDIO_FORMAT_AAC_LATM_LC */ 0x25000002: + return "AUDIO_FORMAT_AAC_LATM_LC"; // (AAC_LATM | AAC_SUB_LC) + case /* AUDIO_FORMAT_AAC_LATM_HE_V1 */ 0x25000010: + return "AUDIO_FORMAT_AAC_LATM_HE_V1"; // (AAC_LATM | AAC_SUB_HE_V1) + case /* AUDIO_FORMAT_AAC_LATM_HE_V2 */ 0x25000100: + return "AUDIO_FORMAT_AAC_LATM_HE_V2"; // (AAC_LATM | AAC_SUB_HE_V2) + case /* AUDIO_FORMAT_E_AC3_JOC */ 0xA000001: + return "AUDIO_FORMAT_E_AC3_JOC"; // (E_AC3 | E_AC3_SUB_JOC) + case /* AUDIO_FORMAT_MAT_1_0 */ 0x24000001: + return "AUDIO_FORMAT_MAT_1_0"; // (MAT | MAT_SUB_1_0) + case /* AUDIO_FORMAT_MAT_2_0 */ 0x24000002: + return "AUDIO_FORMAT_MAT_2_0"; // (MAT | MAT_SUB_2_0) + case /* AUDIO_FORMAT_MAT_2_1 */ 0x24000003: + return "AUDIO_FORMAT_MAT_2_1"; // (MAT | MAT_SUB_2_1) + default: + return "AUDIO_FORMAT_(" + audioFormat + ")"; + } + } + /* Routing bits for the former setRouting/getRouting API */ /** @hide @deprecated */ @Deprecated public static final int ROUTE_EARPIECE = (1 << 0); diff --git a/media/java/android/media/MediaCas.java b/media/java/android/media/MediaCas.java index 97d32d88c7f2..c652628eb425 100644 --- a/media/java/android/media/MediaCas.java +++ b/media/java/android/media/MediaCas.java @@ -388,7 +388,7 @@ public final class MediaCas implements AutoCloseable { @Override public void onReclaimResources() { synchronized (mSessionMap) { - mSessionMap.forEach((casSession, sessionResourceId) -> casSession.close()); + mSessionMap.forEach((casSession, sessionResourceHandle) -> casSession.close()); } mEventHandler.sendMessage(mEventHandler.obtainMessage( EventHandler.MSG_CAS_RESOURCE_LOST)); @@ -868,7 +868,7 @@ public final class MediaCas implements AutoCloseable { } } - private int getSessionResourceId() throws MediaCasException { + private int getSessionResourceHandle() throws MediaCasException { validateInternalStates(); int[] sessionResourceHandle = new int[1]; @@ -881,14 +881,14 @@ public final class MediaCas implements AutoCloseable { "insufficient resource to Open Session"); } } - return sessionResourceHandle[0]; + return sessionResourceHandle[0]; } - private void addSessionToResourceMap(Session session, int sessionResourceId) { + private void addSessionToResourceMap(Session session, int sessionResourceHandle) { - if (sessionResourceId != -1) { + if (sessionResourceHandle != TunerResourceManager.INVALID_RESOURCE_HANDLE) { synchronized (mSessionMap) { - mSessionMap.put(session, sessionResourceId); + mSessionMap.put(session, sessionResourceHandle); } } } @@ -918,13 +918,13 @@ public final class MediaCas implements AutoCloseable { * @throws MediaCasStateException for CAS-specific state exceptions. */ public Session openSession() throws MediaCasException { - int sessionResourceId = getSessionResourceId(); + int sessionResourceHandle = getSessionResourceHandle(); try { OpenSessionCallback cb = new OpenSessionCallback(); mICas.openSession(cb); MediaCasException.throwExceptionIfNeeded(cb.mStatus); - addSessionToResourceMap(cb.mSession, sessionResourceId); + addSessionToResourceMap(cb.mSession, sessionResourceHandle); return cb.mSession; } catch (RemoteException e) { cleanupAndRethrowIllegalState(); @@ -952,7 +952,7 @@ public final class MediaCas implements AutoCloseable { @Nullable public Session openSession(@SessionUsage int sessionUsage, @ScramblingMode int scramblingMode) throws MediaCasException { - int sessionResourceId = getSessionResourceId(); + int sessionResourceHandle = getSessionResourceHandle(); if (mICasV12 == null) { Log.d(TAG, "Open Session with scrambling mode is only supported by cas@1.2+ interface"); @@ -963,7 +963,7 @@ public final class MediaCas implements AutoCloseable { OpenSession_1_2_Callback cb = new OpenSession_1_2_Callback(); mICasV12.openSession_1_2(sessionUsage, scramblingMode, cb); MediaCasException.throwExceptionIfNeeded(cb.mStatus); - addSessionToResourceMap(cb.mSession, sessionResourceId); + addSessionToResourceMap(cb.mSession, sessionResourceHandle); return cb.mSession; } catch (RemoteException e) { cleanupAndRethrowIllegalState(); diff --git a/media/java/android/media/MediaMetrics.java b/media/java/android/media/MediaMetrics.java index 540955f3b393..f6f482dd0cd3 100644 --- a/media/java/android/media/MediaMetrics.java +++ b/media/java/android/media/MediaMetrics.java @@ -38,6 +38,117 @@ import java.util.Objects; public class MediaMetrics { public static final String TAG = "MediaMetrics"; + public static final String SEPARATOR = "."; + + /** + * A list of established MediaMetrics names that can be used for Items. + */ + public static class Name { + public static final String AUDIO = "audio"; + public static final String AUDIO_BLUETOOTH = AUDIO + SEPARATOR + "bluetooth"; + public static final String AUDIO_DEVICE = AUDIO + SEPARATOR + "device"; + public static final String AUDIO_FOCUS = AUDIO + SEPARATOR + "focus"; + public static final String AUDIO_FORCE_USE = AUDIO + SEPARATOR + "forceUse"; + public static final String AUDIO_MIC = AUDIO + SEPARATOR + "mic"; + public static final String AUDIO_SERVICE = AUDIO + SEPARATOR + "service"; + public static final String AUDIO_VOLUME = AUDIO + SEPARATOR + "volume"; + public static final String AUDIO_VOLUME_EVENT = AUDIO_VOLUME + SEPARATOR + "event"; + } + + /** + * A list of established string values. + */ + public static class Value { + public static final String CONNECT = "connect"; + public static final String CONNECTED = "connected"; + public static final String DISCONNECT = "disconnect"; + public static final String DISCONNECTED = "disconnected"; + public static final String DOWN = "down"; + public static final String MUTE = "mute"; + public static final String NO = "no"; + public static final String OFF = "off"; + public static final String ON = "on"; + public static final String UNMUTE = "unmute"; + public static final String UP = "up"; + public static final String YES = "yes"; + } + + /** + * A list of standard property keys for consistent use and type. + */ + public static class Property { + // A use for Bluetooth or USB device addresses + public static final Key<String> ADDRESS = createKey("address", String.class); + // A string representing the Audio Attributes + public static final Key<String> ATTRIBUTES = createKey("attributes", String.class); + + // The calling package responsible for the state change + public static final Key<String> CALLING_PACKAGE = + createKey("callingPackage", String.class); + + // The client name + public static final Key<String> CLIENT_NAME = createKey("clientName", String.class); + + // The device type + public static final Key<Integer> DELAY_MS = createKey("delayMs", Integer.class); + + // The device type + public static final Key<String> DEVICE = createKey("device", String.class); + + // For volume changes, up or down + public static final Key<String> DIRECTION = createKey("direction", String.class); + + // A reason for early return or error + public static final Key<String> EARLY_RETURN = + createKey("earlyReturn", String.class); + // ENCODING_ ... string to match AudioFormat encoding + public static final Key<String> ENCODING = createKey("encoding", String.class); + + public static final Key<String> EVENT = createKey("event#", String.class); + + // event generated is external (yes, no) + public static final Key<String> EXTERNAL = createKey("external", String.class); + + public static final Key<Integer> FLAGS = createKey("flags", Integer.class); + public static final Key<String> FOCUS_CHANGE_HINT = + createKey("focusChangeHint", String.class); + public static final Key<String> FORCE_USE_DUE_TO = + createKey("forceUseDueTo", String.class); + public static final Key<String> FORCE_USE_MODE = + createKey("forceUseMode", String.class); + public static final Key<Double> GAIN_DB = + createKey("gainDb", Double.class); + public static final Key<String> GROUP = + createKey("group", String.class); + // For volume + public static final Key<Integer> INDEX = createKey("index", Integer.class); + public static final Key<Integer> MAX_INDEX = createKey("maxIndex", Integer.class); + public static final Key<Integer> MIN_INDEX = createKey("minIndex", Integer.class); + public static final Key<String> MODE = + createKey("mode", String.class); // audio_mode + public static final Key<String> MUTE = + createKey("mute", String.class); // microphone, on or off. + + // Bluetooth or Usb device name + public static final Key<String> NAME = + createKey("name", String.class); + + // Number of observers + public static final Key<Integer> OBSERVERS = + createKey("observers", Integer.class); + + public static final Key<String> REQUEST = + createKey("request", String.class); + + // For Bluetooth + public static final Key<String> SCO_AUDIO_MODE = + createKey("scoAudioMode", String.class); + public static final Key<Integer> SDK = createKey("sdk", Integer.class); + public static final Key<String> STATE = createKey("state", String.class); + public static final Key<Integer> STATUS = createKey("status", Integer.class); + public static final Key<String> STREAM_TYPE = createKey("streamType", String.class); + } + /** * The TYPE constants below should match those in native MediaMetricsItem.h */ diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java index 0ea962493164..2c65cc4440a4 100644 --- a/media/java/android/media/MediaRouter2.java +++ b/media/java/android/media/MediaRouter2.java @@ -35,7 +35,6 @@ import android.util.Log; import com.android.internal.annotations.GuardedBy; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -379,11 +378,7 @@ public final class MediaRouter2 { */ public void transferTo(@NonNull MediaRoute2Info route) { Objects.requireNonNull(route, "route must not be null"); - - List<RoutingController> controllers = getControllers(); - RoutingController controller = controllers.get(controllers.size() - 1); - - transfer(controller, route); + transfer(getCurrentController(), route); } /** @@ -391,10 +386,7 @@ public final class MediaRouter2 { * controls the media routing, this method is a no-op. */ public void stop() { - List<RoutingController> controllers = getControllers(); - RoutingController controller = controllers.get(controllers.size() - 1); - - controller.release(); + getCurrentController().release(); } /** @@ -417,12 +409,9 @@ public final class MediaRouter2 { return; } - controller.release(); - final int requestId = mControllerCreationRequestCnt.getAndIncrement(); - ControllerCreationRequest request = - new ControllerCreationRequest(requestId, controller, route); + ControllerCreationRequest request = new ControllerCreationRequest(requestId, route); mControllerCreationRequests.add(request); OnGetControllerHintsListener listener = mOnGetControllerHintsListener; @@ -450,6 +439,12 @@ public final class MediaRouter2 { } } + @NonNull + private RoutingController getCurrentController() { + List<RoutingController> controllers = getControllers(); + return controllers.get(controllers.size() - 1); + } + /** * Gets a {@link RoutingController} which can control the routes provided by system. * e.g. Phone speaker, wired headset, Bluetooth, etc. @@ -474,13 +469,8 @@ public final class MediaRouter2 { public List<RoutingController> getControllers() { List<RoutingController> result = new ArrayList<>(); result.add(0, mSystemController); - - Collection<RoutingController> controllers; synchronized (sRouterLock) { - controllers = mRoutingControllers.values(); - if (controllers != null) { - result.addAll(controllers); - } + result.addAll(mRoutingControllers.values()); } return result; } @@ -608,19 +598,33 @@ public final class MediaRouter2 { } } - if (sessionInfo != null) { - RoutingController newController; - if (sessionInfo.isSystemSession()) { - newController = getSystemController(); - } else { - newController = new RoutingController(sessionInfo); - synchronized (sRouterLock) { - mRoutingControllers.put(newController.getId(), newController); - } + if (sessionInfo == null) { + return; + } + + RoutingController oldController = getCurrentController(); + if (!oldController.releaseInternal( + /* shouldReleaseSession= */ true, /* shouldNotifyStop= */ false)) { + // Could not release the controller since it was just released by other thread. + oldController = getSystemController(); + } + + RoutingController newController; + if (sessionInfo.isSystemSession()) { + newController = getSystemController(); + newController.setRoutingSessionInfo(sessionInfo); + } else { + newController = new RoutingController(sessionInfo); + synchronized (sRouterLock) { + mRoutingControllers.put(newController.getId(), newController); } - //TODO: Determine oldController properly when transfer is launched by Output Switcher. - notifyTransfer(matchingRequest != null ? matchingRequest.mController : - getSystemController(), newController); + } + + // Two controller can be same if stop() is called before the result of Cast -> Phone comes. + if (oldController != newController) { + notifyTransfer(oldController, newController); + } else if (matchingRequest != null) { + notifyTransferFailure(matchingRequest.mRoute); } } @@ -687,7 +691,8 @@ public final class MediaRouter2 { return; } - matchingController.releaseInternal(/* shouldReleaseSession= */ false); + matchingController.releaseInternal( + /* shouldReleaseSession= */ false, /* shouldNotifyStop= */ true); } void onGetControllerHintsForCreatingSessionOnHandler(long uniqueRequestId, @@ -814,8 +819,9 @@ public final class MediaRouter2 { public abstract static class TransferCallback { /** * Called when a media is transferred between two different routing controllers. - * This can happen by calling {@link #transferTo(MediaRoute2Info)} or - * {@link RoutingController#release()}. + * This can happen by calling {@link #transferTo(MediaRoute2Info)}. + * The {@code oldController} is released before this method is called, except for the + * {@link #getSystemController() system controller}. * * @param oldController the previous controller that controlled routing * @param newController the new controller to control routing @@ -833,6 +839,9 @@ public final class MediaRouter2 { /** * Called when a media routing stops. It can be stopped by a user or a provider. + * App should not continue playing media locally when this method is called. + * The {@code oldController} is released before this method is called, except for the + * {@link #getSystemController() system controller}. * * @param controller the controller that controlled the stopped media routing. */ @@ -1206,14 +1215,18 @@ public final class MediaRouter2 { */ // TODO: Add tests using {@link MediaRouter2Manager#getActiveSessions()}. public void release() { - releaseInternal(/* shouldReleaseSession= */ true); + releaseInternal(/* shouldReleaseSession= */ true, /* shouldNotifyStop= */ true); } - void releaseInternal(boolean shouldReleaseSession) { + /** + * Returns {@code true} when succeeded to release, {@code false} if the controller is + * already released. + */ + boolean releaseInternal(boolean shouldReleaseSession, boolean shouldNotifyStop) { synchronized (mControllerLock) { if (mIsReleased) { Log.w(TAG, "releaseInternal() called on released controller. Ignoring."); - return; + return false; } mIsReleased = true; } @@ -1232,12 +1245,11 @@ public final class MediaRouter2 { } } - if (Thread.currentThread() == mHandler.getLooper().getThread()) { - notifyStop(this); - } else { + if (shouldNotifyStop) { mHandler.sendMessage(obtainMessage(MediaRouter2::notifyStop, MediaRouter2.this, RoutingController.this)); } + return true; } @Override @@ -1294,13 +1306,14 @@ public final class MediaRouter2 { } @Override - public void release() { - // Do nothing. SystemRoutingController will never be released + public boolean isReleased() { + // SystemRoutingController will never be released + return false; } @Override - public boolean isReleased() { - // SystemRoutingController will never be released + boolean releaseInternal(boolean shouldReleaseSession, boolean shouldNotifyStop) { + // Do nothing. SystemRoutingController will never be released return false; } } @@ -1391,13 +1404,10 @@ public final class MediaRouter2 { static final class ControllerCreationRequest { public final int mRequestId; - public final RoutingController mController; public final MediaRoute2Info mRoute; - ControllerCreationRequest(int requestId, @NonNull RoutingController controller, - @NonNull MediaRoute2Info route) { + ControllerCreationRequest(int requestId, @NonNull MediaRoute2Info route) { mRequestId = requestId; - mController = controller; mRoute = route; } } diff --git a/media/java/android/media/RoutingSessionInfo.java b/media/java/android/media/RoutingSessionInfo.java index 629cf1544bd1..608e29a7a6ca 100644 --- a/media/java/android/media/RoutingSessionInfo.java +++ b/media/java/android/media/RoutingSessionInfo.java @@ -310,19 +310,19 @@ public final class RoutingSessionInfo implements Parcelable { public String toString() { StringBuilder result = new StringBuilder() .append("RoutingSessionInfo{ ") - .append("sessionId=").append(mId) - .append(", name=").append(mName) + .append("sessionId=").append(getId()) + .append(", name=").append(getName()) .append(", selectedRoutes={") - .append(String.join(",", mSelectedRoutes)) + .append(String.join(",", getSelectedRoutes())) .append("}") .append(", selectableRoutes={") - .append(String.join(",", mSelectableRoutes)) + .append(String.join(",", getSelectableRoutes())) .append("}") .append(", deselectableRoutes={") - .append(String.join(",", mDeselectableRoutes)) + .append(String.join(",", getDeselectableRoutes())) .append("}") .append(", transferableRoutes={") - .append(String.join(",", mTransferableRoutes)) + .append(String.join(",", getTransferableRoutes())) .append("}") .append(", volumeHandling=").append(getVolumeHandling()) .append(", volumeMax=").append(getVolumeMax()) diff --git a/media/java/android/media/audiopolicy/AudioVolumeGroupChangeHandler.java b/media/java/android/media/audiopolicy/AudioVolumeGroupChangeHandler.java index adf4d3dcfa09..022cfeeb4e43 100644 --- a/media/java/android/media/audiopolicy/AudioVolumeGroupChangeHandler.java +++ b/media/java/android/media/audiopolicy/AudioVolumeGroupChangeHandler.java @@ -80,7 +80,8 @@ public class AudioVolumeGroupChangeHandler { (AudioManager.VolumeGroupCallback) msg.obj); } } else { - listeners = mListeners; + listeners = (ArrayList<AudioManager.VolumeGroupCallback>) + mListeners.clone(); } } if (listeners.isEmpty()) { diff --git a/media/java/android/media/soundtrigger_middleware/OWNERS b/media/java/android/media/soundtrigger_middleware/OWNERS new file mode 100644 index 000000000000..e5d037003ac4 --- /dev/null +++ b/media/java/android/media/soundtrigger_middleware/OWNERS @@ -0,0 +1,2 @@ +ytai@google.com +elaurent@google.com diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java index d331126e4194..50af60a0ad92 100644 --- a/media/java/android/media/tv/tuner/Tuner.java +++ b/media/java/android/media/tv/tuner/Tuner.java @@ -353,13 +353,16 @@ public class Tuner implements AutoCloseable { @Override public void close() { if (mFrontendHandle != null) { - nativeCloseFrontendByHandle(mFrontendHandle); + int res = nativeCloseFrontend(mFrontendHandle); + if (res != Tuner.RESULT_SUCCESS) { + TunerUtils.throwExceptionForResult(res, "failed to close frontend"); + } mTunerResourceManager.releaseFrontend(mFrontendHandle, mClientId); mFrontendHandle = null; mFrontend = null; } if (mLnb != null) { - releaseLnb(); + mLnb.close(); } if (!mDescramblers.isEmpty()) { for (Map.Entry<Integer, Descrambler> d : mDescramblers.entrySet()) { @@ -374,6 +377,14 @@ public class Tuner implements AutoCloseable { } mFilters.clear(); } + if (mDemuxHandle != null) { + int res = nativeCloseDemux(mDemuxHandle); + if (res != Tuner.RESULT_SUCCESS) { + TunerUtils.throwExceptionForResult(res, "failed to close demux"); + } + mTunerResourceManager.releaseDemux(mDemuxHandle, mClientId); + mFrontendHandle = null; + } TunerUtils.throwExceptionForResult(nativeClose(), "failed to close tuner"); } @@ -425,6 +436,8 @@ public class Tuner implements AutoCloseable { private static native DemuxCapabilities nativeGetDemuxCapabilities(); + private native int nativeCloseDemux(int handle); + private native int nativeCloseFrontend(int handle); private native int nativeClose(); @@ -545,10 +558,11 @@ public class Tuner implements AutoCloseable { @Result public int tune(@NonNull FrontendSettings settings) { mFrontendType = settings.getType(); - checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND); - - mFrontendInfo = null; - return nativeTune(settings.getType(), settings); + if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) { + mFrontendInfo = null; + return nativeTune(settings.getType(), settings); + } + return RESULT_UNAVAILABLE; } /** @@ -584,11 +598,13 @@ public class Tuner implements AutoCloseable { + "started."); } mFrontendType = settings.getType(); - checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND); - mScanCallback = scanCallback; - mScanCallbackExecutor = executor; - mFrontendInfo = null; - return nativeScan(settings.getType(), settings, scanType); + if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) { + mScanCallback = scanCallback; + mScanCallbackExecutor = executor; + mFrontendInfo = null; + return nativeScan(settings.getType(), settings, scanType); + } + return RESULT_UNAVAILABLE; } /** @@ -671,7 +687,9 @@ public class Tuner implements AutoCloseable { * @return the id of hardware A/V sync. */ public int getAvSyncHwId(@NonNull Filter filter) { - checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX); + if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX)) { + return INVALID_AV_SYNC_ID; + } Integer id = nativeGetAvSyncHwId(filter); return id == null ? INVALID_AV_SYNC_ID : id; } @@ -686,7 +704,9 @@ public class Tuner implements AutoCloseable { * @return the current timestamp of hardware A/V sync. */ public long getAvSyncTime(int avSyncHwId) { - checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX); + if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX)) { + return INVALID_TIMESTAMP; + } Long time = nativeGetAvSyncTime(avSyncHwId); return time == null ? INVALID_TIMESTAMP : time; } @@ -702,8 +722,10 @@ public class Tuner implements AutoCloseable { */ @Result public int connectCiCam(int ciCamId) { - checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX); - return nativeConnectCiCam(ciCamId); + if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX)) { + return nativeConnectCiCam(ciCamId); + } + return RESULT_UNAVAILABLE; } /** @@ -715,8 +737,10 @@ public class Tuner implements AutoCloseable { */ @Result public int disconnectCiCam() { - checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX); - return nativeDisconnectCiCam(); + if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX)) { + return nativeDisconnectCiCam(); + } + return RESULT_UNAVAILABLE; } /** @@ -726,7 +750,9 @@ public class Tuner implements AutoCloseable { */ @Nullable public FrontendInfo getFrontendInfo() { - checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND); + if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) { + return null; + } if (mFrontend == null) { throw new IllegalStateException("frontend is not initialized"); } @@ -861,7 +887,9 @@ public class Tuner implements AutoCloseable { public Filter openFilter(@Type int mainType, @Subtype int subType, @BytesLong long bufferSize, @CallbackExecutor @Nullable Executor executor, @Nullable FilterCallback cb) { - checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX); + if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX)) { + return null; + } Filter filter = nativeOpenFilter( mainType, TunerUtils.getFilterSubtype(mainType, subType), bufferSize); if (filter != null) { @@ -891,12 +919,15 @@ public class Tuner implements AutoCloseable { Objects.requireNonNull(executor, "executor must not be null"); Objects.requireNonNull(cb, "LnbCallback must not be null"); if (mLnb != null) { + mLnb.setCallback(executor, cb, this); return mLnb; } if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_LNB) && mLnb != null) { mLnb.setCallback(executor, cb, this); + setLnb(mLnb); + return mLnb; } - return mLnb; + return null; } /** @@ -922,6 +953,7 @@ public class Tuner implements AutoCloseable { } mLnb = newLnb; mLnb.setCallback(executor, cb, this); + setLnb(mLnb); } return mLnb; } @@ -944,7 +976,9 @@ public class Tuner implements AutoCloseable { */ @Nullable public TimeFilter openTimeFilter() { - checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX); + if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX)) { + return null; + } return nativeOpenTimeFilter(); } @@ -956,6 +990,9 @@ public class Tuner implements AutoCloseable { @RequiresPermission(android.Manifest.permission.ACCESS_TV_DESCRAMBLER) @Nullable public Descrambler openDescrambler() { + if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX)) { + return null; + } return requestDescrambler(); } @@ -976,7 +1013,9 @@ public class Tuner implements AutoCloseable { @NonNull OnRecordStatusChangedListener l) { Objects.requireNonNull(executor, "executor must not be null"); Objects.requireNonNull(l, "OnRecordStatusChangedListener must not be null"); - checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX); + if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX)) { + return null; + } DvrRecorder dvr = nativeOpenDvrRecorder(bufferSize); dvr.setListener(executor, l); return dvr; @@ -999,7 +1038,9 @@ public class Tuner implements AutoCloseable { @NonNull OnPlaybackStatusChangedListener l) { Objects.requireNonNull(executor, "executor must not be null"); Objects.requireNonNull(l, "OnPlaybackStatusChangedListener must not be null"); - checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX); + if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_DEMUX)) { + return null; + } DvrPlayback dvr = nativeOpenDvrPlayback(bufferSize); dvr.setListener(executor, l); return dvr; diff --git a/media/java/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl b/media/java/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl index 7077cd1b76a0..487b444eb627 100644 --- a/media/java/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl +++ b/media/java/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl @@ -218,11 +218,11 @@ interface ITunerResourceManager { * <p><strong>Note:</strong> {@link #updateCasInfo(int, int)} must be called before this request. * * @param request {@link CasSessionRequest} information of the current request. - * @param sessionResourceId a one-element array to return the granted cas session id. + * @param casSessionHandle a one-element array to return the granted cas session handle. * * @return true if there is CAS session granted. */ - boolean requestCasSession(in CasSessionRequest request, out int[] sessionResourceId); + boolean requestCasSession(in CasSessionRequest request, out int[] casSessionHandle); /* * This API is used by the Tuner framework to request an available Lnb from the TunerHAL. @@ -276,7 +276,7 @@ interface ITunerResourceManager { * * <p>Client must call this whenever it releases a descrambler. * - * @param demuxHandle the handle of the released Tuner Descrambler. + * @param descramblerHandle the handle of the released Tuner Descrambler. * @param clientId the id of the client that is releasing the descrambler. */ void releaseDescrambler(in int descramblerHandle, int clientId); @@ -288,10 +288,10 @@ interface ITunerResourceManager { * * <p><strong>Note:</strong> {@link #updateCasInfo(int, int)} must be called before this release. * - * @param sessionResourceId the id of the released CAS session. + * @param casSessionHandle the handle of the released CAS session. * @param clientId the id of the client that is releasing the cas session. */ - void releaseCasSession(in int sessionResourceId, int clientId); + void releaseCasSession(in int casSessionHandle, int clientId); /* * Notifies the TRM that the Lnb with the given handle was released. diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java index b4dcc5d8b3df..be102d8acc10 100644 --- a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java +++ b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java @@ -362,17 +362,16 @@ public class TunerResourceManager { * request. * * @param request {@link CasSessionRequest} information of the current request. - * @param sessionResourceId a one-element array to return the granted cas session id. - * If no CAS granted, this will return - * {@link #INVALID_CAS_SESSION_RESOURCE_ID}. + * @param casSessionHandle a one-element array to return the granted cas session handel. + * If no CAS granted, this will return {@link #INVALID_RESOURCE_HANDLE}. * * @return true if there is CAS session granted. */ public boolean requestCasSession(@NonNull CasSessionRequest request, - @NonNull int[] sessionResourceId) { + @NonNull int[] casSessionHandle) { boolean result = false; try { - result = mService.requestCasSession(request, sessionResourceId); + result = mService.requestCasSession(request, casSessionHandle); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -471,12 +470,12 @@ public class TunerResourceManager { * <p><strong>Note:</strong> {@link #updateCasInfo(int, int)} must be called before this * release. * - * @param sessionResourceId the id of the released CAS session. + * @param casSessionHandle the handle of the released CAS session. * @param clientId the id of the client that is releasing the cas session. */ - public void releaseCasSession(int sessionResourceId, int clientId) { + public void releaseCasSession(int casSessionHandle, int clientId) { try { - mService.releaseCasSession(sessionResourceId, clientId); + mService.releaseCasSession(casSessionHandle, clientId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java index e6962a14a2d0..aba74e518a22 100755 --- a/media/java/android/mtp/MtpDatabase.java +++ b/media/java/android/mtp/MtpDatabase.java @@ -46,6 +46,7 @@ import android.view.Display; import android.view.WindowManager; import com.android.internal.annotations.VisibleForNative; +import com.android.internal.annotations.VisibleForTesting; import dalvik.system.CloseGuard; @@ -408,7 +409,8 @@ public class MtpDatabase implements AutoCloseable { } @VisibleForNative - private int beginSendObject(String path, int format, int parent, int storageId) { + @VisibleForTesting + public int beginSendObject(String path, int format, int parent, int storageId) { MtpStorageManager.MtpObject parentObj = parent == 0 ? mManager.getStorageRoot(storageId) : mManager.getObject(parent); if (parentObj == null) { @@ -452,7 +454,8 @@ public class MtpDatabase implements AutoCloseable { } @VisibleForNative - private int getNumObjects(int storageID, int format, int parent) { + @VisibleForTesting + public int getNumObjects(int storageID, int format, int parent) { List<MtpStorageManager.MtpObject> objs = mManager.getObjects(parent, format, storageID); if (objs == null) { @@ -830,7 +833,8 @@ public class MtpDatabase implements AutoCloseable { } @VisibleForNative - private boolean getThumbnailInfo(int handle, long[] outLongs) { + @VisibleForTesting + public boolean getThumbnailInfo(int handle, long[] outLongs) { MtpStorageManager.MtpObject obj = mManager.getObject(handle); if (obj == null) { return false; @@ -866,7 +870,8 @@ public class MtpDatabase implements AutoCloseable { } @VisibleForNative - private byte[] getThumbnailData(int handle) { + @VisibleForTesting + public byte[] getThumbnailData(int handle) { MtpStorageManager.MtpObject obj = mManager.getObject(handle); if (obj == null) { return null; diff --git a/media/java/android/mtp/MtpStorage.java b/media/java/android/mtp/MtpStorage.java index ba752633718c..88c32a3ea72b 100644 --- a/media/java/android/mtp/MtpStorage.java +++ b/media/java/android/mtp/MtpStorage.java @@ -36,7 +36,7 @@ public class MtpStorage { public MtpStorage(StorageVolume volume, int storageId) { mStorageId = storageId; - mPath = volume.getInternalPath(); + mPath = volume.getPath(); mDescription = volume.getDescription(null); mRemovable = volume.isRemovable(); mMaxFileSize = volume.getMaxFileSize(); diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp index 614fe73a76de..ab311c0e245a 100644 --- a/media/jni/android_media_tv_Tuner.cpp +++ b/media/jni/android_media_tv_Tuner.cpp @@ -1130,7 +1130,7 @@ jintArray JTuner::getLnbIds() { lnbIds = ids; res = r; }); - if (res != Result::SUCCESS || mLnbIds.size() == 0) { + if (res != Result::SUCCESS || lnbIds.size() == 0) { ALOGW("Lnb isn't available"); return NULL; } @@ -1797,6 +1797,22 @@ jobject JTuner::getFrontendStatus(jintArray types) { return statusObj; } +jint JTuner::closeFrontend() { + Result r = Result::SUCCESS; + if (mFe != NULL) { + r = mFe->close(); + } + return (jint) r; +} + +jint JTuner::closeDemux() { + Result r = Result::SUCCESS; + if (mDemux != NULL) { + r = mDemux->close(); + } + return (jint) r; +} + } // namespace android //////////////////////////////////////////////////////////////////////////////// @@ -3199,6 +3215,16 @@ static jint android_media_tv_Tuner_close_tuner(JNIEnv* env, jobject thiz) { return (jint) tuner->close(); } +static jint android_media_tv_Tuner_close_demux(JNIEnv* env, jobject thiz, jint /* handle */) { + sp<JTuner> tuner = getTuner(env, thiz); + return tuner->closeDemux(); +} + +static jint android_media_tv_Tuner_close_frontend(JNIEnv* env, jobject thiz, jint /* handle */) { + sp<JTuner> tuner = getTuner(env, thiz); + return tuner->closeFrontend(); +} + static jint android_media_tv_Tuner_attach_filter(JNIEnv *env, jobject dvr, jobject filter) { sp<Dvr> dvrSp = getDvr(env, dvr); if (dvrSp == NULL) { @@ -3526,7 +3552,9 @@ static const JNINativeMethod gTunerMethods[] = { { "nativeGetDemuxCapabilities", "()Landroid/media/tv/tuner/DemuxCapabilities;", (void *)android_media_tv_Tuner_get_demux_caps }, { "nativeOpenDemuxByhandle", "(I)I", (void *)android_media_tv_Tuner_open_demux }, - {"nativeClose", "()I", (void *)android_media_tv_Tuner_close_tuner }, + { "nativeClose", "()I", (void *)android_media_tv_Tuner_close_tuner }, + { "nativeCloseFrontend", "(I)I", (void *)android_media_tv_Tuner_close_frontend }, + { "nativeCloseDemux", "(I)I", (void *)android_media_tv_Tuner_close_demux }, }; static const JNINativeMethod gFilterMethods[] = { diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h index 750b146dbd59..3da78acb2f90 100644 --- a/media/jni/android_media_tv_Tuner.h +++ b/media/jni/android_media_tv_Tuner.h @@ -191,6 +191,8 @@ struct JTuner : public RefBase { jobject getFrontendStatus(jintArray types); Result openDemux(); jint close(); + jint closeFrontend(); + jint closeDemux(); protected: virtual ~JTuner(); diff --git a/media/tests/MtpTests/res/raw/test_bad_thumb.jpg b/media/tests/MtpTests/res/raw/test_bad_thumb.jpg new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/media/tests/MtpTests/res/raw/test_bad_thumb.jpg diff --git a/media/tests/MtpTests/src/android/mtp/MtpDatabaseTest.java b/media/tests/MtpTests/src/android/mtp/MtpDatabaseTest.java new file mode 100644 index 000000000000..e2e8ff4946e0 --- /dev/null +++ b/media/tests/MtpTests/src/android/mtp/MtpDatabaseTest.java @@ -0,0 +1,316 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.mtp; + +import android.annotation.NonNull; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.os.Build; +import android.os.FileUtils; +import android.os.UserHandle; +import android.os.storage.StorageManager; +import android.os.storage.StorageVolume; +import android.util.Log; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.util.Preconditions; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Assume; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * Tests for MtpDatabase functionality. + */ +@RunWith(AndroidJUnit4.class) +public class MtpDatabaseTest { + private static final String TAG = MtpDatabaseTest.class.getSimpleName(); + + private final Context mContext = InstrumentationRegistry.getContext(); + + private static final File mBaseDir = InstrumentationRegistry.getContext().getExternalCacheDir(); + private static final String MAIN_STORAGE_DIR = mBaseDir.getPath() + "/" + TAG + "/"; + private static final String TEST_DIRNAME = "/TestIs"; + + private static final int MAIN_STORAGE_ID = 0x10001; + private static final int SCND_STORAGE_ID = 0x20001; + private static final String MAIN_STORAGE_ID_STR = Integer.toHexString(MAIN_STORAGE_ID); + private static final String SCND_STORAGE_ID_STR = Integer.toHexString(SCND_STORAGE_ID); + + private static final File mMainStorageDir = new File(MAIN_STORAGE_DIR); + + private static ServerHolder mServerHolder; + private MtpDatabase mMtpDatabase; + + private static void logMethodName() { + Log.d(TAG, Thread.currentThread().getStackTrace()[3].getMethodName()); + } + + private static File createNewDir(File parent, String name) { + File ret = new File(parent, name); + if (!ret.mkdir()) + throw new AssertionError( + "Failed to create file: name=" + name + ", " + parent.getPath()); + return ret; + } + + private static void writeNewFile(File newFile) { + try { + new FileOutputStream(newFile).write(new byte[] {0, 0, 0}); + } catch (IOException e) { + Assert.fail(); + } + } + + private static void writeNewFileFromByte(File newFile, byte[] byteData) { + try { + new FileOutputStream(newFile).write(byteData); + } catch (IOException e) { + Assert.fail(); + } + } + + private static class ServerHolder { + @NonNull final MtpServer server; + @NonNull final MtpDatabase database; + + ServerHolder(@NonNull MtpServer server, @NonNull MtpDatabase database) { + Preconditions.checkNotNull(server); + Preconditions.checkNotNull(database); + this.server = server; + this.database = database; + } + + void close() { + this.database.setServer(null); + } + } + + private class OnServerTerminated implements Runnable { + @Override + public void run() { + if (mServerHolder == null) { + Log.e(TAG, "mServerHolder is unexpectedly null."); + return; + } + mServerHolder.close(); + mServerHolder = null; + } + } + + @Before + public void setUp() { + FileUtils.deleteContentsAndDir(mMainStorageDir); + Assert.assertTrue(mMainStorageDir.mkdir()); + + StorageVolume mainStorage = new StorageVolume(MAIN_STORAGE_ID_STR, + mMainStorageDir, mMainStorageDir, "Primary Storage", + true, false, true, false, -1, UserHandle.CURRENT, "", ""); + + final StorageVolume primary = mainStorage; + + mMtpDatabase = new MtpDatabase(mContext, null); + + final MtpServer server = + new MtpServer(mMtpDatabase, null, false, + new OnServerTerminated(), Build.MANUFACTURER, + Build.MODEL, "1.0"); + mMtpDatabase.setServer(server); + mServerHolder = new ServerHolder(server, mMtpDatabase); + + mMtpDatabase.addStorage(mainStorage); + } + + @After + public void tearDown() { + FileUtils.deleteContentsAndDir(mMainStorageDir); + } + + private File stageFile(int resId, File file) throws IOException { + try (InputStream source = mContext.getResources().openRawResource(resId); + OutputStream target = new FileOutputStream(file)) { + android.os.FileUtils.copy(source, target); + } + return file; + } + + /** + * Refer to BitmapUtilTests, but keep here, + * so as to be aware of the behavior or interface change there + */ + private void assertBitmapSize(int expectedWidth, int expectedHeight, Bitmap bitmap) { + Assert.assertTrue( + "Abnormal bitmap.width: " + bitmap.getWidth(), bitmap.getWidth() >= expectedWidth); + Assert.assertTrue( + "Abnormal bitmap.height: " + bitmap.getHeight(), + bitmap.getHeight() >= expectedHeight); + } + + private byte[] createJpegRawData(int sourceWidth, int sourceHeight) throws IOException { + return createRawData(Bitmap.CompressFormat.JPEG, sourceWidth, sourceHeight); + } + + private byte[] createPngRawData(int sourceWidth, int sourceHeight) throws IOException { + return createRawData(Bitmap.CompressFormat.PNG, sourceWidth, sourceHeight); + } + + private byte[] createRawData(Bitmap.CompressFormat format, int sourceWidth, int sourceHeight) + throws IOException { + // Create a temp bitmap as our source + Bitmap b = Bitmap.createBitmap(sourceWidth, sourceHeight, Bitmap.Config.ARGB_8888); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + b.compress(format, 50, outputStream); + final byte[] data = outputStream.toByteArray(); + outputStream.close(); + return data; + } + + /** + * Decodes the bitmap with the given sample size + */ + public static Bitmap decodeBitmapFromBytes(byte[] bytes, int sampleSize) { + final BitmapFactory.Options options; + if (sampleSize <= 1) { + options = null; + } else { + options = new BitmapFactory.Options(); + options.inSampleSize = sampleSize; + } + return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options); + } + + private void testThumbnail(int fileHandle, File imgFile, boolean isGoodThumb) + throws IOException { + boolean isValidThumb; + byte[] byteArray; + long[] outLongs = new long[3]; + + isValidThumb = mMtpDatabase.getThumbnailInfo(fileHandle, outLongs); + Assert.assertTrue(isValidThumb); + + byteArray = mMtpDatabase.getThumbnailData(fileHandle); + + if (isGoodThumb) { + Assert.assertNotNull("Fail to generate thumbnail:" + imgFile.getPath(), byteArray); + + Bitmap testBitmap = decodeBitmapFromBytes(byteArray, 4); + assertBitmapSize(32, 16, testBitmap); + } else Assert.assertNull("Bad image should return null:" + imgFile.getPath(), byteArray); + } + + @Test + @SmallTest + public void testMtpDatabaseThumbnail() throws IOException { + int baseHandle; + int handleJpgBadThumb, handleJpgNoThumb, handleJpgBad; + int handlePng1, handlePngBad; + final String baseTestDirStr = mMainStorageDir.getPath() + TEST_DIRNAME; + + logMethodName(); + + Log.d(TAG, "testMtpDatabaseThumbnail: Generate and insert tested files."); + + baseHandle = mMtpDatabase.beginSendObject(baseTestDirStr, + MtpConstants.FORMAT_ASSOCIATION, 0, MAIN_STORAGE_ID); + + File baseDir = new File(baseTestDirStr); + baseDir.mkdirs(); + + final File jpgfileBadThumb = new File(baseDir, "jpgfileBadThumb.jpg"); + final File jpgFileNoThumb = new File(baseDir, "jpgFileNoThumb.jpg"); + final File jpgfileBad = new File(baseDir, "jpgfileBad.jpg"); + final File pngFile1 = new File(baseDir, "pngFile1.png"); + final File pngFileBad = new File(baseDir, "pngFileBad.png"); + + handleJpgBadThumb = mMtpDatabase.beginSendObject(jpgfileBadThumb.getPath(), + MtpConstants.FORMAT_EXIF_JPEG, baseHandle, MAIN_STORAGE_ID); + stageFile(R.raw.test_bad_thumb, jpgfileBadThumb); + + handleJpgNoThumb = mMtpDatabase.beginSendObject(jpgFileNoThumb.getPath(), + MtpConstants.FORMAT_EXIF_JPEG, baseHandle, MAIN_STORAGE_ID); + writeNewFileFromByte(jpgFileNoThumb, createJpegRawData(128, 64)); + + handleJpgBad = mMtpDatabase.beginSendObject(jpgfileBad.getPath(), + MtpConstants.FORMAT_EXIF_JPEG, baseHandle, MAIN_STORAGE_ID); + writeNewFile(jpgfileBad); + + handlePng1 = mMtpDatabase.beginSendObject(pngFile1.getPath(), + MtpConstants.FORMAT_PNG, baseHandle, MAIN_STORAGE_ID); + writeNewFileFromByte(pngFile1, createPngRawData(128, 64)); + + handlePngBad = mMtpDatabase.beginSendObject(pngFileBad.getPath(), + MtpConstants.FORMAT_PNG, baseHandle, MAIN_STORAGE_ID); + writeNewFile(pngFileBad); + + Log.d(TAG, "testMtpDatabaseThumbnail: Test bad JPG"); + + testThumbnail(handleJpgBadThumb, jpgfileBadThumb, false); + + testThumbnail(handleJpgNoThumb, jpgFileNoThumb, false); + + testThumbnail(handleJpgBad, jpgfileBad, false); + + Log.d(TAG, "testMtpDatabaseThumbnail: Test PNG"); + + testThumbnail(handlePng1, pngFile1, true); + + Log.d(TAG, "testMtpDatabaseThumbnail: Test bad PNG"); + + testThumbnail(handlePngBad, pngFileBad, false); + } + + @Test + @SmallTest + public void testMtpDatabaseExtStorage() throws IOException { + int numObj; + StorageVolume[] mVolumes; + + logMethodName(); + + mVolumes = StorageManager.getVolumeList(UserHandle.myUserId(), 0); + // Currently it may need manual setup for 2nd storage on virtual device testing. + // Thus only run test when 2nd storage exists. + Assume.assumeTrue( + "Skip when 2nd storage not available, volume numbers = " + mVolumes.length, + mVolumes.length >= 2); + + for (int ii = 0; ii < mVolumes.length; ii++) { + StorageVolume volume = mVolumes[ii]; + // Skip Actual Main storage (Internal Storage), + // since we use manipulated path as testing Main storage + if (ii > 0) + mMtpDatabase.addStorage(volume); + } + + numObj = mMtpDatabase.getNumObjects(SCND_STORAGE_ID, 0, 0xFFFFFFFF); + Assert.assertTrue( + "Fail to get objects in 2nd storage, object numbers = " + numObj, numObj >= 0); + } +} diff --git a/packages/CarSystemUI/res-keyguard/layout/keyguard_password_view.xml b/packages/CarSystemUI/res-keyguard/layout/keyguard_password_view.xml index 7004fb64ba06..8b235e6f246e 100644 --- a/packages/CarSystemUI/res-keyguard/layout/keyguard_password_view.xml +++ b/packages/CarSystemUI/res-keyguard/layout/keyguard_password_view.xml @@ -29,8 +29,6 @@ android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" - androidprv:layout_maxWidth="@dimen/keyguard_security_width" - androidprv:layout_maxHeight="@dimen/keyguard_security_height" android:gravity="center"> <include layout="@layout/keyguard_message_area" /> diff --git a/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml index 71e74cff6ccb..d0916b518c92 100644 --- a/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml +++ b/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml @@ -17,7 +17,7 @@ */ --> -<com.android.systemui.navigationbar.car.CarNavigationBarView +<com.android.systemui.car.navigationbar.CarNavigationBarView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res-auto" android:layout_height="match_parent" @@ -36,7 +36,7 @@ android:background="@drawable/system_bar_background" android:animateLayoutChanges="true"> - <com.android.systemui.navigationbar.car.CarNavigationButton + <com.android.systemui.car.navigationbar.CarNavigationButton android:id="@+id/home" android:layout_height="wrap_content" android:layout_width="match_parent" @@ -47,7 +47,7 @@ android:paddingBottom="30dp" /> - <com.android.systemui.navigationbar.car.CarNavigationButton + <com.android.systemui.car.navigationbar.CarNavigationButton android:id="@+id/grid" android:layout_height="wrap_content" android:layout_width="match_parent" @@ -59,7 +59,7 @@ android:paddingBottom="30dp" /> - <com.android.systemui.navigationbar.car.CarNavigationButton + <com.android.systemui.car.navigationbar.CarNavigationButton android:id="@+id/hvac" android:layout_height="wrap_content" android:layout_width="match_parent" @@ -110,4 +110,4 @@ </LinearLayout> -</com.android.systemui.navigationbar.car.CarNavigationBarView> +</com.android.systemui.car.navigationbar.CarNavigationBarView> diff --git a/packages/CarSystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml index f016dbfaa3b7..de5a15068a5c 100644 --- a/packages/CarSystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml +++ b/packages/CarSystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml @@ -17,7 +17,7 @@ */ --> -<com.android.systemui.navigationbar.car.CarNavigationBarView +<com.android.systemui.car.navigationbar.CarNavigationBarView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res-auto" android:layout_height="match_parent" @@ -36,7 +36,7 @@ android:background="@drawable/system_bar_background" android:animateLayoutChanges="true"> - <com.android.systemui.navigationbar.car.CarNavigationButton + <com.android.systemui.car.navigationbar.CarNavigationButton android:id="@+id/home" android:layout_height="wrap_content" android:layout_width="match_parent" @@ -47,7 +47,7 @@ android:paddingBottom="30dp" /> - <com.android.systemui.navigationbar.car.CarNavigationButton + <com.android.systemui.car.navigationbar.CarNavigationButton android:id="@+id/hvac" android:layout_height="wrap_content" android:layout_width="match_parent" @@ -59,4 +59,4 @@ android:paddingBottom="30dp" /> </LinearLayout> -</com.android.systemui.navigationbar.car.CarNavigationBarView> +</com.android.systemui.car.navigationbar.CarNavigationBarView> diff --git a/packages/CarSystemUI/res/layout/car_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_navigation_bar.xml index e2e9a336d614..1418bf8604bf 100644 --- a/packages/CarSystemUI/res/layout/car_navigation_bar.xml +++ b/packages/CarSystemUI/res/layout/car_navigation_bar.xml @@ -15,7 +15,7 @@ ~ limitations under the License --> -<com.android.systemui.navigationbar.car.CarNavigationBarView +<com.android.systemui.car.navigationbar.CarNavigationBarView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" @@ -33,7 +33,7 @@ android:paddingEnd="20dp" android:gravity="center"> - <com.android.systemui.navigationbar.car.CarNavigationButton + <com.android.systemui.car.navigationbar.CarNavigationButton android:id="@+id/home" style="@style/NavigationBarButton" systemui:componentNames="com.android.car.carlauncher/.CarLauncher" @@ -48,7 +48,7 @@ android:layout_height="match_parent" android:layout_weight="1"/> - <com.android.systemui.navigationbar.car.CarNavigationButton + <com.android.systemui.car.navigationbar.CarNavigationButton android:id="@+id/maps_nav" style="@style/NavigationBarButton" systemui:categories="android.intent.category.APP_MAPS" @@ -63,7 +63,7 @@ android:layout_height="match_parent" android:layout_weight="1"/> - <com.android.systemui.navigationbar.car.CarNavigationButton + <com.android.systemui.car.navigationbar.CarNavigationButton android:id="@+id/music_nav" style="@style/NavigationBarButton" systemui:categories="android.intent.category.APP_MUSIC" @@ -79,7 +79,7 @@ android:layout_height="match_parent" android:layout_weight="1"/> - <com.android.systemui.navigationbar.car.CarNavigationButton + <com.android.systemui.car.navigationbar.CarNavigationButton android:id="@+id/phone_nav" style="@style/NavigationBarButton" systemui:icon="@drawable/car_ic_phone" @@ -94,7 +94,7 @@ android:layout_height="match_parent" android:layout_weight="1"/> - <com.android.systemui.navigationbar.car.CarNavigationButton + <com.android.systemui.car.navigationbar.CarNavigationButton android:id="@+id/grid_nav" style="@style/NavigationBarButton" systemui:componentNames="com.android.car.carlauncher/.AppGridActivity" @@ -109,7 +109,7 @@ android:layout_height="match_parent" android:layout_weight="1"/> - <com.android.systemui.navigationbar.car.CarNavigationButton + <com.android.systemui.car.navigationbar.CarNavigationButton android:id="@+id/notifications" style="@style/NavigationBarButton" systemui:icon="@drawable/car_ic_notification" @@ -121,7 +121,7 @@ android:layout_height="match_parent" android:layout_weight="1"/> - <com.android.systemui.navigationbar.car.AssitantButton + <com.android.systemui.car.navigationbar.AssitantButton android:id="@+id/assist" style="@style/NavigationBarButton" systemui:icon="@drawable/ic_mic_white" @@ -140,4 +140,4 @@ android:visibility="gone" /> -</com.android.systemui.navigationbar.car.CarNavigationBarView>
\ No newline at end of file +</com.android.systemui.car.navigationbar.CarNavigationBarView>
\ No newline at end of file diff --git a/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml index 1c5d37ffd60f..a040e800cbfc 100644 --- a/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml +++ b/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml @@ -15,7 +15,7 @@ ~ limitations under the License --> -<com.android.systemui.navigationbar.car.CarNavigationBarView +<com.android.systemui.car.navigationbar.CarNavigationBarView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" @@ -31,7 +31,7 @@ android:paddingStart="@*android:dimen/car_padding_5" android:paddingEnd="@*android:dimen/car_padding_5"> - <com.android.systemui.navigationbar.car.CarNavigationButton + <com.android.systemui.car.navigationbar.CarNavigationButton android:id="@+id/home" android:layout_width="@*android:dimen/car_touch_target_size" android:layout_height="match_parent" @@ -42,5 +42,5 @@ systemui:highlightWhenSelected="true" /> </LinearLayout> -</com.android.systemui.navigationbar.car.CarNavigationBarView> +</com.android.systemui.car.navigationbar.CarNavigationBarView> diff --git a/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml index 327610a892ee..d386ce3300e6 100644 --- a/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml +++ b/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml @@ -17,7 +17,7 @@ */ --> -<com.android.systemui.navigationbar.car.CarNavigationBarView +<com.android.systemui.car.navigationbar.CarNavigationBarView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res-auto" android:layout_height="match_parent" @@ -39,7 +39,7 @@ android:background="@drawable/system_bar_background" android:animateLayoutChanges="true"> - <com.android.systemui.navigationbar.car.CarNavigationButton + <com.android.systemui.car.navigationbar.CarNavigationButton android:id="@+id/home" android:layout_height="wrap_content" android:layout_width="match_parent" @@ -50,7 +50,7 @@ android:paddingBottom="30dp" /> - <com.android.systemui.navigationbar.car.CarNavigationButton + <com.android.systemui.car.navigationbar.CarNavigationButton android:id="@+id/grid" android:layout_height="wrap_content" android:layout_width="match_parent" @@ -62,7 +62,7 @@ android:paddingBottom="30dp" /> - <com.android.systemui.navigationbar.car.CarNavigationButton + <com.android.systemui.car.navigationbar.CarNavigationButton android:id="@+id/hvac" android:layout_height="wrap_content" android:layout_width="match_parent" @@ -113,4 +113,4 @@ </LinearLayout> -</com.android.systemui.navigationbar.car.CarNavigationBarView> +</com.android.systemui.car.navigationbar.CarNavigationBarView> diff --git a/packages/CarSystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml index f016dbfaa3b7..de5a15068a5c 100644 --- a/packages/CarSystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml +++ b/packages/CarSystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml @@ -17,7 +17,7 @@ */ --> -<com.android.systemui.navigationbar.car.CarNavigationBarView +<com.android.systemui.car.navigationbar.CarNavigationBarView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res-auto" android:layout_height="match_parent" @@ -36,7 +36,7 @@ android:background="@drawable/system_bar_background" android:animateLayoutChanges="true"> - <com.android.systemui.navigationbar.car.CarNavigationButton + <com.android.systemui.car.navigationbar.CarNavigationButton android:id="@+id/home" android:layout_height="wrap_content" android:layout_width="match_parent" @@ -47,7 +47,7 @@ android:paddingBottom="30dp" /> - <com.android.systemui.navigationbar.car.CarNavigationButton + <com.android.systemui.car.navigationbar.CarNavigationButton android:id="@+id/hvac" android:layout_height="wrap_content" android:layout_width="match_parent" @@ -59,4 +59,4 @@ android:paddingBottom="30dp" /> </LinearLayout> -</com.android.systemui.navigationbar.car.CarNavigationBarView> +</com.android.systemui.car.navigationbar.CarNavigationBarView> diff --git a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml index ce0d31c6cc47..3389a7a8c6af 100644 --- a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml +++ b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml @@ -15,7 +15,7 @@ ~ limitations under the License --> -<com.android.systemui.navigationbar.car.CarNavigationBarView +<com.android.systemui.car.navigationbar.CarNavigationBarView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res-auto" android:id="@+id/car_top_bar" @@ -36,7 +36,7 @@ android:layout_alignParentStart="true" > - <com.android.systemui.navigationbar.car.CarNavigationButton + <com.android.systemui.car.navigationbar.CarNavigationButton android:id="@+id/hvacleft" android:layout_width="match_parent" android:layout_height="match_parent" @@ -45,7 +45,7 @@ systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end" /> - <com.android.systemui.statusbar.hvac.AnimatedTemperatureView + <com.android.systemui.car.hvac.AnimatedTemperatureView android:id="@+id/lefttext" android:layout_width="wrap_content" android:layout_height="match_parent" @@ -71,7 +71,7 @@ android:layout_height="match_parent" android:layout_centerInParent="true" > - <com.android.systemui.navigationbar.car.CarNavigationButton + <com.android.systemui.car.navigationbar.CarNavigationButton android:id="@+id/qs" android:layout_width="match_parent" android:layout_height="match_parent" @@ -118,7 +118,7 @@ android:layout_alignParentEnd="true" > - <com.android.systemui.navigationbar.car.CarNavigationButton + <com.android.systemui.car.navigationbar.CarNavigationButton android:id="@+id/hvacright" android:layout_width="match_parent" android:layout_height="match_parent" @@ -127,7 +127,7 @@ systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end" /> - <com.android.systemui.statusbar.hvac.AnimatedTemperatureView + <com.android.systemui.car.hvac.AnimatedTemperatureView android:id="@+id/righttext" android:layout_width="wrap_content" android:layout_height="match_parent" @@ -148,4 +148,4 @@ </FrameLayout> </RelativeLayout> -</com.android.systemui.navigationbar.car.CarNavigationBarView> +</com.android.systemui.car.navigationbar.CarNavigationBarView> diff --git a/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml index a71567c48eaf..9634950e4748 100644 --- a/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml +++ b/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml @@ -15,7 +15,7 @@ ~ limitations under the License --> -<com.android.systemui.navigationbar.car.CarNavigationBarView +<com.android.systemui.car.navigationbar.CarNavigationBarView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res-auto" android:id="@+id/car_top_bar" @@ -36,7 +36,7 @@ android:layout_alignParentStart="true" > - <com.android.systemui.navigationbar.car.CarNavigationButton + <com.android.systemui.car.navigationbar.CarNavigationButton android:id="@+id/hvacleft" android:layout_width="match_parent" android:layout_height="match_parent" @@ -45,7 +45,7 @@ systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end" /> - <com.android.systemui.statusbar.hvac.AnimatedTemperatureView + <com.android.systemui.car.hvac.AnimatedTemperatureView android:id="@+id/lefttext" android:layout_width="wrap_content" android:layout_height="match_parent" @@ -70,7 +70,7 @@ android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_centerInParent="true"> - <com.android.systemui.navigationbar.car.CarNavigationButton + <com.android.systemui.car.navigationbar.CarNavigationButton android:id="@+id/qs" android:layout_width="match_parent" android:layout_height="match_parent" @@ -114,7 +114,7 @@ android:layout_alignParentEnd="true" > - <com.android.systemui.navigationbar.car.CarNavigationButton + <com.android.systemui.car.navigationbar.CarNavigationButton android:id="@+id/hvacright" android:layout_width="match_parent" android:layout_height="match_parent" @@ -123,7 +123,7 @@ systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end" /> - <com.android.systemui.statusbar.hvac.AnimatedTemperatureView + <com.android.systemui.car.hvac.AnimatedTemperatureView android:id="@+id/righttext" android:layout_width="wrap_content" android:layout_height="match_parent" @@ -144,4 +144,4 @@ </FrameLayout> </RelativeLayout> -</com.android.systemui.navigationbar.car.CarNavigationBarView> +</com.android.systemui.car.navigationbar.CarNavigationBarView> diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml index e6fb501d02e1..eb1d9d0dd602 100644 --- a/packages/CarSystemUI/res/values/config.xml +++ b/packages/CarSystemUI/res/values/config.xml @@ -86,29 +86,30 @@ <string-array name="config_systemUIServiceComponents" translatable="false"> <item>com.android.systemui.util.NotificationChannels</item> <item>com.android.systemui.keyguard.KeyguardViewMediator</item> - <item>com.android.systemui.recents.Recents</item> - <item>com.android.systemui.volume.VolumeUI</item> - <item>com.android.systemui.stackdivider.Divider</item> +<!-- <item>com.android.systemui.recents.Recents</item>--> +<!-- <item>com.android.systemui.volume.VolumeUI</item>--> +<!-- <item>com.android.systemui.stackdivider.Divider</item>--> <!-- <item>com.android.systemui.statusbar.phone.StatusBar</item>--> <item>com.android.systemui.usb.StorageNotification</item> <item>com.android.systemui.power.PowerUI</item> <item>com.android.systemui.media.RingtonePlayer</item> - <item>com.android.systemui.keyboard.KeyboardUI</item> - <item>com.android.systemui.pip.PipUI</item> - <item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item> +<!-- <item>com.android.systemui.keyboard.KeyboardUI</item>--> +<!-- <item>com.android.systemui.pip.PipUI</item>--> +<!-- <item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item>--> <item>@string/config_systemUIVendorServiceComponent</item> <item>com.android.systemui.util.leak.GarbageMonitor$Service</item> - <item>com.android.systemui.LatencyTester</item> - <item>com.android.systemui.globalactions.GlobalActionsComponent</item> +<!-- <item>com.android.systemui.LatencyTester</item>--> +<!-- <item>com.android.systemui.globalactions.GlobalActionsComponent</item>--> <item>com.android.systemui.ScreenDecorations</item> <item>com.android.systemui.biometrics.AuthController</item> - <item>com.android.systemui.SliceBroadcastRelayHandler</item> +<!-- <item>com.android.systemui.SliceBroadcastRelayHandler</item>--> <item>com.android.systemui.SizeCompatModeActivityController</item> - <item>com.android.systemui.statusbar.notification.InstantAppNotifier</item> +<!-- <item>com.android.systemui.statusbar.notification.InstantAppNotifier</item>--> <item>com.android.systemui.theme.ThemeOverlayController</item> - <item>com.android.systemui.navigationbar.car.CarNavigationBar</item> <item>com.android.systemui.toast.ToastUI</item> - <item>com.android.systemui.voicerecognition.car.ConnectedDeviceVoiceRecognitionNotifier</item> - <item>com.android.systemui.window.SystemUIOverlayWindowManager</item> + <item>com.android.systemui.car.navigationbar.CarNavigationBar</item> + <item>com.android.systemui.car.voicerecognition.ConnectedDeviceVoiceRecognitionNotifier</item> + <item>com.android.systemui.car.window.SystemUIOverlayWindowManager</item> + <item>com.android.systemui.car.volume.VolumeUI</item> </string-array> </resources> diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java index 59fa9d09c9ee..58e4b9a81190 100644 --- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java +++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java @@ -18,19 +18,23 @@ package com.android.systemui; import com.android.systemui.biometrics.AuthController; import com.android.systemui.bubbles.dagger.BubbleModule; +import com.android.systemui.car.navigationbar.CarNavigationBar; import com.android.systemui.car.notification.CarNotificationModule; +import com.android.systemui.car.statusbar.CarStatusBar; +import com.android.systemui.car.statusbar.CarStatusBarModule; +import com.android.systemui.car.voicerecognition.ConnectedDeviceVoiceRecognitionNotifier; +import com.android.systemui.car.volume.VolumeUI; +import com.android.systemui.car.window.OverlayWindowModule; +import com.android.systemui.car.window.SystemUIOverlayWindowManager; import com.android.systemui.globalactions.GlobalActionsComponent; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.dagger.KeyguardModule; -import com.android.systemui.navigationbar.car.CarNavigationBar; import com.android.systemui.pip.PipUI; import com.android.systemui.power.PowerUI; import com.android.systemui.recents.Recents; import com.android.systemui.recents.RecentsModule; import com.android.systemui.shortcut.ShortcutKeyDispatcher; import com.android.systemui.stackdivider.Divider; -import com.android.systemui.statusbar.car.CarStatusBar; -import com.android.systemui.statusbar.car.CarStatusBarModule; import com.android.systemui.statusbar.notification.InstantAppNotifier; import com.android.systemui.statusbar.notification.dagger.NotificationsModule; import com.android.systemui.statusbar.phone.StatusBar; @@ -38,10 +42,6 @@ import com.android.systemui.statusbar.tv.TvStatusBar; import com.android.systemui.theme.ThemeOverlayController; import com.android.systemui.toast.ToastUI; import com.android.systemui.util.leak.GarbageMonitor; -import com.android.systemui.voicerecognition.car.ConnectedDeviceVoiceRecognitionNotifier; -import com.android.systemui.volume.VolumeUI; -import com.android.systemui.window.OverlayWindowModule; -import com.android.systemui.window.SystemUIOverlayWindowManager; import dagger.Binds; import dagger.Module; @@ -65,7 +65,7 @@ public abstract class CarSystemUIBinder { @ClassKey(Divider.class) public abstract SystemUI bindDivider(Divider sysui); - /** */ + /** Inject Car Navigation Bar. */ @Binds @IntoMap @ClassKey(CarNavigationBar.class) diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java index 4ea48ba24fa9..f066bf589b64 100644 --- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java +++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java @@ -25,6 +25,9 @@ import com.android.keyguard.KeyguardViewController; import com.android.systemui.car.CarDeviceProvisionedController; import com.android.systemui.car.CarDeviceProvisionedControllerImpl; import com.android.systemui.car.keyguard.CarKeyguardViewController; +import com.android.systemui.car.statusbar.CarStatusBar; +import com.android.systemui.car.statusbar.CarStatusBarKeyguardViewManager; +import com.android.systemui.car.volume.CarVolumeDialogComponent; import com.android.systemui.dagger.SystemUIRootComponent; import com.android.systemui.dock.DockManager; import com.android.systemui.dock.DockManagerImpl; @@ -39,8 +42,6 @@ import com.android.systemui.stackdivider.DividerModule; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl; -import com.android.systemui.statusbar.car.CarStatusBar; -import com.android.systemui.statusbar.car.CarStatusBarKeyguardViewManager; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.KeyguardBypassController; @@ -55,7 +56,6 @@ import com.android.systemui.statusbar.policy.BatteryControllerImpl; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.HeadsUpManager; -import com.android.systemui.volume.CarVolumeDialogComponent; import com.android.systemui.volume.VolumeDialogComponent; import javax.inject.Named; @@ -87,9 +87,6 @@ public abstract class CarSystemUIModule { groupManager, configurationController); } - @Binds - abstract HeadsUpManager bindHeadsUpManagerPhone(HeadsUpManagerPhone headsUpManagerPhone); - @Singleton @Provides @Named(LEAK_REPORT_EMAIL_NAME) @@ -97,6 +94,16 @@ public abstract class CarSystemUIModule { return "buganizer-system+181579@google.com"; } + @Provides + @Singleton + static Recents provideRecents(Context context, RecentsImplementation recentsImplementation, + CommandQueue commandQueue) { + return new Recents(context, recentsImplementation, commandQueue); + } + + @Binds + abstract HeadsUpManager bindHeadsUpManagerPhone(HeadsUpManagerPhone headsUpManagerPhone); + @Binds abstract EnhancedEstimates bindEnhancedEstimates(EnhancedEstimatesImpl enhancedEstimates); @@ -123,13 +130,6 @@ public abstract class CarSystemUIModule { @Binds abstract ShadeController provideShadeController(ShadeControllerImpl shadeController); - @Provides - @Singleton - static Recents provideRecents(Context context, RecentsImplementation recentsImplementation, - CommandQueue commandQueue) { - return new Recents(context, recentsImplementation, commandQueue); - } - @Binds abstract SystemUIRootComponent bindSystemUIRootComponent( CarSystemUIRootComponent systemUIRootComponent); diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java b/packages/CarSystemUI/src/com/android/systemui/car/bluetooth/CarBatteryController.java index 4e0fd4ab459d..9b5e2712a527 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/bluetooth/CarBatteryController.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.car; +package com.android.systemui.car.bluetooth; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; @@ -116,10 +116,12 @@ public class CarBatteryController extends BroadcastReceiver implements BatteryCo mChangeCallbacks.remove(cb); } + /** Sets {@link BatteryViewHandler}. */ public void addBatteryViewHandler(BatteryViewHandler batteryViewHandler) { mBatteryViewHandler = batteryViewHandler; } + /** Starts listening for bluetooth broadcast messages. */ public void startListening() { IntentFilter filter = new IntentFilter(); filter.addAction(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED); @@ -127,6 +129,7 @@ public class CarBatteryController extends BroadcastReceiver implements BatteryCo mContext.registerReceiver(this, filter); } + /** Stops listening for bluetooth broadcast messages. */ public void stopListening() { mContext.unregisterReceiver(this); } @@ -279,8 +282,10 @@ public class CarBatteryController extends BroadcastReceiver implements BatteryCo * in the {@link CarBatteryController}. */ public interface BatteryViewHandler { + /** Hides the battery view. */ void hideBatteryView(); + /** Shows the battery view. */ void showBatteryView(); } diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java b/packages/CarSystemUI/src/com/android/systemui/car/bluetooth/ConnectedDeviceSignalController.java index 3288927bf730..4642868a225e 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/ConnectedDeviceSignalController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/bluetooth/ConnectedDeviceSignalController.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.car; +package com.android.systemui.car.bluetooth; import static com.android.systemui.statusbar.phone.StatusBar.DEBUG; @@ -125,6 +125,7 @@ public class ConnectedDeviceSignalController extends BroadcastReceiver implement BluetoothProfile.HEADSET_CLIENT); } + /** Starts listening for bluetooth broadcast messages. */ public void startListening() { IntentFilter filter = new IntentFilter(); filter.addAction(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED); @@ -134,6 +135,7 @@ public class ConnectedDeviceSignalController extends BroadcastReceiver implement mController.addCallback(this); } + /** Stops listening for bluetooth broadcast messages. */ public void stopListening() { mContext.unregisterReceiver(this); mController.removeCallback(this); diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/AnimatedTemperatureView.java b/packages/CarSystemUI/src/com/android/systemui/car/hvac/AnimatedTemperatureView.java index 908aaad71893..a7294317f46c 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/AnimatedTemperatureView.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/hvac/AnimatedTemperatureView.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -11,10 +11,10 @@ * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and - * limitations under the License + * limitations under the License. */ -package com.android.systemui.statusbar.hvac; +package com.android.systemui.car.hvac; import android.animation.ObjectAnimator; import android.annotation.SuppressLint; @@ -35,7 +35,6 @@ import android.widget.TextSwitcher; import android.widget.TextView; import com.android.systemui.R; -import com.android.systemui.navigationbar.car.hvac.TemperatureView; /** * Simple text display of HVAC properties, It is designed to show mTemperature and is configured in diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/hvac/HvacController.java b/packages/CarSystemUI/src/com/android/systemui/car/hvac/HvacController.java index fd9c488278ba..af8ddb6a8180 100644 --- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/hvac/HvacController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/hvac/HvacController.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.navigationbar.car.hvac; +package com.android.systemui.car.hvac; import static android.car.VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL; import static android.car.VehiclePropertyIds.HVAC_TEMPERATURE_DISPLAY_UNITS; diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/TemperatureBackgroundAnimator.java b/packages/CarSystemUI/src/com/android/systemui/car/hvac/TemperatureBackgroundAnimator.java index 3c6d623c8ff7..a4c45730a9c2 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/TemperatureBackgroundAnimator.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/hvac/TemperatureBackgroundAnimator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -11,15 +11,15 @@ * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and - * limitations under the License + * limitations under the License. */ -package com.android.systemui.statusbar.hvac; +package com.android.systemui.car.hvac; -import static com.android.systemui.statusbar.hvac.AnimatedTemperatureView.isHorizontal; -import static com.android.systemui.statusbar.hvac.AnimatedTemperatureView.isLeft; -import static com.android.systemui.statusbar.hvac.AnimatedTemperatureView.isTop; -import static com.android.systemui.statusbar.hvac.AnimatedTemperatureView.isVertical; +import static com.android.systemui.car.hvac.AnimatedTemperatureView.isHorizontal; +import static com.android.systemui.car.hvac.AnimatedTemperatureView.isLeft; +import static com.android.systemui.car.hvac.AnimatedTemperatureView.isTop; +import static com.android.systemui.car.hvac.AnimatedTemperatureView.isVertical; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/TemperatureColorStore.java b/packages/CarSystemUI/src/com/android/systemui/car/hvac/TemperatureColorStore.java index a40ffaf850c5..9a7b0b9819c5 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/TemperatureColorStore.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/hvac/TemperatureColorStore.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -11,10 +11,10 @@ * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and - * limitations under the License + * limitations under the License. */ -package com.android.systemui.statusbar.hvac; +package com.android.systemui.car.hvac; import android.graphics.Color; diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/TemperatureTextAnimator.java b/packages/CarSystemUI/src/com/android/systemui/car/hvac/TemperatureTextAnimator.java index 8ee5ef6badc3..74d970464108 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/TemperatureTextAnimator.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/hvac/TemperatureTextAnimator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -11,13 +11,13 @@ * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and - * limitations under the License + * limitations under the License. */ -package com.android.systemui.statusbar.hvac; +package com.android.systemui.car.hvac; -import static com.android.systemui.statusbar.hvac.AnimatedTemperatureView.isHorizontal; -import static com.android.systemui.statusbar.hvac.AnimatedTemperatureView.isLeft; +import static com.android.systemui.car.hvac.AnimatedTemperatureView.isHorizontal; +import static com.android.systemui.car.hvac.AnimatedTemperatureView.isLeft; import android.annotation.NonNull; import android.view.animation.AccelerateDecelerateInterpolator; diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/hvac/TemperatureTextView.java b/packages/CarSystemUI/src/com/android/systemui/car/hvac/TemperatureTextView.java index ad4fcd9b67da..521a665da5f6 100644 --- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/hvac/TemperatureTextView.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/hvac/TemperatureTextView.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.navigationbar.car.hvac; +package com.android.systemui.car.hvac; import android.content.Context; import android.content.res.TypedArray; diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/hvac/TemperatureView.java b/packages/CarSystemUI/src/com/android/systemui/car/hvac/TemperatureView.java index 963f3184c40d..6b903fad505c 100644 --- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/hvac/TemperatureView.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/hvac/TemperatureView.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.navigationbar.car.hvac; +package com.android.systemui.car.hvac; /** * Interface for Views that display temperature HVAC properties diff --git a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java index 60ee19eec25f..b5f648b7ef3b 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java @@ -35,9 +35,11 @@ import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.R; import com.android.systemui.SystemUIFactory; import com.android.systemui.car.CarServiceProvider; +import com.android.systemui.car.navigationbar.CarNavigationBarController; +import com.android.systemui.car.window.OverlayViewController; +import com.android.systemui.car.window.OverlayViewGlobalStateController; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.keyguard.DismissCallbackRegistry; -import com.android.systemui.navigationbar.car.CarNavigationBarController; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.statusbar.phone.BiometricUnlockController; import com.android.systemui.statusbar.phone.KeyguardBouncer; @@ -45,8 +47,6 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.NotificationPanelViewController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.window.OverlayViewController; -import com.android.systemui.window.OverlayViewGlobalStateController; import javax.inject.Inject; import javax.inject.Singleton; @@ -205,6 +205,9 @@ public class CarKeyguardViewController extends OverlayViewController implements @Override public void onCancelClicked() { + getOverlayViewGlobalStateController().setWindowFocusable(/* focusable= */ false); + getOverlayViewGlobalStateController().setWindowNeedsInput(/* needsInput= */ false); + mBouncer.hide(/* destroyView= */ true); mKeyguardCancelClickedListener.onCancelClicked(); } @@ -226,7 +229,8 @@ public class CarKeyguardViewController extends OverlayViewController implements @Override public void setNeedsInput(boolean needsInput) { - getLayout().setFocusable(needsInput); + getOverlayViewGlobalStateController().setWindowFocusable(needsInput); + getOverlayViewGlobalStateController().setWindowNeedsInput(needsInput); } /** diff --git a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewMediator.java b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewMediator.java index db0f5d82e210..5a35c482fb36 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewMediator.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewMediator.java @@ -17,7 +17,7 @@ package com.android.systemui.car.keyguard; import com.android.systemui.car.userswitcher.FullScreenUserSwitcherViewController; -import com.android.systemui.window.OverlayViewMediator; +import com.android.systemui.car.window.OverlayViewMediator; import javax.inject.Inject; import javax.inject.Singleton; diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/AssitantButton.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/AssitantButton.java index 98cc00e376cc..69ec78eab593 100644 --- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/AssitantButton.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/AssitantButton.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.navigationbar.car; +package com.android.systemui.car.navigationbar; import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_ASSIST_GESTURE; diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/ButtonSelectionStateController.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/ButtonSelectionStateController.java index c36aaa092b06..eedcfa548e5a 100644 --- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/ButtonSelectionStateController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/ButtonSelectionStateController.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.navigationbar.car; +package com.android.systemui.car.navigationbar; import android.app.ActivityManager; import android.content.ComponentName; diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/ButtonSelectionStateListener.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/ButtonSelectionStateListener.java index 9da412111f07..13617983b23b 100644 --- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/ButtonSelectionStateListener.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/ButtonSelectionStateListener.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.navigationbar.car; +package com.android.systemui.car.navigationbar; import android.app.ActivityTaskManager; import android.util.Log; diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java index 9fdfc0fff307..2b5cab783916 100644 --- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.navigationbar.car; +package com.android.systemui.car.navigationbar; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_STATUS_BAR; @@ -51,8 +51,6 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.statusbar.AutoHideUiElement; import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.statusbar.NavigationBarController; -import com.android.systemui.statusbar.SuperStatusBarViewFactory; import com.android.systemui.statusbar.phone.AutoHideController; import com.android.systemui.statusbar.phone.BarTransitions; import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy; @@ -79,8 +77,6 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks private final ButtonSelectionStateListener mButtonSelectionStateListener; private final Handler mMainHandler; private final Lazy<KeyguardStateController> mKeyguardStateControllerLazy; - private final Lazy<NavigationBarController> mNavigationBarControllerLazy; - private final SuperStatusBarViewFactory mSuperStatusBarViewFactory; private final ButtonSelectionStateController mButtonSelectionStateController; private final PhoneStatusBarPolicy mIconPolicy; private final StatusBarIconController mIconController; @@ -126,8 +122,6 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks ButtonSelectionStateListener buttonSelectionStateListener, @Main Handler mainHandler, Lazy<KeyguardStateController> keyguardStateControllerLazy, - Lazy<NavigationBarController> navigationBarControllerLazy, - SuperStatusBarViewFactory superStatusBarViewFactory, ButtonSelectionStateController buttonSelectionStateController, PhoneStatusBarPolicy iconPolicy, StatusBarIconController iconController @@ -142,8 +136,6 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks mButtonSelectionStateListener = buttonSelectionStateListener; mMainHandler = mainHandler; mKeyguardStateControllerLazy = keyguardStateControllerLazy; - mNavigationBarControllerLazy = navigationBarControllerLazy; - mSuperStatusBarViewFactory = superStatusBarViewFactory; mButtonSelectionStateController = buttonSelectionStateController; mIconPolicy = iconPolicy; mIconController = iconController; @@ -319,11 +311,6 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks result.mImeWindowVis, result.mImeBackDisposition, result.mShowImeSwitcher); } - - // There has been a car customized nav bar on the default display, so just create nav bars - // on external displays. - mNavigationBarControllerLazy.get().createNavigationBars(/* includeDefaultDisplay= */ false, - result); } private void buildNavBarWindows() { diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarController.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java index 37a82255929a..55c11530fc37 100644 --- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.navigationbar.car; +package com.android.systemui.car.navigationbar; import android.content.Context; import android.view.View; @@ -24,7 +24,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.systemui.R; -import com.android.systemui.navigationbar.car.hvac.HvacController; +import com.android.systemui.car.hvac.HvacController; import javax.inject.Inject; import javax.inject.Singleton; diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarView.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java index 5b99f53af9f9..46a720b88419 100644 --- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarView.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.navigationbar.car; +package com.android.systemui.car.navigationbar; import android.content.Context; import android.util.AttributeSet; @@ -24,7 +24,7 @@ import android.widget.LinearLayout; import com.android.systemui.Dependency; import com.android.systemui.R; -import com.android.systemui.navigationbar.car.CarNavigationBarController.NotificationsShadeController; +import com.android.systemui.car.navigationbar.CarNavigationBarController.NotificationsShadeController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.phone.StatusBarIconController; diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationButton.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationButton.java index b4d478572daf..5f4ac2dcb141 100644 --- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationButton.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationButton.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.navigationbar.car; +package com.android.systemui.car.navigationbar; import android.app.ActivityOptions; import android.content.Context; diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/NavigationBarViewFactory.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java index e47c5d1307ac..3b7b48a77186 100644 --- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/NavigationBarViewFactory.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/NavigationBarViewFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.navigationbar.car; +package com.android.systemui.car.navigationbar; import android.content.Context; import android.util.ArrayMap; diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainer.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainer.java index 53e5d9fe91ec..20fcca0d0220 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainer.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainer.java @@ -30,7 +30,6 @@ import com.android.car.notification.R; import com.android.car.notification.headsup.CarHeadsUpNotificationContainer; import com.android.systemui.car.CarDeviceProvisionedController; import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.statusbar.car.CarStatusBar; import javax.inject.Inject; import javax.inject.Singleton; @@ -43,7 +42,7 @@ import dagger.Lazy; @Singleton public class CarHeadsUpNotificationSystemContainer implements CarHeadsUpNotificationContainer { private final CarDeviceProvisionedController mCarDeviceProvisionedController; - private final Lazy<CarStatusBar> mCarStatusBarLazy; + private final Lazy<NotificationPanelViewController> mNotificationPanelViewControllerLazy; private final ViewGroup mWindow; private final FrameLayout mHeadsUpContentFrame; @@ -55,10 +54,9 @@ public class CarHeadsUpNotificationSystemContainer implements CarHeadsUpNotifica @Main Resources resources, CarDeviceProvisionedController deviceProvisionedController, WindowManager windowManager, - // TODO: Remove dependency on CarStatusBar - Lazy<CarStatusBar> carStatusBarLazy) { + Lazy<NotificationPanelViewController> notificationPanelViewControllerLazy) { mCarDeviceProvisionedController = deviceProvisionedController; - mCarStatusBarLazy = carStatusBarLazy; + mNotificationPanelViewControllerLazy = notificationPanelViewControllerLazy; boolean showOnBottom = resources.getBoolean(R.bool.config_showHeadsUpNotificationOnBottom); @@ -87,7 +85,8 @@ public class CarHeadsUpNotificationSystemContainer implements CarHeadsUpNotifica private void animateShow() { if ((mEnableHeadsUpNotificationWhenNotificationShadeOpen - || !mCarStatusBarLazy.get().isPanelExpanded()) && isCurrentUserSetup()) { + || !mNotificationPanelViewControllerLazy.get().isPanelExpanded()) + && mCarDeviceProvisionedController.isCurrentUserFullySetup()) { mWindow.setVisibility(View.VISIBLE); } } @@ -114,9 +113,4 @@ public class CarHeadsUpNotificationSystemContainer implements CarHeadsUpNotifica public boolean isVisible() { return mWindow.getVisibility() == View.VISIBLE; } - - private boolean isCurrentUserSetup() { - return mCarDeviceProvisionedController.isCurrentUserSetup() - && !mCarDeviceProvisionedController.isCurrentUserSetupInProgress(); - } } diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java index d8a894cfa8ba..a17a0e9d2920 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java @@ -45,13 +45,13 @@ import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.R; import com.android.systemui.car.CarDeviceProvisionedController; import com.android.systemui.car.CarServiceProvider; +import com.android.systemui.car.window.OverlayPanelViewController; +import com.android.systemui.car.window.OverlayViewGlobalStateController; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.FlingAnimationUtils; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.window.OverlayPanelViewController; -import com.android.systemui.window.OverlayViewGlobalStateController; import javax.inject.Inject; import javax.inject.Singleton; diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java index 9d71797794b8..24a84d940577 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java @@ -20,10 +20,9 @@ import android.car.hardware.power.CarPowerManager; import android.content.res.Configuration; import com.android.systemui.car.CarDeviceProvisionedController; -import com.android.systemui.navigationbar.car.CarNavigationBarController; -import com.android.systemui.statusbar.car.PowerManagerHelper; +import com.android.systemui.car.navigationbar.CarNavigationBarController; +import com.android.systemui.car.window.OverlayViewMediator; import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.window.OverlayViewMediator; import javax.inject.Inject; import javax.inject.Singleton; diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/PowerManagerHelper.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/PowerManagerHelper.java index 615a7bae2930..92a11d8db88f 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/PowerManagerHelper.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/PowerManagerHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.car; +package com.android.systemui.car.notification; import android.annotation.NonNull; import android.car.Car; diff --git a/packages/CarSystemUI/src/com/android/systemui/sideloaded/car/CarSideLoadedAppDetector.java b/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/CarSideLoadedAppDetector.java index c0dbb5879d7d..f145b148eaf7 100644 --- a/packages/CarSystemUI/src/com/android/systemui/sideloaded/car/CarSideLoadedAppDetector.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/CarSideLoadedAppDetector.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.sideloaded.car; +package com.android.systemui.car.sideloaded; import android.annotation.NonNull; import android.app.ActivityManager; diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBar.java index ec1dabc1bd72..b6eb015008af 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBar.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.car; +package com.android.systemui.car.statusbar; import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME; @@ -41,6 +41,8 @@ import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.bubbles.BubbleController; import com.android.systemui.car.CarDeviceProvisionedController; import com.android.systemui.car.CarDeviceProvisionedListener; +import com.android.systemui.car.bluetooth.CarBatteryController; +import com.android.systemui.car.navigationbar.CarNavigationBarController; import com.android.systemui.classifier.FalsingLog; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dagger.qualifiers.UiBackground; @@ -49,7 +51,6 @@ import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.keyguard.WakefulnessLifecycle; -import com.android.systemui.navigationbar.car.CarNavigationBarController; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.PluginDependencyProvider; diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java b/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBarKeyguardViewManager.java index e1c051f5012d..96a998a500e1 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarKeyguardViewManager.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBarKeyguardViewManager.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.car; +package com.android.systemui.car.statusbar; import android.content.Context; import android.view.View; @@ -23,8 +23,8 @@ import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.R; +import com.android.systemui.car.navigationbar.CarNavigationBarController; import com.android.systemui.dock.DockManager; -import com.android.systemui.navigationbar.car.CarNavigationBarController; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.phone.NavigationModeController; diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java b/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBarModule.java index f72ab25a8028..dc2eb04c2990 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/statusbar/CarStatusBarModule.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.car; +package com.android.systemui.car.statusbar; import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME; @@ -31,13 +31,13 @@ import com.android.systemui.assist.AssistManager; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.bubbles.BubbleController; import com.android.systemui.car.CarDeviceProvisionedController; +import com.android.systemui.car.navigationbar.CarNavigationBarController; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.keyguard.WakefulnessLifecycle; -import com.android.systemui.navigationbar.car.CarNavigationBarController; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.PluginDependencyProvider; diff --git a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullScreenUserSwitcherViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullScreenUserSwitcherViewController.java index 45ceb6d1551f..10b2b973071a 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullScreenUserSwitcherViewController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullScreenUserSwitcherViewController.java @@ -25,9 +25,9 @@ import android.view.View; import androidx.recyclerview.widget.GridLayoutManager; import com.android.systemui.R; +import com.android.systemui.car.window.OverlayViewController; +import com.android.systemui.car.window.OverlayViewGlobalStateController; import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.window.OverlayViewController; -import com.android.systemui.window.OverlayViewGlobalStateController; import javax.inject.Inject; import javax.inject.Singleton; diff --git a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullscreenUserSwitcherViewMediator.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullscreenUserSwitcherViewMediator.java index 149531f75029..346c38ced766 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullscreenUserSwitcherViewMediator.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/FullscreenUserSwitcherViewMediator.java @@ -17,9 +17,9 @@ package com.android.systemui.car.userswitcher; import com.android.systemui.car.keyguard.CarKeyguardViewController; +import com.android.systemui.car.window.OverlayViewMediator; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.window.OverlayViewMediator; import javax.inject.Inject; import javax.inject.Singleton; diff --git a/packages/CarSystemUI/src/com/android/systemui/voicerecognition/car/ConnectedDeviceVoiceRecognitionNotifier.java b/packages/CarSystemUI/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifier.java index 2f79f960f951..c054d204af98 100644 --- a/packages/CarSystemUI/src/com/android/systemui/voicerecognition/car/ConnectedDeviceVoiceRecognitionNotifier.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifier.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.voicerecognition.car; +package com.android.systemui.car.voicerecognition; import android.bluetooth.BluetoothHeadsetClient; import android.content.BroadcastReceiver; diff --git a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogComponent.java b/packages/CarSystemUI/src/com/android/systemui/car/volume/CarVolumeDialogComponent.java index 5a3443674cf4..a22d1abc9c61 100644 --- a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogComponent.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/volume/CarVolumeDialogComponent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,13 +14,16 @@ * limitations under the License. */ -package com.android.systemui.volume; +package com.android.systemui.car.volume; import android.content.Context; import com.android.systemui.car.CarServiceProvider; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.plugins.VolumeDialog; +import com.android.systemui.volume.Events; +import com.android.systemui.volume.VolumeDialogComponent; +import com.android.systemui.volume.VolumeDialogControllerImpl; import javax.inject.Inject; import javax.inject.Singleton; @@ -45,6 +48,7 @@ public class CarVolumeDialogComponent extends VolumeDialogComponent { @Override protected VolumeDialog createDefault() { mCarVolumeDialog = new CarVolumeDialogImpl(mContext); + mCarVolumeDialog.show(Events.SHOW_REASON_VOLUME_CHANGED); return mCarVolumeDialog; } } diff --git a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java b/packages/CarSystemUI/src/com/android/systemui/car/volume/CarVolumeDialogImpl.java index 873d7d7975e0..12818840af9a 100644 --- a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/volume/CarVolumeDialogImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.volume; +package com.android.systemui.car.volume; import android.animation.Animator; import android.animation.AnimatorInflater; @@ -61,6 +61,9 @@ import androidx.recyclerview.widget.RecyclerView; import com.android.systemui.R; import com.android.systemui.car.CarServiceProvider; import com.android.systemui.plugins.VolumeDialog; +import com.android.systemui.volume.Events; +import com.android.systemui.volume.SystemUIInterpolators; +import com.android.systemui.volume.VolumeDialogImpl; import org.xmlpull.v1.XmlPullParserException; @@ -75,7 +78,8 @@ import java.util.List; */ public class CarVolumeDialogImpl implements VolumeDialog { - private static final String TAG = Util.logTag(CarVolumeDialogImpl.class); + private static final String TAG = "CarVolumeDialog"; + private static final boolean DEBUG = false; private static final String XML_TAG_VOLUME_ITEMS = "carVolumeItems"; private static final String XML_TAG_VOLUME_ITEM = "item"; @@ -134,9 +138,9 @@ public class CarVolumeDialogImpl implements VolumeDialog { // this // callback. Updating the seekbar at the same time could block the continuous // seeking. - if (value != volumeItem.progress && isShowing) { - volumeItem.carVolumeItem.setProgress(value); - volumeItem.progress = value; + if (value != volumeItem.mProgress && isShowing) { + volumeItem.mCarVolumeItem.setProgress(value); + volumeItem.mProgress = value; } if ((flags & AudioManager.FLAG_SHOW_UI) != 0) { mPreviouslyDisplayingGroupId = mCurrentlyDisplayingGroupId; @@ -234,6 +238,20 @@ public class CarVolumeDialogImpl implements VolumeDialog { cleanupAudioManager(); } + /** + * Reveals volume dialog. + */ + public void show(int reason) { + mHandler.obtainMessage(H.SHOW, reason).sendToTarget(); + } + + /** + * Hides volume dialog. + */ + public void dismiss(int reason) { + mHandler.obtainMessage(H.DISMISS, reason).sendToTarget(); + } + private void initDialog() { loadAudioUsageItems(); mCarVolumeLineItems.clear(); @@ -293,7 +311,7 @@ public class CarVolumeDialogImpl implements VolumeDialog { private void showH(int reason) { - if (D.BUG) { + if (DEBUG) { Log.d(TAG, "showH r=" + Events.DISMISS_REASONS[reason]); } @@ -323,7 +341,7 @@ public class CarVolumeDialogImpl implements VolumeDialog { private void clearAllAndSetupDefaultCarVolumeLineItem(int groupId) { mCarVolumeLineItems.clear(); VolumeItem volumeItem = mAvailableVolumeItems.get(groupId); - volumeItem.defaultItem = true; + volumeItem.mDefaultItem = true; addCarVolumeListItem(volumeItem, /* volumeGroupId = */ groupId, R.drawable.car_ic_keyboard_arrow_down, new ExpandIconListener()); } @@ -334,7 +352,7 @@ public class CarVolumeDialogImpl implements VolumeDialog { mHandler.sendMessageDelayed(mHandler .obtainMessage(H.DISMISS, Events.DISMISS_REASON_TIMEOUT), timeout); - if (D.BUG) { + if (DEBUG) { Log.d(TAG, "rescheduleTimeout " + timeout + " " + Debug.getCaller()); } } @@ -348,7 +366,7 @@ public class CarVolumeDialogImpl implements VolumeDialog { } private void dismissH(int reason) { - if (D.BUG) { + if (DEBUG) { Log.d(TAG, "dismissH r=" + Events.DISMISS_REASONS[reason]); } @@ -365,7 +383,7 @@ public class CarVolumeDialogImpl implements VolumeDialog { .setDuration(LISTVIEW_ANIMATION_DURATION_IN_MILLIS) .setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator()) .withEndAction(() -> mHandler.postDelayed(() -> { - if (D.BUG) { + if (DEBUG) { Log.d(TAG, "mDialog.dismiss()"); } mDialog.dismiss(); @@ -410,8 +428,8 @@ public class CarVolumeDialogImpl implements VolumeDialog { /* defValue= */ -1); if (usage >= 0) { VolumeItem volumeItem = new VolumeItem(); - volumeItem.rank = rank; - volumeItem.icon = item.getResourceId( + volumeItem.mRank = rank; + volumeItem.mIcon = item.getResourceId( R.styleable.carVolumeItems_item_icon, /* defValue= */ 0); mVolumeItems.put(usage, volumeItem); rank++; @@ -429,8 +447,8 @@ public class CarVolumeDialogImpl implements VolumeDialog { VolumeItem result = null; for (int usage : usages) { VolumeItem volumeItem = mVolumeItems.get(usage); - if (volumeItem.rank < rank) { - rank = volumeItem.rank; + if (volumeItem.mRank < rank) { + rank = volumeItem.mRank; result = volumeItem; } } @@ -449,7 +467,7 @@ public class CarVolumeDialogImpl implements VolumeDialog { carVolumeItem.setGroupId(volumeGroupId); int color = mContext.getColor(R.color.car_volume_dialog_tint); - Drawable primaryIcon = mContext.getDrawable(volumeItem.icon); + Drawable primaryIcon = mContext.getDrawable(volumeItem.mIcon); primaryIcon.mutate().setTint(color); carVolumeItem.setPrimaryIcon(primaryIcon); if (supplementalIcon != null) { @@ -462,8 +480,8 @@ public class CarVolumeDialogImpl implements VolumeDialog { /* showSupplementalIconDivider= */ false); } - volumeItem.carVolumeItem = carVolumeItem; - volumeItem.progress = seekbarProgressValue; + volumeItem.mCarVolumeItem = carVolumeItem; + volumeItem.mProgress = seekbarProgressValue; return carVolumeItem; } @@ -490,13 +508,12 @@ public class CarVolumeDialogImpl implements VolumeDialog { * Wrapper class which contains information of each volume group. */ private static class VolumeItem { - - private int rank; - private boolean defaultItem = false; + private int mRank; + private boolean mDefaultItem = false; @DrawableRes - private int icon; - private CarVolumeItem carVolumeItem; - private int progress; + private int mIcon; + private CarVolumeItem mCarVolumeItem; + private int mProgress; } private final class H extends Handler { @@ -624,9 +641,9 @@ public class CarVolumeDialogImpl implements VolumeDialog { Log.w(TAG, "Ignoring volume change event because the car isn't connected"); return; } - mAvailableVolumeItems.get(mVolumeGroupId).progress = progress; + mAvailableVolumeItems.get(mVolumeGroupId).mProgress = progress; mAvailableVolumeItems.get( - mVolumeGroupId).carVolumeItem.setProgress(progress); + mVolumeGroupId).mCarVolumeItem.setProgress(progress); mCarAudioManager.setGroupVolume(mVolumeGroupId, progress, 0); } diff --git a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeItem.java b/packages/CarSystemUI/src/com/android/systemui/car/volume/CarVolumeItem.java index b83740f4259d..1e7e5348b7fa 100644 --- a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeItem.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/volume/CarVolumeItem.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.volume; +package com.android.systemui.car.volume; import android.graphics.drawable.Drawable; import android.view.View; @@ -38,12 +38,12 @@ public class CarVolumeItem { private int mMax; private int mProgress; private SeekBar.OnSeekBarChangeListener mOnSeekBarChangeListener; - + /** * Called when {@link CarVolumeItem} is bound to its ViewHolder. */ void bind(CarVolumeItemViewHolder viewHolder) { - viewHolder.bind(/* carVolumeItem= */ this); + viewHolder.bind(/* carVolumeItem= */ this); } /** Sets progress of seekbar. */ diff --git a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeItemAdapter.java b/packages/CarSystemUI/src/com/android/systemui/car/volume/CarVolumeItemAdapter.java index 5c1f8170afc4..7f336b5250a7 100644 --- a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeItemAdapter.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/volume/CarVolumeItemAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.volume; +package com.android.systemui.car.volume; import android.content.Context; import android.view.LayoutInflater; diff --git a/packages/CarSystemUI/src/com/android/systemui/car/volume/VolumeUI.java b/packages/CarSystemUI/src/com/android/systemui/car/volume/VolumeUI.java new file mode 100644 index 000000000000..2bdb85ff9acc --- /dev/null +++ b/packages/CarSystemUI/src/com/android/systemui/car/volume/VolumeUI.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.car.volume; + +import android.car.Car; +import android.car.media.CarAudioManager; +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.os.Handler; +import android.util.Log; + +import com.android.systemui.R; +import com.android.systemui.SystemUI; +import com.android.systemui.car.CarServiceProvider; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.volume.VolumeDialogComponent; + +import java.io.FileDescriptor; +import java.io.PrintWriter; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import dagger.Lazy; + +/** The entry point for controlling the volume ui in cars. */ +@Singleton +public class VolumeUI extends SystemUI { + + private static final String TAG = "VolumeUI"; + private final Resources mResources; + private final Handler mMainHandler; + private final CarServiceProvider mCarServiceProvider; + private final Lazy<VolumeDialogComponent> mVolumeDialogComponentLazy; + + private final CarAudioManager.CarVolumeCallback mVolumeChangeCallback = + new CarAudioManager.CarVolumeCallback() { + @Override + public void onGroupVolumeChanged(int zoneId, int groupId, int flags) { + if (mVolumeDialogComponent == null) { + mMainHandler.post(() -> { + mVolumeDialogComponent = mVolumeDialogComponentLazy.get(); + mVolumeDialogComponent.register(); + }); + mCarAudioManager.unregisterCarVolumeCallback(mVolumeChangeCallback); + } + } + + @Override + public void onMasterMuteChanged(int zoneId, int flags) { + // ignored + } + }; + + private boolean mEnabled; + private CarAudioManager mCarAudioManager; + private VolumeDialogComponent mVolumeDialogComponent; + + @Inject + public VolumeUI( + Context context, + @Main Resources resources, + @Main Handler mainHandler, + CarServiceProvider carServiceProvider, + Lazy<VolumeDialogComponent> volumeDialogComponentLazy + ) { + super(context); + mResources = resources; + mMainHandler = mainHandler; + mCarServiceProvider = carServiceProvider; + mVolumeDialogComponentLazy = volumeDialogComponentLazy; + } + + @Override + public void start() { + boolean enableVolumeUi = mResources.getBoolean(R.bool.enable_volume_ui); + mEnabled = enableVolumeUi; + if (!mEnabled) return; + + mCarServiceProvider.addListener(car -> { + if (mCarAudioManager != null) { + return; + } + + mCarAudioManager = (CarAudioManager) car.getCarManager(Car.AUDIO_SERVICE); + Log.d(TAG, "Registering mVolumeChangeCallback."); + // This volume call back is never unregistered because CarStatusBar is + // never destroyed. + mCarAudioManager.registerCarVolumeCallback(mVolumeChangeCallback); + }); + } + + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + if (!mEnabled) return; + if (mVolumeDialogComponent != null) { + mVolumeDialogComponent.onConfigurationChanged(newConfig); + } + } + + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.print("mEnabled="); pw.println(mEnabled); + if (!mEnabled) return; + if (mVolumeDialogComponent != null) { + mVolumeDialogComponent.dump(fd, pw, args); + } + } +} diff --git a/packages/CarSystemUI/src/com/android/systemui/window/OverlayPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java index 58022f12e58c..90892d5c53e4 100644 --- a/packages/CarSystemUI/src/com/android/systemui/window/OverlayPanelViewController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.window; +package com.android.systemui.car.window; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; diff --git a/packages/CarSystemUI/src/com/android/systemui/window/OverlayViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java index 15ef0be38d4d..87f20208476b 100644 --- a/packages/CarSystemUI/src/com/android/systemui/window/OverlayViewController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.window; +package com.android.systemui.car.window; import android.view.View; import android.view.ViewGroup; diff --git a/packages/CarSystemUI/src/com/android/systemui/window/OverlayViewGlobalStateController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java index 402d742cb949..290505f5042a 100644 --- a/packages/CarSystemUI/src/com/android/systemui/window/OverlayViewGlobalStateController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java @@ -14,13 +14,13 @@ * limitations under the License. */ -package com.android.systemui.window; +package com.android.systemui.car.window; import android.util.Log; import androidx.annotation.VisibleForTesting; -import com.android.systemui.navigationbar.car.CarNavigationBarController; +import com.android.systemui.car.navigationbar.CarNavigationBarController; import java.util.HashSet; import java.util.Set; @@ -123,6 +123,12 @@ public class OverlayViewGlobalStateController { mSystemUIOverlayWindowController.setWindowFocusable(focusable); } + /** Sets the {@link android.view.WindowManager.LayoutParams#FLAG_ALT_FOCUSABLE_IM} flag of the + * sysui overlay window */ + public void setWindowNeedsInput(boolean needsInput) { + mSystemUIOverlayWindowController.setWindowNeedsInput(needsInput); + } + /** Returns {@code true} if the window is focusable. */ public boolean isWindowFocusable() { return mSystemUIOverlayWindowController.isWindowFocusable(); diff --git a/packages/CarSystemUI/src/com/android/systemui/window/OverlayViewMediator.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewMediator.java index 7c34fb494de6..ac574eda4c9f 100644 --- a/packages/CarSystemUI/src/com/android/systemui/window/OverlayViewMediator.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewMediator.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.window; +package com.android.systemui.car.window; /** * Controls when to show and hide {@link OverlayViewController}(s). diff --git a/packages/CarSystemUI/src/com/android/systemui/window/OverlayWindowModule.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayWindowModule.java index 6b4f3e37bc18..c46b287ade5a 100644 --- a/packages/CarSystemUI/src/com/android/systemui/window/OverlayWindowModule.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayWindowModule.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.window; +package com.android.systemui.car.window; import com.android.systemui.car.keyguard.CarKeyguardViewMediator; import com.android.systemui.car.notification.NotificationPanelViewMediator; diff --git a/packages/CarSystemUI/src/com/android/systemui/window/SystemUIOverlayWindowController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java index 04d69eabaeac..bcd96f63a2b4 100644 --- a/packages/CarSystemUI/src/com/android/systemui/window/SystemUIOverlayWindowController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.window; +package com.android.systemui.car.window; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; @@ -132,6 +132,16 @@ public class SystemUIOverlayWindowController implements updateWindow(); } + /** Sets the window to enable IME. */ + public void setWindowNeedsInput(boolean needsInput) { + if (needsInput) { + mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; + } else { + mLpChanged.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; + } + updateWindow(); + } + /** Returns {@code true} if the window is visible */ public boolean isWindowVisible() { return mVisible; diff --git a/packages/CarSystemUI/src/com/android/systemui/window/SystemUIOverlayWindowManager.java b/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowManager.java index af0f17d50ee2..3f88422ba2e5 100644 --- a/packages/CarSystemUI/src/com/android/systemui/window/SystemUIOverlayWindowManager.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowManager.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.window; +package com.android.systemui.car.window; import android.content.Context; diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/hvac/HvacControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/HvacControllerTest.java index a71d1db3ee70..7996170ba7d6 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/hvac/HvacControllerTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/HvacControllerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.navigationbar.car.hvac; +package com.android.systemui.car.hvac; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java index d4cf6ccf4b9e..d40b1af67ca6 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java @@ -38,15 +38,15 @@ import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.car.CarServiceProvider; +import com.android.systemui.car.navigationbar.CarNavigationBarController; +import com.android.systemui.car.window.OverlayViewGlobalStateController; +import com.android.systemui.car.window.SystemUIOverlayWindowController; import com.android.systemui.keyguard.DismissCallbackRegistry; -import com.android.systemui.navigationbar.car.CarNavigationBarController; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.statusbar.phone.BiometricUnlockController; import com.android.systemui.statusbar.phone.KeyguardBouncer; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.window.OverlayViewGlobalStateController; -import com.android.systemui.window.SystemUIOverlayWindowController; import org.junit.Before; import org.junit.Test; diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/ButtonSelectionStateControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/ButtonSelectionStateControllerTest.java index f94dd82dbd98..893057e222a9 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/ButtonSelectionStateControllerTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/ButtonSelectionStateControllerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.navigationbar.car; +package com.android.systemui.car.navigationbar; import static com.google.common.truth.Truth.assertThat; diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java index bbcd0d4eff81..911f624d1fb3 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarControllerTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarControllerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.navigationbar.car; +package com.android.systemui.car.navigationbar; import static com.google.common.truth.Truth.assertThat; @@ -31,7 +31,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; -import com.android.systemui.navigationbar.car.hvac.HvacController; +import com.android.systemui.car.hvac.HvacController; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.statusbar.phone.StatusBarIconController; diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarTest.java index 3ecb29f95092..6620e9d506ab 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.navigationbar.car; +package com.android.systemui.car.navigationbar; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_STATUS_BAR; @@ -37,8 +37,6 @@ import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.car.CarDeviceProvisionedController; import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.statusbar.NavigationBarController; -import com.android.systemui.statusbar.SuperStatusBarViewFactory; import com.android.systemui.statusbar.phone.AutoHideController; import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy; import com.android.systemui.statusbar.phone.StatusBarIconController; @@ -73,10 +71,6 @@ public class CarNavigationBarTest extends SysuiTestCase { @Mock private KeyguardStateController mKeyguardStateController; @Mock - private NavigationBarController mNavigationBarController; - @Mock - private SuperStatusBarViewFactory mSuperStatusBarViewFactory; - @Mock private ButtonSelectionStateController mButtonSelectionStateController; @Mock private PhoneStatusBarPolicy mIconPolicy; @@ -91,9 +85,8 @@ public class CarNavigationBarTest extends SysuiTestCase { mCarNavigationBar = new CarNavigationBar(mContext, mTestableResources.getResources(), mCarNavigationBarController, mWindowManager, mDeviceProvisionedController, new CommandQueue(mContext), mAutoHideController, mButtonSelectionStateListener, - mHandler, () -> mKeyguardStateController, () -> mNavigationBarController, - mSuperStatusBarViewFactory, mButtonSelectionStateController, mIconPolicy, - mIconController); + mHandler, () -> mKeyguardStateController, mButtonSelectionStateController, + mIconPolicy, mIconController); } @Test diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarViewTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarViewTest.java index 9e2131c9ccfb..19e394f69af4 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarViewTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarViewTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.navigationbar.car; +package com.android.systemui.car.navigationbar; import static com.google.common.truth.Truth.assertThat; diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationButtonTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationButtonTest.java index 96d567d3a8b5..11f2fa48783f 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationButtonTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationButtonTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.navigationbar.car; +package com.android.systemui.car.navigationbar; import static com.google.common.truth.Truth.assertThat; diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainerTest.java index 05b8e6a54099..6ac72a681bfe 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainerTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainerTest.java @@ -31,7 +31,6 @@ import androidx.test.filters.SmallTest; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.car.CarDeviceProvisionedController; -import com.android.systemui.statusbar.car.CarStatusBar; import org.junit.Before; import org.junit.Test; @@ -48,7 +47,7 @@ public class CarHeadsUpNotificationSystemContainerTest extends SysuiTestCase { @Mock private CarDeviceProvisionedController mCarDeviceProvisionedController; @Mock - private CarStatusBar mCarStatusBar; + private NotificationPanelViewController mNotificationPanelViewController; @Mock private WindowManager mWindowManager; @@ -61,7 +60,7 @@ public class CarHeadsUpNotificationSystemContainerTest extends SysuiTestCase { public void setUp() { MockitoAnnotations.initMocks(this); - when(mCarStatusBar.isPanelExpanded()).thenReturn(false); + when(mNotificationPanelViewController.isPanelExpanded()).thenReturn(false); when(mCarDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true); when(mCarDeviceProvisionedController.isCurrentUserSetupInProgress()).thenReturn(false); @@ -72,14 +71,14 @@ public class CarHeadsUpNotificationSystemContainerTest extends SysuiTestCase { mDefaultController = new CarHeadsUpNotificationSystemContainer(mContext, testableResources.getResources(), mCarDeviceProvisionedController, mWindowManager, - () -> mCarStatusBar); + () -> mNotificationPanelViewController); testableResources.addOverride( R.bool.config_enableHeadsUpNotificationWhenNotificationShadeOpen, true); mOverrideEnabledController = new CarHeadsUpNotificationSystemContainer(mContext, testableResources.getResources(), mCarDeviceProvisionedController, mWindowManager, - () -> mCarStatusBar); + () -> mNotificationPanelViewController); } @Test @@ -120,14 +119,14 @@ public class CarHeadsUpNotificationSystemContainerTest extends SysuiTestCase { @Test public void testDisplayNotification_notificationPanelExpanded_isInvisible() { - when(mCarStatusBar.isPanelExpanded()).thenReturn(true); + when(mNotificationPanelViewController.isPanelExpanded()).thenReturn(true); mDefaultController.displayNotification(mNotificationView); assertThat(mDefaultController.isVisible()).isFalse(); } @Test public void testDisplayNotification_notificationPanelExpandedEnabledHUNWhenOpen_isVisible() { - when(mCarStatusBar.isPanelExpanded()).thenReturn(true); + when(mNotificationPanelViewController.isPanelExpanded()).thenReturn(true); mOverrideEnabledController.displayNotification(mNotificationView); assertThat(mOverrideEnabledController.isVisible()).isTrue(); } diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/sideloaded/car/CarSideLoadedAppDetectorTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/sideloaded/CarSideLoadedAppDetectorTest.java index aebb0e005019..80f3d1ee5dec 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/sideloaded/car/CarSideLoadedAppDetectorTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/sideloaded/CarSideLoadedAppDetectorTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.sideloaded.car; +package com.android.systemui.car.sideloaded; import static com.google.common.truth.Truth.assertThat; diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/voicerecognition/car/ConnectedDeviceVoiceRecognitionNotifierTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java index 38b47d0aea5d..eca51e34995c 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/voicerecognition/car/ConnectedDeviceVoiceRecognitionNotifierTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,10 +14,10 @@ * limitations under the License. */ -package com.android.systemui.voicerecognition.car; +package com.android.systemui.car.voicerecognition; -import static com.android.systemui.voicerecognition.car.ConnectedDeviceVoiceRecognitionNotifier.INVALID_VALUE; -import static com.android.systemui.voicerecognition.car.ConnectedDeviceVoiceRecognitionNotifier.VOICE_RECOGNITION_STARTED; +import static com.android.systemui.car.voicerecognition.ConnectedDeviceVoiceRecognitionNotifier.INVALID_VALUE; +import static com.android.systemui.car.voicerecognition.ConnectedDeviceVoiceRecognitionNotifier.VOICE_RECOGNITION_STARTED; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.never; diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/window/OverlayPanelViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java index 04f2d06ca71c..70f1d25fe2a4 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/window/OverlayPanelViewControllerTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayPanelViewControllerTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.window; +package com.android.systemui.car.window; import static com.google.common.truth.Truth.assertThat; diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/window/OverlayViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewControllerTest.java index 331326168ba4..c24a3b52e348 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/window/OverlayViewControllerTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewControllerTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.window; +package com.android.systemui.car.window; import static com.google.common.truth.Truth.assertThat; diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/window/OverlayViewGlobalStateControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java index a96b90636891..25dd4f502fb7 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/window/OverlayViewGlobalStateControllerTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.window; +package com.android.systemui.car.window; import static com.google.common.truth.Truth.assertThat; @@ -30,7 +30,7 @@ import android.widget.FrameLayout; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; -import com.android.systemui.navigationbar.car.CarNavigationBarController; +import com.android.systemui.car.navigationbar.CarNavigationBarController; import org.junit.Before; import org.junit.Test; diff --git a/packages/CtsShim/build/Android.bp b/packages/CtsShim/build/Android.bp index 54986a43a870..be790106f42d 100644 --- a/packages/CtsShim/build/Android.bp +++ b/packages/CtsShim/build/Android.bp @@ -70,6 +70,7 @@ android_app { // v2/v3 signature. use_embedded_native_libs: true, apex_available: [ + "//apex_available:platform", "com.android.apex.cts.shim.v1", "com.android.apex.cts.shim.v2", "com.android.apex.cts.shim.v2_no_hashtree", @@ -120,6 +121,7 @@ android_app { }, manifest: "shim/AndroidManifestTargetPSdk.xml", apex_available: [ + "//apex_available:platform", "com.android.apex.cts.shim.v2_apk_in_apex_sdk_target_p", ], } @@ -140,6 +142,7 @@ android_app { manifest: "shim/AndroidManifest.xml", apex_available: [ + "//apex_available:platform", "com.android.apex.cts.shim.v1", "com.android.apex.cts.shim.v2", "com.android.apex.cts.shim.v2_no_hashtree", diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 1007d8379b8e..7baaf494771b 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -1080,14 +1080,14 @@ <string name="power_suggestion_battery_run_out">Battery may run out by <xliff:g id="time" example="12 PM">%1$s</xliff:g></string> <!-- [CHAR_LIMIT=60] label for estimated remaining duration of battery when under a certain amount --> - <string name="power_remaining_less_than_duration_only">Less than <xliff:g id="threshold">%1$s</xliff:g> remaining</string> + <string name="power_remaining_less_than_duration_only">Less than <xliff:g id="threshold">%1$s</xliff:g> left</string> <!-- [CHAR_LIMIT=60] label for estimated remaining duration of battery when under a certain amount with the percentage --> - <string name="power_remaining_less_than_duration">Less than <xliff:g id="threshold">%1$s</xliff:g> remaining (<xliff:g id="level">%2$s</xliff:g>)</string> + <string name="power_remaining_less_than_duration">Less than <xliff:g id="threshold">%1$s</xliff:g> left (<xliff:g id="level">%2$s</xliff:g>)</string> <!-- Used to let users know that they have more than some amount of battery life remaining with percentage. ex: 75% - more than 1 day remaining [CHAR LIMIT = 80] --> - <string name="power_remaining_more_than_subtext">More than <xliff:g id="time_remaining">%1$s</xliff:g> remaining (<xliff:g id="level">%2$s</xliff:g>)</string> + <string name="power_remaining_more_than_subtext">More than <xliff:g id="time_remaining">%1$s</xliff:g> left (<xliff:g id="level">%2$s</xliff:g>)</string> <!-- Used to let users know that they have more than some amount of battery life remaining. ex: more than 1 day remaining [CHAR LIMIT = 40] --> - <string name="power_remaining_only_more_than_subtext">More than <xliff:g id="time_remaining">%1$s</xliff:g> remaining</string> + <string name="power_remaining_only_more_than_subtext">More than <xliff:g id="time_remaining">%1$s</xliff:g> left</string> <!-- [CHAR_LIMIT=50] Short label for imminent shutdown warning of device --> <string name="power_remaining_duration_only_shutdown_imminent" product="default">Phone may shut down soon</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java index 653c8addba98..a210e90a3cfc 100644 --- a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java +++ b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java @@ -33,6 +33,7 @@ import android.util.Log; import android.util.Slog; import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; import android.widget.CompoundButton; import android.widget.ImageView; import android.widget.LinearLayout; @@ -357,46 +358,46 @@ public class EnableZenModeDialog { } }); - // minus button - final ImageView button1 = (ImageView) row.findViewById(android.R.id.button1); - button1.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - onClickTimeButton(row, tag, false /*down*/, rowId); - tag.lines.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE); - } - }); - - // plus button - final ImageView button2 = (ImageView) row.findViewById(android.R.id.button2); - button2.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - onClickTimeButton(row, tag, true /*up*/, rowId); - tag.lines.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE); - } - }); - final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId); + final ImageView minusButton = (ImageView) row.findViewById(android.R.id.button1); + final ImageView plusButton = (ImageView) row.findViewById(android.R.id.button2); if (rowId == COUNTDOWN_CONDITION_INDEX && time > 0) { - button1.setVisibility(View.VISIBLE); - button2.setVisibility(View.VISIBLE); + minusButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + onClickTimeButton(row, tag, false /*down*/, rowId); + tag.lines.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE); + } + }); + + plusButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + onClickTimeButton(row, tag, true /*up*/, rowId); + tag.lines.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE); + } + }); if (mBucketIndex > -1) { - button1.setEnabled(mBucketIndex > 0); - button2.setEnabled(mBucketIndex < MINUTE_BUCKETS.length - 1); + minusButton.setEnabled(mBucketIndex > 0); + plusButton.setEnabled(mBucketIndex < MINUTE_BUCKETS.length - 1); } else { final long span = time - System.currentTimeMillis(); - button1.setEnabled(span > MIN_BUCKET_MINUTES * MINUTES_MS); + minusButton.setEnabled(span > MIN_BUCKET_MINUTES * MINUTES_MS); final Condition maxCondition = ZenModeConfig.toTimeCondition(mContext, MAX_BUCKET_MINUTES, ActivityManager.getCurrentUser()); - button2.setEnabled(!Objects.equals(condition.summary, maxCondition.summary)); + plusButton.setEnabled(!Objects.equals(condition.summary, maxCondition.summary)); } - button1.setAlpha(button1.isEnabled() ? 1f : .5f); - button2.setAlpha(button2.isEnabled() ? 1f : .5f); + minusButton.setAlpha(minusButton.isEnabled() ? 1f : .5f); + plusButton.setAlpha(plusButton.isEnabled() ? 1f : .5f); } else { - button1.setVisibility(View.GONE); - button2.setVisibility(View.GONE); + if (minusButton != null) { + ((ViewGroup) row).removeView(minusButton); + } + + if (plusButton != null) { + ((ViewGroup) row).removeView(plusButton); + } } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java index 342d127abd03..4b779ac4a7f5 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java @@ -137,9 +137,9 @@ public class PowerUtilTest { true /* basedOnUsage */); // shortened string should not have percentage - assertThat(info).isEqualTo("Less than 15 min remaining"); + assertThat(info).isEqualTo("Less than 15 min left"); // Add percentage to string when provided - assertThat(info2).isEqualTo("Less than 15 min remaining (10%)"); + assertThat(info2).isEqualTo("Less than 15 min left (10%)"); } @Test @@ -171,9 +171,9 @@ public class PowerUtilTest { true /* basedOnUsage */); // shortened string should not have percentage - assertThat(info).isEqualTo("More than 2 days remaining"); + assertThat(info).isEqualTo("More than 2 days left"); // Add percentage to string when provided - assertThat(info2).isEqualTo("More than 2 days remaining (10%)"); + assertThat(info2).isEqualTo("More than 2 days left (10%)"); } @Test @@ -181,7 +181,7 @@ public class PowerUtilTest { String info = PowerUtil.getBatteryTipStringFormatted(mContext, THREE_DAYS_MILLIS); - assertThat(info).isEqualTo("More than 3 days remaining"); + assertThat(info).isEqualTo("More than 3 days left"); } @Test diff --git a/packages/SystemUI/res/drawable/bubble_manage_menu_row.xml b/packages/SystemUI/res/drawable/bubble_manage_menu_row.xml new file mode 100644 index 000000000000..a793680a037d --- /dev/null +++ b/packages/SystemUI/res/drawable/bubble_manage_menu_row.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_pressed="true"> + <ripple android:color="#99999999" /> + </item> +</selector>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/screenshot_cancel.xml b/packages/SystemUI/res/drawable/screenshot_cancel.xml index be3c5983bb2e..f0dfd21a830d 100644 --- a/packages/SystemUI/res/drawable/screenshot_cancel.xml +++ b/packages/SystemUI/res/drawable/screenshot_cancel.xml @@ -20,9 +20,9 @@ android:viewportWidth="48.0" android:viewportHeight="48.0"> <path - android:pathData="M24,24m-16,0a16,16 0,1 1,32 0a16,16 0,1 1,-32 0" - android:fillColor="@android:color/white"/> + android:fillColor="@color/global_screenshot_dismiss_background" + android:pathData="M24,24m-16,0a16,16 0,1 1,32 0a16,16 0,1 1,-32 0"/> <path - android:fillColor="@color/GM2_grey_500" + android:fillColor="@color/global_screenshot_dismiss_foreground" android:pathData="M31,18.41L29.59,17 24,22.59 18.41,17 17,18.41 22.59,24 17,29.59 18.41,31 24,25.41 29.59,31 31,29.59 25.41,24z"/> </vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/bubble_manage_menu.xml b/packages/SystemUI/res/layout/bubble_manage_menu.xml new file mode 100644 index 000000000000..129282dae77f --- /dev/null +++ b/packages/SystemUI/res/layout/bubble_manage_menu.xml @@ -0,0 +1,99 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT 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="wrap_content" + android:layout_height="wrap_content" + android:background="@drawable/rounded_bg_full" + android:elevation="@dimen/bubble_manage_menu_elevation" + android:orientation="vertical"> + + <LinearLayout + android:id="@+id/bubble_manage_menu_dismiss_container" + android:background="@drawable/bubble_manage_menu_row" + android:layout_width="match_parent" + android:layout_height="48dp" + android:gravity="center_vertical" + android:paddingStart="16dp" + android:paddingEnd="16dp" + android:orientation="horizontal"> + + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/ic_remove_no_shadow" + android:tint="@color/global_actions_text"/> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:textAppearance="@*android:style/TextAppearance.DeviceDefault" + android:text="@string/bubble_dismiss_text" /> + + </LinearLayout> + + <LinearLayout + android:id="@+id/bubble_manage_menu_dont_bubble_container" + android:background="@drawable/bubble_manage_menu_row" + android:layout_width="match_parent" + android:layout_height="48dp" + android:gravity="center_vertical" + android:paddingStart="16dp" + android:paddingEnd="16dp" + android:orientation="horizontal"> + + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/ic_stop_bubble" + android:tint="@color/global_actions_text"/> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:textAppearance="@*android:style/TextAppearance.DeviceDefault" + android:text="@string/bubbles_dont_bubble_conversation" /> + + </LinearLayout> + + <LinearLayout + android:id="@+id/bubble_manage_menu_settings_container" + android:background="@drawable/bubble_manage_menu_row" + android:layout_width="match_parent" + android:layout_height="48dp" + android:gravity="center_vertical" + android:paddingStart="16dp" + android:paddingEnd="16dp" + android:orientation="horizontal"> + + <ImageView + android:id="@+id/bubble_manage_menu_settings_icon" + android:layout_width="24dp" + android:layout_height="24dp" + android:src="@drawable/ic_remove_no_shadow"/> + + <TextView + android:id="@+id/bubble_manage_menu_settings_name" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:textAppearance="@*android:style/TextAppearance.DeviceDefault" /> + + </LinearLayout> + +</LinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/controls_management.xml b/packages/SystemUI/res/layout/controls_management.xml index ae57563cfb09..835e54e9e433 100644 --- a/packages/SystemUI/res/layout/controls_management.xml +++ b/packages/SystemUI/res/layout/controls_management.xml @@ -17,6 +17,7 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/controls_management_root" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" @@ -25,41 +26,15 @@ android:paddingStart="@dimen/controls_management_side_padding" android:paddingEnd="@dimen/controls_management_side_padding" > - <LinearLayout - android:orientation="horizontal" + + <TextView + android:id="@+id/title" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:focusable="false" - android:clickable="false" - android:gravity="center_vertical"> - - <FrameLayout - android:id="@+id/icon_frame" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:gravity="start|center_vertical" - android:minWidth="56dp" - android:visibility="gone" - android:paddingTop="@dimen/controls_app_icon_frame_top_padding" - android:paddingBottom="@dimen/controls_app_icon_frame_bottom_padding" - android:paddingEnd="@dimen/controls_app_icon_frame_side_padding" - android:paddingStart="@dimen/controls_app_icon_frame_side_padding" > - - <ImageView - android:id="@android:id/icon" - android:layout_width="@dimen/controls_app_icon_size" - android:layout_height="@dimen/controls_app_icon_size" /> - </FrameLayout> - - <TextView - android:id="@+id/title" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textAppearance="?android:attr/textAppearanceLarge" - android:textSize="@dimen/controls_title_size" - android:textAlignment="center" /> + android:textAppearance="?android:attr/textAppearanceLarge" + android:textSize="@dimen/controls_title_size" + android:textAlignment="center" /> - </LinearLayout> <TextView diff --git a/packages/SystemUI/res/layout/global_actions_grid_v2.xml b/packages/SystemUI/res/layout/global_actions_grid_v2.xml index 1c4ec64e9620..66f57fd6f37c 100644 --- a/packages/SystemUI/res/layout/global_actions_grid_v2.xml +++ b/packages/SystemUI/res/layout/global_actions_grid_v2.xml @@ -14,7 +14,6 @@ android:theme="@style/qs_theme" android:clipChildren="false" android:clipToPadding="false" - android:layout_marginTop="@dimen/global_actions_top_margin" android:layout_marginStart="@dimen/global_actions_side_margin" > <LinearLayout @@ -51,6 +50,7 @@ android:paddingBottom="@dimen/global_actions_grid_container_shadow_offset" android:layout_marginBottom="@dimen/global_actions_grid_container_negative_shadow_offset" android:orientation="vertical" + android:scrollbars="none" > <LinearLayout android:id="@+id/global_actions_grid_root" diff --git a/packages/SystemUI/res/layout/global_screenshot.xml b/packages/SystemUI/res/layout/global_screenshot.xml index d506e7e8e700..db109fe8a541 100644 --- a/packages/SystemUI/res/layout/global_screenshot.xml +++ b/packages/SystemUI/res/layout/global_screenshot.xml @@ -68,6 +68,7 @@ android:visibility="gone" android:contentDescription="@string/screenshot_dismiss_ui_description"> <ImageView + android:id="@+id/global_screenshot_dismiss_image" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="@dimen/screenshot_dismiss_button_margin" diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml index 93aa2701ad07..2d5101104237 100644 --- a/packages/SystemUI/res/values-night/colors.xml +++ b/packages/SystemUI/res/values-night/colors.xml @@ -72,9 +72,14 @@ <color name="global_actions_alert_text">@color/GM2_red_300</color> <!-- Global screenshot actions --> - <color name="global_screenshot_button_background">@color/GM2_grey_900</color> + <color name="global_screenshot_button_background">@color/GM2_grey_800</color> <color name="global_screenshot_button_ripple">#42FFFFFF</color> - <color name="global_screenshot_button_text">@color/GM2_blue_300</color> + <color name="global_screenshot_button_text">#FFFFFF</color> + <color name="global_screenshot_button_border">@color/GM2_grey_600</color> + <color name="global_screenshot_button_icon">@color/GM2_blue_300</color> + <color name="global_screenshot_dismiss_background">@color/GM2_grey_800</color> + <color name="global_screenshot_dismiss_foreground">#FFFFFF</color> + <!-- Biometric dialog colors --> <color name="biometric_dialog_gray">#ff888888</color> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index 4482cdac3327..2e217a5e83ea 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -198,6 +198,8 @@ <color name="global_screenshot_button_border">@color/GM2_grey_300</color> <color name="global_screenshot_button_ripple">#1f000000</color> <color name="global_screenshot_button_icon">@color/GM2_blue_500</color> + <color name="global_screenshot_dismiss_background">#FFFFFF</color> + <color name="global_screenshot_dismiss_foreground">@color/GM2_grey_500</color> <!-- GM2 colors --> <color name="GM2_grey_50">#F8F9FA</color> @@ -248,4 +250,7 @@ <color name="control_enabled_thermo_heat_background">@color/GM2_red_200</color> <color name="control_enabled_thermo_cool_background">@color/GM2_blue_200</color> <color name="control_enabled_default_background">@color/GM2_blue_200</color> + + <!-- Docked misalignment message --> + <color name="misalignment_text_color">#F28B82</color> </resources> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 599ed1696ec9..39aa771bc797 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -993,8 +993,6 @@ <dimen name="cell_overlay_padding">18dp</dimen> <!-- Global actions power menu --> - <dimen name="global_actions_top_margin">12dp</dimen> - <dimen name="global_actions_panel_width">120dp</dimen> <dimen name="global_actions_padding">12dp</dimen> <dimen name="global_actions_translate">9dp</dimen> @@ -1014,6 +1012,9 @@ <!-- Margins at the left and right of the power menu and home controls widgets. --> <dimen name="global_actions_side_margin">16dp</dimen> + <!-- Amount to shift the layout when exiting/entering for controls activities --> + <dimen name="global_actions_controls_y_translation">20dp</dimen> + <!-- The maximum offset in either direction that elements are moved horizontally to prevent burn-in on AOD. --> <dimen name="burn_in_prevention_offset_x">8dp</dimen> @@ -1197,6 +1198,7 @@ snap to the dismiss target. --> <dimen name="bubble_dismiss_target_padding_x">40dp</dimen> <dimen name="bubble_dismiss_target_padding_y">20dp</dimen> + <dimen name="bubble_manage_menu_elevation">4dp</dimen> <dimen name="dismiss_circle_size">52dp</dimen> <dimen name="dismiss_target_x_size">24dp</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index cb20e7a15424..d639ed074240 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2629,6 +2629,8 @@ <string name="bubbles_user_education_manage">Tap Manage to turn off bubbles from this app</string> <!-- Button text for dismissing the bubble "manage" button tool tip [CHAR LIMIT=20]--> <string name="bubbles_user_education_got_it">Got it</string> + <!-- Label for the button that takes the user to the notification settings for the given app. --> + <string name="bubbles_app_settings"><xliff:g id="notification_title" example="Android Messages">%1$s</xliff:g> settings</string> <!-- Notification content text when the system navigation mode changes as a result of changing the default launcher [CHAR LIMIT=NONE] --> <string name="notification_content_system_nav_changed">System navigation updated. To make changes, go to Settings.</string> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 7e24f5dbbd50..4ed819e4925b 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -663,8 +663,13 @@ <!-- Controls styles --> <style name="Theme.ControlsManagement" parent="@android:style/Theme.DeviceDefault.NoActionBar"> + <item name="android:windowActivityTransitions">true</item> + <item name="android:windowContentTransitions">false</item> <item name="android:windowIsTranslucent">false</item> - <item name="wallpaperTextColor">@*android:color/primary_text_material_dark</item> + <item name="android:windowBackground">@android:color/black</item> + <item name="android:colorBackground">@android:color/black</item> + <item name="android:windowAnimationStyle">@null</item> + <item name="android:statusBarColor">@*android:color/transparent</item> </style> <style name="TextAppearance.Control"> diff --git a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java index 409ae3f3c7d6..c92174a0d8af 100644 --- a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java +++ b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java @@ -164,7 +164,9 @@ public class PasswordTextView extends View { currentDrawPosition = getPaddingLeft(); } } else { - currentDrawPosition = getWidth() / 2 - totalDrawingWidth / 2; + float maxRight = getWidth() - getPaddingRight() - totalDrawingWidth; + float center = getWidth() / 2f - totalDrawingWidth / 2f; + currentDrawPosition = center > 0 ? center : maxRight; } int length = mTextChars.size(); Rect bounds = getCharBounds(); diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java index 7262f8caac89..1f27ae238533 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java @@ -16,6 +16,8 @@ package com.android.systemui.accessibility; +import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_GLOBAL_ACTIONS; + import android.accessibilityservice.AccessibilityService; import android.app.PendingIntent; import android.app.RemoteAction; @@ -282,8 +284,8 @@ public class SystemActions extends SystemUI { private void handleTakeScreenshot() { ScreenshotHelper screenshotHelper = new ScreenshotHelper(mContext); - screenshotHelper.takeScreenshot(WindowManager.TAKE_SCREENSHOT_FULLSCREEN, - true, true, new Handler(Looper.getMainLooper()), null); + screenshotHelper.takeScreenshot(WindowManager.TAKE_SCREENSHOT_FULLSCREEN, true, true, + SCREENSHOT_GLOBAL_ACTIONS, new Handler(Looper.getMainLooper()), null); } private void handleAccessibilityMenu() { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java index 71f2bc09b983..38bfffbb75d9 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java @@ -90,6 +90,7 @@ class Bubble implements BubbleViewProvider { } private FlyoutMessage mFlyoutMessage; + private Drawable mBadgedAppIcon; private Bitmap mBadgedImage; private int mDotColor; private Path mDotPath; @@ -133,6 +134,10 @@ class Bubble implements BubbleViewProvider { return mBadgedImage; } + public Drawable getBadgedAppIcon() { + return mBadgedAppIcon; + } + @Override public int getDotColor() { return mDotColor; @@ -239,6 +244,7 @@ class Bubble implements BubbleViewProvider { mAppName = info.appName; mFlyoutMessage = info.flyoutMessage; + mBadgedAppIcon = info.badgedAppIcon; mBadgedImage = info.badgedBubbleImage; mDotColor = info.dotColor; mDotPath = info.dotPath; diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index c6883c89bd21..e488cf271fdf 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -605,6 +605,9 @@ public class BubbleController implements ConfigurationController.ConfigurationLi if (mExpandListener != null) { mStackView.setExpandListener(mExpandListener); } + + mStackView.setUnbubbleConversationCallback(notificationEntry -> + onUserChangedBubble(notificationEntry, false /* shouldBubble */)); } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java index 3524696dbc79..bb2365559f74 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java @@ -43,7 +43,6 @@ import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.ShapeDrawable; import android.os.RemoteException; -import android.service.notification.StatusBarNotification; import android.util.AttributeSet; import android.util.Log; import android.view.View; @@ -55,14 +54,13 @@ import com.android.internal.policy.ScreenDecorationsUtils; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.recents.TriangleShape; -import com.android.systemui.shared.system.SysUiStatsLog; import com.android.systemui.statusbar.AlphaOptimizedButton; import com.android.systemui.statusbar.notification.collection.NotificationEntry; /** * Container for the expanded bubble view, handles rendering the caret and settings icon. */ -public class BubbleExpandedView extends LinearLayout implements View.OnClickListener { +public class BubbleExpandedView extends LinearLayout { private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleExpandedView" : TAG_BUBBLES; private enum ActivityViewStatus { @@ -100,9 +98,6 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList private int mPointerWidth; private int mPointerHeight; private ShapeDrawable mPointerDrawable; - private Rect mTempRect = new Rect(); - private int[] mTempLoc = new int[2]; - private int mExpandedViewTouchSlop; @Nullable private Bubble mBubble; @@ -224,7 +219,6 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList mMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height); mOverflowHeight = res.getDimensionPixelSize(R.dimen.bubble_overflow_height); mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin); - mExpandedViewTouchSlop = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_slop); } @Override @@ -239,7 +233,6 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList mPointerWidth = res.getDimensionPixelSize(R.dimen.bubble_pointer_width); mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height); - mPointerDrawable = new ShapeDrawable(TriangleShape.create( mPointerWidth, mPointerHeight, true /* pointUp */)); mPointerView.setBackground(mPointerDrawable); @@ -248,7 +241,6 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList mSettingsIconHeight = getContext().getResources().getDimensionPixelSize( R.dimen.bubble_manage_button_height); mSettingsIcon = findViewById(R.id.settings_button); - mSettingsIcon.setOnClickListener(this); mActivityView = new ActivityView(mContext, null /* attrs */, 0 /* defStyle */, true /* singleTaskInstance */); @@ -289,6 +281,19 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList return mBubble != null ? mBubble.getEntry() : null; } + void setManageClickListener(OnClickListener manageClickListener) { + findViewById(R.id.settings_button).setOnClickListener(manageClickListener); + } + + /** + * Updates the ActivityView's obscured touchable region. This calls onLocationChanged, which + * results in a call to {@link BubbleStackView#subtractObscuredTouchableRegion}. This is useful + * if a view has been added or removed from on top of the ActivityView, such as the manage menu. + */ + void updateObscuredTouchableRegion() { + mActivityView.onLocationChanged(); + } + void applyThemeAttrs() { final TypedArray ta = mContext.obtainStyledAttributes( new int[] { @@ -473,51 +478,6 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList } /** - * Whether the provided x, y values (in raw coordinates) are in a touchable area of the - * expanded view. - * - * The touchable areas are the ActivityView (plus some slop around it) and the manage button. - */ - boolean intersectingTouchableContent(int rawX, int rawY) { - mTempRect.setEmpty(); - if (mActivityView != null) { - mTempLoc = mActivityView.getLocationOnScreen(); - mTempRect.set(mTempLoc[0] - mExpandedViewTouchSlop, - mTempLoc[1] - mExpandedViewTouchSlop, - mTempLoc[0] + mActivityView.getWidth() + mExpandedViewTouchSlop, - mTempLoc[1] + mActivityView.getHeight() + mExpandedViewTouchSlop); - } - if (mTempRect.contains(rawX, rawY)) { - return true; - } - mTempLoc = mSettingsIcon.getLocationOnScreen(); - mTempRect.set(mTempLoc[0], - mTempLoc[1], - mTempLoc[0] + mSettingsIcon.getWidth(), - mTempLoc[1] + mSettingsIcon.getHeight()); - if (mTempRect.contains(rawX, rawY)) { - return true; - } - return false; - } - - @Override - public void onClick(View view) { - if (mBubble == null) { - return; - } - int id = view.getId(); - if (id == R.id.settings_button) { - Intent intent = mBubble.getSettingsIntent(); - mStackView.collapseStack(() -> { - mContext.startActivityAsUser(intent, mBubble.getEntry().getSbn().getUser()); - logBubbleClickEvent(mBubble, - SysUiStatsLog.BUBBLE_UICHANGED__ACTION__HEADER_GO_TO_SETTINGS); - }); - } - } - - /** * Update appearance of the expanded view being displayed. */ public void updateView() { @@ -547,10 +507,8 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList * Position of the manage button displayed in the expanded view. Used for placing user * education about the manage button. */ - public Rect getManageButtonLocationOnScreen() { - mTempLoc = mSettingsIcon.getLocationOnScreen(); - return new Rect(mTempLoc[0], mTempLoc[1], mTempLoc[0] + mSettingsIcon.getWidth(), - mTempLoc[1] + mSettingsIcon.getHeight()); + public void getManageButtonBoundsOnScreen(Rect rect) { + mSettingsIcon.getBoundsOnScreen(rect); } /** @@ -611,26 +569,4 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList } return INVALID_DISPLAY; } - - /** - * Logs bubble UI click event. - * - * @param bubble the bubble notification entry that user is interacting with. - * @param action the user interaction enum. - */ - private void logBubbleClickEvent(Bubble bubble, int action) { - StatusBarNotification notification = bubble.getEntry().getSbn(); - SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED, - notification.getPackageName(), - notification.getNotification().getChannelId(), - notification.getId(), - mStackView.getBubbleIndex(mStackView.getExpandedBubble()), - mStackView.getBubbleCount(), - action, - mStackView.getNormalizedXPosition(), - mStackView.getNormalizedYPosition(), - bubble.showInShade(), - bubble.isOngoing(), - false /* isAppForeground (unused) */); - } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index 0aabdff96e1e..c9069316028c 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -33,12 +33,14 @@ import android.animation.ValueAnimator; import android.annotation.SuppressLint; import android.app.Notification; import android.content.Context; +import android.content.Intent; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Color; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; +import android.graphics.Outline; import android.graphics.Paint; import android.graphics.Point; import android.graphics.PointF; @@ -47,6 +49,7 @@ import android.graphics.RectF; import android.graphics.Region; import android.os.Bundle; import android.os.Vibrator; +import android.service.notification.StatusBarNotification; import android.util.Log; import android.view.Choreographer; import android.view.DisplayCutout; @@ -55,6 +58,7 @@ import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import android.view.ViewOutlineProvider; import android.view.ViewTreeObserver; import android.view.WindowInsets; import android.view.WindowManager; @@ -62,6 +66,7 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.view.animation.AccelerateDecelerateInterpolator; import android.widget.FrameLayout; +import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.MainThread; @@ -83,6 +88,7 @@ import com.android.systemui.bubbles.animation.StackAnimationController; import com.android.systemui.model.SysUiState; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.SysUiStatsLog; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.phone.NotificationShadeWindowController; import com.android.systemui.util.DismissCircleView; import com.android.systemui.util.FloatingContentCoordinator; @@ -97,6 +103,7 @@ import java.math.RoundingMode; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.function.Consumer; /** * Renders bubbles in a stack and handles animating expanded and collapsed states. @@ -224,7 +231,7 @@ public class BubbleStackView extends FrameLayout { private int mPointerHeight; private int mStatusBarHeight; private int mImeOffset; - private BubbleViewProvider mExpandedBubble; + @Nullable private BubbleViewProvider mExpandedBubble; private boolean mIsExpanded; /** Whether the stack is currently on the left side of the screen, or animating there. */ @@ -244,6 +251,10 @@ public class BubbleStackView extends FrameLayout { } private BubbleController.BubbleExpandListener mExpandListener; + + /** Callback to run when we want to unbubble the given notification's conversation. */ + private Consumer<NotificationEntry> mUnbubbleConversationCallback; + private SysUiState mSysUiState; private boolean mViewUpdatedRequested = false; @@ -255,9 +266,7 @@ public class BubbleStackView extends FrameLayout { private LayoutInflater mInflater; - // Used for determining view / touch intersection - int[] mTempLoc = new int[2]; - RectF mTempRect = new RectF(); + private Rect mTempRect = new Rect(); private final List<Rect> mSystemGestureExclusionRects = Collections.singletonList(new Rect()); @@ -471,6 +480,11 @@ public class BubbleStackView extends FrameLayout { return true; } + // If the manage menu is visible, just hide it. + if (mShowingManage) { + showManageMenu(false /* show */); + } + if (mBubbleData.isExpanded()) { maybeShowManageEducation(false /* show */); @@ -627,6 +641,13 @@ public class BubbleStackView extends FrameLayout { private BubbleManageEducationView mManageEducationView; private boolean mAnimatingManageEducationAway; + private ViewGroup mManageMenu; + private ImageView mManageSettingsIcon; + private TextView mManageSettingsText; + private boolean mShowingManage = false; + private PhysicsAnimator.SpringConfig mManageSpringConfig = new PhysicsAnimator.SpringConfig( + SpringForce.STIFFNESS_MEDIUM, SpringForce.DAMPING_RATIO_LOW_BOUNCY); + @SuppressLint("ClickableViewAccessibility") public BubbleStackView(Context context, BubbleData data, @Nullable SurfaceSynchronizer synchronizer, @@ -689,6 +710,8 @@ public class BubbleStackView extends FrameLayout { mExpandedViewContainer.setClipChildren(false); addView(mExpandedViewContainer); + setUpManageMenu(); + setUpFlyout(); mFlyoutTransitionSpring.setSpring(new SpringForce() .setStiffness(SpringForce.STIFFNESS_LOW) @@ -838,7 +861,9 @@ public class BubbleStackView extends FrameLayout { // ActivityViews, etc.) were touched. Collapse the stack if it's expanded. setOnTouchListener((view, ev) -> { if (ev.getAction() == MotionEvent.ACTION_DOWN) { - if (mBubbleData.isExpanded()) { + if (mShowingManage) { + showManageMenu(false /* show */); + } else if (mBubbleData.isExpanded()) { mBubbleData.setExpanded(false); } } @@ -847,6 +872,66 @@ public class BubbleStackView extends FrameLayout { }); } + private void setUpManageMenu() { + if (mManageMenu != null) { + removeView(mManageMenu); + } + + mManageMenu = (ViewGroup) LayoutInflater.from(getContext()).inflate( + R.layout.bubble_manage_menu, this, false); + mManageMenu.setVisibility(View.INVISIBLE); + + PhysicsAnimator.getInstance(mManageMenu).setDefaultSpringConfig(mManageSpringConfig); + + final TypedArray ta = mContext.obtainStyledAttributes( + new int[] {android.R.attr.dialogCornerRadius}); + final int menuCornerRadius = ta.getDimensionPixelSize(0, 0); + ta.recycle(); + + mManageMenu.setOutlineProvider(new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), menuCornerRadius); + } + }); + mManageMenu.setClipToOutline(true); + + mManageMenu.findViewById(R.id.bubble_manage_menu_dismiss_container).setOnClickListener( + view -> { + showManageMenu(false /* show */); + dismissBubbleIfExists(mBubbleData.getSelectedBubble()); + }); + + mManageMenu.findViewById(R.id.bubble_manage_menu_dont_bubble_container).setOnClickListener( + view -> { + showManageMenu(false /* show */); + final Bubble bubble = mBubbleData.getSelectedBubble(); + if (bubble != null && mBubbleData.hasBubbleWithKey(bubble.getKey())) { + mUnbubbleConversationCallback.accept(bubble.getEntry()); + } + }); + + mManageMenu.findViewById(R.id.bubble_manage_menu_settings_container).setOnClickListener( + view -> { + showManageMenu(false /* show */); + final Bubble bubble = mBubbleData.getSelectedBubble(); + if (bubble != null && mBubbleData.hasBubbleWithKey(bubble.getKey())) { + final Intent intent = bubble.getSettingsIntent(); + collapseStack(() -> { + mContext.startActivityAsUser( + intent, bubble.getEntry().getSbn().getUser()); + logBubbleClickEvent( + bubble, + SysUiStatsLog.BUBBLE_UICHANGED__ACTION__HEADER_GO_TO_SETTINGS); + }); + } + }); + + mManageSettingsIcon = mManageMenu.findViewById(R.id.bubble_manage_menu_settings_icon); + mManageSettingsText = mManageMenu.findViewById(R.id.bubble_manage_menu_settings_name); + addView(mManageMenu); + } + private void setUpUserEducation() { if (mUserEducationView != null) { removeView(mUserEducationView); @@ -934,6 +1019,7 @@ public class BubbleStackView extends FrameLayout { setUpFlyout(); setUpOverflow(); setUpUserEducation(); + setUpManageMenu(); } /** Respond to the phone being rotated by repositioning the stack and hiding any flyouts. */ @@ -960,6 +1046,9 @@ public class BubbleStackView extends FrameLayout { Math.max(0f, Math.min(1f, mVerticalPosPercentBeforeRotation)); addOnLayoutChangeListener(mOrientationChangedListener); hideFlyoutImmediate(); + + mManageMenu.setVisibility(View.INVISIBLE); + mShowingManage = false; } @Override @@ -1100,6 +1189,12 @@ public class BubbleStackView extends FrameLayout { mExpandListener = listener; } + /** Sets the function to call to un-bubble the given conversation. */ + public void setUnbubbleConversationCallback( + Consumer<NotificationEntry> unbubbleConversationCallback) { + mUnbubbleConversationCallback = unbubbleConversationCallback; + } + /** * Whether the stack of bubbles is expanded or not. */ @@ -1361,15 +1456,14 @@ public class BubbleStackView extends FrameLayout { mManageEducationView.setAlpha(0); mManageEducationView.setVisibility(VISIBLE); mManageEducationView.post(() -> { - final Rect position = - mExpandedBubble.getExpandedView().getManageButtonLocationOnScreen(); + mExpandedBubble.getExpandedView().getManageButtonBoundsOnScreen(mTempRect); final int viewHeight = mManageEducationView.getManageViewHeight(); final int inset = getResources().getDimensionPixelSize( R.dimen.bubbles_manage_education_top_inset); mManageEducationView.bringToFront(); - mManageEducationView.setManageViewPosition(position.left, - position.top - viewHeight + inset); - mManageEducationView.setPointerPosition(position.centerX() - position.left); + mManageEducationView.setManageViewPosition(mTempRect.left, + mTempRect.top - viewHeight + inset); + mManageEducationView.setPointerPosition(mTempRect.centerX() - mTempRect.left); mManageEducationView.animate() .setDuration(ANIMATE_STACK_USER_EDUCATION_DURATION) .setInterpolator(FAST_OUT_SLOW_IN).alpha(1); @@ -1443,6 +1537,9 @@ public class BubbleStackView extends FrameLayout { } private void animateCollapse() { + // Hide the menu if it's visible. + showManageMenu(false); + mIsExpanded = false; final BubbleViewProvider previouslySelected = mExpandedBubble; beforeExpandedViewAnimation(); @@ -1570,9 +1667,9 @@ public class BubbleStackView extends FrameLayout { */ @Override public void subtractObscuredTouchableRegion(Region touchableRegion, View view) { - // If the notification shade is expanded, we shouldn't let the ActivityView steal any touch - // events from any location. - if (mNotificationShadeWindowController.getPanelExpanded()) { + // If the notification shade is expanded, or the manage menu is open, we shouldn't let the + // ActivityView steal any touch events from any location. + if (mNotificationShadeWindowController.getPanelExpanded() || mShowingManage) { touchableRegion.setEmpty(); } } @@ -1658,17 +1755,20 @@ public class BubbleStackView extends FrameLayout { private void dismissMagnetizedObject() { if (mIsExpanded) { final View draggedOutBubbleView = (View) mMagnetizedObject.getUnderlyingObject(); - final Bubble draggedOutBubble = mBubbleData.getBubbleWithView(draggedOutBubbleView); + dismissBubbleIfExists(mBubbleData.getBubbleWithView(draggedOutBubbleView)); - if (mBubbleData.hasBubbleWithKey(draggedOutBubble.getKey())) { - mBubbleData.notificationEntryRemoved( - draggedOutBubble.getEntry(), BubbleController.DISMISS_USER_GESTURE); - } } else { mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE); } } + private void dismissBubbleIfExists(@Nullable Bubble bubble) { + if (bubble != null && mBubbleData.hasBubbleWithKey(bubble.getKey())) { + mBubbleData.notificationEntryRemoved( + bubble.getEntry(), BubbleController.DISMISS_USER_GESTURE); + } + } + /** Prepares and starts the desaturate/darken animation on the bubble stack. */ private void animateDesaturateAndDarken(View targetView, boolean desaturateAndDarken) { mDesaturateAndDarkenTargetView = targetView; @@ -1912,6 +2012,63 @@ public class BubbleStackView extends FrameLayout { invalidate(); } + private void showManageMenu(boolean show) { + mShowingManage = show; + + // This should not happen, since the manage menu is only visible when there's an expanded + // bubble. If we end up in this state, just hide the menu immediately. + if (mExpandedBubble == null || mExpandedBubble.getExpandedView() == null) { + mManageMenu.setVisibility(View.INVISIBLE); + return; + } + + // If available, update the manage menu's settings option with the expanded bubble's app + // name and icon. + if (show && mBubbleData.hasBubbleWithKey(mExpandedBubble.getKey())) { + final Bubble bubble = mBubbleData.getBubbleWithKey(mExpandedBubble.getKey()); + mManageSettingsIcon.setImageDrawable(bubble.getBadgedAppIcon()); + mManageSettingsText.setText(getResources().getString( + R.string.bubbles_app_settings, bubble.getAppName())); + } + + mExpandedBubble.getExpandedView().getManageButtonBoundsOnScreen(mTempRect); + + // When the menu is open, it should be at these coordinates. This will make the menu's + // bottom left corner match up with the button's bottom left corner. + final float targetX = mTempRect.left; + final float targetY = mTempRect.bottom - mManageMenu.getHeight(); + + if (show) { + mManageMenu.setScaleX(0.5f); + mManageMenu.setScaleY(0.5f); + mManageMenu.setTranslationX(targetX - mManageMenu.getWidth() / 4); + mManageMenu.setTranslationY(targetY + mManageMenu.getHeight() / 4); + mManageMenu.setAlpha(0f); + + PhysicsAnimator.getInstance(mManageMenu) + .spring(DynamicAnimation.ALPHA, 1f) + .spring(DynamicAnimation.SCALE_X, 1f) + .spring(DynamicAnimation.SCALE_Y, 1f) + .spring(DynamicAnimation.TRANSLATION_X, targetX) + .spring(DynamicAnimation.TRANSLATION_Y, targetY) + .start(); + + mManageMenu.setVisibility(View.VISIBLE); + } else { + PhysicsAnimator.getInstance(mManageMenu) + .spring(DynamicAnimation.ALPHA, 0f) + .spring(DynamicAnimation.SCALE_X, 0.5f) + .spring(DynamicAnimation.SCALE_Y, 0.5f) + .spring(DynamicAnimation.TRANSLATION_X, targetX - mManageMenu.getWidth() / 4) + .spring(DynamicAnimation.TRANSLATION_Y, targetY + mManageMenu.getHeight() / 4) + .withEndActions(() -> mManageMenu.setVisibility(View.INVISIBLE)) + .start(); + } + + // Update the AV's obscured touchable region for the new menu visibility state. + mExpandedBubble.getExpandedView().updateObscuredTouchableRegion(); + } + private void updateExpandedBubble() { if (DEBUG_BUBBLE_STACK_VIEW) { Log.d(TAG, "updateExpandedBubble()"); @@ -1921,6 +2078,7 @@ public class BubbleStackView extends FrameLayout { && mExpandedBubble.getExpandedView() != null) { BubbleExpandedView bev = mExpandedBubble.getExpandedView(); mExpandedViewContainer.addView(bev); + bev.setManageClickListener((view) -> showManageMenu(!mShowingManage)); bev.populateExpandedView(); mExpandedViewContainer.setVisibility(VISIBLE); mExpandedViewContainer.setAlpha(1.0f); @@ -2089,4 +2247,26 @@ public class BubbleStackView extends FrameLayout { } return bubbles; } + + /** + * Logs bubble UI click event. + * + * @param bubble the bubble notification entry that user is interacting with. + * @param action the user interaction enum. + */ + private void logBubbleClickEvent(Bubble bubble, int action) { + StatusBarNotification notification = bubble.getEntry().getSbn(); + SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED, + notification.getPackageName(), + notification.getNotification().getChannelId(), + notification.getId(), + getBubbleIndex(getExpandedBubble()), + getBubbleCount(), + action, + getNormalizedXPosition(), + getNormalizedYPosition(), + bubble.showInShade(), + bubble.isOngoing(), + false /* isAppForeground (unused) */); + } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java index c96f9a470ca4..8a57a735f6cb 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java @@ -116,6 +116,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask ShortcutInfo shortcutInfo; String appName; Bitmap badgedBubbleImage; + Drawable badgedAppIcon; int dotColor; Path dotPath; Bubble.FlyoutMessage flyoutMessage; @@ -176,6 +177,7 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask } BitmapInfo badgeBitmapInfo = iconFactory.getBadgeBitmap(badgedIcon); + info.badgedAppIcon = badgedIcon; info.badgedBubbleImage = iconFactory.getBubbleBitmap(bubbleDrawable, badgeBitmapInfo).icon; diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java index ef84c73b3145..ca3e2e27fa9a 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java @@ -20,15 +20,17 @@ import android.graphics.Bitmap; import android.graphics.Path; import android.view.View; +import androidx.annotation.Nullable; + /** * Interface to represent actual Bubbles and UI elements that act like bubbles, like BubbleOverflow. */ interface BubbleViewProvider { - BubbleExpandedView getExpandedView(); + @Nullable BubbleExpandedView getExpandedView(); void setContentVisibility(boolean visible); - View getIconView(); + @Nullable View getIconView(); void logUIEvent(int bubbleCount, int action, float normalX, float normalY, int index); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java index 0002e862bb41..35406c71a080 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java @@ -128,17 +128,31 @@ public class ExpandedAnimationController */ private boolean mBubbleDraggedOutEnough = false; + /** End action to run when the lead bubble's expansion animation completes. */ + @Nullable private Runnable mLeadBubbleEndAction; + /** - * Animates expanding the bubbles into a row along the top of the screen. + * Animates expanding the bubbles into a row along the top of the screen, optionally running an + * end action when the entire animation completes, and an end action when the lead bubble's + * animation ends. */ - public void expandFromStack(@Nullable Runnable after) { + public void expandFromStack( + @Nullable Runnable after, @Nullable Runnable leadBubbleEndAction) { mAnimatingCollapse = false; mAnimatingExpand = true; mAfterExpand = after; + mLeadBubbleEndAction = leadBubbleEndAction; startOrUpdatePathAnimation(true /* expanding */); } + /** + * Animates expanding the bubbles into a row along the top of the screen. + */ + public void expandFromStack(@Nullable Runnable after) { + expandFromStack(after, null /* leadBubbleEndAction */); + } + /** Animate collapsing the bubbles back to their stacked position. */ public void collapseBackToStack(PointF collapsePoint, Runnable after) { mAnimatingExpand = false; @@ -237,11 +251,17 @@ public class ExpandedAnimationController ? (index * 10) : ((mLayout.getChildCount() - index) * 10); + final boolean isLeadBubble = + (firstBubbleLeads && index == 0) + || (!firstBubbleLeads && index == mLayout.getChildCount() - 1); + animation .followAnimatedTargetAlongPath( path, EXPAND_COLLAPSE_TARGET_ANIM_DURATION /* targetAnimDuration */, - Interpolators.LINEAR /* targetAnimInterpolator */) + Interpolators.LINEAR /* targetAnimInterpolator */, + isLeadBubble ? mLeadBubbleEndAction : null /* endAction */, + () -> mLeadBubbleEndAction = null /* endAction */) .withStartDelay(startDelay) .withStiffness(EXPAND_COLLAPSE_ANIM_STIFFNESS); }).startAll(after); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java index b1bbafc1ed8f..a7d1be1a766a 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java @@ -758,21 +758,34 @@ public class PhysicsAnimationLayout extends FrameLayout { * or {@link #position}, ultimately animating the view's position to the final point on the * given path. * - * Any provided end listeners will be called when the physics-based animations kicked off by - * the moving target have completed - not when the target animation completes. + * @param pathAnimEndActions End actions to run after the animator that moves the target + * along the path ends. The views following the target may still + * be moving. */ public PhysicsPropertyAnimator followAnimatedTargetAlongPath( Path path, int targetAnimDuration, TimeInterpolator targetAnimInterpolator, - Runnable... endActions) { + Runnable... pathAnimEndActions) { mPathAnimator = ObjectAnimator.ofFloat( this, mCurrentPointOnPathXProperty, mCurrentPointOnPathYProperty, path); + + if (pathAnimEndActions != null) { + mPathAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + for (Runnable action : pathAnimEndActions) { + if (action != null) { + action.run(); + } + } + } + }); + } + mPathAnimator.setDuration(targetAnimDuration); mPathAnimator.setInterpolator(targetAnimInterpolator); - mPositionEndActions = endActions; - // Remove translation related values since we're going to ignore them and follow the // path instead. clearTranslationValues(); diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt index 7e8fec716b1f..e84f439c1fe2 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt @@ -31,6 +31,7 @@ import com.android.internal.annotations.VisibleForTesting import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.util.concurrency.DelayableExecutor import dagger.Lazy +import java.util.concurrent.atomic.AtomicBoolean import javax.inject.Inject import javax.inject.Singleton @@ -284,13 +285,19 @@ open class ControlsBindingControllerImpl @Inject constructor( val requestLimit: Long ) : IControlsSubscriber.Stub() { val loadedControls = ArrayList<Control>() - private var isTerminated = false + private var isTerminated = AtomicBoolean(false) private var _loadCancelInternal: (() -> Unit)? = null private lateinit var subscription: IControlsSubscription + /** + * Potentially cancel a subscriber. The subscriber may also have terminated, in which case + * the request is ignored. + */ fun loadCancel() = Runnable { - Log.d(TAG, "Cancel load requested") - _loadCancelInternal?.invoke() + _loadCancelInternal?.let { + Log.d(TAG, "Canceling loadSubscribtion") + it.invoke() + } } override fun onSubscribe(token: IBinder, subs: IControlsSubscription) { @@ -301,7 +308,7 @@ open class ControlsBindingControllerImpl @Inject constructor( override fun onNext(token: IBinder, c: Control) { backgroundExecutor.execute { - if (isTerminated) return@execute + if (isTerminated.get()) return@execute loadedControls.add(c) @@ -325,13 +332,15 @@ open class ControlsBindingControllerImpl @Inject constructor( } private fun maybeTerminateAndRun(postTerminateFn: Runnable) { - if (isTerminated) return + if (isTerminated.get()) return - isTerminated = true _loadCancelInternal = {} currentProvider?.cancelLoadTimeout() - backgroundExecutor.execute(postTerminateFn) + backgroundExecutor.execute { + isTerminated.compareAndSet(false, true) + postTerminateFn.run() + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt index 79a5b0a1ce52..bc97c10756fd 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt @@ -52,19 +52,17 @@ interface ControlsController : UserAwareController { * Load all available [Control] for a given service. * * @param componentName the [ComponentName] of the [ControlsProviderService] to load from - * @param dataCallback a callback in which to retrieve the result. + * @param dataCallback a callback in which to retrieve the result + * @param cancelWrapper a callback to receive a [Runnable] that can be run to cancel the + * request */ fun loadForComponent( componentName: ComponentName, - dataCallback: Consumer<LoadData> + dataCallback: Consumer<LoadData>, + cancelWrapper: Consumer<Runnable> ) /** - * Cancels a pending load call - */ - fun cancelLoad() - - /** * Request to subscribe for favorited controls per structure * * @param structureInfo structure to limit the subscription to diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt index 5626a5de2e3c..8e88756b16fd 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt @@ -77,8 +77,6 @@ class ControlsControllerImpl @Inject constructor ( private var userChanging: Boolean = true - private var loadCanceller: Runnable? = null - private var seedingInProgress = false private val seedingCallbacks = mutableListOf<Consumer<Boolean>>() @@ -276,28 +274,29 @@ class ControlsControllerImpl @Inject constructor ( override fun loadForComponent( componentName: ComponentName, - dataCallback: Consumer<ControlsController.LoadData> + dataCallback: Consumer<ControlsController.LoadData>, + cancelWrapper: Consumer<Runnable> ) { if (!confirmAvailability()) { if (userChanging) { // Try again later, userChanging should not last forever. If so, we have bigger // problems. This will return a runnable that allows to cancel the delayed version, // it will not be able to cancel the load if - loadCanceller = executor.executeDelayed( - { loadForComponent(componentName, dataCallback) }, - USER_CHANGE_RETRY_DELAY, - TimeUnit.MILLISECONDS + executor.executeDelayed( + { loadForComponent(componentName, dataCallback, cancelWrapper) }, + USER_CHANGE_RETRY_DELAY, + TimeUnit.MILLISECONDS ) - } else { - dataCallback.accept(createLoadDataObject(emptyList(), emptyList(), true)) } - return + + dataCallback.accept(createLoadDataObject(emptyList(), emptyList(), true)) } - loadCanceller = bindingController.bindAndLoad( + + cancelWrapper.accept( + bindingController.bindAndLoad( componentName, object : ControlsBindingController.LoadCallback { override fun accept(controls: List<Control>) { - loadCanceller = null executor.execute { val favoritesForComponentKeys = Favorites .getControlsForComponent(componentName).map { it.controlId } @@ -333,7 +332,6 @@ class ControlsControllerImpl @Inject constructor ( } override fun error(message: String) { - loadCanceller = null executor.execute { val controls = Favorites.getStructuresForComponent(componentName) .flatMap { st -> @@ -348,6 +346,7 @@ class ControlsControllerImpl @Inject constructor ( } } } + ) ) } @@ -432,12 +431,6 @@ class ControlsControllerImpl @Inject constructor ( seedingCallbacks.clear() } - override fun cancelLoad() { - loadCanceller?.let { - executor.execute(it) - } - } - private fun createRemovedStatus( componentName: ComponentName, controlInfo: ControlInfo, diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsAnimations.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsAnimations.kt new file mode 100644 index 000000000000..4ca47d1d41ba --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsAnimations.kt @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.controls.management + +import android.animation.Animator +import android.animation.AnimatorListenerAdapter +import android.animation.AnimatorSet +import android.animation.ObjectAnimator +import android.annotation.IdRes +import android.content.Intent + +import android.transition.Transition +import android.transition.TransitionValues +import android.util.Log +import android.view.View +import android.view.ViewGroup +import android.view.Window + +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleObserver +import androidx.lifecycle.OnLifecycleEvent + +import com.android.systemui.Interpolators +import com.android.systemui.R + +import com.android.systemui.controls.ui.ControlsUiController + +object ControlsAnimations { + + private const val ALPHA_EXIT_DURATION = 167L + private const val ALPHA_ENTER_DELAY = ALPHA_EXIT_DURATION + private const val ALPHA_ENTER_DURATION = 350L - ALPHA_ENTER_DELAY + + private const val Y_TRANSLATION_EXIT_DURATION = 183L + private const val Y_TRANSLATION_ENTER_DELAY = Y_TRANSLATION_EXIT_DURATION - ALPHA_ENTER_DELAY + private const val Y_TRANSLATION_ENTER_DURATION = 400L - Y_TRANSLATION_EXIT_DURATION + private var translationY: Float = -1f + + /** + * Setup an activity to handle enter/exit animations. [view] should be the root of the content. + * Fade and translate together. + */ + fun observerForAnimations(view: ViewGroup, window: Window, intent: Intent): LifecycleObserver { + return object : LifecycleObserver { + var showAnimation = intent.getBooleanExtra(ControlsUiController.EXTRA_ANIMATE, false) + + init { + // Must flag the parent group to move it all together, and set the initial + // transitionAlpha to 0.0f. This property is reserved for fade animations. + view.setTransitionGroup(true) + view.transitionAlpha = 0.0f + + if (translationY == -1f) { + translationY = view.context.resources.getDimensionPixelSize( + R.dimen.global_actions_controls_y_translation).toFloat() + } + } + + @OnLifecycleEvent(Lifecycle.Event.ON_START) + fun setup() { + with(window) { + allowEnterTransitionOverlap = true + enterTransition = enterWindowTransition(view.getId()) + exitTransition = exitWindowTransition(view.getId()) + } + } + + @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) + fun enterAnimation() { + if (showAnimation) { + ControlsAnimations.enterAnimation(view).start() + showAnimation = false + } + } + } + } + + fun enterAnimation(view: View): Animator { + Log.d(ControlsUiController.TAG, "Enter animation for $view") + + view.transitionAlpha = 0.0f + view.alpha = 1.0f + + view.translationY = translationY + + val alphaAnimator = ObjectAnimator.ofFloat(view, "transitionAlpha", 0.0f, 1.0f).apply { + interpolator = Interpolators.DECELERATE_QUINT + startDelay = ALPHA_ENTER_DELAY + duration = ALPHA_ENTER_DURATION + } + + val yAnimator = ObjectAnimator.ofFloat(view, "translationY", 0.0f).apply { + interpolator = Interpolators.DECELERATE_QUINT + startDelay = Y_TRANSLATION_ENTER_DURATION + duration = Y_TRANSLATION_ENTER_DURATION + } + + return AnimatorSet().apply { + playTogether(alphaAnimator, yAnimator) + } + } + + /** + * Properly handle animations originating from dialogs. Activity transitions require + * transitioning between two activities, so expose this method for dialogs to animate + * on exit. + */ + @JvmStatic + fun exitAnimation(view: View, onEnd: Runnable? = null): Animator { + Log.d(ControlsUiController.TAG, "Exit animation for $view") + + val alphaAnimator = ObjectAnimator.ofFloat(view, "transitionAlpha", 0.0f).apply { + interpolator = Interpolators.ACCELERATE + duration = ALPHA_EXIT_DURATION + } + + view.translationY = 0.0f + val yAnimator = ObjectAnimator.ofFloat(view, "translationY", -translationY).apply { + interpolator = Interpolators.ACCELERATE + duration = Y_TRANSLATION_EXIT_DURATION + } + + return AnimatorSet().apply { + playTogether(alphaAnimator, yAnimator) + onEnd?.let { + addListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + it.run() + } + }) + } + } + } + + fun enterWindowTransition(@IdRes id: Int) = + WindowTransition({ view: View -> enterAnimation(view) }).apply { + addTarget(id) + } + + fun exitWindowTransition(@IdRes id: Int) = + WindowTransition({ view: View -> exitAnimation(view) }).apply { + addTarget(id) + } +} + +/** + * In order to animate, at least one property must be marked on each view that should move. + * Setting "item" is just a flag to indicate that it should move by the animator. + */ +class WindowTransition( + val animator: (view: View) -> Animator +) : Transition() { + override fun captureStartValues(tv: TransitionValues) { + tv.values["item"] = 0.0f + } + + override fun captureEndValues(tv: TransitionValues) { + tv.values["item"] = 1.0f + } + + override fun createAnimator( + sceneRoot: ViewGroup, + startValues: TransitionValues?, + endValues: TransitionValues? + ): Animator? = animator(startValues!!.view) +} diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt index ee1ce7ab3d83..640c90d2ba59 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt @@ -16,11 +16,12 @@ package com.android.systemui.controls.management -import android.app.Activity +import android.app.ActivityOptions import android.content.ComponentName import android.content.Intent import android.os.Bundle import android.view.View +import android.view.ViewGroup import android.view.ViewStub import android.widget.Button import android.widget.TextView @@ -31,7 +32,9 @@ import com.android.systemui.R import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.controls.controller.ControlsControllerImpl import com.android.systemui.controls.controller.StructureInfo +import com.android.systemui.globalactions.GlobalActionsComponent import com.android.systemui.settings.CurrentUserTracker +import com.android.systemui.util.LifecycleActivity import javax.inject.Inject /** @@ -39,8 +42,9 @@ import javax.inject.Inject */ class ControlsEditingActivity @Inject constructor( private val controller: ControlsControllerImpl, - broadcastDispatcher: BroadcastDispatcher -) : Activity() { + broadcastDispatcher: BroadcastDispatcher, + private val globalActionsComponent: GlobalActionsComponent +) : LifecycleActivity() { companion object { private const val TAG = "ControlsEditingActivity" @@ -84,14 +88,31 @@ class ControlsEditingActivity @Inject constructor( bindViews() bindButtons() + } + override fun onStart() { + super.onStart() setUpList() currentUserTracker.startTracking() } + override fun onStop() { + super.onStop() + currentUserTracker.stopTracking() + } + private fun bindViews() { setContentView(R.layout.controls_management) + + getLifecycle().addObserver( + ControlsAnimations.observerForAnimations( + requireViewById<ViewGroup>(R.id.controls_management_root), + window, + intent + ) + ) + requireViewById<ViewStub>(R.id.stub).apply { layoutResource = R.layout.controls_management_editing inflate() @@ -113,17 +134,26 @@ class ControlsEditingActivity @Inject constructor( putExtras(this@ControlsEditingActivity.intent) putExtra(ControlsFavoritingActivity.EXTRA_SINGLE_STRUCTURE, true) } - startActivity(intent) - finish() + startActivity(intent, ActivityOptions + .makeSceneTransitionAnimation(this@ControlsEditingActivity).toBundle()) } } + val rootView = requireViewById<ViewGroup>(R.id.controls_management_root) saveButton = requireViewById<Button>(R.id.done).apply { isEnabled = false setText(R.string.save) setOnClickListener { saveFavorites() - finishAffinity() + ControlsAnimations.exitAnimation( + rootView, + object : Runnable { + override fun run() { + finish() + } + } + ).start() + globalActionsComponent.handleShowGlobalActionsMenu() } } } @@ -151,26 +181,38 @@ class ControlsEditingActivity @Inject constructor( val controls = controller.getFavoritesForStructure(component, structure) model = FavoritesModel(component, controls, favoritesModelCallback) val elevation = resources.getFloat(R.dimen.control_card_elevation) - val adapter = ControlAdapter(elevation) - val recycler = requireViewById<RecyclerView>(R.id.list) + val recyclerView = requireViewById<RecyclerView>(R.id.list) + recyclerView.alpha = 0.0f + val adapter = ControlAdapter(elevation).apply { + registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { + var hasAnimated = false + override fun onChanged() { + if (!hasAnimated) { + hasAnimated = true + ControlsAnimations.enterAnimation(recyclerView).start() + } + } + }) + } + val margin = resources .getDimensionPixelSize(R.dimen.controls_card_margin) val itemDecorator = MarginItemDecorator(margin, margin) - recycler.apply { + recyclerView.apply { this.adapter = adapter - layoutManager = GridLayoutManager(recycler.context, 2).apply { + layoutManager = GridLayoutManager(recyclerView.context, 2).apply { spanSizeLookup = adapter.spanSizeLookup } addItemDecoration(itemDecorator) } adapter.changeModel(model) model.attachAdapter(adapter) - ItemTouchHelper(model.itemTouchHelperCallback).attachToRecyclerView(recycler) + ItemTouchHelper(model.itemTouchHelperCallback).attachToRecyclerView(recyclerView) } override fun onDestroy() { currentUserTracker.stopTracking() super.onDestroy() } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt index 6f34deeb8547..9a2ccb52132b 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt @@ -16,11 +16,10 @@ package com.android.systemui.controls.management -import android.app.Activity +import android.app.ActivityOptions import android.content.ComponentName import android.content.Intent import android.content.res.Configuration -import android.graphics.drawable.Drawable import android.os.Bundle import android.text.TextUtils import android.view.Gravity @@ -29,7 +28,6 @@ import android.view.ViewGroup import android.view.ViewStub import android.widget.Button import android.widget.FrameLayout -import android.widget.ImageView import android.widget.TextView import androidx.viewpager2.widget.ViewPager2 import com.android.systemui.Prefs @@ -40,7 +38,9 @@ import com.android.systemui.controls.TooltipManager import com.android.systemui.controls.controller.ControlsControllerImpl import com.android.systemui.controls.controller.StructureInfo import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.globalactions.GlobalActionsComponent import com.android.systemui.settings.CurrentUserTracker +import com.android.systemui.util.LifecycleActivity import java.text.Collator import java.util.concurrent.Executor import java.util.function.Consumer @@ -50,8 +50,9 @@ class ControlsFavoritingActivity @Inject constructor( @Main private val executor: Executor, private val controller: ControlsControllerImpl, private val listingController: ControlsListingController, - broadcastDispatcher: BroadcastDispatcher -) : Activity() { + broadcastDispatcher: BroadcastDispatcher, + private val globalActionsComponent: GlobalActionsComponent +) : LifecycleActivity() { companion object { private const val TAG = "ControlsFavoritingActivity" @@ -73,14 +74,15 @@ class ControlsFavoritingActivity @Inject constructor( private lateinit var structurePager: ViewPager2 private lateinit var statusText: TextView private lateinit var titleView: TextView - private lateinit var iconView: ImageView - private lateinit var iconFrame: View private lateinit var pageIndicator: ManagementPageIndicator private var mTooltipManager: TooltipManager? = null private lateinit var doneButton: View + private lateinit var otherAppsButton: View private var listOfStructures = emptyList<StructureContainer>() private lateinit var comparator: Comparator<StructureContainer> + private var cancelLoadRunnable: Runnable? = null + private var isPagerLoaded = false private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) { private val startingUser = controller.currentUserId @@ -94,17 +96,10 @@ class ControlsFavoritingActivity @Inject constructor( } private val listingCallback = object : ControlsListingController.ControlsListingCallback { - private var icon: Drawable? = null override fun onServicesUpdated(serviceInfos: List<ControlsServiceInfo>) { - val newIcon = serviceInfos.firstOrNull { it.componentName == component }?.loadIcon() - if (icon == newIcon) return - icon = newIcon - executor.execute { - if (icon != null) { - iconView.setImageDrawable(icon) - } - iconFrame.visibility = if (icon != null) View.VISIBLE else View.GONE + if (serviceInfos.size > 1) { + otherAppsButton.visibility = View.VISIBLE } } } @@ -115,6 +110,7 @@ class ControlsFavoritingActivity @Inject constructor( override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + val collator = Collator.getInstance(resources.configuration.locales[0]) comparator = compareBy(collator) { it.structureName } appName = intent.getCharSequenceExtra(EXTRA_APP) @@ -122,14 +118,6 @@ class ControlsFavoritingActivity @Inject constructor( component = intent.getParcelableExtra<ComponentName>(Intent.EXTRA_COMPONENT_NAME) bindViews() - - setUpPager() - - loadControls() - - listingController.addCallback(listingCallback) - - currentUserTracker.startTracking() } private val controlsModelCallback = object : ControlsModel.ControlsModelCallback { @@ -174,12 +162,17 @@ class ControlsFavoritingActivity @Inject constructor( pageIndicator.setLocation(0f) pageIndicator.visibility = if (listOfStructures.size > 1) View.VISIBLE else View.GONE + + ControlsAnimations.enterAnimation(pageIndicator).start() + ControlsAnimations.enterAnimation(structurePager).start() } - }) + }, Consumer { runnable -> cancelLoadRunnable = runnable }) } } private fun setUpPager() { + structurePager.alpha = 0.0f + pageIndicator.alpha = 0.0f structurePager.apply { adapter = StructureAdapter(emptyList()) registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { @@ -203,6 +196,15 @@ class ControlsFavoritingActivity @Inject constructor( private fun bindViews() { setContentView(R.layout.controls_management) + + getLifecycle().addObserver( + ControlsAnimations.observerForAnimations( + requireViewById<ViewGroup>(R.id.controls_management_root), + window, + intent + ) + ) + requireViewById<ViewStub>(R.id.stub).apply { layoutResource = R.layout.controls_management_favorites inflate() @@ -259,8 +261,6 @@ class ControlsFavoritingActivity @Inject constructor( } requireViewById<TextView>(R.id.subtitle).text = resources.getText(R.string.controls_favorite_subtitle) - iconView = requireViewById(com.android.internal.R.id.icon) - iconFrame = requireViewById(R.id.icon_frame) structurePager = requireViewById<ViewPager2>(R.id.structure_pager) structurePager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { override fun onPageSelected(position: Int) { @@ -272,16 +272,17 @@ class ControlsFavoritingActivity @Inject constructor( } private fun bindButtons() { - requireViewById<Button>(R.id.other_apps).apply { - visibility = View.VISIBLE + otherAppsButton = requireViewById<Button>(R.id.other_apps).apply { setOnClickListener { val i = Intent() i.setComponent( ComponentName(context, ControlsProviderSelectorActivity::class.java)) - context.startActivity(i) + startActivity(i, ActivityOptions + .makeSceneTransitionAnimation(this@ControlsFavoritingActivity).toBundle()) } } + val rootView = requireViewById<ViewGroup>(R.id.controls_management_root) doneButton = requireViewById<Button>(R.id.done).apply { isEnabled = false setOnClickListener { @@ -292,7 +293,16 @@ class ControlsFavoritingActivity @Inject constructor( StructureInfo(component!!, it.structureName, favoritesForStorage) ) } - finishAffinity() + + ControlsAnimations.exitAnimation( + rootView, + object : Runnable { + override fun run() { + finish() + } + } + ).start() + globalActionsComponent.handleShowGlobalActionsMenu() } } } @@ -302,15 +312,39 @@ class ControlsFavoritingActivity @Inject constructor( mTooltipManager?.hide(false) } + override fun onStart() { + super.onStart() + + listingController.addCallback(listingCallback) + currentUserTracker.startTracking() + } + + override fun onResume() { + super.onResume() + + // only do once, to make sure that any user changes do not get replaces if resume is called + // more than once + if (!isPagerLoaded) { + setUpPager() + loadControls() + isPagerLoaded = true + } + } + + override fun onStop() { + super.onStop() + + listingController.removeCallback(listingCallback) + currentUserTracker.stopTracking() + } + override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) mTooltipManager?.hide(false) } override fun onDestroy() { - currentUserTracker.stopTracking() - listingController.removeCallback(listingCallback) - controller.cancelLoad() + cancelLoadRunnable?.run() super.onDestroy() } diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt index 3be59009f531..00448544d691 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt @@ -16,15 +16,19 @@ package com.android.systemui.controls.management +import android.app.ActivityOptions import android.content.ComponentName import android.content.Intent import android.os.Bundle import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup import android.view.ViewStub import android.widget.Button import android.widget.TextView import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver import com.android.systemui.R import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.controls.controller.ControlsController @@ -66,12 +70,42 @@ class ControlsProviderSelectorActivity @Inject constructor( super.onCreate(savedInstanceState) setContentView(R.layout.controls_management) + + getLifecycle().addObserver( + ControlsAnimations.observerForAnimations( + requireViewById<ViewGroup>(R.id.controls_management_root), + window, + intent + ) + ) + requireViewById<ViewStub>(R.id.stub).apply { layoutResource = R.layout.controls_management_apps inflate() } recyclerView = requireViewById(R.id.list) + recyclerView.layoutManager = LinearLayoutManager(applicationContext) + + requireViewById<TextView>(R.id.title).apply { + text = resources.getText(R.string.controls_providers_title) + } + + requireViewById<Button>(R.id.other_apps).apply { + visibility = View.VISIBLE + setText(com.android.internal.R.string.cancel) + setOnClickListener { + this@ControlsProviderSelectorActivity.finishAffinity() + } + } + requireViewById<View>(R.id.done).visibility = View.GONE + } + + override fun onStart() { + super.onStart() + currentUserTracker.startTracking() + + recyclerView.alpha = 0.0f recyclerView.adapter = AppAdapter( backExecutor, executor, @@ -80,17 +114,22 @@ class ControlsProviderSelectorActivity @Inject constructor( LayoutInflater.from(this), ::launchFavoritingActivity, FavoritesRenderer(resources, controlsController::countFavoritesForComponent), - resources) - recyclerView.layoutManager = LinearLayoutManager(applicationContext) - - requireViewById<TextView>(R.id.title).text = - resources.getText(R.string.controls_providers_title) - - requireViewById<Button>(R.id.done).setOnClickListener { - this@ControlsProviderSelectorActivity.finishAffinity() + resources).apply { + registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { + var hasAnimated = false + override fun onChanged() { + if (!hasAnimated) { + hasAnimated = true + ControlsAnimations.enterAnimation(recyclerView).start() + } + } + }) } + } - currentUserTracker.startTracking() + override fun onStop() { + super.onStop() + currentUserTracker.stopTracking() } /** @@ -98,7 +137,7 @@ class ControlsProviderSelectorActivity @Inject constructor( * @param component a component name for a [ControlsProviderService] */ fun launchFavoritingActivity(component: ComponentName?) { - backExecutor.execute { + executor.execute { component?.let { val intent = Intent(applicationContext, ControlsFavoritingActivity::class.java) .apply { @@ -107,7 +146,7 @@ class ControlsProviderSelectorActivity @Inject constructor( putExtra(Intent.EXTRA_COMPONENT_NAME, it) flags = Intent.FLAG_ACTIVITY_SINGLE_TOP } - startActivity(intent) + startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle()) } } } diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt index 61a1a986c091..aed7cd316bc7 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt @@ -26,9 +26,10 @@ interface ControlsUiController { companion object { public const val TAG = "ControlsUiController" + public const val EXTRA_ANIMATE = "extra_animate" } - fun show(parent: ViewGroup) + fun show(parent: ViewGroup, dismissGlobalActions: Runnable) fun hide() fun onRefreshState(componentName: ComponentName, controls: List<Control>) fun onActionResponse( diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt index 2adfb1bd6d4f..cfd8df059567 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt @@ -85,7 +85,7 @@ class ControlsUiControllerImpl @Inject constructor ( private const val PREF_STRUCTURE = "controls_structure" private const val USE_PANELS = "systemui.controls_use_panel" - private const val FADE_IN_MILLIS = 225L + private const val FADE_IN_MILLIS = 200L private val EMPTY_COMPONENT = ComponentName("", "") private val EMPTY_STRUCTURE = StructureInfo( @@ -104,6 +104,7 @@ class ControlsUiControllerImpl @Inject constructor ( private var popup: ListPopupWindow? = null private var activeDialog: Dialog? = null private var hidden = true + private lateinit var dismissGlobalActions: Runnable override val available: Boolean get() = controlsController.get().available @@ -134,9 +135,10 @@ class ControlsUiControllerImpl @Inject constructor ( } } - override fun show(parent: ViewGroup) { + override fun show(parent: ViewGroup, dismissGlobalActions: Runnable) { Log.d(ControlsUiController.TAG, "show()") this.parent = parent + this.dismissGlobalActions = dismissGlobalActions hidden = false allStructures = controlsController.get().getFavorites() @@ -169,7 +171,7 @@ class ControlsUiControllerImpl @Inject constructor ( fadeAnim.setDuration(FADE_IN_MILLIS) fadeAnim.addListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { - show(parent) + show(parent, dismissGlobalActions) val showAnim = ObjectAnimator.ofFloat(parent, "alpha", 0.0f, 1.0f) showAnim.setInterpolator(DecelerateInterpolator(1.0f)) showAnim.setDuration(FADE_IN_MILLIS) @@ -256,9 +258,10 @@ class ControlsUiControllerImpl @Inject constructor ( } private fun startActivity(context: Context, intent: Intent) { - val closeDialog = Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS) - context.sendBroadcast(closeDialog) + // Force animations when transitioning from a dialog to an activity + intent.putExtra(ControlsUiController.EXTRA_ANIMATE, true) context.startActivity(intent) + dismissGlobalActions.run() } private fun showControlsView(items: List<SelectionItem>) { diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java index 88f96a8b19fe..8c572fe8f842 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java @@ -24,7 +24,6 @@ import android.content.Context; import androidx.annotation.Nullable; import com.android.keyguard.KeyguardViewController; -import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dock.DockManager; import com.android.systemui.dock.DockManagerImpl; import com.android.systemui.plugins.qs.QSFactory; @@ -34,7 +33,6 @@ import com.android.systemui.power.EnhancedEstimatesImpl; import com.android.systemui.qs.tileimpl.QSFactoryImpl; import com.android.systemui.recents.Recents; import com.android.systemui.recents.RecentsImplementation; -import com.android.systemui.settings.CurrentUserContextTracker; import com.android.systemui.stackdivider.DividerModule; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationLockscreenUserManager; @@ -138,15 +136,4 @@ public abstract class SystemUIDefaultModule { @Binds abstract KeyguardViewController bindKeyguardViewController( StatusBarKeyguardViewManager statusBarKeyguardViewManager); - - @Singleton - @Provides - static CurrentUserContextTracker provideCurrentUserContextTracker( - Context context, - BroadcastDispatcher broadcastDispatcher) { - CurrentUserContextTracker tracker = - new CurrentUserContextTracker(context, broadcastDispatcher); - tracker.initialize(); - return tracker; - } } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 2e9ce1200c85..90cd13fd1330 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -29,6 +29,7 @@ import com.android.systemui.log.dagger.LogModule; import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.recents.Recents; +import com.android.systemui.settings.dagger.SettingsModule; import com.android.systemui.stackdivider.Divider; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder; @@ -61,6 +62,7 @@ import dagger.Provides; ConcurrencyModule.class, LogModule.class, PeopleHubModule.class, + SettingsModule.class }, subcomponents = {StatusBarComponent.class, NotificationRowComponent.class, diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index 322660521ee0..a24fede36e4c 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -16,6 +16,8 @@ package com.android.systemui.globalactions; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; +import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_GLOBAL_ACTIONS; +import static android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED; @@ -110,6 +112,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.controls.ControlsServiceInfo; import com.android.systemui.controls.controller.ControlsController; +import com.android.systemui.controls.management.ControlsAnimations; import com.android.systemui.controls.management.ControlsListingController; import com.android.systemui.controls.ui.ControlsUiController; import com.android.systemui.dagger.qualifiers.Background; @@ -126,7 +129,6 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.EmergencyDialerConstants; import com.android.systemui.util.RingerModeTracker; import com.android.systemui.util.leak.RotationUtils; -import com.android.systemui.volume.SystemUIInterpolators.LogAccelerateInterpolator; import java.util.ArrayList; import java.util.List; @@ -479,7 +481,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, */ @VisibleForTesting protected int getMaxShownPowerItems() { - if (shouldUseControlsLayout()) { + // TODO: Overflow disabled on keyguard while we solve for touch blocking issues. + if (shouldUseControlsLayout() && !mKeyguardShowing) { return mResources.getInteger(com.android.systemui.R.integer.power_menu_max_columns); } else { return Integer.MAX_VALUE; @@ -829,7 +832,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mHandler.postDelayed(new Runnable() { @Override public void run() { - mScreenshotHelper.takeScreenshot(1, true, true, mHandler, null); + mScreenshotHelper.takeScreenshot(TAKE_SCREENSHOT_FULLSCREEN, true, true, + SCREENSHOT_GLOBAL_ACTIONS, mHandler, null); mMetricsLogger.action(MetricsEvent.ACTION_SCREENSHOT_POWER_MENU); mUiEventLogger.log(GlobalActionsEvent.GA_SCREENSHOT_PRESS); } @@ -1815,7 +1819,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, case MESSAGE_DISMISS: if (mDialog != null) { if (SYSTEM_DIALOG_REASON_DREAM.equals(msg.obj)) { - mDialog.dismissImmediately(); + mDialog.completeDismiss(); } else { mDialog.dismiss(); } @@ -1894,6 +1898,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, private ControlsUiController mControlsUiController; private ViewGroup mControlsView; + private ViewGroup mContainer; ActionsDialog(Context context, MyAdapter adapter, MyOverflowAdapter overflowAdapter, GlobalActionsPanelPlugin.PanelViewController plugin, @@ -2046,6 +2051,11 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, }); mGlobalActionsLayout.setRotationListener(this::onRotate); mGlobalActionsLayout.setAdapter(mAdapter); + mContainer = findViewById(com.android.systemui.R.id.global_actions_container); + // Some legacy dialog layouts don't have the outer container + if (mContainer == null) { + mContainer = mGlobalActionsLayout; + } mOverflowPopup = createPowerOverflowPopup(); @@ -2172,10 +2182,10 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mHadTopUi = mNotificationShadeWindowController.getForceHasTopUi(); mNotificationShadeWindowController.setForceHasTopUi(true); mBackgroundDrawable.setAlpha(0); - mGlobalActionsLayout.setTranslationX(mGlobalActionsLayout.getAnimationOffsetX()); - mGlobalActionsLayout.setTranslationY(mGlobalActionsLayout.getAnimationOffsetY()); - mGlobalActionsLayout.setAlpha(0); - mGlobalActionsLayout.animate() + mContainer.setTranslationX(mGlobalActionsLayout.getAnimationOffsetX()); + mContainer.setTranslationY(mGlobalActionsLayout.getAnimationOffsetY()); + mContainer.setAlpha(0); + mContainer.animate() .alpha(1) .translationX(0) .translationY(0) @@ -2200,50 +2210,55 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, return WindowInsets.CONSUMED; }); if (mControlsUiController != null) { - mControlsUiController.show(mControlsView); + mControlsUiController.show(mControlsView, this::dismissForControlsActivity); } } @Override public void dismiss() { + dismissWithAnimation(() -> { + mContainer.setTranslationX(0); + mContainer.setTranslationY(0); + mContainer.setAlpha(1); + mContainer.animate() + .alpha(0) + .translationX(mGlobalActionsLayout.getAnimationOffsetX()) + .translationY(mGlobalActionsLayout.getAnimationOffsetY()) + .setDuration(450) + .withEndAction(this::completeDismiss) + .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) + .setUpdateListener(animation -> { + float animatedValue = 1f - animation.getAnimatedFraction(); + int alpha = (int) (animatedValue * mScrimAlpha * 255); + mBackgroundDrawable.setAlpha(alpha); + mDepthController.updateGlobalDialogVisibility(animatedValue, + mGlobalActionsLayout); + }) + .start(); + }); + } + + private void dismissForControlsActivity() { + dismissWithAnimation(() -> { + ViewGroup root = (ViewGroup) mGlobalActionsLayout.getParent(); + ControlsAnimations.exitAnimation(root, this::completeDismiss).start(); + }); + } + + void dismissWithAnimation(Runnable animation) { if (!mShowing) { return; } mShowing = false; - if (mControlsUiController != null) mControlsUiController.hide(); - mGlobalActionsLayout.setTranslationX(0); - mGlobalActionsLayout.setTranslationY(0); - mGlobalActionsLayout.setAlpha(1); - mGlobalActionsLayout.animate() - .alpha(0) - .translationX(mGlobalActionsLayout.getAnimationOffsetX()) - .translationY(mGlobalActionsLayout.getAnimationOffsetY()) - .setDuration(550) - .withEndAction(this::completeDismiss) - .setInterpolator(new LogAccelerateInterpolator()) - .setUpdateListener(animation -> { - float animatedValue = 1f - animation.getAnimatedFraction(); - int alpha = (int) (animatedValue * mScrimAlpha * 255); - mBackgroundDrawable.setAlpha(alpha); - mDepthController.updateGlobalDialogVisibility(animatedValue, - mGlobalActionsLayout); - }) - .start(); - dismissPanel(); - dismissOverflow(); - resetOrientation(); + animation.run(); } - void dismissImmediately() { + private void completeDismiss() { mShowing = false; - if (mControlsUiController != null) mControlsUiController.hide(); + resetOrientation(); dismissPanel(); dismissOverflow(); - resetOrientation(); - completeDismiss(); - } - - private void completeDismiss() { + if (mControlsUiController != null) mControlsUiController.hide(); mNotificationShadeWindowController.setForceHasTopUi(mHadTopUi); mDepthController.updateGlobalDialogVisibility(0, null /* view */); super.dismiss(); @@ -2304,7 +2319,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, initializeLayout(); mGlobalActionsLayout.updateList(); if (mControlsUiController != null) { - mControlsUiController.show(mControlsView); + mControlsUiController.show(mControlsView, this::dismissForControlsActivity); } } @@ -2343,10 +2358,9 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, && mControlsUiController.getAvailable() && !mControlsServiceInfos.isEmpty(); } - // TODO: Remove legacy layout XML and classes. protected boolean shouldUseControlsLayout() { // always use new controls layout return true; } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java index 14e3e9390825..123cf78d74f8 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java @@ -61,6 +61,18 @@ public class LogModule { return buffer; } + /** Provides a logging buffer for all logs related to the data layer of notifications. */ + @Provides + @Singleton + @NotifInteractionLog + public static LogBuffer provideNotifInteractionLogBuffer( + LogcatEchoTracker echoTracker, + DumpManager dumpManager) { + LogBuffer buffer = new LogBuffer("NotifInteractionLog", 50, 10, echoTracker); + buffer.attach(dumpManager); + return buffer; + } + /** Provides a logging buffer for all logs related to Quick Settings. */ @Provides @Singleton diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/NotifInteractionLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/NotifInteractionLog.java new file mode 100644 index 000000000000..20fc6ff445a6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/NotifInteractionLog.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.log.dagger; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import com.android.systemui.log.LogBuffer; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import javax.inject.Qualifier; + +/** + * A {@link LogBuffer} for messages related to the user interacting with notifications (e.g. + * clicking on them). + */ +@Qualifier +@Documented +@Retention(RUNTIME) +public @interface NotifInteractionLog { +} diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java index f25de6a553e8..8dcf52827145 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java @@ -26,14 +26,18 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.ColorStateList; import android.graphics.Bitmap; +import android.graphics.ImageDecoder; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; +import android.graphics.drawable.Icon; import android.graphics.drawable.RippleDrawable; import android.media.MediaDescription; import android.media.MediaMetadata; +import android.media.ThumbnailUtils; import android.media.session.MediaController; import android.media.session.MediaSession; import android.media.session.PlaybackState; +import android.net.Uri; import android.service.media.MediaBrowserService; import android.util.Log; import android.view.LayoutInflater; @@ -59,6 +63,7 @@ import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.qs.QSMediaBrowser; import com.android.systemui.util.Assert; +import java.io.IOException; import java.util.List; import java.util.concurrent.Executor; @@ -99,6 +104,13 @@ public class MediaControlPanel { com.android.internal.R.id.action4 }; + // URI fields to try loading album art from + private static final String[] ART_URIS = { + MediaMetadata.METADATA_KEY_ALBUM_ART_URI, + MediaMetadata.METADATA_KEY_ART_URI, + MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI + }; + private final MediaController.Callback mSessionCallback = new MediaController.Callback() { @Override public void onSessionDestroyed() { @@ -205,14 +217,16 @@ public class MediaControlPanel { * Update the media panel view for the given media session * @param token * @param iconDrawable + * @param largeIcon * @param iconColor * @param bgColor * @param contentIntent * @param appNameString * @param key */ - public void setMediaSession(MediaSession.Token token, Drawable iconDrawable, int iconColor, - int bgColor, PendingIntent contentIntent, String appNameString, String key) { + public void setMediaSession(MediaSession.Token token, Drawable iconDrawable, Icon largeIcon, + int iconColor, int bgColor, PendingIntent contentIntent, String appNameString, + String key) { // Ensure that component names are updated if token has changed if (mToken == null || !mToken.equals(token)) { mToken = token; @@ -303,7 +317,7 @@ public class MediaControlPanel { ImageView albumView = mMediaNotifView.findViewById(R.id.album_art); if (albumView != null) { // Resize art in a background thread - mBackgroundExecutor.execute(() -> processAlbumArt(mediaMetadata, albumView)); + mBackgroundExecutor.execute(() -> processAlbumArt(mediaMetadata, largeIcon, albumView)); } // Song name @@ -396,30 +410,82 @@ public class MediaControlPanel { * @param albumView view to hold the album art */ protected void processAlbumArt(MediaDescription description, ImageView albumView) { - Bitmap albumArt = description.getIconBitmap(); - //TODO check other fields (b/151054111, b/152067055) + Bitmap albumArt = null; + + // First try loading from URI + albumArt = loadBitmapFromUri(description.getIconUri()); + + // Then check bitmap + if (albumArt == null) { + albumArt = description.getIconBitmap(); + } + processAlbumArtInternal(albumArt, albumView); } /** * Process album art for layout * @param metadata media metadata + * @param largeIcon from notification, checked as a fallback if metadata does not have art * @param albumView view to hold the album art */ - private void processAlbumArt(MediaMetadata metadata, ImageView albumView) { - Bitmap albumArt = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART); - //TODO check other fields (b/151054111, b/152067055) + private void processAlbumArt(MediaMetadata metadata, Icon largeIcon, ImageView albumView) { + Bitmap albumArt = null; + + // First look in URI fields + for (String field : ART_URIS) { + String uriString = metadata.getString(field); + if (uriString != null) { + albumArt = loadBitmapFromUri(Uri.parse(uriString)); + if (albumArt != null) { + Log.d(TAG, "loaded art from " + field); + break; + } + } + } + + // Then check bitmap field + if (albumArt == null) { + albumArt = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART); + } + + // Finally try the notification's largeIcon + if (albumArt == null && largeIcon != null) { + albumArt = largeIcon.getBitmap(); + } + processAlbumArtInternal(albumArt, albumView); } - private void processAlbumArtInternal(Bitmap albumArt, ImageView albumView) { - float radius = mContext.getResources().getDimension(R.dimen.qs_media_corner_radius); + /** + * Load a bitmap from a URI + * @param uri + * @return bitmap, or null if couldn't be loaded + */ + private Bitmap loadBitmapFromUri(Uri uri) { + ImageDecoder.Source source = ImageDecoder.createSource(mContext.getContentResolver(), uri); + try { + return ImageDecoder.decodeBitmap(source); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + /** + * Resize and crop the image if provided and update the control view + * @param albumArt Bitmap of art to display, or null to hide view + * @param albumView View that will hold the art + */ + private void processAlbumArtInternal(@Nullable Bitmap albumArt, ImageView albumView) { + // Resize RoundedBitmapDrawable roundedDrawable = null; if (albumArt != null) { + float radius = mContext.getResources().getDimension(R.dimen.qs_media_corner_radius); Bitmap original = albumArt.copy(Bitmap.Config.ARGB_8888, true); int albumSize = (int) mContext.getResources().getDimension( R.dimen.qs_media_album_size); - Bitmap scaled = Bitmap.createScaledBitmap(original, albumSize, albumSize, false); + Bitmap scaled = ThumbnailUtils.extractThumbnail(original, albumSize, albumSize); roundedDrawable = RoundedBitmapDrawableFactory.create(mContext.getResources(), scaled); roundedDrawable.setCornerRadius(radius); } else { @@ -566,8 +632,10 @@ public class MediaControlPanel { public void onError() { Log.d(TAG, "Cannot resume with " + componentName); mServiceComponent = null; - clearControls(); - // remove + if (!hasMediaSession()) { + // If it's not active and we can't resume, remove + removePlayer(); + } } }, componentName); diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java index dba43430b490..f322489b8dc2 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java @@ -53,16 +53,23 @@ public class PipAnimationController { public static final int TRANSITION_DIRECTION_SAME = 1; public static final int TRANSITION_DIRECTION_TO_PIP = 2; public static final int TRANSITION_DIRECTION_TO_FULLSCREEN = 3; + public static final int TRANSITION_DIRECTION_TO_SPLIT_SCREEN = 4; @IntDef(prefix = { "TRANSITION_DIRECTION_" }, value = { TRANSITION_DIRECTION_NONE, TRANSITION_DIRECTION_SAME, TRANSITION_DIRECTION_TO_PIP, - TRANSITION_DIRECTION_TO_FULLSCREEN + TRANSITION_DIRECTION_TO_FULLSCREEN, + TRANSITION_DIRECTION_TO_SPLIT_SCREEN }) @Retention(RetentionPolicy.SOURCE) public @interface TransitionDirection {} + public static boolean isOutPipDirection(@TransitionDirection int direction) { + return direction == TRANSITION_DIRECTION_TO_FULLSCREEN + || direction == TRANSITION_DIRECTION_TO_SPLIT_SCREEN; + } + private final Interpolator mFastOutSlowInInterpolator; private final PipSurfaceTransactionHelper mSurfaceTransactionHelper; @@ -253,14 +260,13 @@ public class PipAnimationController { } boolean shouldApplyCornerRadius() { - return mTransitionDirection != TRANSITION_DIRECTION_TO_FULLSCREEN; + return !isOutPipDirection(mTransitionDirection); } boolean inScaleTransition() { if (mAnimationType != ANIM_TYPE_BOUNDS) return false; final int direction = getTransitionDirection(); - return direction != TRANSITION_DIRECTION_TO_FULLSCREEN - && direction != TRANSITION_DIRECTION_TO_PIP; + return !isOutPipDirection(direction) && direction != TRANSITION_DIRECTION_TO_PIP; } /** diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java index 0125153542c1..9eae3ca232ff 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java @@ -16,7 +16,6 @@ package com.android.systemui.pip; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_ALPHA; @@ -25,6 +24,8 @@ import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTI import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_SAME; import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_TO_FULLSCREEN; import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP; +import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_TO_SPLIT_SCREEN; +import static com.android.systemui.pip.PipAnimationController.isOutPipDirection; import android.annotation.NonNull; import android.annotation.Nullable; @@ -48,6 +49,7 @@ import android.window.WindowOrganizer; import com.android.internal.os.SomeArgs; import com.android.systemui.R; import com.android.systemui.pip.phone.PipUpdateThread; +import com.android.systemui.stackdivider.Divider; import java.util.ArrayList; import java.util.HashMap; @@ -85,6 +87,7 @@ public class PipTaskOrganizer extends TaskOrganizer { private final int mEnterExitAnimationDuration; private final PipSurfaceTransactionHelper mSurfaceTransactionHelper; private final Map<IBinder, Rect> mBoundsToRestore = new HashMap<>(); + private final Divider mSplitDivider; // These callbacks are called on the update thread private final PipAnimationController.PipAnimationCallback mPipAnimationCallback = @@ -189,7 +192,8 @@ public class PipTaskOrganizer extends TaskOrganizer { mSurfaceControlTransactionFactory; public PipTaskOrganizer(Context context, @NonNull PipBoundsHandler boundsHandler, - @NonNull PipSurfaceTransactionHelper surfaceTransactionHelper) { + @NonNull PipSurfaceTransactionHelper surfaceTransactionHelper, + @Nullable Divider divider) { mMainHandler = new Handler(Looper.getMainLooper()); mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks); mPipBoundsHandler = boundsHandler; @@ -198,6 +202,7 @@ public class PipTaskOrganizer extends TaskOrganizer { mSurfaceTransactionHelper = surfaceTransactionHelper; mPipAnimationController = new PipAnimationController(context, surfaceTransactionHelper); mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new; + mSplitDivider = divider; } public Handler getUpdateHandler() { @@ -226,20 +231,21 @@ public class PipTaskOrganizer extends TaskOrganizer { /** * Dismiss PiP, this is done in two phases using {@link WindowContainerTransaction} - * - setActivityWindowingMode to fullscreen at beginning of the transaction. without changing - * the windowing mode of the Task itself. This makes sure the activity render it's fullscreen + * - setActivityWindowingMode to undefined at beginning of the transaction. without changing + * the windowing mode of the Task itself. This makes sure the activity render it's final * configuration while the Task is still in PiP. - * - setWindowingMode to fullscreen at the end of transition + * - setWindowingMode to undefined at the end of transition * @param animationDurationMs duration in millisecond for the exiting PiP transition */ public void dismissPip(int animationDurationMs) { final WindowContainerTransaction wct = new WindowContainerTransaction(); - wct.setActivityWindowingMode(mToken, WINDOWING_MODE_FULLSCREEN); + wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED); WindowOrganizer.applyTransaction(wct); final Rect destinationBounds = mBoundsToRestore.remove(mToken.asBinder()); + final int direction = syncWithSplitScreenBounds(destinationBounds) + ? TRANSITION_DIRECTION_TO_SPLIT_SCREEN : TRANSITION_DIRECTION_TO_FULLSCREEN; scheduleAnimateResizePip(mLastReportedBounds, destinationBounds, - TRANSITION_DIRECTION_TO_FULLSCREEN, animationDurationMs, - null /* updateBoundsCallback */); + direction, animationDurationMs, null /* updateBoundsCallback */); mInPip = false; } @@ -282,6 +288,9 @@ public class PipTaskOrganizer extends TaskOrganizer { */ @Override public void onTaskVanished(ActivityManager.RunningTaskInfo info) { + if (!mInPip) { + return; + } final WindowContainerToken token = info.token; Objects.requireNonNull(token, "Requires valid WindowContainerToken"); if (token.asBinder() != mToken.asBinder()) { @@ -519,14 +528,13 @@ public class PipTaskOrganizer extends TaskOrganizer { mLastReportedBounds.set(destinationBounds); final WindowContainerTransaction wct = new WindowContainerTransaction(); final Rect taskBounds; - if (direction == TRANSITION_DIRECTION_TO_FULLSCREEN) { + if (isOutPipDirection(direction)) { // If we are animating to fullscreen, then we need to reset the override bounds - // on the task to ensure that the task "matches" the parent's bounds, this applies - // also to the final windowing mode, which should be reset to undefined rather than - // fullscreen. - taskBounds = null; - wct.setWindowingMode(mToken, WINDOWING_MODE_UNDEFINED) - .setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED); + // on the task to ensure that the task "matches" the parent's bounds. + taskBounds = (direction == TRANSITION_DIRECTION_TO_FULLSCREEN) + ? null : destinationBounds; + // As for the final windowing mode, simply reset it to undefined. + wct.setWindowingMode(mToken, WINDOWING_MODE_UNDEFINED); } else { taskBounds = destinationBounds; } @@ -578,6 +586,24 @@ public class PipTaskOrganizer extends TaskOrganizer { } /** + * Sync with {@link #mSplitDivider} on destination bounds if PiP is going to split screen. + * + * @param destinationBoundsOut contain the updated destination bounds if applicable + * @return {@code true} if destinationBounds is altered for split screen + */ + private boolean syncWithSplitScreenBounds(Rect destinationBoundsOut) { + if (mSplitDivider == null || !mSplitDivider.inSplitMode()) { + // bail early if system is not in split screen mode + return false; + } + // PiP window will go to split-secondary mode instead of fullscreen, populates the + // split screen bounds here. + destinationBoundsOut.set( + mSplitDivider.getView().getNonMinimizedSplitScreenSecondaryBounds()); + return true; + } + + /** * Callback interface for PiP transitions (both from and to PiP mode) */ public interface PipTransitionCallback { diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java index ba9a30fb554f..78975735ef0f 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java @@ -19,7 +19,7 @@ package com.android.systemui.pip.phone; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; -import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_TO_FULLSCREEN; +import static com.android.systemui.pip.PipAnimationController.isOutPipDirection; import android.annotation.Nullable; import android.app.ActivityManager; @@ -53,6 +53,7 @@ import com.android.systemui.shared.system.InputConsumerController; import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener; import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.shared.system.WindowManagerWrapper; +import com.android.systemui.stackdivider.Divider; import com.android.systemui.util.DeviceConfigProxy; import com.android.systemui.util.FloatingContentCoordinator; import com.android.systemui.wm.DisplayChangeController; @@ -199,7 +200,8 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio DeviceConfigProxy deviceConfig, PipBoundsHandler pipBoundsHandler, PipSnapAlgorithm pipSnapAlgorithm, - PipSurfaceTransactionHelper surfaceTransactionHelper) { + PipSurfaceTransactionHelper surfaceTransactionHelper, + Divider divider) { mContext = context; mActivityManager = ActivityManager.getService(); @@ -214,7 +216,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio final IActivityTaskManager activityTaskManager = ActivityTaskManager.getService(); mPipBoundsHandler = pipBoundsHandler; mPipTaskOrganizer = new PipTaskOrganizer(context, pipBoundsHandler, - surfaceTransactionHelper); + surfaceTransactionHelper, divider); mPipTaskOrganizer.registerPipTransitionCallback(this); mInputConsumerController = InputConsumerController.getPipInputConsumer(); mMediaController = new PipMediaController(context, mActivityManager, broadcastDispatcher); @@ -312,7 +314,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio @Override public void onPipTransitionStarted(ComponentName activity, int direction) { - if (direction == TRANSITION_DIRECTION_TO_FULLSCREEN) { + if (isOutPipDirection(direction)) { // On phones, the expansion animation that happens on pip tap before restoring // to fullscreen makes it so that the bounds received here are the expanded // bounds. We want to restore to the unexpanded bounds when re-entering pip, diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java index 3a2d786cebe4..6c5312d57b2a 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java @@ -57,6 +57,7 @@ import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener; import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.shared.system.WindowManagerWrapper; +import com.android.systemui.stackdivider.Divider; import java.util.ArrayList; import java.util.List; @@ -232,7 +233,8 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio @Inject public PipManager(Context context, BroadcastDispatcher broadcastDispatcher, PipBoundsHandler pipBoundsHandler, - PipSurfaceTransactionHelper surfaceTransactionHelper) { + PipSurfaceTransactionHelper surfaceTransactionHelper, + Divider divider) { if (mInitialized) { return; } @@ -249,7 +251,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio mResizeAnimationDuration = context.getResources() .getInteger(R.integer.config_pipResizeAnimationDuration); mPipTaskOrganizer = new PipTaskOrganizer(mContext, mPipBoundsHandler, - surfaceTransactionHelper); + surfaceTransactionHelper, divider); mPipTaskOrganizer.registerPipTransitionCallback(this); mActivityTaskManager = ActivityTaskManager.getService(); ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java index 0f065661a470..9e574a1fa621 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java @@ -23,6 +23,7 @@ import android.content.Context; import android.content.pm.PackageManager; import android.content.res.ColorStateList; import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; import android.media.MediaDescription; import android.media.session.MediaController; import android.media.session.MediaSession; @@ -115,8 +116,8 @@ public class QSMediaPlayer extends MediaControlPanel { } // Set what we can normally - super.setMediaSession(token, icon, iconColor, bgColor, contentIntent, appName.toString(), - null); + super.setMediaSession(token, icon, null, iconColor, bgColor, contentIntent, + appName.toString(), null); // Then add info from MediaDescription ImageView albumView = mMediaNotifView.findViewById(R.id.album_art); @@ -149,6 +150,7 @@ public class QSMediaPlayer extends MediaControlPanel { * Update media panel view for the given media session * @param token token for this media session * @param icon app notification icon + * @param largeIcon notification's largeIcon, used as a fallback for album art * @param iconColor foreground color (for text, icons) * @param bgColor background color * @param actionsContainer a LinearLayout containing the media action buttons @@ -156,11 +158,12 @@ public class QSMediaPlayer extends MediaControlPanel { * @param appName Application title * @param key original notification's key */ - public void setMediaSession(MediaSession.Token token, Drawable icon, int iconColor, - int bgColor, View actionsContainer, PendingIntent contentIntent, String appName, - String key) { + public void setMediaSession(MediaSession.Token token, Drawable icon, Icon largeIcon, + int iconColor, int bgColor, View actionsContainer, PendingIntent contentIntent, + String appName, String key) { - super.setMediaSession(token, icon, iconColor, bgColor, contentIntent, appName, key); + super.setMediaSession(token, icon, largeIcon, iconColor, bgColor, contentIntent, appName, + key); // Media controls if (actionsContainer != null) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 1eb577852a71..1252008755a7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -32,6 +32,7 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Color; import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; import android.media.MediaDescription; import android.media.session.MediaSession; import android.metrics.LogMaker; @@ -225,14 +226,16 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne * Add or update a player for the associated media session * @param token * @param icon + * @param largeIcon * @param iconColor * @param bgColor * @param actionsContainer * @param notif * @param key */ - public void addMediaSession(MediaSession.Token token, Drawable icon, int iconColor, int bgColor, - View actionsContainer, StatusBarNotification notif, String key) { + public void addMediaSession(MediaSession.Token token, Drawable icon, Icon largeIcon, + int iconColor, int bgColor, View actionsContainer, StatusBarNotification notif, + String key) { if (!useQsMediaPlayer(mContext)) { // Shouldn't happen, but just in case Log.e(TAG, "Tried to add media session without player!"); @@ -296,7 +299,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne Log.d(TAG, "setting player session"); String appName = Notification.Builder.recoverBuilder(getContext(), notif.getNotification()) .loadHeaderAppName(); - player.setMediaSession(token, icon, iconColor, bgColor, actionsContainer, + player.setMediaSession(token, icon, largeIcon, iconColor, bgColor, actionsContainer, notif.getNotification().contentIntent, appName, key); if (mMediaPlayers.size() > 0) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java index 7ba7c5fe499e..89b36da0c834 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java @@ -19,6 +19,7 @@ package com.android.systemui.qs; import android.app.PendingIntent; import android.content.Context; import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; import android.media.session.MediaController; import android.media.session.MediaSession; import android.view.View; @@ -58,6 +59,7 @@ public class QuickQSMediaPlayer extends MediaControlPanel { * Update media panel view for the given media session * @param token token for this media session * @param icon app notification icon + * @param largeIcon notification's largeIcon, used as a fallback for album art * @param iconColor foreground color (for text, icons) * @param bgColor background color * @param actionsContainer a LinearLayout containing the media action buttons @@ -66,8 +68,9 @@ public class QuickQSMediaPlayer extends MediaControlPanel { * @param contentIntent Intent to send when user taps on the view * @param key original notification's key */ - public void setMediaSession(MediaSession.Token token, Drawable icon, int iconColor, int bgColor, - View actionsContainer, int[] actionsToShow, PendingIntent contentIntent, String key) { + public void setMediaSession(MediaSession.Token token, Drawable icon, Icon largeIcon, + int iconColor, int bgColor, View actionsContainer, int[] actionsToShow, + PendingIntent contentIntent, String key) { // Only update if this is a different session and currently playing String oldPackage = ""; if (getController() != null) { @@ -82,7 +85,7 @@ public class QuickQSMediaPlayer extends MediaControlPanel { return; } - super.setMediaSession(token, icon, iconColor, bgColor, contentIntent, null, key); + super.setMediaSession(token, icon, largeIcon, iconColor, bgColor, contentIntent, null, key); LinearLayout parentActionsLayout = (LinearLayout) actionsContainer; int i = 0; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java index 9cee7e7ccba4..1a6a104387ac 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java @@ -18,7 +18,9 @@ package com.android.systemui.qs.tiles; import android.content.Intent; import android.service.quicksettings.Tile; +import android.text.TextUtils; import android.util.Log; +import android.widget.Switch; import com.android.systemui.R; import com.android.systemui.plugins.qs.QSTile; @@ -88,6 +90,10 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState> state.icon = ResourceIcon.get(R.drawable.ic_qs_screenrecord); state.secondaryLabel = mContext.getString(R.string.quick_settings_screen_record_start); } + state.contentDescription = TextUtils.isEmpty(state.secondaryLabel) + ? state.label + : TextUtils.concat(state.label, ", ", state.secondaryLabel); + state.expandedAccessibilityClassName = Switch.class.getName(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 66bc177da81d..fecb7b602012 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -20,6 +20,7 @@ import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; import static android.view.MotionEvent.ACTION_CANCEL; import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_UP; +import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_OVERVIEW; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_INPUT_MONITOR; @@ -380,7 +381,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis public void handleImageAsScreenshot(Bitmap screenImage, Rect locationInScreen, Insets visibleInsets, int taskId) { mScreenshotHelper.provideScreenshot(screenImage, locationInScreen, visibleInsets, - taskId, mHandler, null); + taskId, SCREENSHOT_OVERVIEW, mHandler, null); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java index e1cc0b0c90c2..70454d4d63df 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java @@ -36,6 +36,7 @@ import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Insets; @@ -76,6 +77,7 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.Toast; +import com.android.internal.logging.UiEventLogger; import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.shared.system.ActivityManagerWrapper; @@ -104,7 +106,6 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset */ static class SaveImageInBackgroundData { public Bitmap image; - public Uri imageUri; public Consumer<Uri> finisher; public GlobalScreenshot.ActionsReadyListener mActionsReadyListener; public int errorMsgResId; @@ -112,13 +113,33 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset void clearImage() { image = null; - imageUri = null; + } + } + + /** + * Structure returned by the SaveImageInBackgroundTask + */ + static class SavedImageData { + public Uri uri; + public Notification.Action shareAction; + public Notification.Action editAction; + public Notification.Action deleteAction; + public List<Notification.Action> smartActions; + + /** + * Used to reset the return data on error + */ + public void reset() { + uri = null; + shareAction = null; + editAction = null; + deleteAction = null; + smartActions = null; } } abstract static class ActionsReadyListener { - abstract void onActionsReady(Uri imageUri, List<Notification.Action> smartActions, - List<Notification.Action> actions); + abstract void onActionsReady(SavedImageData imageData); } // These strings are used for communicating the action invoked to @@ -147,6 +168,7 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset private static final int MESSAGE_CORNER_TIMEOUT = 2; private final ScreenshotNotificationsController mNotificationsController; + private final UiEventLogger mUiEventLogger; private final Context mContext; private final WindowManager mWindowManager; @@ -163,9 +185,11 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset private final LinearLayout mActionsView; private final ImageView mBackgroundProtection; private final FrameLayout mDismissButton; + private final ImageView mDismissImage; private Bitmap mScreenBitmap; private Animator mScreenshotAnimation; + private boolean mInDarkMode = false; private float mScreenshotOffsetXPx; private float mScreenshotOffsetYPx; @@ -185,6 +209,7 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset public void handleMessage(Message msg) { switch (msg.what) { case MESSAGE_CORNER_TIMEOUT: + mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_INTERACTION_TIMEOUT); GlobalScreenshot.this.clearScreenshot("timeout"); break; default: @@ -199,9 +224,11 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset @Inject public GlobalScreenshot( Context context, @Main Resources resources, LayoutInflater layoutInflater, - ScreenshotNotificationsController screenshotNotificationsController) { + ScreenshotNotificationsController screenshotNotificationsController, + UiEventLogger uiEventLogger) { mContext = context; mNotificationsController = screenshotNotificationsController; + mUiEventLogger = uiEventLogger; // Inflate the screenshot layout mScreenshotLayout = layoutInflater.inflate(R.layout.global_screenshot, null); @@ -222,7 +249,11 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset mBackgroundProtection = mScreenshotLayout.findViewById( R.id.global_screenshot_actions_background); mDismissButton = mScreenshotLayout.findViewById(R.id.global_screenshot_dismiss_button); - mDismissButton.setOnClickListener(view -> clearScreenshot("dismiss_button")); + mDismissButton.setOnClickListener(view -> { + mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_EXPLICIT_DISMISSAL); + clearScreenshot("dismiss_button"); + }); + mDismissImage = mDismissButton.findViewById(R.id.global_screenshot_dismiss_image); mScreenshotFlash = mScreenshotLayout.findViewById(R.id.global_screenshot_flash); mScreenshotSelectorView = mScreenshotLayout.findViewById(R.id.global_screenshot_selector); @@ -329,6 +360,8 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset mScreenBitmap.setHasAlpha(false); mScreenBitmap.prepareToDraw(); + updateDarkTheme(); + mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams); mScreenshotLayout.getViewTreeObserver().addOnComputeInternalInsetsListener(this); @@ -407,7 +440,7 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset * Clears current screenshot */ private void clearScreenshot(String reason) { - Log.e(TAG, "clearing screenshot: " + reason); + Log.v(TAG, "clearing screenshot: " + reason); if (mScreenshotLayout.isAttachedToWindow()) { mWindowManager.removeView(mScreenshotLayout); } @@ -427,6 +460,41 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset } /** + * Update assets (called when the dark theme status changes). We only need to update the dismiss + * button and the actions container background, since the buttons are re-inflated on demand. + */ + private void reloadAssets() { + mDismissImage.setImageDrawable(mContext.getDrawable(R.drawable.screenshot_cancel)); + mActionsContainer.setBackground( + mContext.getDrawable(R.drawable.action_chip_container_background)); + + } + + /** + * Checks the current dark theme status and updates if it has changed. + */ + private void updateDarkTheme() { + int currentNightMode = mContext.getResources().getConfiguration().uiMode + & Configuration.UI_MODE_NIGHT_MASK; + switch (currentNightMode) { + case Configuration.UI_MODE_NIGHT_NO: + // Night mode is not active, we're using the light theme + if (mInDarkMode) { + mInDarkMode = false; + reloadAssets(); + } + break; + case Configuration.UI_MODE_NIGHT_YES: + // Night mode is active, we're using dark theme + if (!mInDarkMode) { + mInDarkMode = true; + reloadAssets(); + } + break; + } + } + + /** * Starts the animation after taking the screenshot */ private void startAnimation(final Consumer<Uri> finisher, int w, int h, @@ -445,12 +513,14 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() { @Override - void onActionsReady(Uri uri, List<Notification.Action> smartActions, - List<Notification.Action> actions) { - if (uri == null) { + void onActionsReady(SavedImageData imageData) { + finisher.accept(imageData.uri); + if (imageData.uri == null) { + mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED); mNotificationsController.notifyScreenshotError( R.string.screenshot_failed_to_capture_text); } else { + mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED); mScreenshotHandler.post(() -> { if (mScreenshotAnimation != null && mScreenshotAnimation.isRunning()) { mScreenshotAnimation.addListener( @@ -458,13 +528,12 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); - createScreenshotActionsShadeAnimation( - smartActions, actions).start(); + createScreenshotActionsShadeAnimation(imageData) + .start(); } }); } else { - createScreenshotActionsShadeAnimation(smartActions, - actions).start(); + createScreenshotActionsShadeAnimation(imageData).start(); } }); } @@ -567,8 +636,7 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset return dropInAnimation; } - private ValueAnimator createScreenshotActionsShadeAnimation( - List<Notification.Action> smartActions, List<Notification.Action> actions) { + private ValueAnimator createScreenshotActionsShadeAnimation(SavedImageData imageData) { LayoutInflater inflater = LayoutInflater.from(mContext); mActionsView.removeAllViews(); mActionsContainer.setScrollX(0); @@ -583,45 +651,63 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset } catch (RemoteException e) { } - for (Notification.Action smartAction : smartActions) { + for (Notification.Action smartAction : imageData.smartActions) { ScreenshotActionChip actionChip = (ScreenshotActionChip) inflater.inflate( R.layout.global_screenshot_action_chip, mActionsView, false); actionChip.setText(smartAction.title); actionChip.setIcon(smartAction.getIcon(), false); actionChip.setPendingIntent(smartAction.actionIntent, - () -> clearScreenshot("chip tapped")); + () -> { + mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SMART_ACTION_TAPPED); + clearScreenshot("chip tapped"); + }); mActionsView.addView(actionChip); } - for (Notification.Action action : actions) { - ScreenshotActionChip actionChip = (ScreenshotActionChip) inflater.inflate( - R.layout.global_screenshot_action_chip, mActionsView, false); - actionChip.setText(action.title); - actionChip.setIcon(action.getIcon(), true); - actionChip.setPendingIntent(action.actionIntent, () -> clearScreenshot("chip tapped")); - if (action.actionIntent.getIntent().getAction().equals(Intent.ACTION_EDIT)) { - mScreenshotView.setOnClickListener(v -> { - try { - action.actionIntent.send(); - clearScreenshot("screenshot preview tapped"); - } catch (PendingIntent.CanceledException e) { - Log.e(TAG, "Intent cancelled", e); - } - }); - mScreenshotView.setContentDescription(action.title); + ScreenshotActionChip shareChip = (ScreenshotActionChip) inflater.inflate( + R.layout.global_screenshot_action_chip, mActionsView, false); + shareChip.setText(imageData.shareAction.title); + shareChip.setIcon(imageData.shareAction.getIcon(), true); + shareChip.setPendingIntent(imageData.shareAction.actionIntent, () -> { + mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SHARE_TAPPED); + clearScreenshot("chip tapped"); + }); + mActionsView.addView(shareChip); + + ScreenshotActionChip editChip = (ScreenshotActionChip) inflater.inflate( + R.layout.global_screenshot_action_chip, mActionsView, false); + editChip.setText(imageData.editAction.title); + editChip.setIcon(imageData.editAction.getIcon(), true); + editChip.setPendingIntent(imageData.editAction.actionIntent, () -> { + mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_EDIT_TAPPED); + clearScreenshot("chip tapped"); + }); + mActionsView.addView(editChip); + + mScreenshotView.setOnClickListener(v -> { + try { + imageData.editAction.actionIntent.send(); + mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_PREVIEW_TAPPED); + clearScreenshot("screenshot preview tapped"); + } catch (PendingIntent.CanceledException e) { + Log.e(TAG, "Intent cancelled", e); } - mActionsView.addView(actionChip); - } + }); + mScreenshotView.setContentDescription(imageData.editAction.title); + if (DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI, SCREENSHOT_SCROLLING_ENABLED, false)) { ScreenshotActionChip scrollChip = (ScreenshotActionChip) inflater.inflate( R.layout.global_screenshot_action_chip, mActionsView, false); Toast scrollNotImplemented = Toast.makeText( mContext, "Not implemented", Toast.LENGTH_SHORT); - scrollChip.setText("Extend"); // TODO (mkephart): add resource and translate + scrollChip.setText("Extend"); // TODO: add resource and translate scrollChip.setIcon( Icon.createWithResource(mContext, R.drawable.ic_arrow_downward), true); - scrollChip.setOnClickListener(v -> scrollNotImplemented.show()); + scrollChip.setOnClickListener(v -> { + mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SCROLL_TAPPED); + scrollNotImplemented.show(); + }); mActionsView.addView(scrollChip); } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java index f3614ffbdb1b..095c32f4a2ce 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshotLegacy.java @@ -24,7 +24,6 @@ import android.animation.AnimatorSet; import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; import android.annotation.Nullable; -import android.app.Notification; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; @@ -53,7 +52,6 @@ import android.widget.Toast; import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Main; -import java.util.List; import java.util.function.Consumer; import javax.inject.Inject; @@ -347,14 +345,13 @@ public class GlobalScreenshotLegacy { // Save the screenshot once we have a bit of time now saveScreenshotInWorkerThread(finisher, new GlobalScreenshot.ActionsReadyListener() { @Override - void onActionsReady(Uri uri, List<Notification.Action> smartActions, - List<Notification.Action> actions) { - if (uri == null) { + void onActionsReady(GlobalScreenshot.SavedImageData actionData) { + if (actionData.uri == null) { mNotificationsController.notifyScreenshotError( R.string.screenshot_failed_to_capture_text); } else { mNotificationsController - .showScreenshotActionsNotification(uri, smartActions, actions); + .showScreenshotActionsNotification(actionData); } } }); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java index c828c4cccce5..170174deaeb3 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java @@ -83,6 +83,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { private final Context mContext; private final GlobalScreenshot.SaveImageInBackgroundData mParams; + private final GlobalScreenshot.SavedImageData mImageData; private final String mImageFileName; private final long mImageTime; private final ScreenshotNotificationSmartActionsProvider mSmartActionsProvider; @@ -93,6 +94,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { SaveImageInBackgroundTask(Context context, GlobalScreenshot.SaveImageInBackgroundData data) { mContext = context; + mImageData = new GlobalScreenshot.SavedImageData(); // Prepare all the output metadata mParams = data; @@ -145,6 +147,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { values.put(MediaColumns.IS_PENDING, 1); final Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); + try { // First, write the actual data for our screenshot try (OutputStream out = resolver.openOutputStream(uri)) { @@ -192,8 +195,6 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { throw e; } - List<Notification.Action> actions = - populateNotificationActions(mContext, r, uri); List<Notification.Action> smartActions = new ArrayList<>(); if (mSmartActionsEnabled) { int timeoutMs = DeviceConfig.getInt( @@ -206,8 +207,14 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { mSmartActionsProvider), mContext)); } - mParams.mActionsReadyListener.onActionsReady(uri, smartActions, actions); - mParams.imageUri = uri; + + mImageData.uri = uri; + mImageData.smartActions = smartActions; + mImageData.shareAction = createShareAction(mContext, mContext.getResources(), uri); + mImageData.editAction = createEditAction(mContext, mContext.getResources(), uri); + mImageData.deleteAction = createDeleteAction(mContext, mContext.getResources(), uri); + + mParams.mActionsReadyListener.onActionsReady(mImageData); mParams.image = null; mParams.errorMsgResId = 0; } catch (Exception e) { @@ -216,29 +223,26 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { Slog.e(TAG, "unable to save screenshot", e); mParams.clearImage(); mParams.errorMsgResId = R.string.screenshot_failed_to_save_text; - mParams.mActionsReadyListener.onActionsReady(null, null, null); + mImageData.reset(); + mParams.mActionsReadyListener.onActionsReady(mImageData); } return null; } @Override - protected void onPostExecute(Void params) { - mParams.finisher.accept(mParams.imageUri); - } - - @Override protected void onCancelled(Void params) { // If we are cancelled while the task is running in the background, we may get null // params. The finisher is expected to always be called back, so just use the baked-in // params from the ctor in any case. - mParams.mActionsReadyListener.onActionsReady(null, null, null); + mImageData.reset(); + mParams.mActionsReadyListener.onActionsReady(mImageData); mParams.finisher.accept(null); mParams.clearImage(); } @VisibleForTesting - List<Notification.Action> populateNotificationActions(Context context, Resources r, Uri uri) { + Notification.Action createShareAction(Context context, Resources r, Uri uri) { // Note: Both the share and edit actions are proxied through ActionProxyReceiver in // order to do some common work like dismissing the keyguard and sending // closeSystemWindows @@ -263,8 +267,6 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { // by setting the (otherwise unused) request code to the current user id. int requestCode = context.getUserId(); - ArrayList<Notification.Action> actions = new ArrayList<>(); - PendingIntent chooserAction = PendingIntent.getBroadcast(context, requestCode, new Intent(context, GlobalScreenshot.TargetChosenReceiver.class), PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT); @@ -288,7 +290,15 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { Notification.Action.Builder shareActionBuilder = new Notification.Action.Builder( Icon.createWithResource(r, R.drawable.ic_screenshot_share), r.getString(com.android.internal.R.string.share), shareAction); - actions.add(shareActionBuilder.build()); + + return shareActionBuilder.build(); + } + + @VisibleForTesting + Notification.Action createEditAction(Context context, Resources r, Uri uri) { + // Note: Both the share and edit actions are proxied through ActionProxyReceiver in + // order to do some common work like dismissing the keyguard and sending + // closeSystemWindows // Create an edit intent, if a specific package is provided as the editor, then // launch that directly @@ -302,6 +312,10 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); editIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + // Make sure pending intents for the system user are still unique across users + // by setting the (otherwise unused) request code to the current user id. + int requestCode = mContext.getUserId(); + // Create a edit action PendingIntent editAction = PendingIntent.getBroadcastAsUser(context, requestCode, new Intent(context, GlobalScreenshot.ActionProxyReceiver.class) @@ -317,24 +331,30 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { Notification.Action.Builder editActionBuilder = new Notification.Action.Builder( Icon.createWithResource(r, R.drawable.ic_screenshot_edit), r.getString(com.android.internal.R.string.screenshot_edit), editAction); - actions.add(editActionBuilder.build()); - - if (mCreateDeleteAction) { - // Create a delete action for the notification - PendingIntent deleteAction = PendingIntent.getBroadcast(context, requestCode, - new Intent(context, GlobalScreenshot.DeleteScreenshotReceiver.class) - .putExtra(GlobalScreenshot.SCREENSHOT_URI_ID, uri.toString()) - .putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId) - .putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED, - mSmartActionsEnabled) - .addFlags(Intent.FLAG_RECEIVER_FOREGROUND), - PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT); - Notification.Action.Builder deleteActionBuilder = new Notification.Action.Builder( - Icon.createWithResource(r, R.drawable.ic_screenshot_delete), - r.getString(com.android.internal.R.string.delete), deleteAction); - actions.add(deleteActionBuilder.build()); - } - return actions; + + return editActionBuilder.build(); + } + + @VisibleForTesting + Notification.Action createDeleteAction(Context context, Resources r, Uri uri) { + // Make sure pending intents for the system user are still unique across users + // by setting the (otherwise unused) request code to the current user id. + int requestCode = mContext.getUserId(); + + // Create a delete action for the notification + PendingIntent deleteAction = PendingIntent.getBroadcast(context, requestCode, + new Intent(context, GlobalScreenshot.DeleteScreenshotReceiver.class) + .putExtra(GlobalScreenshot.SCREENSHOT_URI_ID, uri.toString()) + .putExtra(GlobalScreenshot.EXTRA_ID, mScreenshotId) + .putExtra(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED, + mSmartActionsEnabled) + .addFlags(Intent.FLAG_RECEIVER_FOREGROUND), + PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT); + Notification.Action.Builder deleteActionBuilder = new Notification.Action.Builder( + Icon.createWithResource(r, R.drawable.ic_screenshot_delete), + r.getString(com.android.internal.R.string.delete), deleteAction); + + return deleteActionBuilder.build(); } private int getUserHandleOfForegroundApplication(Context context) { diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java new file mode 100644 index 000000000000..20fa991dcc1f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.screenshot; + +import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_ACCESSIBILITY_ACTIONS; +import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_GLOBAL_ACTIONS; +import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD; +import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_OTHER; +import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_OTHER; +import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_OVERVIEW; + +import com.android.internal.logging.UiEvent; +import com.android.internal.logging.UiEventLogger; + +public enum ScreenshotEvent implements UiEventLogger.UiEventEnum { + @UiEvent(doc = "screenshot requested from global actions") + SCREENSHOT_REQUESTED_GLOBAL_ACTIONS(302), + @UiEvent(doc = "screenshot requested from key chord") + SCREENSHOT_REQUESTED_KEY_CHORD(303), + @UiEvent(doc = "screenshot requested from other key press (e.g. ctrl-s)") + SCREENSHOT_REQUESTED_KEY_OTHER(384), + @UiEvent(doc = "screenshot requested from overview") + SCREENSHOT_REQUESTED_OVERVIEW(304), + @UiEvent(doc = "screenshot requested from accessibility actions") + SCREENSHOT_REQUESTED_ACCESSIBILITY_ACTIONS(382), + @UiEvent(doc = "screenshot requested (other)") + SCREENSHOT_REQUESTED_OTHER(305), + @UiEvent(doc = "screenshot was saved") + SCREENSHOT_SAVED(306), + @UiEvent(doc = "screenshot failed to save") + SCREENSHOT_NOT_SAVED(336), + @UiEvent(doc = "screenshot preview tapped") + SCREENSHOT_PREVIEW_TAPPED(307), + @UiEvent(doc = "screenshot edit button tapped") + SCREENSHOT_EDIT_TAPPED(308), + @UiEvent(doc = "screenshot share button tapped") + SCREENSHOT_SHARE_TAPPED(309), + @UiEvent(doc = "screenshot smart action chip tapped") + SCREENSHOT_SMART_ACTION_TAPPED(374), + @UiEvent(doc = "screenshot scroll tapped") + SCREENSHOT_SCROLL_TAPPED(373), + @UiEvent(doc = "screenshot interaction timed out") + SCREENSHOT_INTERACTION_TIMEOUT(310), + @UiEvent(doc = "screenshot explicitly dismissed") + SCREENSHOT_EXPLICIT_DISMISSAL(311); + + private final int mId; + + ScreenshotEvent(int id) { + mId = id; + } + + @Override + public int getId() { + return mId; + } + + static ScreenshotEvent getScreenshotSource(int source) { + switch (source) { + case SCREENSHOT_GLOBAL_ACTIONS: + return ScreenshotEvent.SCREENSHOT_REQUESTED_GLOBAL_ACTIONS; + case SCREENSHOT_KEY_CHORD: + return ScreenshotEvent.SCREENSHOT_REQUESTED_KEY_CHORD; + case SCREENSHOT_KEY_OTHER: + return ScreenshotEvent.SCREENSHOT_REQUESTED_KEY_OTHER; + case SCREENSHOT_OVERVIEW: + return ScreenshotEvent.SCREENSHOT_REQUESTED_OVERVIEW; + case SCREENSHOT_ACCESSIBILITY_ACTIONS: + return ScreenshotEvent.SCREENSHOT_REQUESTED_ACCESSIBILITY_ACTIONS; + case SCREENSHOT_OTHER: + default: + return ScreenshotEvent.SCREENSHOT_REQUESTED_OTHER; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java index 811a8d936b77..fbcd6ba0ff47 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java @@ -32,7 +32,6 @@ import android.graphics.ColorMatrixColorFilter; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Picture; -import android.net.Uri; import android.os.UserHandle; import android.util.DisplayMetrics; import android.view.WindowManager; @@ -42,8 +41,6 @@ import com.android.systemui.R; import com.android.systemui.SystemUI; import com.android.systemui.util.NotificationChannels; -import java.util.List; - import javax.inject.Inject; /** @@ -185,23 +182,20 @@ public class ScreenshotNotificationsController { /** * Shows a notification with the saved screenshot and actions that can be taken with it. * - * @param imageUri URI for the saved image - * @param actions a list of notification actions which can be taken + * @param actionData SavedImageData struct with image URI and actions */ public void showScreenshotActionsNotification( - Uri imageUri, - List<Notification.Action> smartActions, - List<Notification.Action> actions) { - for (Notification.Action action : actions) { - mNotificationBuilder.addAction(action); - } - for (Notification.Action smartAction : smartActions) { + GlobalScreenshot.SavedImageData actionData) { + mNotificationBuilder.addAction(actionData.shareAction); + mNotificationBuilder.addAction(actionData.editAction); + mNotificationBuilder.addAction(actionData.deleteAction); + for (Notification.Action smartAction : actionData.smartActions) { mNotificationBuilder.addAction(smartAction); } // Create the intent to show the screenshot in gallery Intent launchIntent = new Intent(Intent.ACTION_VIEW); - launchIntent.setDataAndType(imageUri, "image/png"); + launchIntent.setDataAndType(actionData.uri, "image/png"); launchIntent.setFlags( Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java index 8b8b6f8071e1..f68cb745e4c8 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java @@ -32,6 +32,9 @@ import android.os.UserManager; import android.util.Log; import android.view.WindowManager; +import com.android.internal.logging.UiEventLogger; +import com.android.internal.util.ScreenshotHelper; + import java.util.function.Consumer; import javax.inject.Inject; @@ -42,6 +45,7 @@ public class TakeScreenshotService extends Service { private final GlobalScreenshot mScreenshot; private final GlobalScreenshotLegacy mScreenshotLegacy; private final UserManager mUserManager; + private final UiEventLogger mUiEventLogger; private Handler mHandler = new Handler(Looper.myLooper()) { @Override @@ -64,14 +68,22 @@ public class TakeScreenshotService extends Service { return; } - // TODO (mkephart): clean up once notifications flow is fully deprecated + // TODO: clean up once notifications flow is fully deprecated boolean useCornerFlow = true; + + ScreenshotHelper.ScreenshotRequest screenshotRequest = + (ScreenshotHelper.ScreenshotRequest) msg.obj; + + mUiEventLogger.log(ScreenshotEvent.getScreenshotSource(screenshotRequest.getSource())); + switch (msg.what) { case WindowManager.TAKE_SCREENSHOT_FULLSCREEN: if (useCornerFlow) { mScreenshot.takeScreenshot(finisher); } else { - mScreenshotLegacy.takeScreenshot(finisher, msg.arg1 > 0, msg.arg2 > 0); + mScreenshotLegacy.takeScreenshot( + finisher, screenshotRequest.getHasStatusBar(), + screenshotRequest.getHasNavBar()); } break; case WindowManager.TAKE_SCREENSHOT_SELECTED_REGION: @@ -79,17 +91,15 @@ public class TakeScreenshotService extends Service { mScreenshot.takeScreenshotPartial(finisher); } else { mScreenshotLegacy.takeScreenshotPartial( - finisher, msg.arg1 > 0, msg.arg2 > 0); + finisher, screenshotRequest.getHasStatusBar(), + screenshotRequest.getHasNavBar()); } break; case WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE: - Bitmap screenshot = msg.getData().getParcelable( - WindowManager.PARCEL_KEY_SCREENSHOT_BITMAP); - Rect screenBounds = msg.getData().getParcelable( - WindowManager.PARCEL_KEY_SCREENSHOT_BOUNDS); - Insets insets = msg.getData().getParcelable( - WindowManager.PARCEL_KEY_SCREENSHOT_INSETS); - int taskId = msg.getData().getInt(WindowManager.PARCEL_KEY_SCREENSHOT_TASK_ID); + Bitmap screenshot = screenshotRequest.getBitmap(); + Rect screenBounds = screenshotRequest.getBoundsInScreen(); + Insets insets = screenshotRequest.getInsets(); + int taskId = screenshotRequest.getTaskId(); if (useCornerFlow) { mScreenshot.handleImageAsScreenshot( screenshot, screenBounds, insets, taskId, finisher); @@ -106,10 +116,12 @@ public class TakeScreenshotService extends Service { @Inject public TakeScreenshotService(GlobalScreenshot globalScreenshot, - GlobalScreenshotLegacy globalScreenshotLegacy, UserManager userManager) { + GlobalScreenshotLegacy globalScreenshotLegacy, UserManager userManager, + UiEventLogger uiEventLogger) { mScreenshot = globalScreenshot; mScreenshotLegacy = globalScreenshotLegacy; mUserManager = userManager; + mUiEventLogger = uiEventLogger; } @Override @@ -120,7 +132,7 @@ public class TakeScreenshotService extends Service { @Override public boolean onUnbind(Intent intent) { if (mScreenshot != null) mScreenshot.stopScreenshot(); - // TODO (mkephart) remove once notifications flow is fully deprecated + // TODO remove once notifications flow is fully deprecated if (mScreenshotLegacy != null) mScreenshotLegacy.stopScreenshot(); return true; } diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContextTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContextTracker.kt index 4de978c77128..825a7f3dbadb 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContextTracker.kt +++ b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContextTracker.kt @@ -22,14 +22,13 @@ import androidx.annotation.VisibleForTesting import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.util.Assert import java.lang.IllegalStateException -import javax.inject.Inject -import javax.inject.Singleton /** * Tracks a reference to the context for the current user + * + * Constructor is injected at SettingsModule */ -@Singleton -class CurrentUserContextTracker @Inject constructor( +class CurrentUserContextTracker internal constructor( private val sysuiContext: Context, broadcastDispatcher: BroadcastDispatcher ) { diff --git a/packages/SystemUI/src/com/android/systemui/settings/dagger/SettingsModule.java b/packages/SystemUI/src/com/android/systemui/settings/dagger/SettingsModule.java new file mode 100644 index 000000000000..2c5c3ceb6e66 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/settings/dagger/SettingsModule.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.settings.dagger; + +import android.content.Context; + +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.settings.CurrentUserContextTracker; + +import javax.inject.Singleton; + +import dagger.Module; +import dagger.Provides; + +/** + * Dagger Module for classes found within the com.android.systemui.settings package. + */ +@Module +public interface SettingsModule { + + /** + * Provides and initializes a CurrentUserContextTracker + */ + @Singleton + @Provides + static CurrentUserContextTracker provideCurrentUserContextTracker( + Context context, + BroadcastDispatcher broadcastDispatcher) { + CurrentUserContextTracker tracker = + new CurrentUserContextTracker(context, broadcastDispatcher); + tracker.initialize(); + return tracker; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java index a4b1310687aa..f5d6cb6fd4b0 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java @@ -33,10 +33,8 @@ import android.view.SurfaceControl; import android.view.SurfaceSession; import android.window.TaskOrganizer; -import java.util.ArrayList; - class SplitScreenTaskOrganizer extends TaskOrganizer { - private static final String TAG = "SplitScreenTaskOrganizer"; + private static final String TAG = "SplitScreenTaskOrg"; private static final boolean DEBUG = Divider.DEBUG; RunningTaskInfo mPrimary; @@ -45,7 +43,6 @@ class SplitScreenTaskOrganizer extends TaskOrganizer { SurfaceControl mSecondarySurface; SurfaceControl mPrimaryDim; SurfaceControl mSecondaryDim; - ArrayList<SurfaceControl> mHomeAndRecentsSurfaces = new ArrayList<>(); Rect mHomeBounds = new Rect(); final Divider mDivider; private boolean mSplitScreenSupported = false; @@ -110,6 +107,15 @@ class SplitScreenTaskOrganizer extends TaskOrganizer { * presentations based on the contents of the split regions. */ private void handleTaskInfoChanged(RunningTaskInfo info) { + if (!mSplitScreenSupported) { + // This shouldn't happen; but apparently there is a chance that SysUI crashes without + // system server receiving binder-death (or maybe it receives binder-death too late?). + // In this situation, when sys-ui restarts, the split root-tasks will still exist so + // there is a small window of time during init() where WM might send messages here + // before init() fails. So, avoid a cycle of crashes by returning early. + Log.e(TAG, "Got handleTaskInfoChanged when not initialized: " + info); + return; + } final boolean secondaryWasHomeOrRecents = mSecondary.topActivityType == ACTIVITY_TYPE_HOME || mSecondary.topActivityType == ACTIVITY_TYPE_RECENTS; final boolean primaryWasEmpty = mPrimary.topActivityType == ACTIVITY_TYPE_UNDEFINED; diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java index 5aa7946bcb7f..3027bd225216 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java @@ -174,12 +174,8 @@ public class WindowManagerProxy { if (rootTasks.isEmpty()) { return false; } - tiles.mHomeAndRecentsSurfaces.clear(); for (int i = rootTasks.size() - 1; i >= 0; --i) { final ActivityManager.RunningTaskInfo rootTask = rootTasks.get(i); - if (isHomeOrRecentTask(rootTask)) { - tiles.mHomeAndRecentsSurfaces.add(rootTask.token.getLeash()); - } // Only move resizeable task to split secondary. WM will just ignore this anyways... if (!rootTask.isResizable()) continue; // Only move fullscreen tasks to split secondary. @@ -211,7 +207,6 @@ public class WindowManagerProxy { // Set launch root first so that any task created after getChildContainers and // before reparent (pretty unlikely) are put into fullscreen. TaskOrganizer.setLaunchRoot(Display.DEFAULT_DISPLAY, null); - tiles.mHomeAndRecentsSurfaces.clear(); // TODO(task-org): Once task-org is more complete, consider using Appeared/Vanished // plus specific APIs to clean this up. List<ActivityManager.RunningTaskInfo> primaryChildren = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 11b54ad19636..43b47232d27d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -311,7 +311,7 @@ public class KeyguardIndicationController implements StateListener, mTextView.switchIndication(mTransientIndication); } else if (!TextUtils.isEmpty(mAlignmentIndication)) { mTextView.switchIndication(mAlignmentIndication); - mTextView.setTextColor(Utils.getColorError(mContext)); + mTextView.setTextColor(mContext.getColor(R.color.misalignment_text_color)); } else if (mPowerPluggedIn) { String indication = computePowerIndication(); if (animate) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java index 1b7524521d76..8c24c540e743 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java @@ -44,6 +44,7 @@ import com.android.systemui.statusbar.phone.BarTransitions.TransitionMode; import com.android.systemui.statusbar.phone.LightBarController; import com.android.systemui.statusbar.phone.NavigationBarFragment; import com.android.systemui.statusbar.phone.NavigationBarView; +import com.android.systemui.statusbar.phone.NavigationModeController; import com.android.systemui.statusbar.policy.BatteryController; import javax.inject.Inject; @@ -139,7 +140,8 @@ public class NavigationBarController implements Callbacks { ? Dependency.get(LightBarController.class) : new LightBarController(context, Dependency.get(DarkIconDispatcher.class), - Dependency.get(BatteryController.class)); + Dependency.get(BatteryController.class), + Dependency.get(NavigationModeController.class)); navBar.setLightBarController(lightBarController); // TODO(b/118592525): to support multi-display, we start to add something which is diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java index afb50027f03d..02c41e5aaefe 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java @@ -35,6 +35,7 @@ import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.inflation.LowPriorityInflationHelper; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; @@ -71,6 +72,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle protected final VisualStabilityManager mVisualStabilityManager; private final SysuiStatusBarStateController mStatusBarStateController; private final NotificationEntryManager mEntryManager; + private final LowPriorityInflationHelper mLowPriorityInflationHelper; /** * {@code true} if notifications not part of a group should by default be rendered in their @@ -108,7 +110,8 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle BubbleController bubbleController, DynamicPrivacyController privacyController, ForegroundServiceSectionController fgsSectionController, - DynamicChildBindController dynamicChildBindController) { + DynamicChildBindController dynamicChildBindController, + LowPriorityInflationHelper lowPriorityInflationHelper) { mContext = context; mHandler = mainHandler; mLockscreenUserManager = notificationLockscreenUserManager; @@ -124,6 +127,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle mBubbleController = bubbleController; mDynamicPrivacyController = privacyController; mDynamicChildBindController = dynamicChildBindController; + mLowPriorityInflationHelper = lowPriorityInflationHelper; } public void setUpWithPresenter(NotificationPresenter presenter, @@ -177,6 +181,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle currentUserId); ent.setSensitive(sensitive, deviceSensitive); ent.getRow().setNeedsRedaction(needsRedaction); + mLowPriorityInflationHelper.recheckLowPriorityViewAndInflate(ent, ent.getRow()); boolean isChildInGroup = mGroupManager.isChildInGroupWithSummary(ent.getSbn()); boolean groupChangesAllowed = mVisualStabilityManager.areGroupChangesAllowed() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java index e64b423aab60..de7e36d97b22 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java @@ -37,6 +37,7 @@ import com.android.systemui.statusbar.notification.DynamicChildBindController; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.VisualStabilityManager; +import com.android.systemui.statusbar.notification.collection.inflation.LowPriorityInflationHelper; import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.NotificationGroupManager; @@ -143,7 +144,8 @@ public interface StatusBarDependenciesModule { BubbleController bubbleController, DynamicPrivacyController privacyController, ForegroundServiceSectionController fgsSectionController, - DynamicChildBindController dynamicChildBindController) { + DynamicChildBindController dynamicChildBindController, + LowPriorityInflationHelper lowPriorityInflationHelper) { return new NotificationViewHierarchyManager( context, mainHandler, @@ -156,7 +158,8 @@ public interface StatusBarDependenciesModule { bubbleController, privacyController, fgsSectionController, - dynamicChildBindController); + dynamicChildBindController, + lowPriorityInflationHelper); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java index 4e6df0ad1ba4..d364689a65d4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java @@ -23,11 +23,14 @@ import android.view.View; import com.android.systemui.DejankUtils; import com.android.systemui.bubbles.BubbleController; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.phone.StatusBar; import java.util.Optional; +import javax.inject.Inject; + /** * Click handler for generic clicks on notifications. Clicks on specific areas (expansion caret, * app ops icon, etc) are handled elsewhere. @@ -35,15 +38,19 @@ import java.util.Optional; public final class NotificationClicker implements View.OnClickListener { private static final String TAG = "NotificationClicker"; - private final Optional<StatusBar> mStatusBar; private final BubbleController mBubbleController; + private final NotificationClickerLogger mLogger; + private final Optional<StatusBar> mStatusBar; private final NotificationActivityStarter mNotificationActivityStarter; - public NotificationClicker(Optional<StatusBar> statusBar, + private NotificationClicker( BubbleController bubbleController, + NotificationClickerLogger logger, + Optional<StatusBar> statusBar, NotificationActivityStarter notificationActivityStarter) { - mStatusBar = statusBar; mBubbleController = bubbleController; + mLogger = logger; + mStatusBar = statusBar; mNotificationActivityStarter = notificationActivityStarter; } @@ -58,25 +65,26 @@ public final class NotificationClicker implements View.OnClickListener { SystemClock.uptimeMillis(), v, "NOTIFICATION_CLICK")); final ExpandableNotificationRow row = (ExpandableNotificationRow) v; - final StatusBarNotification sbn = row.getEntry().getSbn(); - if (sbn == null) { - Log.e(TAG, "NotificationClicker called on an unclickable notification,"); - return; - } + final NotificationEntry entry = row.getEntry(); + mLogger.logOnClick(entry); // Check if the notification is displaying the menu, if so slide notification back if (isMenuVisible(row)) { + mLogger.logMenuVisible(entry); row.animateTranslateNotification(0); return; } else if (row.isChildInGroup() && isMenuVisible(row.getNotificationParent())) { + mLogger.logParentMenuVisible(entry); row.getNotificationParent().animateTranslateNotification(0); return; } else if (row.isSummaryWithChildren() && row.areChildrenExpanded()) { // We never want to open the app directly if the user clicks in between // the notifications. + mLogger.logChildrenExpanded(entry); return; } else if (row.areGutsExposed()) { // ignore click if guts are exposed + mLogger.logGutsExposed(entry); return; } @@ -88,7 +96,7 @@ public final class NotificationClicker implements View.OnClickListener { mBubbleController.collapseStack(); } - mNotificationActivityStarter.onNotificationClicked(sbn, row); + mNotificationActivityStarter.onNotificationClicked(entry.getSbn(), row); } private boolean isMenuVisible(ExpandableNotificationRow row) { @@ -107,4 +115,30 @@ public final class NotificationClicker implements View.OnClickListener { row.setOnClickListener(null); } } + + /** Daggerized builder for NotificationClicker. */ + public static class Builder { + private final BubbleController mBubbleController; + private final NotificationClickerLogger mLogger; + + @Inject + public Builder( + BubbleController bubbleController, + NotificationClickerLogger logger) { + mBubbleController = bubbleController; + mLogger = logger; + } + + /** Builds an instance. */ + public NotificationClicker build( + Optional<StatusBar> statusBar, + NotificationActivityStarter notificationActivityStarter + ) { + return new NotificationClicker( + mBubbleController, + mLogger, + statusBar, + notificationActivityStarter); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClickerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClickerLogger.kt new file mode 100644 index 000000000000..fbf033bd2291 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClickerLogger.kt @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification + +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel +import com.android.systemui.log.dagger.NotifInteractionLog +import com.android.systemui.statusbar.notification.collection.NotificationEntry +import javax.inject.Inject + +class NotificationClickerLogger @Inject constructor( + @NotifInteractionLog private val buffer: LogBuffer +) { + fun logOnClick(entry: NotificationEntry) { + buffer.log(TAG, LogLevel.DEBUG, { + str1 = entry.key + str2 = entry.ranking.channel.id + }, { + "CLICK $str1 (channel=$str2)" + }) + } + + fun logMenuVisible(entry: NotificationEntry) { + buffer.log(TAG, LogLevel.DEBUG, { + str1 = entry.key + }, { + "Ignoring click on $str1; menu is visible" + }) + } + + fun logParentMenuVisible(entry: NotificationEntry) { + buffer.log(TAG, LogLevel.DEBUG, { + str1 = entry.key + }, { + "Ignoring click on $str1; parent menu is visible" + }) + } + + fun logChildrenExpanded(entry: NotificationEntry) { + buffer.log(TAG, LogLevel.DEBUG, { + str1 = entry.key + }, { + "Ignoring click on $str1; children are expanded" + }) + } + + fun logGutsExposed(entry: NotificationEntry) { + buffer.log(TAG, LogLevel.DEBUG, { + str1 = entry.key + }, { + "Ignoring click on $str1; guts are exposed" + }) + } +} + +private const val TAG = "NotificationClicker" diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java index 12d190b26eba..f1cb783742bb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -619,7 +619,8 @@ public class NotificationEntryManager implements entry.setSbn(notification); for (NotifCollectionListener listener : mNotifCollectionListeners) { listener.onEntryBind(entry, notification); - } mGroupManager.onEntryUpdated(entry, oldSbn); + } + mGroupManager.onEntryUpdated(entry, oldSbn); mLogger.logNotifUpdated(entry.getKey()); for (NotificationEntryListener listener : mNotificationEntryListeners) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/LowPriorityInflationHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/LowPriorityInflationHelper.java new file mode 100644 index 000000000000..73c0fdc56b8d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/LowPriorityInflationHelper.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.collection.inflation; + +import com.android.systemui.statusbar.FeatureFlags; +import com.android.systemui.statusbar.notification.collection.GroupEntry; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; +import com.android.systemui.statusbar.notification.row.RowContentBindParams; +import com.android.systemui.statusbar.notification.row.RowContentBindStage; +import com.android.systemui.statusbar.phone.NotificationGroupManager; + +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * Helper class that provide methods to help check when we need to inflate a low priority version + * ot notification content. + */ +@Singleton +public class LowPriorityInflationHelper { + private final FeatureFlags mFeatureFlags; + private final NotificationGroupManager mGroupManager; + private final RowContentBindStage mRowContentBindStage; + + @Inject + LowPriorityInflationHelper( + FeatureFlags featureFlags, + NotificationGroupManager groupManager, + RowContentBindStage rowContentBindStage) { + mFeatureFlags = featureFlags; + mGroupManager = groupManager; + mRowContentBindStage = rowContentBindStage; + } + + /** + * Check if we inflated the wrong version of the view and if we need to reinflate the + * content views to be their low priority version or not. + * + * Whether we inflate the low priority view or not depends on the notification being visually + * part of a group. Since group membership is determined AFTER inflation, we're forced to check + * again at a later point in the pipeline to see if we inflated the wrong view and reinflate + * the correct one here. + * + * TODO: The group manager should run before inflation so that we don't deal with this + */ + public void recheckLowPriorityViewAndInflate( + NotificationEntry entry, + ExpandableNotificationRow row) { + RowContentBindParams params = mRowContentBindStage.getStageParams(entry); + final boolean shouldBeLowPriority = shouldUseLowPriorityView(entry); + if (!row.isRemoved() && row.isLowPriority() != shouldBeLowPriority) { + params.setUseLowPriority(shouldBeLowPriority); + mRowContentBindStage.requestRebind(entry, + en -> row.setIsLowPriority(shouldBeLowPriority)); + } + } + + /** + * Whether the notification should inflate a low priority version of its content views. + */ + public boolean shouldUseLowPriorityView(NotificationEntry entry) { + boolean isGroupChild; + if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) { + isGroupChild = (entry.getParent() != GroupEntry.ROOT_ENTRY); + } else { + isGroupChild = mGroupManager.isChildInGroupWithSummary(entry.getSbn()); + } + return entry.isAmbient() && !isGroupChild; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java index e6a4cff0d893..673aa3903156 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java @@ -64,6 +64,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { private final ExpandableNotificationRowComponent.Builder mExpandableNotificationRowComponentBuilder; private final IconManager mIconManager; + private final LowPriorityInflationHelper mLowPriorityInflationHelper; private NotificationPresenter mPresenter; private NotificationListContainer mListContainer; @@ -81,7 +82,8 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { NotificationInterruptStateProvider notificationInterruptionStateProvider, Provider<RowInflaterTask> rowInflaterTaskProvider, ExpandableNotificationRowComponent.Builder expandableNotificationRowComponentBuilder, - IconManager iconManager) { + IconManager iconManager, + LowPriorityInflationHelper lowPriorityInflationHelper) { mContext = context; mNotifBindPipeline = notifBindPipeline; mRowContentBindStage = rowContentBindStage; @@ -92,6 +94,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { mRowInflaterTaskProvider = rowInflaterTaskProvider; mExpandableNotificationRowComponentBuilder = expandableNotificationRowComponentBuilder; mIconManager = iconManager; + mLowPriorityInflationHelper = lowPriorityInflationHelper; } /** @@ -225,11 +228,15 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { @Nullable NotificationRowContentBinder.InflationCallback inflationCallback) { final boolean useIncreasedCollapsedHeight = mMessagingUtil.isImportantMessaging(entry.getSbn(), entry.getImportance()); - final boolean isLowPriority = entry.isAmbient(); + // If this is our first time inflating, we don't actually know the groupings for real + // yet, so we might actually inflate a low priority content view incorrectly here and have + // to correct it later in the pipeline. On subsequent inflations (i.e. updates), this + // should inflate the correct view. + final boolean isLowPriority = mLowPriorityInflationHelper.shouldUseLowPriorityView(entry); RowContentBindParams params = mRowContentBindStage.getStageParams(entry); params.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight); - params.setUseLowPriority(entry.isAmbient()); + params.setUseLowPriority(isLowPriority); // TODO: Replace this API with RowContentBindParams directly. Also move to a separate // redaction controller. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt index 7f2f898565d8..c9754048e1d1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt @@ -17,7 +17,6 @@ package com.android.systemui.statusbar.notification.init import android.service.notification.StatusBarNotification -import com.android.systemui.bubbles.BubbleController import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption import com.android.systemui.statusbar.FeatureFlags import com.android.systemui.statusbar.NotificationListener @@ -62,12 +61,12 @@ class NotificationsControllerImpl @Inject constructor( private val deviceProvisionedController: DeviceProvisionedController, private val notificationRowBinder: NotificationRowBinderImpl, private val remoteInputUriController: RemoteInputUriController, - private val bubbleController: BubbleController, private val groupManager: NotificationGroupManager, private val groupAlertTransferHelper: NotificationGroupAlertTransferHelper, private val headsUpManager: HeadsUpManager, private val headsUpController: HeadsUpController, - private val headsUpViewBinder: HeadsUpViewBinder + private val headsUpViewBinder: HeadsUpViewBinder, + private val clickerBuilder: NotificationClicker.Builder ) : NotificationsController { override fun initialize( @@ -87,10 +86,7 @@ class NotificationsControllerImpl @Inject constructor( listController.bind() notificationRowBinder.setNotificationClicker( - NotificationClicker( - Optional.of(statusBar), - bubbleController, - notificationActivityStarter)) + clickerBuilder.build(Optional.of(statusBar), notificationActivityStarter)) notificationRowBinder.setUpWithPresenter( presenter, listContainer, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index c613e6443882..ba72e2879f14 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -240,7 +240,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private ExpandableNotificationRow mNotificationParent; private OnExpandClickListener mOnExpandClickListener; private View.OnClickListener mOnAppOpsClickListener; - private boolean mIsChildInGroup; // Listener will be called when receiving a long click event. // Use #setLongPressPosition to optionally assign positional data with the long press. @@ -848,15 +847,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } mNotificationParent = isChildInGroup ? parent : null; mPrivateLayout.setIsChildInGroup(isChildInGroup); - // TODO: Move inflation logic out of this call - if (mIsChildInGroup != isChildInGroup) { - mIsChildInGroup = isChildInGroup; - if (!isRemoved() && mIsLowPriority) { - RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry); - params.setUseLowPriority(mIsLowPriority); - mRowContentBindStage.requestRebind(mEntry, null /* callback */); - } - } + resetBackgroundAlpha(); updateBackgroundForGroupState(); updateClickAndFocus(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java index 9d5443729d45..582e3e5b6c34 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java @@ -132,7 +132,6 @@ public class NotificationContentInflater implements NotificationRowContentBinder mConversationProcessor, row, bindParams.isLowPriority, - bindParams.isChildInGroup, bindParams.usesIncreasedHeight, bindParams.usesIncreasedHeadsUpHeight, callback, @@ -156,7 +155,6 @@ public class NotificationContentInflater implements NotificationRowContentBinder InflationProgress result = createRemoteViews(reInflateFlags, builder, bindParams.isLowPriority, - bindParams.isChildInGroup, bindParams.usesIncreasedHeight, bindParams.usesIncreasedHeadsUpHeight, packageContext); @@ -285,11 +283,9 @@ public class NotificationContentInflater implements NotificationRowContentBinder } private static InflationProgress createRemoteViews(@InflationFlag int reInflateFlags, - Notification.Builder builder, boolean isLowPriority, boolean isChildInGroup, - boolean usesIncreasedHeight, boolean usesIncreasedHeadsUpHeight, - Context packageContext) { + Notification.Builder builder, boolean isLowPriority, boolean usesIncreasedHeight, + boolean usesIncreasedHeadsUpHeight, Context packageContext) { InflationProgress result = new InflationProgress(); - isLowPriority = isLowPriority && !isChildInGroup; if ((reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0) { result.newContentView = createContentView(builder, isLowPriority, usesIncreasedHeight); @@ -702,7 +698,6 @@ public class NotificationContentInflater implements NotificationRowContentBinder private final Context mContext; private final boolean mInflateSynchronously; private final boolean mIsLowPriority; - private final boolean mIsChildInGroup; private final boolean mUsesIncreasedHeight; private final InflationCallback mCallback; private final boolean mUsesIncreasedHeadsUpHeight; @@ -728,7 +723,6 @@ public class NotificationContentInflater implements NotificationRowContentBinder ConversationNotificationProcessor conversationProcessor, ExpandableNotificationRow row, boolean isLowPriority, - boolean isChildInGroup, boolean usesIncreasedHeight, boolean usesIncreasedHeadsUpHeight, InflationCallback callback, @@ -743,7 +737,6 @@ public class NotificationContentInflater implements NotificationRowContentBinder mRemoteViewCache = cache; mContext = mRow.getContext(); mIsLowPriority = isLowPriority; - mIsChildInGroup = isChildInGroup; mUsesIncreasedHeight = usesIncreasedHeight; mUsesIncreasedHeadsUpHeight = usesIncreasedHeadsUpHeight; mRemoteViewClickHandler = remoteViewClickHandler; @@ -781,7 +774,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder mConversationProcessor.processNotification(mEntry, recoveredBuilder); } InflationProgress inflationProgress = createRemoteViews(mReInflateFlags, - recoveredBuilder, mIsLowPriority, mIsChildInGroup, mUsesIncreasedHeight, + recoveredBuilder, mIsLowPriority, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, packageContext); return inflateSmartReplyViews(inflationProgress, mReInflateFlags, mEntry, mRow.getContext(), packageContext, mRow.getHeadsUpManager(), diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java index 9bd8d4782672..a9f83c8b9e6b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java @@ -114,11 +114,6 @@ public interface NotificationRowContentBinder { public boolean isLowPriority; /** - * Bind child version of content views. - */ - public boolean isChildInGroup; - - /** * Use increased height when binding contracted view. */ public boolean usesIncreasedHeight; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java index d3fec695f012..f26ecc32821d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java @@ -27,7 +27,6 @@ import com.android.systemui.statusbar.notification.row.NotificationRowContentBin */ public final class RowContentBindParams { private boolean mUseLowPriority; - private boolean mUseChildInGroup; private boolean mUseIncreasedHeight; private boolean mUseIncreasedHeadsUpHeight; private boolean mViewsNeedReinflation; @@ -56,20 +55,6 @@ public final class RowContentBindParams { } /** - * Set whether content should use group child version of its content views. - */ - public void setUseChildInGroup(boolean useChildInGroup) { - if (mUseChildInGroup != useChildInGroup) { - mDirtyContentViews |= (FLAG_CONTENT_VIEW_CONTRACTED | FLAG_CONTENT_VIEW_EXPANDED); - } - mUseChildInGroup = useChildInGroup; - } - - public boolean useChildInGroup() { - return mUseChildInGroup; - } - - /** * Set whether content should use an increased height version of its contracted view. */ public void setUseIncreasedCollapsedHeight(boolean useIncreasedHeight) { @@ -163,10 +148,10 @@ public final class RowContentBindParams { @Override public String toString() { return String.format("RowContentBindParams[mContentViews=%x mDirtyContentViews=%x " - + "mUseLowPriority=%b mUseChildInGroup=%b mUseIncreasedHeight=%b " + + "mUseLowPriority=%b mUseIncreasedHeight=%b " + "mUseIncreasedHeadsUpHeight=%b mViewsNeedReinflation=%b]", - mContentViews, mDirtyContentViews, mUseLowPriority, mUseChildInGroup, - mUseIncreasedHeight, mUseIncreasedHeadsUpHeight, mViewsNeedReinflation); + mContentViews, mDirtyContentViews, mUseLowPriority, mUseIncreasedHeight, + mUseIncreasedHeadsUpHeight, mViewsNeedReinflation); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java index c632f3eb22a2..c6f0a135cd34 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java @@ -71,7 +71,6 @@ public class RowContentBindStage extends BindStage<RowContentBindParams> { BindParams bindParams = new BindParams(); bindParams.isLowPriority = params.useLowPriority(); - bindParams.isChildInGroup = params.useChildInGroup(); bindParams.usesIncreasedHeight = params.useIncreasedHeight(); bindParams.usesIncreasedHeadsUpHeight = params.useIncreasedHeadsUpHeight(); boolean forceInflate = params.needsReinflation(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java index 796f22cbb3cb..b96cff830f31 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java @@ -191,6 +191,7 @@ public class NotificationMediaTemplateViewWrapper extends NotificationTemplateVi Drawable iconDrawable = notif.getSmallIcon().loadDrawable(mContext); panel.getMediaPlayer().setMediaSession(token, iconDrawable, + notif.getLargeIcon(), tintColor, mBackgroundColor, mActions, @@ -201,6 +202,7 @@ public class NotificationMediaTemplateViewWrapper extends NotificationTemplateVi com.android.systemui.R.id.quick_settings_panel); bigPanel.addMediaSession(token, iconDrawable, + notif.getLargeIcon(), tintColor, mBackgroundColor, mActions, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java index 7d422e3c15a2..f9119c7a010f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java @@ -53,11 +53,13 @@ import android.view.WindowManagerGlobal; import com.android.internal.policy.GestureNavigationSettingsObserver; import com.android.systemui.Dependency; import com.android.systemui.R; +import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.bubbles.BubbleController; import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.NavigationEdgeBackPlugin; import com.android.systemui.plugins.PluginListener; import com.android.systemui.recents.OverviewProxyService; +import com.android.systemui.settings.CurrentUserTracker; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.QuickStepContract; @@ -74,7 +76,7 @@ import java.util.concurrent.Executor; /** * Utility class to handle edge swipes for back gesture */ -public class EdgeBackGestureHandler implements DisplayListener, +public class EdgeBackGestureHandler extends CurrentUserTracker implements DisplayListener, PluginListener<NavigationEdgeBackPlugin>, ProtoTraceable<SystemUiTraceProto> { private static final String TAG = "EdgeBackGestureHandler"; @@ -165,6 +167,7 @@ public class EdgeBackGestureHandler implements DisplayListener, private boolean mIsGesturalModeEnabled; private boolean mIsEnabled; private boolean mIsNavBarShownTransiently; + private boolean mIsBackGestureAllowed; private InputMonitor mInputMonitor; private InputEventReceiver mInputEventReceiver; @@ -200,7 +203,7 @@ public class EdgeBackGestureHandler implements DisplayListener, public EdgeBackGestureHandler(Context context, OverviewProxyService overviewProxyService, SysUiState sysUiFlagContainer, PluginManager pluginManager) { - final Resources res = context.getResources(); + super(Dependency.get(BroadcastDispatcher.class)); mContext = context; mDisplayId = context.getDisplayId(); mMainExecutor = context.getMainExecutor(); @@ -216,20 +219,30 @@ public class EdgeBackGestureHandler implements DisplayListener, ViewConfiguration.getLongPressTimeout()); mGestureNavigationSettingsObserver = new GestureNavigationSettingsObserver( - mContext.getMainThreadHandler(), mContext, () -> updateCurrentUserResources(res)); + mContext.getMainThreadHandler(), mContext, this::updateCurrentUserResources); - updateCurrentUserResources(res); + updateCurrentUserResources(); sysUiFlagContainer.addCallback(sysUiFlags -> mSysUiFlags = sysUiFlags); } - public void updateCurrentUserResources(Resources res) { + public void updateCurrentUserResources() { + Resources res = Dependency.get(NavigationModeController.class).getCurrentUserContext() + .getResources(); mEdgeWidthLeft = mGestureNavigationSettingsObserver.getLeftSensitivity(res); mEdgeWidthRight = mGestureNavigationSettingsObserver.getRightSensitivity(res); + mIsBackGestureAllowed = + !mGestureNavigationSettingsObserver.areNavigationButtonForcedVisible(); mBottomGestureHeight = res.getDimensionPixelSize( com.android.internal.R.dimen.navigation_bar_gesture_height); } + @Override + public void onUserSwitched(int newUserId) { + updateIsEnabled(); + updateCurrentUserResources(); + } + /** * @see NavigationBarView#onAttachedToWindow() */ @@ -243,6 +256,7 @@ public class EdgeBackGestureHandler implements DisplayListener, Settings.Global.getUriFor(FIXED_ROTATION_TRANSFORM_SETTING_NAME), false /* notifyForDescendants */, mFixedRotationObserver, UserHandle.USER_ALL); updateIsEnabled(); + startTracking(); } /** @@ -255,6 +269,7 @@ public class EdgeBackGestureHandler implements DisplayListener, } mContext.getContentResolver().unregisterContentObserver(mFixedRotationObserver); updateIsEnabled(); + stopTracking(); } private void setRotationCallbacks(boolean enable) { @@ -269,10 +284,13 @@ public class EdgeBackGestureHandler implements DisplayListener, } } - public void onNavigationModeChanged(int mode, Context currentUserContext) { + /** + * @see NavigationModeController.ModeChangedListener#onNavigationModeChanged + */ + public void onNavigationModeChanged(int mode) { mIsGesturalModeEnabled = QuickStepContract.isGesturalMode(mode); updateIsEnabled(); - updateCurrentUserResources(currentUserContext.getResources()); + updateCurrentUserResources(); } public void onNavBarTransientStateChanged(boolean isTransient) { @@ -363,6 +381,10 @@ public class EdgeBackGestureHandler implements DisplayListener, updateDisplaySize(); } + public boolean isHandlingGestures() { + return mIsEnabled && mIsBackGestureAllowed; + } + private WindowManager.LayoutParams createLayoutParams() { Resources resources = mContext.getResources(); WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams( @@ -469,9 +491,9 @@ public class EdgeBackGestureHandler implements DisplayListener, mIsOnLeftEdge = ev.getX() <= mEdgeWidthLeft + mLeftInset; mLogGesture = false; mInRejectedExclusion = false; - mAllowGesture = !QuickStepContract.isBackGestureDisabled(mSysUiFlags) - && isWithinTouchRegion((int) ev.getX(), (int) ev.getY()) - && !mDisabledForQuickstep; + mAllowGesture = !mDisabledForQuickstep && mIsBackGestureAllowed + && !QuickStepContract.isBackGestureDisabled(mSysUiFlags) + && isWithinTouchRegion((int) ev.getX(), (int) ev.getY()); if (mAllowGesture) { mEdgeBackPlugin.setIsLeftPanel(mIsOnLeftEdge); mEdgeBackPlugin.onMotionEvent(ev); @@ -599,6 +621,7 @@ public class EdgeBackGestureHandler implements DisplayListener, public void dump(PrintWriter pw) { pw.println("EdgeBackGestureHandler:"); pw.println(" mIsEnabled=" + mIsEnabled); + pw.println(" mIsBackGestureAllowed=" + mIsBackGestureAllowed); pw.println(" mAllowGesture=" + mAllowGesture); pw.println(" mDisabledForQuickstep=" + mDisabledForQuickstep); pw.println(" mInRejectedExclusion" + mInRejectedExclusion); 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 d35e1e1e176a..3e5eb5fba8f2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java @@ -33,6 +33,7 @@ import com.android.internal.view.AppearanceRegion; import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.plugins.DarkIconDispatcher; +import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.policy.BatteryController; import java.io.FileDescriptor; @@ -58,6 +59,7 @@ public class LightBarController implements BatteryController.BatteryStateChangeC private AppearanceRegion[] mAppearanceRegions = new AppearanceRegion[0]; private int mStatusBarMode; private int mNavigationBarMode; + private int mNavigationMode; private final Color mDarkModeColor; /** @@ -84,11 +86,14 @@ public class LightBarController implements BatteryController.BatteryStateChangeC @Inject public LightBarController(Context ctx, DarkIconDispatcher darkIconDispatcher, - BatteryController batteryController) { + BatteryController batteryController, NavigationModeController navModeController) { mDarkModeColor = Color.valueOf(ctx.getColor(R.color.dark_mode_icon_color_single_tone)); mStatusBarIconController = (SysuiDarkIconDispatcher) darkIconDispatcher; mBatteryController = batteryController; mBatteryController.addCallback(this); + mNavigationMode = navModeController.addListener((mode) -> { + mNavigationMode = mode; + }); } public void setNavigationBar(LightBarTransitionsController navigationBar) { @@ -234,7 +239,8 @@ public class LightBarController implements BatteryController.BatteryStateChangeC } private void updateNavigation() { - if (mNavigationBarController != null) { + if (mNavigationBarController != null + && !QuickStepContract.isGesturalMode(mNavigationMode)) { mNavigationBarController.setIconsDark(mNavigationLight, animateChange()); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index 84aecd4e0759..2978772cac5e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -123,7 +123,7 @@ public class NavigationBarView extends FrameLayout implements private KeyButtonDrawable mRecentIcon; private KeyButtonDrawable mDockedIcon; - private final EdgeBackGestureHandler mEdgeBackGestureHandler; + private EdgeBackGestureHandler mEdgeBackGestureHandler; private final DeadZone mDeadZone; private boolean mDeadZoneConsuming = false; private final NavigationBarTransitions mBarTransitions; @@ -244,7 +244,7 @@ public class NavigationBarView extends FrameLayout implements private final OnComputeInternalInsetsListener mOnComputeInternalInsetsListener = info -> { // When the nav bar is in 2-button or 3-button mode, or when IME is visible in fully // gestural mode, the entire nav bar should be touchable. - if (!isGesturalMode(mNavBarMode) || mImeVisible) { + if (!mEdgeBackGestureHandler.isHandlingGestures() || mImeVisible) { info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME); return; } @@ -296,8 +296,6 @@ public class NavigationBarView extends FrameLayout implements R.style.RotateButtonCCWStart90, isGesturalMode ? mFloatingRotationButton : rotateSuggestionButton); - final ContextualButton backButton = new ContextualButton(R.id.back, 0); - mConfiguration = new Configuration(); mTmpLastConfiguration = new Configuration(); mConfiguration.updateFrom(context.getResources().getConfiguration()); @@ -305,7 +303,7 @@ public class NavigationBarView extends FrameLayout implements mScreenPinningNotify = new ScreenPinningNotify(mContext); mBarTransitions = new NavigationBarTransitions(this, Dependency.get(CommandQueue.class)); - mButtonDispatchers.put(R.id.back, backButton); + mButtonDispatchers.put(R.id.back, new ButtonDispatcher(R.id.back)); mButtonDispatchers.put(R.id.home, new ButtonDispatcher(R.id.home)); mButtonDispatchers.put(R.id.home_handle, new ButtonDispatcher(R.id.home_handle)); mButtonDispatchers.put(R.id.recent_apps, new ButtonDispatcher(R.id.recent_apps)); @@ -659,7 +657,7 @@ public class NavigationBarView extends FrameLayout implements boolean disableHomeHandle = disableRecent && ((mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0); - boolean disableBack = !useAltBack && (isGesturalMode(mNavBarMode) + boolean disableBack = !useAltBack && (mEdgeBackGestureHandler.isHandlingGestures() || ((mDisabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0)); // When screen pinning, don't hide back and home when connected service or back and @@ -686,9 +684,9 @@ public class NavigationBarView extends FrameLayout implements } } - getBackButton().setVisibility(disableBack ? View.INVISIBLE : View.VISIBLE); - getHomeButton().setVisibility(disableHome ? View.INVISIBLE : View.VISIBLE); - getRecentsButton().setVisibility(disableRecent ? View.INVISIBLE : View.VISIBLE); + getBackButton().setVisibility(disableBack ? View.INVISIBLE : View.VISIBLE); + getHomeButton().setVisibility(disableHome ? View.INVISIBLE : View.VISIBLE); + getRecentsButton().setVisibility(disableRecent ? View.INVISIBLE : View.VISIBLE); getHomeHandle().setVisibility(disableHomeHandle ? View.INVISIBLE : View.VISIBLE); } @@ -838,10 +836,9 @@ public class NavigationBarView extends FrameLayout implements @Override public void onNavigationModeChanged(int mode) { - Context curUserCtx = Dependency.get(NavigationModeController.class).getCurrentUserContext(); mNavBarMode = mode; mBarTransitions.onNavigationModeChanged(mNavBarMode); - mEdgeBackGestureHandler.onNavigationModeChanged(mNavBarMode, curUserCtx); + mEdgeBackGestureHandler.onNavigationModeChanged(mNavBarMode); mRecentsOnboarding.onNavigationModeChanged(mNavBarMode); getRotateSuggestionButton().onNavigationModeChanged(mNavBarMode); @@ -864,6 +861,7 @@ public class NavigationBarView extends FrameLayout implements @Override public void onFinishInflate() { + super.onFinishInflate(); mNavigationInflaterView = findViewById(R.id.navigation_inflater); mNavigationInflaterView.setButtonDispatchers(mButtonDispatchers); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java index d24ccf343a3a..6061b1e73d1c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java @@ -17,9 +17,6 @@ package com.android.systemui.statusbar.phone; import static android.content.Intent.ACTION_OVERLAY_CHANGED; -import static android.content.Intent.ACTION_PREFERRED_ACTIVITY_CHANGED; -import static android.os.UserHandle.USER_CURRENT; -import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY; @@ -38,17 +35,14 @@ import android.os.UserHandle; import android.provider.Settings; import android.provider.Settings.Secure; import android.util.Log; -import android.util.SparseBooleanArray; import com.android.systemui.Dumpable; import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.shared.system.ActivityManagerWrapper; -import com.android.systemui.statusbar.policy.DeviceProvisionedController; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.Arrays; import java.util.concurrent.Executor; import javax.inject.Inject; @@ -70,104 +64,34 @@ public class NavigationModeController implements Dumpable { private final Context mContext; private Context mCurrentUserContext; private final IOverlayManager mOverlayManager; - private final DeviceProvisionedController mDeviceProvisionedController; private final Executor mUiBgExecutor; - private SparseBooleanArray mRestoreGesturalNavBarMode = new SparseBooleanArray(); - private ArrayList<ModeChangedListener> mListeners = new ArrayList<>(); private BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - switch (intent.getAction()) { - case ACTION_OVERLAY_CHANGED: if (DEBUG) { Log.d(TAG, "ACTION_OVERLAY_CHANGED"); } updateCurrentInteractionMode(true /* notify */); - break; - } } }; - private final DeviceProvisionedController.DeviceProvisionedListener mDeviceProvisionedCallback = - new DeviceProvisionedController.DeviceProvisionedListener() { - @Override - public void onDeviceProvisionedChanged() { - if (DEBUG) { - Log.d(TAG, "onDeviceProvisionedChanged: " - + mDeviceProvisionedController.isDeviceProvisioned()); - } - // Once the device has been provisioned, check if we can restore gestural nav - restoreGesturalNavOverlayIfNecessary(); - } - - @Override - public void onUserSetupChanged() { - if (DEBUG) { - Log.d(TAG, "onUserSetupChanged: " - + mDeviceProvisionedController.isCurrentUserSetup()); - } - // Once the user has been setup, check if we can restore gestural nav - restoreGesturalNavOverlayIfNecessary(); - } - - @Override - public void onUserSwitched() { - if (DEBUG) { - Log.d(TAG, "onUserSwitched: " - + ActivityManagerWrapper.getInstance().getCurrentUserId()); - } - - // Update the nav mode for the current user - updateCurrentInteractionMode(true /* notify */); - - // When switching users, defer enabling the gestural nav overlay until the user - // is all set up - deferGesturalNavOverlayIfNecessary(); - } - }; - @Inject - public NavigationModeController(Context context, - DeviceProvisionedController deviceProvisionedController, - @UiBackground Executor uiBgExecutor) { + public NavigationModeController(Context context, @UiBackground Executor uiBgExecutor) { mContext = context; mCurrentUserContext = context; mOverlayManager = IOverlayManager.Stub.asInterface( ServiceManager.getService(Context.OVERLAY_SERVICE)); mUiBgExecutor = uiBgExecutor; - mDeviceProvisionedController = deviceProvisionedController; - mDeviceProvisionedController.addCallback(mDeviceProvisionedCallback); IntentFilter overlayFilter = new IntentFilter(ACTION_OVERLAY_CHANGED); overlayFilter.addDataScheme("package"); overlayFilter.addDataSchemeSpecificPart("android", PatternMatcher.PATTERN_LITERAL); mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, overlayFilter, null, null); - IntentFilter preferredActivityFilter = new IntentFilter(ACTION_PREFERRED_ACTIVITY_CHANGED); - mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, preferredActivityFilter, null, - null); - updateCurrentInteractionMode(false /* notify */); - - // Check if we need to defer enabling gestural nav - deferGesturalNavOverlayIfNecessary(); - } - - private boolean setGestureModeOverlayForMainLauncher() { - if (getCurrentInteractionMode(mCurrentUserContext) == NAV_BAR_MODE_GESTURAL) { - // Already in gesture mode - return true; - } - - Log.d(TAG, "Switching system navigation to full-gesture mode:" - + " contextUser=" - + mCurrentUserContext.getUserId()); - - setModeOverlay(NAV_BAR_MODE_GESTURAL_OVERLAY, USER_CURRENT); - return true; } public void updateCurrentInteractionMode(boolean notify) { @@ -176,10 +100,9 @@ public class NavigationModeController implements Dumpable { if (mode == NAV_BAR_MODE_GESTURAL) { switchToDefaultGestureNavOverlayIfNecessary(); } - mUiBgExecutor.execute(() -> { + mUiBgExecutor.execute(() -> Settings.Secure.putString(mCurrentUserContext.getContentResolver(), - Secure.NAVIGATION_MODE, String.valueOf(mode)); - }); + Secure.NAVIGATION_MODE, String.valueOf(mode))); if (DEBUG) { Log.e(TAG, "updateCurrentInteractionMode: mode=" + mode); dumpAssetPaths(mCurrentUserContext); @@ -230,61 +153,11 @@ public class NavigationModeController implements Dumpable { } } - private void deferGesturalNavOverlayIfNecessary() { - final int userId = mDeviceProvisionedController.getCurrentUser(); - mRestoreGesturalNavBarMode.put(userId, false); - if (mDeviceProvisionedController.isDeviceProvisioned() - && mDeviceProvisionedController.isCurrentUserSetup()) { - // User is already setup and device is provisioned, nothing to do - if (DEBUG) { - Log.d(TAG, "deferGesturalNavOverlayIfNecessary: device is provisioned and user is " - + "setup"); - } - return; - } - - ArrayList<String> defaultOverlays = new ArrayList<>(); - try { - defaultOverlays.addAll(Arrays.asList(mOverlayManager.getDefaultOverlayPackages())); - } catch (RemoteException e) { - Log.e(TAG, "deferGesturalNavOverlayIfNecessary: failed to fetch default overlays"); - } - if (!defaultOverlays.contains(NAV_BAR_MODE_GESTURAL_OVERLAY)) { - // No default gesture nav overlay - if (DEBUG) { - Log.d(TAG, "deferGesturalNavOverlayIfNecessary: no default gestural overlay, " - + "default=" + defaultOverlays); - } - return; - } - - // If the default is gestural, force-enable three button mode until the device is - // provisioned - setModeOverlay(NAV_BAR_MODE_3BUTTON_OVERLAY, USER_CURRENT); - mRestoreGesturalNavBarMode.put(userId, true); - - if (DEBUG) { - Log.d(TAG, "deferGesturalNavOverlayIfNecessary: setting to 3 button mode"); - } - } - - private void restoreGesturalNavOverlayIfNecessary() { - if (DEBUG) { - Log.d(TAG, "restoreGesturalNavOverlayIfNecessary: needs restore=" - + mRestoreGesturalNavBarMode); - } - final int userId = mDeviceProvisionedController.getCurrentUser(); - if (mRestoreGesturalNavBarMode.get(userId)) { - // Restore the gestural state if necessary - setGestureModeOverlayForMainLauncher(); - mRestoreGesturalNavBarMode.put(userId, false); - } - } - private void switchToDefaultGestureNavOverlayIfNecessary() { final int userId = mCurrentUserContext.getUserId(); try { - final IOverlayManager om = mOverlayManager; + final IOverlayManager om = IOverlayManager.Stub.asInterface( + ServiceManager.getService(Context.OVERLAY_SERVICE)); final OverlayInfo info = om.getOverlayInfo(NAV_BAR_MODE_GESTURAL_OVERLAY, userId); if (info != null && !info.isEnabled()) { // Enable the default gesture nav overlay, and move the back gesture inset scale to @@ -309,20 +182,6 @@ public class NavigationModeController implements Dumpable { } } - public void setModeOverlay(String overlayPkg, int userId) { - mUiBgExecutor.execute(() -> { - try { - mOverlayManager.setEnabledExclusiveInCategory(overlayPkg, userId); - if (DEBUG) { - Log.d(TAG, "setModeOverlay: overlayPackage=" + overlayPkg - + " userId=" + userId); - } - } catch (SecurityException | IllegalStateException | RemoteException e) { - Log.e(TAG, "Failed to enable overlay " + overlayPkg + " for user " + userId); - } - }); - } - @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("NavigationModeController:"); @@ -334,11 +193,6 @@ public class NavigationModeController implements Dumpable { defaultOverlays = "failed_to_fetch"; } pw.println(" defaultOverlays=" + defaultOverlays); - pw.println(" restoreGesturalNavMode:"); - for (int i = 0; i < mRestoreGesturalNavBarMode.size(); i++) { - pw.println(" userId=" + mRestoreGesturalNavBarMode.keyAt(i) - + " shouldRestore=" + mRestoreGesturalNavBarMode.valueAt(i)); - } dumpAssetPaths(mCurrentUserContext); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java index 1a6b415f87db..bf52a7ae2bf9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java @@ -148,11 +148,6 @@ public class RegionSamplingHelper implements View.OnAttachStateChangeListener, updateSamplingRect(); } - private void postUpdateSamplingListener() { - mHandler.removeCallbacks(mUpdateSamplingListener); - mHandler.post(mUpdateSamplingListener); - } - private void updateSamplingListener() { boolean isSamplingEnabled = mSamplingEnabled && !mSamplingRequestBounds.isEmpty() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index 53fa2630a9c3..fbe3e9b19248 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -40,7 +40,6 @@ import android.service.notification.NotificationStats; import android.service.notification.StatusBarNotification; import android.text.TextUtils; import android.util.EventLog; -import android.util.Log; import android.view.RemoteAnimationAdapter; import android.view.View; @@ -91,92 +90,119 @@ import dagger.Lazy; */ public class StatusBarNotificationActivityStarter implements NotificationActivityStarter { - private static final String TAG = "NotifActivityStarter"; - protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + private final Context mContext; + + private final CommandQueue mCommandQueue; + private final Handler mMainThreadHandler; + private final Handler mBackgroundHandler; + private final Executor mUiBgExecutor; + private final NotificationEntryManager mEntryManager; + private final NotifPipeline mNotifPipeline; + private final NotifCollection mNotifCollection; + private final HeadsUpManagerPhone mHeadsUpManager; + private final ActivityStarter mActivityStarter; + private final IStatusBarService mBarService; + private final StatusBarStateController mStatusBarStateController; + private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; + private final KeyguardManager mKeyguardManager; + private final IDreamManager mDreamManager; + private final BubbleController mBubbleController; private final Lazy<AssistManager> mAssistManagerLazy; - private final NotificationGroupManager mGroupManager; - private final StatusBarRemoteInputCallback mStatusBarRemoteInputCallback; private final NotificationRemoteInputManager mRemoteInputManager; + private final NotificationGroupManager mGroupManager; private final NotificationLockscreenUserManager mLockscreenUserManager; private final ShadeController mShadeController; - private final StatusBar mStatusBar; private final KeyguardStateController mKeyguardStateController; - private final ActivityStarter mActivityStarter; - private final NotificationEntryManager mEntryManager; - private final NotifPipeline mNotifPipeline; - private final NotifCollection mNotifCollection; - private final FeatureFlags mFeatureFlags; - private final StatusBarStateController mStatusBarStateController; private final NotificationInterruptStateProvider mNotificationInterruptStateProvider; + private final LockPatternUtils mLockPatternUtils; + private final StatusBarRemoteInputCallback mStatusBarRemoteInputCallback; + private final ActivityIntentHelper mActivityIntentHelper; + + private final FeatureFlags mFeatureFlags; private final MetricsLogger mMetricsLogger; - private final Context mContext; - private final NotificationPanelViewController mNotificationPanel; + private final StatusBarNotificationActivityStarterLogger mLogger; + + private final StatusBar mStatusBar; private final NotificationPresenter mPresenter; - private final LockPatternUtils mLockPatternUtils; - private final HeadsUpManagerPhone mHeadsUpManager; - private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; - private final KeyguardManager mKeyguardManager; + private final NotificationPanelViewController mNotificationPanel; private final ActivityLaunchAnimator mActivityLaunchAnimator; - private final IStatusBarService mBarService; - private final CommandQueue mCommandQueue; - private final IDreamManager mDreamManager; - private final Handler mMainThreadHandler; - private final Handler mBackgroundHandler; - private final ActivityIntentHelper mActivityIntentHelper; - private final BubbleController mBubbleController; - private final Executor mUiBgExecutor; private boolean mIsCollapsingToShowActivityOverLockscreen; - private StatusBarNotificationActivityStarter(Context context, CommandQueue commandQueue, - Lazy<AssistManager> assistManagerLazy, NotificationPanelViewController panel, - NotificationPresenter presenter, NotificationEntryManager entryManager, - HeadsUpManagerPhone headsUpManager, ActivityStarter activityStarter, - ActivityLaunchAnimator activityLaunchAnimator, IStatusBarService statusBarService, + private StatusBarNotificationActivityStarter( + Context context, + CommandQueue commandQueue, + Handler mainThreadHandler, + Handler backgroundHandler, + Executor uiBgExecutor, + NotificationEntryManager entryManager, + NotifPipeline notifPipeline, + NotifCollection notifCollection, + HeadsUpManagerPhone headsUpManager, + ActivityStarter activityStarter, + IStatusBarService statusBarService, StatusBarStateController statusBarStateController, StatusBarKeyguardViewManager statusBarKeyguardViewManager, KeyguardManager keyguardManager, - IDreamManager dreamManager, NotificationRemoteInputManager remoteInputManager, - StatusBarRemoteInputCallback remoteInputCallback, NotificationGroupManager groupManager, + IDreamManager dreamManager, + BubbleController bubbleController, + Lazy<AssistManager> assistManagerLazy, + NotificationRemoteInputManager remoteInputManager, + NotificationGroupManager groupManager, NotificationLockscreenUserManager lockscreenUserManager, - ShadeController shadeController, StatusBar statusBar, + ShadeController shadeController, KeyguardStateController keyguardStateController, NotificationInterruptStateProvider notificationInterruptStateProvider, - MetricsLogger metricsLogger, LockPatternUtils lockPatternUtils, - Handler mainThreadHandler, Handler backgroundHandler, Executor uiBgExecutor, - ActivityIntentHelper activityIntentHelper, BubbleController bubbleController, - FeatureFlags featureFlags, NotifPipeline notifPipeline, - NotifCollection notifCollection) { + LockPatternUtils lockPatternUtils, + StatusBarRemoteInputCallback remoteInputCallback, + ActivityIntentHelper activityIntentHelper, + + FeatureFlags featureFlags, + MetricsLogger metricsLogger, + StatusBarNotificationActivityStarterLogger logger, + + StatusBar statusBar, + NotificationPresenter presenter, + NotificationPanelViewController panel, + ActivityLaunchAnimator activityLaunchAnimator) { mContext = context; - mNotificationPanel = panel; - mPresenter = presenter; + mCommandQueue = commandQueue; + mMainThreadHandler = mainThreadHandler; + mBackgroundHandler = backgroundHandler; + mUiBgExecutor = uiBgExecutor; + mEntryManager = entryManager; + mNotifPipeline = notifPipeline; + mNotifCollection = notifCollection; mHeadsUpManager = headsUpManager; - mActivityLaunchAnimator = activityLaunchAnimator; + mActivityStarter = activityStarter; mBarService = statusBarService; - mCommandQueue = commandQueue; + mStatusBarStateController = statusBarStateController; mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; mKeyguardManager = keyguardManager; mDreamManager = dreamManager; + mBubbleController = bubbleController; + mAssistManagerLazy = assistManagerLazy; mRemoteInputManager = remoteInputManager; + mGroupManager = groupManager; mLockscreenUserManager = lockscreenUserManager; mShadeController = shadeController; - // TODO: use KeyguardStateController#isOccluded to remove this dependency - mStatusBar = statusBar; mKeyguardStateController = keyguardStateController; - mActivityStarter = activityStarter; - mEntryManager = entryManager; - mStatusBarStateController = statusBarStateController; mNotificationInterruptStateProvider = notificationInterruptStateProvider; - mMetricsLogger = metricsLogger; - mAssistManagerLazy = assistManagerLazy; - mGroupManager = groupManager; mLockPatternUtils = lockPatternUtils; - mBackgroundHandler = backgroundHandler; - mUiBgExecutor = uiBgExecutor; + mStatusBarRemoteInputCallback = remoteInputCallback; + mActivityIntentHelper = activityIntentHelper; + mFeatureFlags = featureFlags; - mNotifPipeline = notifPipeline; - mNotifCollection = notifCollection; + mMetricsLogger = metricsLogger; + mLogger = logger; + + // TODO: use KeyguardStateController#isOccluded to remove this dependency + mStatusBar = statusBar; + mPresenter = presenter; + mNotificationPanel = panel; + mActivityLaunchAnimator = activityLaunchAnimator; + if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) { mEntryManager.addNotificationEntryListener(new NotificationEntryListener() { @Override @@ -192,11 +218,6 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit } }); } - - mStatusBarRemoteInputCallback = remoteInputCallback; - mMainThreadHandler = mainThreadHandler; - mActivityIntentHelper = activityIntentHelper; - mBubbleController = bubbleController; } /** @@ -207,6 +228,8 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit */ @Override public void onNotificationClicked(StatusBarNotification sbn, ExpandableNotificationRow row) { + mLogger.logStartingActivityFromClick(sbn.getKey()); + RemoteInputController controller = mRemoteInputManager.getController(); if (controller.isRemoteInputActive(row.getEntry()) && !TextUtils.isEmpty(row.getActiveRemoteInputText())) { @@ -225,7 +248,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit // The only valid case is Bubble notifications. Guard against other cases // entering here. if (intent == null && !isBubble) { - Log.e(TAG, "onNotificationClicked called for non-clickable notification!"); + mLogger.logNonClickableNotification(sbn.getKey()); return; } @@ -258,6 +281,8 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit boolean isActivityIntent, boolean wasOccluded, boolean showOverLockscreen) { + mLogger.logHandleClickAfterKeyguardDismissed(sbn.getKey()); + // TODO: Some of this code may be able to move to NotificationEntryManager. if (mHeadsUpManager != null && mHeadsUpManager.isAlerting(sbn.getKey())) { // Release the HUN notification to the shade. @@ -304,6 +329,8 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit boolean isActivityIntent, boolean wasOccluded, NotificationEntry parentToCancelFinal) { + mLogger.logHandleClickAfterPanelCollapsed(sbn.getKey()); + String notificationKey = sbn.getKey(); try { // The intent we are sending is for the application, which @@ -343,9 +370,11 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit remoteInputText.toString()); } if (isBubble) { + mLogger.logExpandingBubble(notificationKey); expandBubbleStackOnMainThread(notificationKey); } else { - startNotificationIntent(intent, fillInIntent, row, wasOccluded, isActivityIntent); + startNotificationIntent( + intent, fillInIntent, entry, row, wasOccluded, isActivityIntent); } if (isActivityIntent || isBubble) { mAssistManagerLazy.get().hideAssist(); @@ -392,10 +421,16 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit } } - private void startNotificationIntent(PendingIntent intent, Intent fillInIntent, - View row, boolean wasOccluded, boolean isActivityIntent) { + private void startNotificationIntent( + PendingIntent intent, + Intent fillInIntent, + NotificationEntry entry, + View row, + boolean wasOccluded, + boolean isActivityIntent) { RemoteAnimationAdapter adapter = mActivityLaunchAnimator.getLaunchAnimation(row, wasOccluded); + mLogger.logStartNotificationIntent(entry.getKey(), intent); try { if (adapter != null) { ActivityTaskManager.getService() @@ -408,7 +443,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit } catch (RemoteException | PendingIntent.CanceledException e) { // the stack trace isn't very helpful here. // Just log the exception message. - Log.w(TAG, "Sending contentIntent failed: " + e); + mLogger.logSendingIntentFailed(e); // TODO: Dismiss Keyguard. } } @@ -438,13 +473,9 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit private void handleFullScreenIntent(NotificationEntry entry) { if (mNotificationInterruptStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) { if (shouldSuppressFullScreenIntent(entry)) { - if (DEBUG) { - Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + entry.getKey()); - } + mLogger.logFullScreenIntentSuppressedByDnD(entry.getKey()); } else if (entry.getImportance() < NotificationManager.IMPORTANCE_HIGH) { - if (DEBUG) { - Log.d(TAG, "No Fullscreen intent: not important enough: " + entry.getKey()); - } + mLogger.logFullScreenIntentNotImportantEnough(entry.getKey()); } else { // Stop screensaver if the notification has a fullscreen intent. // (like an incoming phone call) @@ -457,13 +488,13 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit }); // not immersive & a fullscreen alert should be shown - if (DEBUG) { - Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent"); - } + final PendingIntent fullscreenIntent = + entry.getSbn().getNotification().fullScreenIntent; + mLogger.logSendingFullScreenIntent(entry.getKey(), fullscreenIntent); try { EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION, entry.getKey()); - entry.getSbn().getNotification().fullScreenIntent.send(); + fullscreenIntent.send(); entry.notifyFullScreenIntentLaunched(); mMetricsLogger.count("note_fullscreen", 1); } catch (PendingIntent.CanceledException e) { @@ -578,9 +609,10 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit public static class Builder { private final Context mContext; private final CommandQueue mCommandQueue; - private final Lazy<AssistManager> mAssistManagerLazy; + private final Handler mMainThreadHandler; + private final Handler mBackgroundHandler; + private final Executor mUiBgExecutor; private final NotificationEntryManager mEntryManager; - private final FeatureFlags mFeatureFlags; private final NotifPipeline mNotifPipeline; private final NotifCollection mNotifCollection; private final HeadsUpManagerPhone mHeadsUpManager; @@ -590,30 +622,37 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; private final KeyguardManager mKeyguardManager; private final IDreamManager mDreamManager; + private final BubbleController mBubbleController; + private final Lazy<AssistManager> mAssistManagerLazy; private final NotificationRemoteInputManager mRemoteInputManager; - private final StatusBarRemoteInputCallback mRemoteInputCallback; private final NotificationGroupManager mGroupManager; private final NotificationLockscreenUserManager mLockscreenUserManager; + private final ShadeController mShadeController; private final KeyguardStateController mKeyguardStateController; - private final MetricsLogger mMetricsLogger; + private final NotificationInterruptStateProvider mNotificationInterruptStateProvider; private final LockPatternUtils mLockPatternUtils; - private final Handler mMainThreadHandler; - private final Handler mBackgroundHandler; - private final Executor mUiBgExecutor; + private final StatusBarRemoteInputCallback mRemoteInputCallback; private final ActivityIntentHelper mActivityIntentHelper; - private final BubbleController mBubbleController; - private NotificationPanelViewController mNotificationPanelViewController; - private NotificationInterruptStateProvider mNotificationInterruptStateProvider; - private final ShadeController mShadeController; + + private final FeatureFlags mFeatureFlags; + private final MetricsLogger mMetricsLogger; + private final StatusBarNotificationActivityStarterLogger mLogger; + + private StatusBar mStatusBar; private NotificationPresenter mNotificationPresenter; + private NotificationPanelViewController mNotificationPanelViewController; private ActivityLaunchAnimator mActivityLaunchAnimator; - private StatusBar mStatusBar; @Inject - public Builder(Context context, + public Builder( + Context context, CommandQueue commandQueue, - Lazy<AssistManager> assistManagerLazy, + @Main Handler mainThreadHandler, + @Background Handler backgroundHandler, + @UiBackground Executor uiBgExecutor, NotificationEntryManager entryManager, + NotifPipeline notifPipeline, + NotifCollection notifCollection, HeadsUpManagerPhone headsUpManager, ActivityStarter activityStarter, IStatusBarService statusBarService, @@ -621,27 +660,30 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit StatusBarKeyguardViewManager statusBarKeyguardViewManager, KeyguardManager keyguardManager, IDreamManager dreamManager, + BubbleController bubbleController, + Lazy<AssistManager> assistManagerLazy, NotificationRemoteInputManager remoteInputManager, - StatusBarRemoteInputCallback remoteInputCallback, NotificationGroupManager groupManager, NotificationLockscreenUserManager lockscreenUserManager, + ShadeController shadeController, KeyguardStateController keyguardStateController, NotificationInterruptStateProvider notificationInterruptStateProvider, - MetricsLogger metricsLogger, LockPatternUtils lockPatternUtils, - @Main Handler mainThreadHandler, - @Background Handler backgroundHandler, - @UiBackground Executor uiBgExecutor, + StatusBarRemoteInputCallback remoteInputCallback, ActivityIntentHelper activityIntentHelper, - BubbleController bubbleController, - ShadeController shadeController, + FeatureFlags featureFlags, - NotifPipeline notifPipeline, - NotifCollection notifCollection) { + MetricsLogger metricsLogger, + StatusBarNotificationActivityStarterLogger logger) { + mContext = context; mCommandQueue = commandQueue; - mAssistManagerLazy = assistManagerLazy; + mMainThreadHandler = mainThreadHandler; + mBackgroundHandler = backgroundHandler; + mUiBgExecutor = uiBgExecutor; mEntryManager = entryManager; + mNotifPipeline = notifPipeline; + mNotifCollection = notifCollection; mHeadsUpManager = headsUpManager; mActivityStarter = activityStarter; mStatusBarService = statusBarService; @@ -649,23 +691,21 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; mKeyguardManager = keyguardManager; mDreamManager = dreamManager; + mBubbleController = bubbleController; + mAssistManagerLazy = assistManagerLazy; mRemoteInputManager = remoteInputManager; - mRemoteInputCallback = remoteInputCallback; mGroupManager = groupManager; mLockscreenUserManager = lockscreenUserManager; + mShadeController = shadeController; mKeyguardStateController = keyguardStateController; mNotificationInterruptStateProvider = notificationInterruptStateProvider; - mMetricsLogger = metricsLogger; mLockPatternUtils = lockPatternUtils; - mMainThreadHandler = mainThreadHandler; - mBackgroundHandler = backgroundHandler; - mUiBgExecutor = uiBgExecutor; + mRemoteInputCallback = remoteInputCallback; mActivityIntentHelper = activityIntentHelper; - mBubbleController = bubbleController; - mShadeController = shadeController; + mFeatureFlags = featureFlags; - mNotifPipeline = notifPipeline; - mNotifCollection = notifCollection; + mMetricsLogger = metricsLogger; + mLogger = logger; } /** Sets the status bar to use as {@link StatusBar}. */ @@ -692,37 +732,42 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit } public StatusBarNotificationActivityStarter build() { - return new StatusBarNotificationActivityStarter(mContext, - mCommandQueue, mAssistManagerLazy, - mNotificationPanelViewController, - mNotificationPresenter, + return new StatusBarNotificationActivityStarter( + mContext, + mCommandQueue, + mMainThreadHandler, + mBackgroundHandler, + mUiBgExecutor, mEntryManager, + mNotifPipeline, + mNotifCollection, mHeadsUpManager, mActivityStarter, - mActivityLaunchAnimator, mStatusBarService, mStatusBarStateController, mStatusBarKeyguardViewManager, mKeyguardManager, mDreamManager, + mBubbleController, + mAssistManagerLazy, mRemoteInputManager, - mRemoteInputCallback, mGroupManager, mLockscreenUserManager, mShadeController, - mStatusBar, mKeyguardStateController, mNotificationInterruptStateProvider, - mMetricsLogger, mLockPatternUtils, - mMainThreadHandler, - mBackgroundHandler, - mUiBgExecutor, + mRemoteInputCallback, mActivityIntentHelper, - mBubbleController, + mFeatureFlags, - mNotifPipeline, - mNotifCollection); + mMetricsLogger, + mLogger, + + mStatusBar, + mNotificationPresenter, + mNotificationPanelViewController, + mActivityLaunchAnimator); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt new file mode 100644 index 000000000000..d118747a0365 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone + +import android.app.PendingIntent +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel.DEBUG +import com.android.systemui.log.LogLevel.ERROR +import com.android.systemui.log.LogLevel.INFO +import com.android.systemui.log.LogLevel.WARNING +import com.android.systemui.log.dagger.NotifInteractionLog +import javax.inject.Inject + +class StatusBarNotificationActivityStarterLogger @Inject constructor( + @NotifInteractionLog private val buffer: LogBuffer +) { + fun logStartingActivityFromClick(key: String) { + buffer.log(TAG, DEBUG, { + str1 = key + }, { + "(1/4) onNotificationClicked: $str1" + }) + } + + fun logHandleClickAfterKeyguardDismissed(key: String) { + buffer.log(TAG, DEBUG, { + str1 = key + }, { + "(2/4) handleNotificationClickAfterKeyguardDismissed: $str1" + }) + } + + fun logHandleClickAfterPanelCollapsed(key: String) { + buffer.log(TAG, DEBUG, { + str1 = key + }, { + "(3/4) handleNotificationClickAfterPanelCollapsed: $str1" + }) + } + + fun logStartNotificationIntent(key: String, pendingIntent: PendingIntent) { + buffer.log(TAG, INFO, { + str1 = key + str2 = pendingIntent.intent.toString() + }, { + "(4/4) Starting $str2 for notification $str1" + }) + } + + fun logExpandingBubble(key: String) { + buffer.log(TAG, DEBUG, { + str1 = key + }, { + "Expanding bubble for $str1 (rather than firing intent)" + }) + } + + fun logSendingIntentFailed(e: Exception) { + buffer.log(TAG, WARNING, { + str1 = e.toString() + }, { + "Sending contentIntentFailed: $str1" + }) + } + + fun logNonClickableNotification(key: String) { + buffer.log(TAG, ERROR, { + str1 = key + }, { + "onNotificationClicked called for non-clickable notification! $str1" + }) + } + + fun logFullScreenIntentSuppressedByDnD(key: String) { + buffer.log(TAG, DEBUG, { + str1 = key + }, { + "No Fullscreen intent: suppressed by DND: $str1" + }) + } + + fun logFullScreenIntentNotImportantEnough(key: String) { + buffer.log(TAG, DEBUG, { + str1 = key + }, { + "No Fullscreen intent: not important enough: $str1" + }) + } + + fun logSendingFullScreenIntent(key: String, pendingIntent: PendingIntent) { + buffer.log(TAG, INFO, { + str1 = key + str2 = pendingIntent.intent.toString() + }, { + "Notification $str1 has fullScreenIntent; sending fullScreenIntent $str2" + }) + } +} + +private const val TAG = "NotifActivityStarter" diff --git a/packages/SystemUI/src/com/android/systemui/util/RelativeTouchListener.kt b/packages/SystemUI/src/com/android/systemui/util/RelativeTouchListener.kt index d65b285adb0c..8880df9959c1 100644 --- a/packages/SystemUI/src/com/android/systemui/util/RelativeTouchListener.kt +++ b/packages/SystemUI/src/com/android/systemui/util/RelativeTouchListener.kt @@ -115,7 +115,9 @@ abstract class RelativeTouchListener : View.OnTouchListener { performedLongClick = false handler.postDelayed({ - performedLongClick = v.performLongClick() + if (v.isLongClickable) { + performedLongClick = v.performLongClick() + } }, ViewConfiguration.getLongPressTimeout().toLong()) } diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt b/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt index 8625d63a3c7e..db08d64acc10 100644 --- a/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt +++ b/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt @@ -61,14 +61,17 @@ internal val animators = WeakHashMap<Any, PhysicsAnimator<*>>() /** * Default spring configuration to use for animations where stiffness and/or damping ratio - * were not provided. + * were not provided, and a default spring was not set via [PhysicsAnimator.setDefaultSpringConfig]. */ -private val defaultSpring = PhysicsAnimator.SpringConfig( +private val globalDefaultSpring = PhysicsAnimator.SpringConfig( SpringForce.STIFFNESS_MEDIUM, SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY) -/** Default fling configuration to use for animations where friction was not provided. */ -private val defaultFling = PhysicsAnimator.FlingConfig( +/** + * Default fling configuration to use for animations where friction was not provided, and a default + * fling config was not set via [PhysicsAnimator.setDefaultFlingConfig]. + */ +private val globalDefaultFling = PhysicsAnimator.FlingConfig( friction = 1f, min = -Float.MAX_VALUE, max = Float.MAX_VALUE) /** Whether to log helpful debug information about animations. */ @@ -111,6 +114,12 @@ class PhysicsAnimator<T> private constructor (val target: T) { /** End actions to run when all animations have completed. */ private val endActions = ArrayList<EndAction>() + /** SpringConfig to use by default for properties whose springs were not provided. */ + private var defaultSpring: SpringConfig = globalDefaultSpring + + /** FlingConfig to use by default for properties whose fling configs were not provided. */ + private var defaultFling: FlingConfig = globalDefaultFling + /** * Internal listeners that respond to DynamicAnimations updating and ending, and dispatch to * the listeners provided via [addUpdateListener] and [addEndListener]. This allows us to add @@ -204,6 +213,19 @@ class PhysicsAnimator<T> private constructor (val target: T) { } /** + * Springs a property to a given value using the provided configuration options, and a start + * velocity of 0f. + * + * @see spring + */ + fun spring( + property: FloatPropertyCompat<in T>, + toPosition: Float + ): PhysicsAnimator<T> { + return spring(property, toPosition, 0f) + } + + /** * Flings a property using the given start velocity, using a [FlingAnimation] configured using * the provided configuration settings. * @@ -392,6 +414,14 @@ class PhysicsAnimator<T> private constructor (val target: T) { return this } + fun setDefaultSpringConfig(defaultSpring: SpringConfig) { + this.defaultSpring = defaultSpring + } + + fun setDefaultFlingConfig(defaultFling: FlingConfig) { + this.defaultFling = defaultFling + } + /** Starts the animations! */ fun start() { startAction() @@ -752,7 +782,7 @@ class PhysicsAnimator<T> private constructor (val target: T) { ) { constructor() : - this(defaultSpring.stiffness, defaultSpring.dampingRatio) + this(globalDefaultSpring.stiffness, globalDefaultSpring.dampingRatio) constructor(stiffness: Float, dampingRatio: Float) : this(stiffness = stiffness, dampingRatio = dampingRatio, startVelocity = 0f) @@ -782,10 +812,10 @@ class PhysicsAnimator<T> private constructor (val target: T) { internal var startVelocity: Float ) { - constructor() : this(defaultFling.friction) + constructor() : this(globalDefaultFling.friction) constructor(friction: Float) : - this(friction, defaultFling.min, defaultFling.max) + this(friction, globalDefaultFling.min, globalDefaultFling.max) constructor(friction: Float, min: Float, max: Float) : this(friction, min, max, startVelocity = 0f) diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java index cc6d607a60cf..8acfbf2b6996 100644 --- a/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java +++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java @@ -137,6 +137,36 @@ public abstract class ConcurrencyModule { } /** + * Provide a Background-Thread Executor by default. + */ + @Provides + @Singleton + public static RepeatableExecutor provideRepeatableExecutor(@Background DelayableExecutor exec) { + return new RepeatableExecutorImpl(exec); + } + + /** + * Provide a Background-Thread Executor. + */ + @Provides + @Singleton + @Background + public static RepeatableExecutor provideBackgroundRepeatableExecutor( + @Background DelayableExecutor exec) { + return new RepeatableExecutorImpl(exec); + } + + /** + * Provide a Main-Thread Executor. + */ + @Provides + @Singleton + @Main + public static RepeatableExecutor provideMainRepeatableExecutor(@Main DelayableExecutor exec) { + return new RepeatableExecutorImpl(exec); + } + + /** * Provide an Executor specifically for running UI operations on a separate thread. * * Keep submitted runnables short and to the point, just as with any other UI code. diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/RepeatableExecutor.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/RepeatableExecutor.java new file mode 100644 index 000000000000..aefdc992e831 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/RepeatableExecutor.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util.concurrency; + +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; + +/** + * A sub-class of {@link Executor} that allows scheduling commands to execute periodically. + */ +public interface RepeatableExecutor extends Executor { + + /** + * Execute supplied Runnable on the Executors thread after initial delay, and subsequently with + * the given delay between the termination of one execution and the commencement of the next. + * + * Each invocation of the supplied Runnable will be scheduled after the previous invocation + * completes. For example, if you schedule the Runnable with a 60 second delay, and the Runnable + * itself takes 1 second, the effective delay will be 61 seconds between each invocation. + * + * See {@link java.util.concurrent.ScheduledExecutorService#scheduleRepeatedly(Runnable, + * long, long)} + * + * @return A Runnable that, when run, removes the supplied argument from the Executor queue. + */ + default Runnable executeRepeatedly(Runnable r, long initialDelayMillis, long delayMillis) { + return executeRepeatedly(r, initialDelayMillis, delayMillis, TimeUnit.MILLISECONDS); + } + + /** + * Execute supplied Runnable on the Executors thread after initial delay, and subsequently with + * the given delay between the termination of one execution and the commencement of the next.. + * + * See {@link java.util.concurrent.ScheduledExecutorService#scheduleRepeatedly(Runnable, + * long, long)} + * + * @return A Runnable that, when run, removes the supplied argument from the Executor queue. + */ + Runnable executeRepeatedly(Runnable r, long initialDelay, long delay, TimeUnit unit); +} diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/RepeatableExecutorImpl.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/RepeatableExecutorImpl.java new file mode 100644 index 000000000000..c03e10e5c981 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/RepeatableExecutorImpl.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util.concurrency; + +import java.util.concurrent.TimeUnit; + +/** + * Implementation of {@link RepeatableExecutor} for SystemUI. + */ +class RepeatableExecutorImpl implements RepeatableExecutor { + + private final DelayableExecutor mExecutor; + + RepeatableExecutorImpl(DelayableExecutor executor) { + mExecutor = executor; + } + + @Override + public void execute(Runnable command) { + mExecutor.execute(command); + } + + @Override + public Runnable executeRepeatedly(Runnable r, long initDelay, long delay, TimeUnit unit) { + ExecutionToken token = new ExecutionToken(r, delay, unit); + token.start(initDelay, unit); + return token::cancel; + } + + private class ExecutionToken implements Runnable { + private final Runnable mCommand; + private final long mDelay; + private final TimeUnit mUnit; + private final Object mLock = new Object(); + private Runnable mCancel; + + ExecutionToken(Runnable r, long delay, TimeUnit unit) { + mCommand = r; + mDelay = delay; + mUnit = unit; + } + + @Override + public void run() { + mCommand.run(); + synchronized (mLock) { + if (mCancel != null) { + mCancel = mExecutor.executeDelayed(this, mDelay, mUnit); + } + } + } + + /** Starts execution that will repeat the command until {@link cancel}. */ + public void start(long startDelay, TimeUnit unit) { + synchronized (mLock) { + mCancel = mExecutor.executeDelayed(this, startDelay, unit); + } + } + + /** Cancel repeated execution of command. */ + public void cancel() { + synchronized (mLock) { + if (mCancel != null) { + mCancel.run(); + mCancel = null; + } + } + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt index f6ee46b0303a..e3f25c6b0c72 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt @@ -89,6 +89,7 @@ class ControlsControllerImplTest : SysuiTestCase() { @Captor private lateinit var controlLoadCallbackCaptor: ArgumentCaptor<ControlsBindingController.LoadCallback> + @Captor private lateinit var broadcastReceiverCaptor: ArgumentCaptor<BroadcastReceiver> @Captor @@ -97,6 +98,7 @@ class ControlsControllerImplTest : SysuiTestCase() { private lateinit var delayableExecutor: FakeExecutor private lateinit var controller: ControlsControllerImpl + private lateinit var canceller: DidRunRunnable companion object { fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture() @@ -146,6 +148,9 @@ class ControlsControllerImplTest : SysuiTestCase() { } } + canceller = DidRunRunnable() + `when`(bindingController.bindAndLoad(any(), any())).thenReturn(canceller) + controller = ControlsControllerImpl( wrapper, delayableExecutor, @@ -266,7 +271,7 @@ class ControlsControllerImplTest : SysuiTestCase() { assertTrue(favorites.isEmpty()) assertFalse(data.errorOnLoad) - }) + }, Consumer {}) verify(bindingController).bindAndLoad(eq(TEST_COMPONENT), capture(controlLoadCallbackCaptor)) @@ -301,7 +306,7 @@ class ControlsControllerImplTest : SysuiTestCase() { assertEquals(1, favorites.size) assertEquals(TEST_CONTROL_ID, favorites[0]) assertFalse(data.errorOnLoad) - }) + }, Consumer {}) verify(bindingController).bindAndLoad(eq(TEST_COMPONENT), capture(controlLoadCallbackCaptor)) @@ -332,7 +337,7 @@ class ControlsControllerImplTest : SysuiTestCase() { assertEquals(1, favorites.size) assertEquals(TEST_CONTROL_ID, favorites[0]) assertFalse(data.errorOnLoad) - }) + }, Consumer {}) verify(bindingController).bindAndLoad(eq(TEST_COMPONENT), capture(controlLoadCallbackCaptor)) @@ -363,7 +368,7 @@ class ControlsControllerImplTest : SysuiTestCase() { assertEquals(1, favorites.size) assertEquals(TEST_CONTROL_ID, favorites[0]) assertTrue(data.errorOnLoad) - }) + }, Consumer {}) verify(bindingController).bindAndLoad(eq(TEST_COMPONENT), capture(controlLoadCallbackCaptor)) @@ -377,22 +382,15 @@ class ControlsControllerImplTest : SysuiTestCase() { @Test fun testCancelLoad() { - val canceller = object : Runnable { - var ran = false - override fun run() { - ran = true - } - } - `when`(bindingController.bindAndLoad(any(), any())).thenReturn(canceller) - var loaded = false + var cancelRunnable: Runnable? = null controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO) delayableExecutor.runAllReady() controller.loadForComponent(TEST_COMPONENT, Consumer { loaded = true - }) + }, Consumer { runnable -> cancelRunnable = runnable }) - controller.cancelLoad() + cancelRunnable?.run() delayableExecutor.runAllReady() assertFalse(loaded) @@ -400,61 +398,47 @@ class ControlsControllerImplTest : SysuiTestCase() { } @Test - fun testCancelLoad_noCancelAfterSuccessfulLoad() { - val canceller = object : Runnable { - var ran = false - override fun run() { - ran = true - } - } - `when`(bindingController.bindAndLoad(any(), any())).thenReturn(canceller) - + fun testCancelLoad_afterSuccessfulLoad() { var loaded = false + var cancelRunnable: Runnable? = null controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO) delayableExecutor.runAllReady() controller.loadForComponent(TEST_COMPONENT, Consumer { loaded = true - }) + }, Consumer { runnable -> cancelRunnable = runnable }) verify(bindingController).bindAndLoad(eq(TEST_COMPONENT), capture(controlLoadCallbackCaptor)) controlLoadCallbackCaptor.value.accept(emptyList()) - controller.cancelLoad() + cancelRunnable?.run() delayableExecutor.runAllReady() assertTrue(loaded) - assertFalse(canceller.ran) + assertTrue(canceller.ran) } @Test - fun testCancelLoad_noCancelAfterErrorLoad() { - val canceller = object : Runnable { - var ran = false - override fun run() { - ran = true - } - } - `when`(bindingController.bindAndLoad(any(), any())).thenReturn(canceller) - + fun testCancelLoad_afterErrorLoad() { var loaded = false + var cancelRunnable: Runnable? = null controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO) delayableExecutor.runAllReady() controller.loadForComponent(TEST_COMPONENT, Consumer { loaded = true - }) + }, Consumer { runnable -> cancelRunnable = runnable }) verify(bindingController).bindAndLoad(eq(TEST_COMPONENT), capture(controlLoadCallbackCaptor)) controlLoadCallbackCaptor.value.error("") - controller.cancelLoad() + cancelRunnable?.run() delayableExecutor.runAllReady() assertTrue(loaded) - assertFalse(canceller.ran) + assertTrue(canceller.ran) } @Test @@ -465,7 +449,7 @@ class ControlsControllerImplTest : SysuiTestCase() { val newControlInfo = TEST_CONTROL_INFO.copy(controlTitle = TEST_CONTROL_TITLE_2) val control = statelessBuilderFromInfo(newControlInfo).build() - controller.loadForComponent(TEST_COMPONENT, Consumer {}) + controller.loadForComponent(TEST_COMPONENT, Consumer {}, Consumer {}) verify(bindingController).bindAndLoad(eq(TEST_COMPONENT), capture(controlLoadCallbackCaptor)) @@ -963,3 +947,10 @@ class ControlsControllerImplTest : SysuiTestCase() { assertTrue(controller.getFavoritesForStructure(TEST_COMPONENT_2, TEST_STRUCTURE).isEmpty()) } } + +private class DidRunRunnable() : Runnable { + var ran = false + override fun run() { + ran = true + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java index e8e98b49c47f..4ac5912d0690 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java @@ -133,4 +133,12 @@ public class ScreenRecordTileTest extends SysuiTestCase { verify(mController, times(1)).stopRecording(); } + + @Test + public void testContentDescriptionHasTileName() { + mTile.refreshState(); + mTestableLooper.processAllMessages(); + + assertTrue(mTile.getState().contentDescription.toString().contains(mTile.getState().label)); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java index 11649ca34709..ffe3cd5bd504 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java @@ -16,6 +16,10 @@ package com.android.systemui.screenshot; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; @@ -40,7 +44,6 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SystemUIFactory; import com.android.systemui.SysuiTestCase; -import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -82,9 +85,9 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase { CompletableFuture<List<Notification.Action>> smartActionsFuture = ScreenshotSmartActions.getSmartActionsFuture("", "", bitmap, smartActionsProvider, true, false); - Assert.assertNotNull(smartActionsFuture); + assertNotNull(smartActionsFuture); List<Notification.Action> smartActions = smartActionsFuture.get(5, TimeUnit.MILLISECONDS); - Assert.assertEquals(Collections.emptyList(), smartActions); + assertEquals(Collections.emptyList(), smartActions); } // Tests any exception thrown in waiting for smart actions future to complete does @@ -99,7 +102,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase { RuntimeException.class); List<Notification.Action> actions = ScreenshotSmartActions.getSmartActions( "", "", smartActionsFuture, timeoutMs, mSmartActionsProvider); - Assert.assertEquals(Collections.emptyList(), actions); + assertEquals(Collections.emptyList(), actions); } // Tests any exception thrown in notifying feedback does not affect regular screenshot flow. @@ -123,9 +126,9 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase { mSmartActionsProvider, true, true); verify(mSmartActionsProvider, never()).getActions(any(), any(), any(), any(), eq(false)); - Assert.assertNotNull(smartActionsFuture); + assertNotNull(smartActionsFuture); List<Notification.Action> smartActions = smartActionsFuture.get(5, TimeUnit.MILLISECONDS); - Assert.assertEquals(Collections.emptyList(), smartActions); + assertEquals(Collections.emptyList(), smartActions); } // Tests for a hardware bitmap, ScreenshotNotificationSmartActionsProvider is invoked once. @@ -152,14 +155,14 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase { ScreenshotSmartActions.getSmartActionsFuture("", "", bitmap, actionsProvider, true, true); - Assert.assertNotNull(smartActionsFuture); + assertNotNull(smartActionsFuture); List<Notification.Action> smartActions = smartActionsFuture.get(5, TimeUnit.MILLISECONDS); - Assert.assertEquals(smartActions.size(), 0); + assertEquals(smartActions.size(), 0); } - // Tests for notification action extras. + // Tests for share action extras @Test - public void testNotificationActionExtras() { + public void testShareActionExtras() { if (Looper.myLooper() == null) { Looper.prepare(); } @@ -169,35 +172,70 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase { data.image = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); data.finisher = null; data.mActionsReadyListener = null; - data.createDeleteAction = true; SaveImageInBackgroundTask task = new SaveImageInBackgroundTask(mContext, data); - List<Notification.Action> actions = task.populateNotificationActions( - mContext, mContext.getResources(), + + Notification.Action shareAction = task.createShareAction(mContext, mContext.getResources(), Uri.parse("Screenshot_123.png")); - Assert.assertEquals(actions.size(), 3); - boolean isShareFound = false; - boolean isEditFound = false; - boolean isDeleteFound = false; - for (Notification.Action action : actions) { - Intent intent = action.actionIntent.getIntent(); - Assert.assertNotNull(intent); - Bundle bundle = intent.getExtras(); - Assert.assertTrue(bundle.containsKey(GlobalScreenshot.EXTRA_ID)); - Assert.assertTrue( - bundle.containsKey(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED)); - - if (action.title.equals(GlobalScreenshot.ACTION_TYPE_DELETE)) { - isDeleteFound = intent.getAction() == null; - } else if (action.title.equals(GlobalScreenshot.ACTION_TYPE_EDIT)) { - isEditFound = Intent.ACTION_EDIT.equals(intent.getAction()); - } else if (action.title.equals(GlobalScreenshot.ACTION_TYPE_SHARE)) { - isShareFound = Intent.ACTION_SEND.equals(intent.getAction()); - } + Intent intent = shareAction.actionIntent.getIntent(); + assertNotNull(intent); + Bundle bundle = intent.getExtras(); + assertTrue(bundle.containsKey(GlobalScreenshot.EXTRA_ID)); + assertTrue(bundle.containsKey(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED)); + assertEquals(GlobalScreenshot.ACTION_TYPE_SHARE, shareAction.title); + assertEquals(Intent.ACTION_SEND, intent.getAction()); + } + + // Tests for edit action extras + @Test + public void testEditActionExtras() { + if (Looper.myLooper() == null) { + Looper.prepare(); } - Assert.assertTrue(isEditFound); - Assert.assertTrue(isDeleteFound); - Assert.assertTrue(isShareFound); + GlobalScreenshot.SaveImageInBackgroundData + data = new GlobalScreenshot.SaveImageInBackgroundData(); + data.image = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); + data.finisher = null; + data.mActionsReadyListener = null; + SaveImageInBackgroundTask task = new SaveImageInBackgroundTask(mContext, data); + + Notification.Action editAction = task.createEditAction(mContext, mContext.getResources(), + Uri.parse("Screenshot_123.png")); + + Intent intent = editAction.actionIntent.getIntent(); + assertNotNull(intent); + Bundle bundle = intent.getExtras(); + assertTrue(bundle.containsKey(GlobalScreenshot.EXTRA_ID)); + assertTrue(bundle.containsKey(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED)); + assertEquals(GlobalScreenshot.ACTION_TYPE_EDIT, editAction.title); + assertEquals(Intent.ACTION_EDIT, intent.getAction()); + } + + // Tests for share action extras + @Test + public void testDeleteActionExtras() { + if (Looper.myLooper() == null) { + Looper.prepare(); + } + + GlobalScreenshot.SaveImageInBackgroundData + data = new GlobalScreenshot.SaveImageInBackgroundData(); + data.image = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); + data.finisher = null; + data.mActionsReadyListener = null; + SaveImageInBackgroundTask task = new SaveImageInBackgroundTask(mContext, data); + + Notification.Action deleteAction = task.createDeleteAction(mContext, + mContext.getResources(), + Uri.parse("Screenshot_123.png")); + + Intent intent = deleteAction.actionIntent.getIntent(); + assertNotNull(intent); + Bundle bundle = intent.getExtras(); + assertTrue(bundle.containsKey(GlobalScreenshot.EXTRA_ID)); + assertTrue(bundle.containsKey(GlobalScreenshot.EXTRA_SMART_ACTIONS_ENABLED)); + assertEquals(deleteAction.title, GlobalScreenshot.ACTION_TYPE_DELETE); + assertNull(intent.getAction()); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java index fb401778d891..23099d783586 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java @@ -193,7 +193,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { assertThat(mTextView.getText()).isEqualTo( mContext.getResources().getString(R.string.dock_alignment_slow_charging)); assertThat(mTextView.getCurrentTextColor()).isEqualTo( - Utils.getColorError(mContext).getDefaultColor()); + mContext.getColor(R.color.misalignment_text_color)); } @Test @@ -211,7 +211,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { assertThat(mTextView.getText()).isEqualTo( mContext.getResources().getString(R.string.dock_alignment_not_charging)); assertThat(mTextView.getCurrentTextColor()).isEqualTo( - Utils.getColorError(mContext).getDefaultColor()); + mContext.getColor(R.color.misalignment_text_color)); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java index e55ea41d94e5..d41b6cfb32c0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java @@ -46,6 +46,7 @@ import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.inflation.LowPriorityInflationHelper; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; @@ -110,7 +111,8 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase { mock(BubbleController.class), mock(DynamicPrivacyController.class), mock(ForegroundServiceSectionController.class), - mock(DynamicChildBindController.class)); + mock(DynamicChildBindController.class), + mock(LowPriorityInflationHelper.class)); mViewHierarchyManager.setUpWithPresenter(mPresenter, mListContainer); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java index 855f524db3f8..2894abb8f364 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java @@ -63,6 +63,7 @@ import com.android.systemui.statusbar.notification.NotificationFilter; import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationRankingManager; +import com.android.systemui.statusbar.notification.collection.inflation.LowPriorityInflationHelper; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; import com.android.systemui.statusbar.notification.icon.IconBuilder; @@ -264,7 +265,8 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { new IconManager( mEntryManager, mock(LauncherApps.class), - new IconBuilder(mContext))); + new IconBuilder(mContext)), + mock(LowPriorityInflationHelper.class)); mEntryManager.setUpWithPresenter(mPresenter); mEntryManager.addNotificationEntryListener(mEntryListener); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java index 96a58e27ed0d..ad3bd711c23f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java @@ -147,30 +147,6 @@ public class RowContentBindStageTest extends SysuiTestCase { } @Test - public void testSetUseGroupInChild() { - // GIVEN a view with all content bound. - RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry); - params.requireContentViews(FLAG_CONTENT_VIEW_ALL); - params.clearDirtyContentViews(); - - // WHEN use group is set and stage executed. - params.setUseChildInGroup(true); - mRowContentBindStage.executeStage(mEntry, mRow, (en) -> { }); - - // THEN binder is called with use group view and contracted/expanded are called to bind. - ArgumentCaptor<BindParams> bindParamsCaptor = ArgumentCaptor.forClass(BindParams.class); - verify(mBinder).bindContent( - eq(mEntry), - any(), - eq(FLAG_CONTENT_VIEW_CONTRACTED | FLAG_CONTENT_VIEW_EXPANDED), - bindParamsCaptor.capture(), - anyBoolean(), - any()); - BindParams usedParams = bindParamsCaptor.getValue(); - assertTrue(usedParams.isChildInGroup); - } - - @Test public void testSetUseIncreasedHeight() { // GIVEN a view with all content bound. RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java index 8ab660c3fca3..3e469073694f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java @@ -56,7 +56,7 @@ public class LightBarControllerTest extends SysuiTestCase { when(mStatusBarIconController.getTransitionsController()).thenReturn( mLightBarTransitionsController); mLightBarController = new LightBarController(mContext, mStatusBarIconController, - mock(BatteryController.class)); + mock(BatteryController.class), mock(NavigationModeController.class)); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarTransitionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarTransitionsTest.java index 27a50027cfe2..14c6e9f9624d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarTransitionsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarTransitionsTest.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.phone; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; @@ -50,9 +51,11 @@ public class NavigationBarTransitionsTest extends SysuiTestCase { mDependency.injectMockDependency(IWindowManager.class); mDependency.injectMockDependency(AssistManager.class); mDependency.injectMockDependency(OverviewProxyService.class); - mDependency.injectMockDependency(NavigationModeController.class); mDependency.injectMockDependency(StatusBarStateController.class); mDependency.injectMockDependency(KeyguardStateController.class); + doReturn(mContext) + .when(mDependency.injectMockDependency(NavigationModeController.class)) + .getCurrentUserContext(); NavigationBarView navBar = spy(new NavigationBarView(mContext, null)); when(navBar.getCurrentView()).thenReturn(navBar); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java index dd28687e749c..1afe132f00a9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java @@ -176,25 +176,43 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE); when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(false); - mNotificationActivityStarter = (new StatusBarNotificationActivityStarter.Builder( - getContext(), mock(CommandQueue.class), () -> mAssistManager, - mEntryManager, mock(HeadsUpManagerPhone.class), - mActivityStarter, mStatusBarService, - mock(StatusBarStateController.class), mStatusBarKeyguardViewManager, - mock(KeyguardManager.class), - mock(IDreamManager.class), mRemoteInputManager, - mock(StatusBarRemoteInputCallback.class), mock(NotificationGroupManager.class), - mock(NotificationLockscreenUserManager.class), - mKeyguardStateController, - mock(NotificationInterruptStateProvider.class), mock(MetricsLogger.class), - mock(LockPatternUtils.class), mHandler, mHandler, mUiBgExecutor, - mActivityIntentHelper, mBubbleController, mShadeController, mFeatureFlags, - mNotifPipeline, mNotifCollection) + mNotificationActivityStarter = + new StatusBarNotificationActivityStarter.Builder( + getContext(), + mock(CommandQueue.class), + mHandler, + mHandler, + mUiBgExecutor, + mEntryManager, + mNotifPipeline, + mNotifCollection, + mock(HeadsUpManagerPhone.class), + mActivityStarter, + mStatusBarService, + mock(StatusBarStateController.class), + mStatusBarKeyguardViewManager, + mock(KeyguardManager.class), + mock(IDreamManager.class), + mBubbleController, + () -> mAssistManager, + mRemoteInputManager, + mock(NotificationGroupManager.class), + mock(NotificationLockscreenUserManager.class), + mShadeController, + mKeyguardStateController, + mock(NotificationInterruptStateProvider.class), + mock(LockPatternUtils.class), + mock(StatusBarRemoteInputCallback.class), + mActivityIntentHelper, + + mFeatureFlags, + mock(MetricsLogger.class), + mock(StatusBarNotificationActivityStarterLogger.class)) .setStatusBar(mStatusBar) - .setNotificationPanelViewController(mock(NotificationPanelViewController.class)) .setNotificationPresenter(mock(NotificationPresenter.class)) - .setActivityLaunchAnimator(mock(ActivityLaunchAnimator.class))) - .build(); + .setNotificationPanelViewController(mock(NotificationPanelViewController.class)) + .setActivityLaunchAnimator(mock(ActivityLaunchAnimator.class)) + .build(); // set up dismissKeyguardThenExecute to synchronously invoke the OnDismissAction arg doAnswer(mCallOnDismiss).when(mActivityStarter).dismissKeyguardThenExecute( diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/RepeatableExecutorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/RepeatableExecutorTest.java new file mode 100644 index 000000000000..00f37ae6f6cb --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/RepeatableExecutorTest.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util.concurrency; + +import static com.google.common.truth.Truth.assertThat; + +import android.testing.AndroidTestingRunner; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.util.time.FakeSystemClock; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class RepeatableExecutorTest extends SysuiTestCase { + + private static final int DELAY = 100; + + private FakeSystemClock mFakeClock; + private FakeExecutor mFakeExecutor; + private RepeatableExecutor mExecutor; + private CountingTask mCountingTask; + + @Before + public void setUp() throws Exception { + mFakeClock = new FakeSystemClock(); + mFakeExecutor = new FakeExecutor(mFakeClock); + mCountingTask = new CountingTask(); + mExecutor = new RepeatableExecutorImpl(mFakeExecutor); + } + + /** + * Test FakeExecutor that receives non-delayed items to execute. + */ + @Test + public void testExecute() { + mExecutor.execute(mCountingTask); + mFakeExecutor.runAllReady(); + assertThat(mCountingTask.getCount()).isEqualTo(1); + } + + @Test + public void testRepeats() { + // GIVEN that a command is queued to repeat + mExecutor.executeRepeatedly(mCountingTask, DELAY, DELAY); + // WHEN The clock advances and the task is run + mFakeExecutor.advanceClockToNext(); + mFakeExecutor.runAllReady(); + // THEN another task is queued + assertThat(mCountingTask.getCount()).isEqualTo(1); + assertThat(mFakeExecutor.numPending()).isEqualTo(1); + } + + @Test + public void testNoExecutionBeforeStartDelay() { + // WHEN a command is queued with a start delay + mExecutor.executeRepeatedly(mCountingTask, 2 * DELAY, DELAY); + mFakeExecutor.runAllReady(); + // THEN then it doesn't run immediately + assertThat(mCountingTask.getCount()).isEqualTo(0); + assertThat(mFakeExecutor.numPending()).isEqualTo(1); + } + + @Test + public void testExecuteAfterStartDelay() { + // GIVEN that a command is queued to repeat with a longer start delay + mExecutor.executeRepeatedly(mCountingTask, 2 * DELAY, DELAY); + // WHEN the clock advances the start delay + mFakeClock.advanceTime(2 * DELAY); + mFakeExecutor.runAllReady(); + // THEN the command has run and another task is queued + assertThat(mCountingTask.getCount()).isEqualTo(1); + assertThat(mFakeExecutor.numPending()).isEqualTo(1); + } + + @Test + public void testExecuteWithZeroStartDelay() { + // WHEN a command is queued with no start delay + mExecutor.executeRepeatedly(mCountingTask, 0L, DELAY); + mFakeExecutor.runAllReady(); + // THEN the command has run and another task is queued + assertThat(mCountingTask.getCount()).isEqualTo(1); + assertThat(mFakeExecutor.numPending()).isEqualTo(1); + } + + @Test + public void testAdvanceTimeTwice() { + // GIVEN that a command is queued to repeat + mExecutor.executeRepeatedly(mCountingTask, DELAY, DELAY); + // WHEN the clock advances the time DELAY twice + mFakeClock.advanceTime(DELAY); + mFakeExecutor.runAllReady(); + mFakeClock.advanceTime(DELAY); + mFakeExecutor.runAllReady(); + // THEN the command has run twice and another task is queued + assertThat(mCountingTask.getCount()).isEqualTo(2); + assertThat(mFakeExecutor.numPending()).isEqualTo(1); + } + + @Test + public void testCancel() { + // GIVEN that a scheduled command has been cancelled + Runnable cancel = mExecutor.executeRepeatedly(mCountingTask, DELAY, DELAY); + cancel.run(); + // WHEN the clock advances the time DELAY + mFakeClock.advanceTime(DELAY); + mFakeExecutor.runAllReady(); + // THEN the comamnd has not run and no further tasks are queued + assertThat(mCountingTask.getCount()).isEqualTo(0); + assertThat(mFakeExecutor.numPending()).isEqualTo(0); + } + + @Test + public void testCancelAfterStart() { + // GIVEN that a command has reapeated a few times + Runnable cancel = mExecutor.executeRepeatedly(mCountingTask, DELAY, DELAY); + mFakeClock.advanceTime(DELAY); + mFakeExecutor.runAllReady(); + // WHEN cancelled and time advances + cancel.run(); + // THEN the command has only run the first time + assertThat(mCountingTask.getCount()).isEqualTo(1); + assertThat(mFakeExecutor.numPending()).isEqualTo(0); + } + + /** + * Runnable used for testing that counts the number of times run() is invoked. + */ + private static class CountingTask implements Runnable { + + private int mRunCount; + + @Override + public void run() { + mRunCount++; + } + + /** Gets the run count. */ + public int getCount() { + return mRunCount; + } + } +} diff --git a/packages/Tethering/res/values-af/strings.xml b/packages/Tethering/res/values-af/strings.xml index 1258805378ea..056168b12e89 100644 --- a/packages/Tethering/res/values-af/strings.xml +++ b/packages/Tethering/res/values-af/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Verbinding of Wi-Fi-warmkol aktief"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Tik om op te stel."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Verbinding is gedeaktiveer"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Kontak jou administrateur vir besonderhede"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Verbinding of warmkol is aktief"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Tik om op te stel."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Verbinding is gedeaktiveer"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Kontak jou administrateur vir besonderhede"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Warmkol- en verbindingstatus"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-am/strings.xml b/packages/Tethering/res/values-am/strings.xml index 9c36192257c0..ac468dd14414 100644 --- a/packages/Tethering/res/values-am/strings.xml +++ b/packages/Tethering/res/values-am/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"መሰካት ወይም ገባሪ ድረስ ነጥብ"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"ለማዋቀር መታ ያድርጉ።"</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"እንደ ሞደም መሰካት ተሰናክሏል"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"ለዝርዝሮች የእርስዎን አስተዳዳሪ ያነጋግሩ"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"እንደ ሞደም መሰካት ወይም መገናኛ ነጥብ ገባሪ"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"ለማዋቀር መታ ያድርጉ።"</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"እንደ ሞደም መሰካት ተሰናክሏል"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"ለዝርዝሮች የእርስዎን አስተዳዳሪ ያነጋግሩ"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"መገናኛ ነጥብ እና እንደ ሞደም የመሰካት ሁኔታ"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-ar/strings.xml b/packages/Tethering/res/values-ar/strings.xml index 9f84ce4090c6..7d5bad34da04 100644 --- a/packages/Tethering/res/values-ar/strings.xml +++ b/packages/Tethering/res/values-ar/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"النطاق أو نقطة الاتصال نشطة"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"انقر للإعداد."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"تم إيقاف التوصيل"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"اتصل بالمشرف للحصول على التفاصيل"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"النطاق نشط أو نقطة الاتصال نشطة"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"انقر للإعداد."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"التوصيل متوقف."</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"تواصَل مع المشرف للحصول على التفاصيل."</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"حالة نقطة الاتصال والتوصيل"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-as/strings.xml b/packages/Tethering/res/values-as/strings.xml index 8855822e7c8b..091350455ba0 100644 --- a/packages/Tethering/res/values-as/strings.xml +++ b/packages/Tethering/res/values-as/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"টেডাৰিং বা হটস্প\'ট সক্ৰিয় অৱস্থাত আছে"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"ছেট আপ কৰিবলৈ টিপক।"</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"টেডাৰিং অক্ষম কৰি থোৱা হৈছে"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"সবিশেষ জানিবলৈ আপোনাৰ প্ৰশাসকৰ সৈতে যোগাযোগ কৰক"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"টে\'ডাৰিং অথবা হ\'টস্প\'ট সক্ৰিয় অৱস্থাত আছে"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"ছেট আপ কৰিবলৈ টিপক।"</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"টে\'ডাৰিঙৰ সুবিধাটো অক্ষম কৰি থোৱা হৈছে"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"সবিশেষ জানিবলৈ আপোনাৰ প্ৰশাসকৰ সৈতে যোগাযোগ কৰক"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"হ’টস্প\'ট আৰু টে\'ডাৰিঙৰ স্থিতি"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-az/strings.xml b/packages/Tethering/res/values-az/strings.xml index eba50eb636c1..dce70da178f1 100644 --- a/packages/Tethering/res/values-az/strings.xml +++ b/packages/Tethering/res/values-az/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Tezerinq və ya hotspot aktivdir"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Quraşdırmaq üçün tıklayın."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Birləşmə deaktivdir"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Məlumat üçün adminlə əlaqə saxlayın"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Birləşmə və ya hotspot aktivdir"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Ayarlamaq üçün toxunun."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Birləşmə deaktivdir"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Detallar üçün adminlə əlaqə saxlayın"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot & birləşmə statusu"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-b+sr+Latn/strings.xml b/packages/Tethering/res/values-b+sr+Latn/strings.xml index 5b0e488ba5e6..b0774ec9a840 100644 --- a/packages/Tethering/res/values-b+sr+Latn/strings.xml +++ b/packages/Tethering/res/values-b+sr+Latn/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Aktivno povezivanje sa internetom preko mobilnog uređaja ili hotspot"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Dodirnite da biste podesili."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Privezivanje je onemogućeno"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Potražite detalje od administratora"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Privezivanje ili hotspot je aktivan"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Dodirnite da biste podesili."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Privezivanje je onemogućeno"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Potražite detalje od administratora"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status hotspota i privezivanja"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-be/strings.xml b/packages/Tethering/res/values-be/strings.xml index 5966c7155ecd..a8acebe2e992 100644 --- a/packages/Tethering/res/values-be/strings.xml +++ b/packages/Tethering/res/values-be/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"USB-мадэм або хот-спот Wi-Fi актыўныя"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Дакраніцеся, каб наладзіць."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Рэжым мадэма адключаны"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Звярніцеся да адміністратара па падрабязную інфармацыю"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Мадэм або хот-спот актыўныя"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Дакраніцеся, каб наладзіць."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Рэжым мадэма выключаны"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Звярніцеся да адміністратара па падрабязную інфармацыю"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Стан \"Хот-спот і мадэм\""</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-bg/strings.xml b/packages/Tethering/res/values-bg/strings.xml index ed58d7311aca..94fb2d8f176a 100644 --- a/packages/Tethering/res/values-bg/strings.xml +++ b/packages/Tethering/res/values-bg/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Има активна споделена връзка или безжична точка за достъп"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Докоснете, за да настроите."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Функцията за тетъринг е деактивирана"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Свържете се с администратора си за подробности"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Има активна споделена връзка или точка за достъп"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Докоснете, за да настроите."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Функцията за тетъринг е деактивирана"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Свържете се с администратора си за подробности"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Състояние на функцията за точка за достъп и тетъринг"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-bn/strings.xml b/packages/Tethering/res/values-bn/strings.xml index 8d9880aa9aca..aea02b9ddff8 100644 --- a/packages/Tethering/res/values-bn/strings.xml +++ b/packages/Tethering/res/values-bn/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"টিথারিং বা হটস্পট সক্রিয় আছে"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"সেট-আপ করার জন্য আলতো চাপুন৷"</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"টিথারিং অক্ষম করা আছে"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"বিশদ বিবরণের জন্য প্রশাসকের সাথে যোগাযোগ করুন"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"টিথারিং বা হটস্পট চালু আছে"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"সেট-আপ করতে ট্যাপ করুন।"</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"টিথারিং বন্ধ করা আছে"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"বিশদে জানতে অ্যাডমিনের সাথে যোগাযোগ করুন"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"হটস্পট ও টিথারিং স্ট্যাটাস"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-bs/strings.xml b/packages/Tethering/res/values-bs/strings.xml index 2361b9dd382c..de232724c5d9 100644 --- a/packages/Tethering/res/values-bs/strings.xml +++ b/packages/Tethering/res/values-bs/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Uređaj dijeli vezu ili djeluje kao pristupna tačka"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Dodirnite za postavke"</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Povezivanje putem mobitela je onemogućeno"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Kontaktirajte svog administratora za dodatne detalje"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Aktivno je povezivanje putem mobitela ili pristupna tačka"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Dodirnite da postavite."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Povezivanje putem mobitela je onemogućeno"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Kontaktirajte svog administratora za detalje"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status pristupne tačke i povezivanja putem mobitela"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-ca/strings.xml b/packages/Tethering/res/values-ca/strings.xml index 6752b519e218..88b795c1f8b2 100644 --- a/packages/Tethering/res/values-ca/strings.xml +++ b/packages/Tethering/res/values-ca/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Compartició de xarxa o punt d\'accés Wi-Fi activat"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Toca per configurar."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"La compartició de xarxa està desactivada"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Contacta amb el teu administrador per obtenir més informació"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Compartició de xarxa o punt d\'accés Wi‑Fi actius"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Toca per configurar."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"La compartició de xarxa està desactivada"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Contacta amb el teu administrador per obtenir més informació"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Estat del punt d\'accés Wi‑Fi i de la compartició de xarxa"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-cs/strings.xml b/packages/Tethering/res/values-cs/strings.xml index 5fdd53adf15c..8c1b83bf3ee3 100644 --- a/packages/Tethering/res/values-cs/strings.xml +++ b/packages/Tethering/res/values-cs/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Sdílené připojení nebo hotspot je aktivní."</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Klepnutím zahájíte nastavení."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering je zakázán"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"O podrobnosti požádejte administrátora"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering nebo hotspot je aktivní"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Klepnutím zahájíte nastavení."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering je zakázán"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"O podrobnosti požádejte administrátora"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Stav hotspotu a tetheringu"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-da/strings.xml b/packages/Tethering/res/values-da/strings.xml index 2775dfa551cb..f413e7054819 100644 --- a/packages/Tethering/res/values-da/strings.xml +++ b/packages/Tethering/res/values-da/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Netdeling eller hotspot er aktivt"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Tryk for at konfigurere"</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Netdeling er deaktiveret"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Kontakt din administrator for at få oplysninger"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Netdeling eller hotspot er aktivt"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Tryk for at konfigurere."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Netdeling er deaktiveret"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Kontakt din administrator for at få oplysninger"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status for hotspot og netdeling"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-de/strings.xml b/packages/Tethering/res/values-de/strings.xml index 9046cd5e1144..f057d7824e81 100644 --- a/packages/Tethering/res/values-de/strings.xml +++ b/packages/Tethering/res/values-de/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering oder Hotspot aktiv"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Zum Einrichten tippen."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering ist deaktiviert"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Bitte wende dich für weitere Informationen an den Administrator"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering oder Hotspot aktiv"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Zum Einrichten tippen."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering ist deaktiviert"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Bitte wende dich für weitere Informationen an den Administrator"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot- und Tethering-Status"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-el/strings.xml b/packages/Tethering/res/values-el/strings.xml index 3b9f53733b8a..b3c986bdafd6 100644 --- a/packages/Tethering/res/values-el/strings.xml +++ b/packages/Tethering/res/values-el/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Πρόσδεση ή σύνδεση σημείου πρόσβασης ενεργή"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Πατήστε για ρύθμιση."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Η σύνδεση είναι απενεργοποιημένη"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Επικοινωνήστε με τον διαχειριστή σας για λεπτομέρειες"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Πρόσδεση ή σύνδεση σημείου πρόσβασης ενεργή"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Πατήστε για ρύθμιση."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Η σύνδεση είναι απενεργοποιημένη"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Επικοινωνήστε με τον διαχειριστή σας για λεπτομέρειες"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Κατάσταση σημείου πρόσβασης Wi-Fi και σύνδεσης"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-en-rAU/strings.xml b/packages/Tethering/res/values-en-rAU/strings.xml index 56b88a5fb3e1..769e01208afc 100644 --- a/packages/Tethering/res/values-en-rAU/strings.xml +++ b/packages/Tethering/res/values-en-rAU/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering or hotspot active"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Tap to set up."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering is disabled"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Contact your admin for details"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering or hotspot active"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Tap to set up."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering is disabled"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Contact your admin for details"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot and tethering status"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-en-rCA/strings.xml b/packages/Tethering/res/values-en-rCA/strings.xml index 56b88a5fb3e1..769e01208afc 100644 --- a/packages/Tethering/res/values-en-rCA/strings.xml +++ b/packages/Tethering/res/values-en-rCA/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering or hotspot active"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Tap to set up."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering is disabled"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Contact your admin for details"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering or hotspot active"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Tap to set up."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering is disabled"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Contact your admin for details"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot and tethering status"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-en-rGB/strings.xml b/packages/Tethering/res/values-en-rGB/strings.xml index 56b88a5fb3e1..769e01208afc 100644 --- a/packages/Tethering/res/values-en-rGB/strings.xml +++ b/packages/Tethering/res/values-en-rGB/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering or hotspot active"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Tap to set up."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering is disabled"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Contact your admin for details"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering or hotspot active"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Tap to set up."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering is disabled"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Contact your admin for details"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot and tethering status"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-en-rIN/strings.xml b/packages/Tethering/res/values-en-rIN/strings.xml index 56b88a5fb3e1..769e01208afc 100644 --- a/packages/Tethering/res/values-en-rIN/strings.xml +++ b/packages/Tethering/res/values-en-rIN/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering or hotspot active"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Tap to set up."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering is disabled"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Contact your admin for details"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering or hotspot active"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Tap to set up."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering is disabled"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Contact your admin for details"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot and tethering status"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-en-rXC/strings.xml b/packages/Tethering/res/values-en-rXC/strings.xml index 7f47fc89d2af..f1674bed4eb7 100644 --- a/packages/Tethering/res/values-en-rXC/strings.xml +++ b/packages/Tethering/res/values-en-rXC/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering or hotspot active"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Tap to set up."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering is disabled"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Contact your admin for details"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering or hotspot active"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Tap to set up."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering is disabled"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Contact your admin for details"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot & tethering status"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-es-rUS/strings.xml b/packages/Tethering/res/values-es-rUS/strings.xml index e4618b8cec96..63689f43997c 100644 --- a/packages/Tethering/res/values-es-rUS/strings.xml +++ b/packages/Tethering/res/values-es-rUS/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Anclaje a red o zona activa conectados"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Presiona para configurar."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Se inhabilitó la conexión mediante dispositivo portátil"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Para obtener más información, comunícate con el administrador"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Conexión a red o hotspot conectados"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Presiona para configurar esta opción."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Se inhabilitó la conexión mediante dispositivo portátil"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Para obtener más información, comunícate con el administrador"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Estado del hotspot y la conexión mediante dispositivo portátil"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-es/strings.xml b/packages/Tethering/res/values-es/strings.xml index 8dc1575ce8ea..9a34ed5e388a 100644 --- a/packages/Tethering/res/values-es/strings.xml +++ b/packages/Tethering/res/values-es/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Compartir conexión/Zona Wi-Fi activada"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Toca para configurar."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"La conexión compartida está inhabilitada"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Ponte en contacto con el administrador para obtener más información"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Conexión compartida o punto de acceso activos"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Toca para configurar."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"La conexión compartida está inhabilitada"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Solicita más información a tu administrador"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Estado del punto de acceso y de la conexión compartida"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-et/strings.xml b/packages/Tethering/res/values-et/strings.xml index 872c8a74cc59..0970341ab0cd 100644 --- a/packages/Tethering/res/values-et/strings.xml +++ b/packages/Tethering/res/values-et/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Jagamine või kuumkoht on aktiivne"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Puudutage seadistamiseks."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Jagamine on keelatud"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Lisateabe saamiseks võtke ühendust oma administraatoriga"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Jagamine või kuumkoht on aktiivne"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Puudutage seadistamiseks."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Jagamine on keelatud"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Lisateabe saamiseks võtke ühendust oma administraatoriga"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Kuumkoha ja jagamise olek"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-eu/strings.xml b/packages/Tethering/res/values-eu/strings.xml index 6c4605e616c1..632019e2ef1b 100644 --- a/packages/Tethering/res/values-eu/strings.xml +++ b/packages/Tethering/res/values-eu/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Konexioa partekatzea edo sare publikoa aktibo"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Sakatu konfiguratzeko."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Desgaituta dago konexioa partekatzeko aukera"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Xehetasunak lortzeko, jarri administratzailearekin harremanetan"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Konexioa partekatzea edo wifi-gunea aktibo dago"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Sakatu konfiguratzeko."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Desgaituta dago konexioa partekatzeko aukera"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Xehetasunak lortzeko, jarri administratzailearekin harremanetan"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Wifi-gunearen eta konexioa partekatzeko eginbidearen egoera"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-fa/strings.xml b/packages/Tethering/res/values-fa/strings.xml index bc2ee23609c3..2e21c85fa179 100644 --- a/packages/Tethering/res/values-fa/strings.xml +++ b/packages/Tethering/res/values-fa/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"اشتراکگذاری اینترنت یا نقطه اتصال فعال"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"برای راهاندازی ضربه بزنید."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"اشتراکگذاری اینترنت غیرفعال است"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"برای جزئیات، با سرپرستتان تماس بگیرید"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"اشتراکگذاری اینترنت یا نقطه اتصال فعال"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"برای راهاندازی ضربه بزنید."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"اشتراکگذاری اینترنت غیرفعال است"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"برای جزئیات، با سرپرستتان تماس بگیرید"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"وضعیت نقطه اتصال و اشتراکگذاری اینترنت"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-fi/strings.xml b/packages/Tethering/res/values-fi/strings.xml index ff0fca6502df..413db3f0f8c9 100644 --- a/packages/Tethering/res/values-fi/strings.xml +++ b/packages/Tethering/res/values-fi/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Internetin jakaminen tai yhteyspiste käytössä"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Määritä napauttamalla."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Yhteyden jakaminen poistettu käytöstä"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Kysy lisätietoja järjestelmänvalvojalta."</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Yhteyden jakaminen tai hotspot käytössä"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Ota käyttöön napauttamalla."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Yhteyden jakaminen on poistettu käytöstä"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Pyydä lisätietoja järjestelmänvalvojalta"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspotin ja yhteyden jakamisen tila"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-fr-rCA/strings.xml b/packages/Tethering/res/values-fr-rCA/strings.xml index 1f5df0ee0cb0..eb2e4ba54000 100644 --- a/packages/Tethering/res/values-fr-rCA/strings.xml +++ b/packages/Tethering/res/values-fr-rCA/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Partage de connexion ou point d\'accès sans fil activé"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Touchez pour configurer."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Le partage de connexion est désactivé"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Communiquez avec votre administrateur pour obtenir plus de détails"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Partage de connexion ou point d\'accès sans fil activé"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Touchez pour configurer."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Le partage de connexion est désactivé"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Communiquez avec votre administrateur pour obtenir plus de détails"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Point d\'accès et partage de connexion"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-fr/strings.xml b/packages/Tethering/res/values-fr/strings.xml index daf7c9d830d5..22259c52ab9e 100644 --- a/packages/Tethering/res/values-fr/strings.xml +++ b/packages/Tethering/res/values-fr/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Partage de connexion ou point d\'accès sans fil activé"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Appuyez ici pour configurer."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Le partage de connexion est désactivé"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Pour en savoir plus, contactez votre administrateur"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Partage de connexion ou point d\'accès activé"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Appuyez pour effectuer la configuration."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Le partage de connexion est désactivé"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Pour en savoir plus, contactez votre administrateur"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"État du point d\'accès et du partage de connexion"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-gl/strings.xml b/packages/Tethering/res/values-gl/strings.xml index 0d16a1de094f..ded82fcd54ad 100644 --- a/packages/Tethering/res/values-gl/strings.xml +++ b/packages/Tethering/res/values-gl/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Conexión compartida ou zona wifi activada"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Tocar para configurar."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"A conexión compartida está desactivada"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Contacta co administrador para obter información"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Conexión compartida ou zona wifi activada"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Toca para configurar."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"A conexión compartida está desactivada"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Contacta co administrador para obter información"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Estado da zona wifi e da conexión compartida"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-gu/strings.xml b/packages/Tethering/res/values-gu/strings.xml index 9d6b02f85fc9..7cbbc2de3d91 100644 --- a/packages/Tethering/res/values-gu/strings.xml +++ b/packages/Tethering/res/values-gu/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"ટિથરિંગ અથવા હૉટસ્પૉટ સક્રિય"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"સેટ કરવા માટે ટૅપ કરો."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"ટિથરિંગ અક્ષમ કરેલ છે"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"વિગતો માટે તમારા વ્યવસ્થાપકનો સંપર્ક કરો"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"ઇન્ટરનેટ શેર કરવાની સુવિધા અથવા હૉટસ્પૉટ સક્રિય છે"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"સેટઅપ કરવા માટે ટૅપ કરો."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"ઇન્ટરનેટ શેર કરવાની સુવિધા બંધ કરી છે"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"વિગતો માટે તમારા વ્યવસ્થાપકનો સંપર્ક કરો"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"હૉટસ્પૉટ અને ઇન્ટરનેટ શેર કરવાની સુવિધાનું સ્ટેટસ"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-hi/strings.xml b/packages/Tethering/res/values-hi/strings.xml index 9c29d9a8f9a1..08af81b826b3 100644 --- a/packages/Tethering/res/values-hi/strings.xml +++ b/packages/Tethering/res/values-hi/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"टेदरिंग या हॉटस्पॉट सक्रिय"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"सेट करने के लिए टैप करें."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"टेदरिंग अक्षम है"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"जानकारी के लिए अपने एडमिन से संपर्क करें"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"टेदरिंग या हॉटस्पॉट चालू है"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"सेट अप करने के लिए टैप करें."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"टेदरिंग बंद है"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"जानकारी के लिए अपने एडमिन से संपर्क करें"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"हॉटस्पॉट और टेदरिंग की स्थिति"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-hr/strings.xml b/packages/Tethering/res/values-hr/strings.xml index d0d25bb755aa..827c135f205d 100644 --- a/packages/Tethering/res/values-hr/strings.xml +++ b/packages/Tethering/res/values-hr/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Ograničenje ili aktivan hotspot"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Dodirnite da biste postavili."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Modemsko je povezivanje onemogućeno"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Obratite se administratoru da biste saznali pojedinosti"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Modemsko povezivanje ili žarišna točka aktivni"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Dodirnite da biste postavili."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Modemsko je povezivanje onemogućeno"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Obratite se administratoru da biste saznali pojedinosti"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status žarišne točke i modemskog povezivanja"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-hu/strings.xml b/packages/Tethering/res/values-hu/strings.xml index 31296599231e..eb68d6babf8f 100644 --- a/packages/Tethering/res/values-hu/strings.xml +++ b/packages/Tethering/res/values-hu/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Megosztás vagy aktív hotspot"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Koppintson a beállításhoz."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Az internetmegosztás le van tiltva"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"A részletekért forduljon rendszergazdájához"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Megosztás vagy aktív hotspot"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Koppintson a beállításhoz."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Az internetmegosztás le van tiltva"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"A részletekért forduljon rendszergazdájához"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot és internetmegosztás állapota"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-hy/strings.xml b/packages/Tethering/res/values-hy/strings.xml index 8ba6435fd58e..912941e53835 100644 --- a/packages/Tethering/res/values-hy/strings.xml +++ b/packages/Tethering/res/values-hy/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Մոդեմի ռեժիմը միացված է"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Հպեք՝ կարգավորելու համար:"</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Մոդեմի ռեժիմն անջատված է"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Մանրամասների համար դիմեք ձեր ադմինիստրատորին"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Մոդեմի ռեժիմը միացված է"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Հպեք՝ կարգավորելու համար։"</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Մոդեմի ռեժիմն անջատված է"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Մանրամասների համար դիմեք ձեր ադմինիստրատորին"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Թեժ կետի և մոդեմի ռեժիմի կարգավիճակը"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-in/strings.xml b/packages/Tethering/res/values-in/strings.xml index 1e093ab237e8..a4e175a439e9 100644 --- a/packages/Tethering/res/values-in/strings.xml +++ b/packages/Tethering/res/values-in/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering (Penambatan) atau hotspot aktif"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Ketuk untuk menyiapkan."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering dinonaktifkan"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Hubungi admin untuk mengetahui detailnya"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering atau hotspot aktif"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Ketuk untuk menyiapkan."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering dinonaktifkan"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Hubungi admin untuk mengetahui detailnya"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status hotspot & tethering"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-is/strings.xml b/packages/Tethering/res/values-is/strings.xml index f5769d5344ae..e9f6670bcd09 100644 --- a/packages/Tethering/res/values-is/strings.xml +++ b/packages/Tethering/res/values-is/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Kveikt á tjóðrun eða aðgangsstað"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Ýttu til að setja upp."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Slökkt er á tjóðrun"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Hafðu samband við kerfisstjórann til að fá upplýsingar"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Kveikt á tjóðrun eða aðgangsstað"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Ýttu til að setja upp."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Slökkt er á tjóðrun"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Hafðu samband við kerfisstjórann til að fá upplýsingar"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Staða heits reits og tjóðrunar"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-it/strings.xml b/packages/Tethering/res/values-it/strings.xml index e0b3724325fc..ffb9196f5ee3 100644 --- a/packages/Tethering/res/values-it/strings.xml +++ b/packages/Tethering/res/values-it/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering oppure hotspot attivo"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Tocca per impostare."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering disattivato"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Contatta il tuo amministratore per avere informazioni dettagliate"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Hotspot o tethering attivo"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Tocca per impostare."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering disattivato"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Contatta il tuo amministratore per avere informazioni dettagliate"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Stato hotspot e tethering"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-iw/strings.xml b/packages/Tethering/res/values-iw/strings.xml index c002c44b2361..7adcb47350c1 100644 --- a/packages/Tethering/res/values-iw/strings.xml +++ b/packages/Tethering/res/values-iw/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"שיתוף אינטרנט פעיל"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"הקש כדי להגדיר."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"שיתוף האינטרנט בין ניידים מושבת"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"לפרטים, יש לפנות למנהל המערכת"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"נקודה לשיתוף אינטרנט או שיתוף אינטרנט בין מכשירים: בסטטוס פעיל"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"יש להקיש כדי להגדיר."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"שיתוף האינטרנט בין מכשירים מושבת"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"לפרטים, יש לפנות למנהל המערכת"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"סטטוס של נקודה לשיתוף אינטרנט ושיתוף אינטרנט בין מכשירים"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-ja/strings.xml b/packages/Tethering/res/values-ja/strings.xml index 314bde00df02..f68a73010b36 100644 --- a/packages/Tethering/res/values-ja/strings.xml +++ b/packages/Tethering/res/values-ja/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"テザリングまたはアクセスポイントが有効です"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"タップしてセットアップします。"</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"テザリングは無効に設定されています"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"詳しくは、管理者にお問い合わせください"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"テザリングまたはアクセス ポイントが有効です"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"タップしてセットアップします。"</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"テザリングは無効に設定されています"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"詳しくは、管理者にお問い合わせください"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"アクセス ポイントとテザリングのステータス"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-ka/strings.xml b/packages/Tethering/res/values-ka/strings.xml index 7bbd81d3435a..7c22e82bd370 100644 --- a/packages/Tethering/res/values-ka/strings.xml +++ b/packages/Tethering/res/values-ka/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"ტეტერინგი ან უსადენო ქსელი აქტიურია"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"შეეხეთ დასაყენებლად."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"ტეტერინგი გათიშულია"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"დამატებითი ინფორმაციისთვის დაუკავშირდით თქვენს ადმინისტრატორს"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"ტეტერინგი ან უსადენო ქსელი აქტიურია"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"შეეხეთ დასაყენებლად."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"ტეტერინგი გათიშულია"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"დამატებითი ინფორმაციისთვის დაუკავშირდით თქვენს ადმინისტრატორს"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"უსადენო ქსელის და ტეტერინგის სტატუსი"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-kk/strings.xml b/packages/Tethering/res/values-kk/strings.xml index 7fd87a159657..0857d06de243 100644 --- a/packages/Tethering/res/values-kk/strings.xml +++ b/packages/Tethering/res/values-kk/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Тетеринг немесе хотспот қосулы"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Реттеу үшін түртіңіз."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Тетеринг өшірілді"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Мәліметтерді әкімшіден алыңыз"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Тетеринг немесе хотспот қосулы"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Реттеу үшін түртіңіз."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Тетеринг өшірілді."</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Мәліметтерді әкімшіден алыңыз."</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Хотспот және тетеринг күйі"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-km/strings.xml b/packages/Tethering/res/values-km/strings.xml index 2f852246790c..536e3d1703b1 100644 --- a/packages/Tethering/res/values-km/strings.xml +++ b/packages/Tethering/res/values-km/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"ភ្ជាប់ ឬហតស្ពតសកម្ម"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"ប៉ះដើម្បីកំណត់"</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"ការភ្ជាប់ត្រូវបានបិទ"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"ទាក់ទងអ្នកគ្រប់គ្រងរបស់អ្នកសម្រាប់ព័ត៌មានលម្អិត"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"ការភ្ជាប់ ឬហតស្ប៉តកំពុងដំណើរការ"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"ចុចដើម្បីរៀបចំ។"</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"ការភ្ជាប់ត្រូវបានបិទ"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"ទាក់ទងអ្នកគ្រប់គ្រងរបស់អ្នក ដើម្បីទទួលបានព័ត៌មានលម្អិត"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"ស្ថានភាពនៃការភ្ជាប់ និងហតស្ប៉ត"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-kn/strings.xml b/packages/Tethering/res/values-kn/strings.xml index f11a83ea40ee..32f54926f4bf 100644 --- a/packages/Tethering/res/values-kn/strings.xml +++ b/packages/Tethering/res/values-kn/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"ಟೆಥರಿಂಗ್ ಅಥವಾ ಹಾಟ್ಸ್ಪಾಟ್ ಸಕ್ರಿಯವಾಗಿದೆ"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"ಹೊಂದಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"ಟೆಥರಿಂಗ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"ವಿವರಗಳಿಗಾಗಿ ನಿಮ್ಮ ನಿರ್ವಾಹಕರನ್ನು ಸಂಪರ್ಕಿಸಿ"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"ಟೆಥರಿಂಗ್ ಅಥವಾ ಹಾಟ್ಸ್ಪಾಟ್ ಸಕ್ರಿಯವಾಗಿದೆ"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"ಸೆಟಪ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"ಟೆಥರಿಂಗ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"ವಿವರಗಳಿಗಾಗಿ ನಿಮ್ಮ ನಿರ್ವಾಹಕರನ್ನು ಸಂಪರ್ಕಿಸಿ"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"ಹಾಟ್ಸ್ಪಾಟ್ ಮತ್ತು ಟೆಥರಿಂಗ್ ಸ್ಥಿತಿ"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-ko/strings.xml b/packages/Tethering/res/values-ko/strings.xml index 57f24f5b1ae2..156b24786db5 100644 --- a/packages/Tethering/res/values-ko/strings.xml +++ b/packages/Tethering/res/values-ko/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"테더링 또는 핫스팟 사용"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"설정하려면 탭하세요."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"테더링이 사용 중지됨"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"자세한 정보는 관리자에게 문의하세요."</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"테더링 또는 핫스팟 사용"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"설정하려면 탭하세요."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"테더링이 사용 중지됨"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"자세한 정보는 관리자에게 문의하세요."</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"핫스팟 및 테더링 상태"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-ky/strings.xml b/packages/Tethering/res/values-ky/strings.xml index 79854859d41e..18ee5fd35718 100644 --- a/packages/Tethering/res/values-ky/strings.xml +++ b/packages/Tethering/res/values-ky/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Жалгаштыруу же хотспот жандырылган"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Жөндөө үчүн таптап коюңуз."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Жалгаштыруу функциясы өчүрүлгөн"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Кеңири маалымат үчүн администраторуңузга кайрылыңыз"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Модем режими күйүп турат"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Жөндөө үчүн таптап коюңуз."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Телефонду модем катары колдонууга болбойт"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Кеңири маалымат үчүн администраторуңузга кайрылыңыз"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Байланыш түйүнүнүн жана модем режиминин статусу"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-lo/strings.xml b/packages/Tethering/res/values-lo/strings.xml index 78f1585f60f7..b12767018c3a 100644 --- a/packages/Tethering/res/values-lo/strings.xml +++ b/packages/Tethering/res/values-lo/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"ເປີດການປ່ອຍສັນຍານ ຫຼືຮັອດສະປອດແລ້ວ"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"ແຕະເພື່ອຕັ້ງຄ່າ."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"ການປ່ອຍສັນຍານຖືກປິດໄວ້"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"ຕິດຕໍ່ຜູ້ເບິ່ງແຍງລະບົບສຳລັບລາຍລະອຽດ"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"ເປີດການປ່ອຍສັນຍານ ຫຼື ຮັອດສະປອດແລ້ວ"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"ແຕະເພື່ອຕັ້ງຄ່າ."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"ການປ່ອຍສັນຍານຖືກປິດໄວ້"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"ຕິດຕໍ່ຜູ້ເບິ່ງແຍງລະບົບສຳລັບລາຍລະອຽດ"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"ສະຖານະຮັອດສະປອດ ແລະ ການປ່ອຍສັນຍານ"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-lt/strings.xml b/packages/Tethering/res/values-lt/strings.xml index ebff8ac9d1f1..8427baf39f31 100644 --- a/packages/Tethering/res/values-lt/strings.xml +++ b/packages/Tethering/res/values-lt/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Susietas ar aktyvus"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Palieskite, kad nustatytumėte."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Įrenginio kaip modemo naudojimas išjungtas"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Jei reikia išsamios informacijos, susisiekite su administratoriumi"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Įrenginys naudojamas kaip modemas arba įjungtas viešosios interneto prieigos taškas"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Palieskite, kad nustatytumėte."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Įrenginio kaip modemo naudojimas išjungtas"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Jei reikia išsamios informacijos, susisiekite su administratoriumi"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Viešosios interneto prieigos taško ir įrenginio kaip modemo naudojimo būsena"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-lv/strings.xml b/packages/Tethering/res/values-lv/strings.xml index 54d0048b526a..aa2d6990e04f 100644 --- a/packages/Tethering/res/values-lv/strings.xml +++ b/packages/Tethering/res/values-lv/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Piesaiste vai tīklājs ir aktīvs."</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Pieskarieties, lai iestatītu."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Piesaiste ir atspējota"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Lai iegūtu detalizētu informāciju, sazinieties ar savu administratoru."</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Piesaiste vai tīklājs ir aktīvs."</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Pieskarieties, lai to iestatītu."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Piesaiste ir atspējota"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Lai iegūtu detalizētu informāciju, sazinieties ar savu administratoru."</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Tīklāja un piesaistes statuss"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-af/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-af/strings.xml new file mode 100644 index 000000000000..19d659c6ce36 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-af/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Verbinding het nie internet nie"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Toestelle kan nie koppel nie"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Skakel verbinding af"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Warmkol of verbinding is aan"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Bykomende heffings kan geld terwyl jy swerf"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-am/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-am/strings.xml new file mode 100644 index 000000000000..8995430b4f09 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-am/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"ማስተሳሰር ምንም በይነመረብ የለውም"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"መሣሪያዎችን ማገናኘት አይቻልም"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ማስተሳሰርን አጥፋ"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"መገናኛ ነጥብ ወይም ማስተሳሰር በርቷል"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"በሚያንዣብብበት ጊዜ ተጨማሪ ክፍያዎች ተፈጻሚ ሊሆኑ ይችላሉ"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-ar/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ar/strings.xml new file mode 100644 index 000000000000..54f3b5389ae9 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-ar/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"ما مِن اتصال بالإنترنت خلال التوصيل"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"تعذّر اتصال الأجهزة"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"إيقاف التوصيل"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"نقطة الاتصال أو التوصيل مفعّلان"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"قد يتم تطبيق رسوم إضافية أثناء التجوال."</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-as/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-as/strings.xml new file mode 100644 index 000000000000..e215141c9eb6 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-as/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"টে\'ডাৰিঙৰ ইণ্টাৰনেট নাই"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"ডিভাইচসমূহ সংযোগ কৰিব নোৱাৰি"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"টে\'ডাৰিং অফ কৰক"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"হটস্পট অথবা টে\'ডাৰিং অন আছে"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"ৰ\'মিঙত থাকিলে অতিৰিক্ত মাচুল প্ৰযোজ্য হ’ব পাৰে"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-az/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-az/strings.xml new file mode 100644 index 000000000000..1fd8e4c963a7 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-az/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Modemin internetə girişi yoxdur"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Cihazları qoşmaq mümkün deyil"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Modemi deaktiv edin"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot və ya modem aktivdir"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Rouminq zamanı əlavə ödənişlər tətbiq edilə bilər"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-b+sr+Latn/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-b+sr+Latn/strings.xml new file mode 100644 index 000000000000..1abe4f3aa3c7 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-b+sr+Latn/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Privezivanje nema pristup internetu"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Povezivanje uređaja nije uspelo"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Isključi privezivanje"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Uključen je hotspot ili privezivanje"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Možda važe dodatni troškovi u romingu"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-be/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-be/strings.xml new file mode 100644 index 000000000000..38dbd1e3914f --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-be/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Рэжым мадэма выкарыстоўваецца без доступу да інтэрнэту"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Не ўдалося падключыць прылады"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Выключыць рэжым мадэма"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Хот-спот або рэжым мадэма ўключаны"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Пры выкарыстанні роўмінгу можа спаганяцца дадатковая плата"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-bg/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-bg/strings.xml new file mode 100644 index 000000000000..04b44db5c1a4 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-bg/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Тетърингът няма връзка с интернет"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Устройствата не могат да установят връзка"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Изключване на тетъринга"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Точката за достъп или тетърингът са включени"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Възможно е да ви бъдат начислени допълнителни такси при роуминг"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-bn/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-bn/strings.xml new file mode 100644 index 000000000000..579d1be1c1ea --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-bn/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"টিথারিং করার জন্য কোনও ইন্টারনেট কানেকশন নেই"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"ডিভাইস কানেক্ট করতে পারছে না"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"টিথারিং বন্ধ করুন"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"হটস্পট বা টিথারিং চালু আছে"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"রোমিংয়ের সময় অতিরিক্ত চার্জ করা হতে পারে"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-bs/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-bs/strings.xml new file mode 100644 index 000000000000..9ce3efe6c39d --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-bs/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Povezivanje putem mobitela nema internet"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Uređaji se ne mogu povezati"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Isključi povezivanje putem mobitela"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Pristupna tačka ili povezivanje putem mobitela je uključeno"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Mogu nastati dodatni troškovi u romingu"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-ca/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ca/strings.xml new file mode 100644 index 000000000000..46d4c35b9b83 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-ca/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"La compartició de xarxa no té accés a Internet"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"No es poden connectar els dispositius"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Desactiva la compartició de xarxa"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"S\'ha activat el punt d\'accés Wi‑Fi o la compartició de xarxa"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"És possible que s\'apliquin costos addicionals en itinerància"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-cs/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-cs/strings.xml new file mode 100644 index 000000000000..cc13860b3da1 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-cs/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering nemá připojení k internetu"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Zařízení se nemůžou připojit"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Vypnout tethering"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Je zapnutý hotspot nebo tethering"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Při roamingu mohou být účtovány dodatečné poplatky"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-da/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-da/strings.xml new file mode 100644 index 000000000000..92c3ae11567d --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-da/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Netdeling har ingen internetforbindelse"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Enheder kan ikke oprette forbindelse"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Deaktiver netdeling"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot eller netdeling er aktiveret"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Der opkræves muligvis yderligere gebyrer ved roaming"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-de/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-de/strings.xml new file mode 100644 index 000000000000..967eb4db2e77 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-de/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering hat keinen Internetzugriff"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Geräte können sich nicht verbinden"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Tethering deaktivieren"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot oder Tethering ist aktiviert"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Für das Roaming können zusätzliche Gebühren anfallen"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-el/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-el/strings.xml new file mode 100644 index 000000000000..5fb497451f6d --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-el/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Η σύνδεση δεν έχει πρόσβαση στο διαδίκτυο"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Δεν είναι δυνατή η σύνδεση των συσκευών"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Απενεργοποιήστε τη σύνδεση"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Ενεργό σημείο πρόσβασης Wi-Fi ή ενεργή σύνδεση"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Ενδέχεται να ισχύουν επιπλέον χρεώσεις κατά την περιαγωγή."</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-en-rAU/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-en-rAU/strings.xml new file mode 100644 index 000000000000..45647f93f246 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-en-rAU/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering has no Internet"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Devices can’t connect"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Turn off tethering"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot or tethering is on"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Additional charges may apply while roaming"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-en-rCA/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-en-rCA/strings.xml new file mode 100644 index 000000000000..45647f93f246 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-en-rCA/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering has no Internet"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Devices can’t connect"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Turn off tethering"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot or tethering is on"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Additional charges may apply while roaming"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-en-rGB/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-en-rGB/strings.xml new file mode 100644 index 000000000000..45647f93f246 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-en-rGB/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering has no Internet"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Devices can’t connect"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Turn off tethering"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot or tethering is on"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Additional charges may apply while roaming"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-en-rIN/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-en-rIN/strings.xml new file mode 100644 index 000000000000..45647f93f246 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-en-rIN/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering has no Internet"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Devices can’t connect"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Turn off tethering"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot or tethering is on"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Additional charges may apply while roaming"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-en-rXC/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-en-rXC/strings.xml new file mode 100644 index 000000000000..7877074afc66 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-en-rXC/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering has no internet"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Devices can’t connect"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Turn off tethering"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot or tethering is on"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Additional charges may apply while roaming"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-es-rUS/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-es-rUS/strings.xml new file mode 100644 index 000000000000..08edd81a6b04 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-es-rUS/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"La conexión mediante dispositivo móvil no tiene Internet"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"No se pueden conectar los dispositivos"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Desactivar conexión mediante dispositivo móvil"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Se activó el hotspot o la conexión mediante dispositivo móvil"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Es posible que se apliquen cargos adicionales por roaming"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-es/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-es/strings.xml new file mode 100644 index 000000000000..79f51d00e2e8 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-es/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"La conexión no se puede compartir, porque no hay acceso a Internet"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Los dispositivos no se pueden conectar"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Desactivar conexión compartida"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Punto de acceso o conexión compartida activados"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Puede que se apliquen cargos adicionales en itinerancia"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-et/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-et/strings.xml new file mode 100644 index 000000000000..2da5f8a6d62a --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-et/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Jagamisel puudub internetiühendus"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Seadmed ei saa ühendust luua"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Lülita jagamine välja"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Kuumkoht või jagamine on sisse lülitatud"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Rändluse kasutamisega võivad kaasneda lisatasud"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-eu/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-eu/strings.xml new file mode 100644 index 000000000000..2073f2806c18 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-eu/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Konexioa partekatzeko aukerak ez du Interneteko konexiorik"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Ezin dira konektatu gailuak"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Desaktibatu konexioa partekatzeko aukera"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Wifi-gunea edo konexioa partekatzeko aukera aktibatuta dago"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Baliteke kostu gehigarriak ordaindu behar izatea ibiltaritzan"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-fa/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-fa/strings.xml new file mode 100644 index 000000000000..e21b2a0852c7 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-fa/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"«اشتراکگذاری اینترنت» به اینترنت دسترسی ندارد"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"دستگاهها متصل نمیشوند"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"خاموش کردن «اشتراکگذاری اینترنت»"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"«نقطه اتصال» یا «اشتراکگذاری اینترنت» روشن است"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"ممکن است درحین فراگردی تغییرات دیگر اعمال شود"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-fi/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-fi/strings.xml new file mode 100644 index 000000000000..88b0b13eb4b5 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-fi/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Ei jaettavaa internetyhteyttä"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Laitteet eivät voi muodostaa yhteyttä"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Laita yhteyden jakaminen pois päältä"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot tai yhteyden jakaminen on päällä"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Roaming voi aiheuttaa lisämaksuja"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-fr-rCA/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-fr-rCA/strings.xml new file mode 100644 index 000000000000..3b781bc8db31 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-fr-rCA/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Le partage de connexion n\'est pas connecté à Internet"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Impossible de connecter les appareils"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Désactiver le partage de connexion"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Le point d\'accès ou le partage de connexion est activé"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"En itinérance, des frais supplémentaires peuvent s\'appliquer"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-fr/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-fr/strings.xml new file mode 100644 index 000000000000..51d7203c3652 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-fr/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Aucune connexion à Internet n\'est disponible pour le partage de connexion"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Impossible de connecter les appareils"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Désactiver le partage de connexion"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Le point d\'accès ou le partage de connexion est activé"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"En itinérance, des frais supplémentaires peuvent s\'appliquer"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-gl/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-gl/strings.xml new file mode 100644 index 000000000000..008ccb475d66 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-gl/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"A conexión compartida non ten Internet"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Non se puideron conectar os dispositivos"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Desactivar conexión compartida"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Está activada a zona wifi ou a conexión compartida"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Pódense aplicar cargos adicionais en itinerancia"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-gu/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-gu/strings.xml new file mode 100644 index 000000000000..f2e3b4df782f --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-gu/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"ઇન્ટરનેટ શેર કરવાની સુવિધામાં ઇન્ટરનેટ નથી"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"ડિવાઇસ કનેક્ટ કરી શકાતા નથી"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ઇન્ટરનેટ શેર કરવાની સુવિધા બંધ કરો"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"હૉટસ્પૉટ અથવા ઇન્ટરનેટ શેર કરવાની સુવિધા ચાલુ છે"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"રોમિંગમાં વધારાના શુલ્ક લાગી શકે છે"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-hi/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-hi/strings.xml new file mode 100644 index 000000000000..b11839d760c8 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-hi/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"टेदरिंग से इंटरनेट नहीं चल रहा"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"डिवाइस कनेक्ट नहीं हो पा रहे"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"टेदरिंग बंद करें"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"हॉटस्पॉट या टेदरिंग चालू है"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"रोमिंग के दौरान अतिरिक्त शुल्क लग सकता है"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-hr/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-hr/strings.xml new file mode 100644 index 000000000000..0a5aca25b1a9 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-hr/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Modemsko povezivanje nema internet"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Uređaji se ne mogu povezati"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Isključivanje modemskog povezivanja"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Uključena je žarišna točka ili modemsko povezivanje"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"U roamingu su mogući dodatni troškovi"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-hu/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-hu/strings.xml new file mode 100644 index 000000000000..21c689a44ef8 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-hu/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Nincs internetkapcsolat az internet megosztásához"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Az eszközök nem tudnak csatlakozni"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Internetmegosztás kikapcsolása"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"A hotspot vagy az internetmegosztás be van kapcsolva"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Roaming során további díjak léphetnek fel"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-hy/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-hy/strings.xml new file mode 100644 index 000000000000..689d92870e50 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-hy/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Մոդեմի ռեժիմի կապը բացակայում է"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Չհաջողվեց միացնել սարքը"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Անջատել մոդեմի ռեժիմը"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Թեժ կետը կամ մոդեմի ռեժիմը միացված է"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Ռոումինգում կարող են լրացուցիչ վճարներ գանձվել"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-in/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-in/strings.xml new file mode 100644 index 000000000000..a5f4d19abfe9 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-in/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tidak ada koneksi internet di tethering"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Perangkat tidak dapat terhubung"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Nonaktifkan tethering"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot atau tethering aktif"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Biaya tambahan mungkin berlaku saat roaming"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-is/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-is/strings.xml new file mode 100644 index 000000000000..fc7e8aaf4e42 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-is/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tjóðrun er ekki með internettengingu"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Tæki geta ekki tengst"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Slökkva á tjóðrun"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Kveikt er á heitum reit eða tjóðrun"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Viðbótargjöld kunna að eiga við í reiki"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-it/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-it/strings.xml new file mode 100644 index 000000000000..6456dd1b806a --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-it/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Nessuna connessione a Internet per il tethering"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Impossibile connettere i dispositivi"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Disattiva il tethering"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot o tethering attivi"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Potrebbero essere applicati costi aggiuntivi durante il roaming"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-iw/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-iw/strings.xml new file mode 100644 index 000000000000..46b24bd3c508 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-iw/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"אי אפשר להפעיל את תכונת שיתוף האינטרנט בין מכשירים כי אין חיבור לאינטרנט"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"למכשירים אין אפשרות להתחבר"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"השבתה של שיתוף האינטרנט בין מכשירים"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"תכונת הנקודה לשיתוף אינטרנט או תכונת שיתוף האינטרנט בין מכשירים פועלת"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"ייתכנו חיובים נוספים בעת נדידה"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-ja/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ja/strings.xml new file mode 100644 index 000000000000..e6eb277b90dd --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-ja/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"テザリングがインターネットに接続されていません"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"デバイスを接続できません"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"テザリングを OFF にする"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"アクセス ポイントまたはテザリングが ON です"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"ローミング時に追加料金が発生することがあります"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-ka/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ka/strings.xml new file mode 100644 index 000000000000..aeddd7101da0 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-ka/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"ტეტერინგს არ აქვს ინტერნეტზე წვდომა"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"მოწყობილობები ვერ ახერხებენ დაკავშირებას"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ტეტერინგის გამორთვა"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ჩართულია უსადენო ქსელი ან ტეტერინგი"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"როუმინგის გამოყენებისას შეიძლება ჩამოგეჭრათ დამატებითი საფასური"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-kk/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-kk/strings.xml new file mode 100644 index 000000000000..255f0a276f95 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-kk/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Тетеринг режимі интернет байланысынсыз пайдаланылуда"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Құрылғыларды байланыстыру мүмкін емес"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Тетерингіні өшіру"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Хотспот немесе тетеринг қосулы"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Роуминг кезінде қосымша ақы алынуы мүмкін."</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-km/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-km/strings.xml new file mode 100644 index 000000000000..2bceb1cf7788 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-km/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"ការភ្ជាប់មិនមានអ៊ីនធឺណិតទេ"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"មិនអាចភ្ជាប់ឧបករណ៍បានទេ"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"បិទការភ្ជាប់"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ហតស្ប៉ត ឬការភ្ជាប់ត្រូវបានបើក"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"អាចមានការគិតថ្លៃបន្ថែម នៅពេលរ៉ូមីង"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-kn/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-kn/strings.xml new file mode 100644 index 000000000000..ed769305a679 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-kn/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"ಟೆಥರಿಂಗ್ ಯಾವುದೇ ಇಂಟರ್ನೆಟ್ ಕನೆಕ್ಷನ್ ಹೊಂದಿಲ್ಲ"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"ಸಾಧನಗಳನ್ನು ಕನೆಕ್ಟ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ಟೆಥರಿಂಗ್ ಆಫ್ ಮಾಡಿ"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ಹಾಟ್ಸ್ಪಾಟ್ ಅಥವಾ ಟೆಥರಿಂಗ್ ಆನ್ ಆಗಿದೆ"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"ರೋಮಿಂಗ್ನಲ್ಲಿರುವಾಗ ಹೆಚ್ಚುವರಿ ಶುಲ್ಕಗಳು ಅನ್ವಯವಾಗಬಹುದು"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-ko/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ko/strings.xml new file mode 100644 index 000000000000..6e504941eb8b --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-ko/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"테더링으로 인터넷을 사용할 수 없음"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"기기에서 연결할 수 없음"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"테더링 사용 중지"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"핫스팟 또는 테더링 켜짐"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"로밍 중에는 추가 요금이 발생할 수 있습니다."</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-ky/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ky/strings.xml new file mode 100644 index 000000000000..d68128b9a554 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-ky/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Модем режими Интернети жок колдонулууда"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Түзмөктөр туташпай жатат"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Модем режимин өчүрүү"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Байланыш түйүнү же модем режими күйүк"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Роумингде кошумча акы алынышы мүмкүн"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-lo/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-lo/strings.xml new file mode 100644 index 000000000000..03e134a0fc79 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-lo/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"ການປ່ອຍສັນຍານບໍ່ມີອິນເຕີເນັດ"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"ອຸປະກອນບໍ່ສາມາດເຊື່ອມຕໍ່ໄດ້"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ປິດການປ່ອຍສັນຍານ"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ເປີດໃຊ້ຮັອດສະປອດ ຫຼື ການປ່ອຍສັນຍານຢູ່"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"ອາດມີຄ່າໃຊ້ຈ່າຍເພີ່ມເຕີມໃນລະຫວ່າງການໂຣມມິງ"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-lt/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-lt/strings.xml new file mode 100644 index 000000000000..652cedc6e6ae --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-lt/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Nėra įrenginio kaip modemo naudojimo interneto ryšio"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Nepavyko susieti įrenginių"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Išjungti įrenginio kaip modemo naudojimą"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Įjungtas viešosios interneto prieigos taškas arba įrenginio kaip modemo naudojimas"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Veikiant tarptinkliniam ryšiui gali būti taikomi papildomi mokesčiai"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-lv/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-lv/strings.xml new file mode 100644 index 000000000000..221972298c18 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-lv/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Piesaistei nav interneta savienojuma"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Nevar savienot ierīces"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Izslēgt piesaisti"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Ir ieslēgts tīklājs vai piesaiste"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Viesabonēšanas laikā var tikt piemērota papildu samaksa"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-mk/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-mk/strings.xml new file mode 100644 index 000000000000..227f9e346651 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-mk/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Нема интернет преку мобилен"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Уредите не може да се поврзат"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Исклучи интернет преку мобилен"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Точката на пристап или интернетот преку мобилен е вклучен"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"При роаминг може да се наплатат дополнителни трошоци"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-ml/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ml/strings.xml new file mode 100644 index 000000000000..ec4388512645 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-ml/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"ടെതറിംഗിന് ഇന്റർനെറ്റ് ഇല്ല"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"ഉപകരണങ്ങൾ കണക്റ്റ് ചെയ്യാനാവില്ല"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ടെതറിംഗ് ഓഫാക്കുക"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ഹോട്ട്സ്പോട്ട് അല്ലെങ്കിൽ ടെതറിംഗ് ഓണാണ്"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"റോമിംഗ് ചെയ്യുമ്പോൾ അധിക നിരക്കുകൾ ബാധകമായേക്കാം"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-mn/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-mn/strings.xml new file mode 100644 index 000000000000..e263573799ed --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-mn/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Модемд интернэт алга байна"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Төхөөрөмжүүд холбогдох боломжгүй байна"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Модем болгохыг унтраах"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Сүлжээний цэг эсвэл модем болгох асаалттай байна"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Роумингийн үеэр нэмэлт төлбөр нэхэмжилж болзошгүй"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-mr/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-mr/strings.xml new file mode 100644 index 000000000000..adf845d078bf --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-mr/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"टेदरिंगला इंटरनेट नाही"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"डिव्हाइस कनेक्ट होऊ शकत नाहीत"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"टेदरिंग बंद करा"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"हॉटस्पॉट किंवा टेदरिंग सुरू आहे"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"रोमिंगदरम्यान अतिरिक्त शुल्क लागू होऊ शकतात"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-ms/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ms/strings.xml new file mode 100644 index 000000000000..f65c451e4c21 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-ms/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Penambatan tiada Internet"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Peranti tidak dapat disambungkan"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Matikan penambatan"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Tempat liputan atau penambatan dihidupkan"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Caj tambahan mungkin digunakan semasa perayauan"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-my/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-my/strings.xml new file mode 100644 index 000000000000..4118e775cd84 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-my/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်းတွင် အင်တာနက် မရှိပါ"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"စက်များ ချိတ်ဆက်၍ မရပါ"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်း ပိတ်ရန်"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ဟော့စပေါ့ (သို့) မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်း ဖွင့်ထားသည်"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"ပြင်ပကွန်ရက်နှင့် ချိတ်ဆက်သည့်အခါ နောက်ထပ်ကျသင့်မှုများ ရှိနိုင်သည်"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-nb/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-nb/strings.xml new file mode 100644 index 000000000000..36853583ce82 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-nb/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Internettdeling har ikke internettilgang"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Enhetene kan ikke koble til"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Slå av internettdeling"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Wi-Fi-sone eller internettdeling er på"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Ytterligere kostnader kan påløpe under roaming"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-ne/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ne/strings.xml new file mode 100644 index 000000000000..2a7330098faf --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-ne/strings.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- no translation found for no_upstream_notification_title (5030042590486713460) --> + <skip /> + <!-- no translation found for no_upstream_notification_message (3843613362272973447) --> + <skip /> + <!-- no translation found for no_upstream_notification_disable_button (6385491461813507624) --> + <skip /> + <!-- no translation found for upstream_roaming_notification_title (3015912166812283303) --> + <skip /> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"रोमिङ सेवा प्रयोग गर्दा अतिरिक्त शुल्क लाग्न सक्छ"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-nl/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-nl/strings.xml new file mode 100644 index 000000000000..1d888942f49b --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-nl/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering heeft geen internet"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Apparaten kunnen niet worden verbonden"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Tethering uitschakelen"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot of tethering is ingeschakeld"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Er kunnen extra kosten voor roaming in rekening worden gebracht."</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-or/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-or/strings.xml new file mode 100644 index 000000000000..8038815fe804 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-or/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"ଟିଥରିଂ ପାଇଁ କୌଣସି ଇଣ୍ଟର୍ନେଟ୍ ସଂଯୋଗ ନାହିଁ"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"ଡିଭାଇସଗୁଡ଼ିକ ସଂଯୋଗ କରାଯାଇପାରିବ ନାହିଁ"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ଟିଥରିଂ ବନ୍ଦ କରନ୍ତୁ"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ହଟସ୍ପଟ୍ କିମ୍ବା ଟିଥରିଂ ଚାଲୁ ଅଛି"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"ରୋମିଂରେ ଥିବା ସମୟରେ ଅତିରିକ୍ତ ଶୁଳ୍କ ଲାଗୁ ହୋଇପାରେ"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-pa/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-pa/strings.xml new file mode 100644 index 000000000000..819833eab07f --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-pa/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"ਟੈਦਰਿੰਗ ਕੋਲ ਇੰਟਰਨੈੱਟ ਪਹੁੰਚ ਨਹੀਂ ਹੈ"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"ਡੀਵਾਈਸ ਕਨੈਕਟ ਨਹੀਂ ਕੀਤੇ ਜਾ ਸਕਦੇ"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ਟੈਦਰਿੰਗ ਬੰਦ ਕਰੋ"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ਹੌਟਸਪੌਟ ਜਾਂ ਟੈਦਰਿੰਗ ਚਾਲੂ ਹੈ"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"ਰੋਮਿੰਗ ਦੌਰਾਨ ਵਧੀਕ ਖਰਚੇ ਲਾਗੂ ਹੋ ਸਕਦੇ ਹਨ"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-pl/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-pl/strings.xml new file mode 100644 index 000000000000..65e4380e3916 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-pl/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering nie ma internetu"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Urządzenia nie mogą się połączyć"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Wyłącz tethering"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot lub tethering jest włączony"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Podczas korzystania z roamingu mogą zostać naliczone dodatkowe opłaty"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-pt-rBR/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-pt-rBR/strings.xml new file mode 100644 index 000000000000..d8866170c191 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-pt-rBR/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"O tethering não tem Internet"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Não é possível conectar os dispositivos"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Desativar o tethering"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Ponto de acesso ou tethering ativado"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Pode haver cobranças extras durante o roaming"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-pt-rPT/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-pt-rPT/strings.xml new file mode 100644 index 000000000000..bfd45ca0a3e1 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-pt-rPT/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"A ligação (à Internet) via telemóvel não tem Internet"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Não é possível ligar os dispositivos"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Desativar ligação (à Internet) via telemóvel"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"A zona Wi-Fi ou a ligação (à Internet) via telemóvel está ativada"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Podem aplicar-se custos adicionais em roaming."</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-pt/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-pt/strings.xml new file mode 100644 index 000000000000..d8866170c191 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-pt/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"O tethering não tem Internet"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Não é possível conectar os dispositivos"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Desativar o tethering"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Ponto de acesso ou tethering ativado"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Pode haver cobranças extras durante o roaming"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-ro/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ro/strings.xml new file mode 100644 index 000000000000..8d87a9e516ad --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-ro/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Procesul de tethering nu are internet"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Dispozitivele nu se pot conecta"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Dezactivați procesul de tethering"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"S-a activat hotspotul sau tethering"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Se pot aplica taxe suplimentare pentru roaming"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-ru/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ru/strings.xml new file mode 100644 index 000000000000..dbdb9ebe4931 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-ru/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Режим модема используется без доступа к Интернету"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Невозможно подключить устройства."</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Отключить режим модема"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Включены точка доступа или режим модема"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"За использование услуг связи в роуминге может взиматься дополнительная плата."</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-si/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-si/strings.xml new file mode 100644 index 000000000000..d8301e41c2b2 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-si/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"ටෙදරින් හට අන්තර්ජාලය නැත"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"උපාංගවලට සම්බන්ධ විය නොහැකිය"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ටෙදරින් ක්රියාවිරහිත කරන්න"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"හොට්ස්පොට් හෝ ටෙදරින් ක්රියාත්මකයි"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"රෝමිං අතරතුර අමතර ගාස්තු අදාළ විය හැකිය"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-sk/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-sk/strings.xml new file mode 100644 index 000000000000..bef71363f450 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-sk/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering nemá internetové pripojenie"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Zariadenia sa nemôžu pripojiť"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Vypnúť tethering"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Je zapnutý hotspot alebo tethering"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Počas roamingu vám môžu byť účtované ďalšie poplatky"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-sl/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-sl/strings.xml new file mode 100644 index 000000000000..3202c62e8a3a --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-sl/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Internetna povezava prek mobilnega telefona ni vzpostavljena"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Napravi se ne moreta povezati"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Izklopi internetno povezavo prek mobilnega telefona"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Dostopna točka ali internetna povezava prek mobilnega telefona je vklopljena"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Med gostovanjem lahko nastanejo dodatni stroški"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-sq/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-sq/strings.xml new file mode 100644 index 000000000000..37f6ad286880 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-sq/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Ndarja e internetit nuk ka internet"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Pajisjet nuk mund të lidhen"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Çaktivizo ndarjen e internetit"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Zona e qasjes për internet ose ndarja e internetit është aktive"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Mund të zbatohen tarifime shtesë kur je në roaming"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-sr/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-sr/strings.xml new file mode 100644 index 000000000000..5566d03ed13a --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-sr/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Привезивање нема приступ интернету"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Повезивање уређаја није успело"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Искључи привезивање"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Укључен је хотспот или привезивање"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Можда важе додатни трошкови у ромингу"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-sv/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-sv/strings.xml new file mode 100644 index 000000000000..9765acd0cf46 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-sv/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Det finns ingen internetanslutning för internetdelningen"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Enheterna kan inte anslutas"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Inaktivera internetdelning"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Surfzon eller internetdelning har aktiverats"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Ytterligare avgifter kan tillkomma vid roaming"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-sw/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-sw/strings.xml new file mode 100644 index 000000000000..cf850c9cd222 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-sw/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Kipengele cha kusambaza mtandao hakina intaneti"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Imeshindwa kuunganisha vifaa"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Zima kipengele cha kusambaza mtandao"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Umewasha kipengele cha kusambaza mtandao au mtandao pepe"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Huenda ukatozwa gharama za ziada ukitumia mitandao ya ng\'ambo"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-ta/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ta/strings.xml new file mode 100644 index 000000000000..ea04821e33bd --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-ta/strings.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- no translation found for no_upstream_notification_title (5030042590486713460) --> + <skip /> + <!-- no translation found for no_upstream_notification_message (3843613362272973447) --> + <skip /> + <!-- no translation found for no_upstream_notification_disable_button (6385491461813507624) --> + <skip /> + <!-- no translation found for upstream_roaming_notification_title (3015912166812283303) --> + <skip /> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"ரோமிங்கின்போது கூடுதல் கட்டணங்கள் விதிக்கப்படக்கூடும்"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-te/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-te/strings.xml new file mode 100644 index 000000000000..937d34d52027 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-te/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"టెథరింగ్ చేయడానికి ఇంటర్నెట్ కనెక్షన్ లేదు"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"పరికరాలు కనెక్ట్ అవ్వడం లేదు"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"టెథరింగ్ను ఆఫ్ చేయండి"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"హాట్స్పాట్ లేదా టెథరింగ్ ఆన్లో ఉంది"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"రోమింగ్లో ఉన్నప్పుడు అదనపు ఛార్జీలు వర్తించవచ్చు"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-th/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-th/strings.xml new file mode 100644 index 000000000000..f781fae5252e --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-th/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"การเชื่อมต่ออินเทอร์เน็ตผ่านมือถือไม่มีอินเทอร์เน็ต"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"อุปกรณ์เชื่อมต่อไม่ได้"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ปิดการเชื่อมต่ออินเทอร์เน็ตผ่านมือถือ"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ฮอตสปอตหรือการเชื่อมต่ออินเทอร์เน็ตผ่านมือถือเปิดอยู่"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"อาจมีค่าใช้จ่ายเพิ่มเติมขณะโรมมิ่ง"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-tl/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-tl/strings.xml new file mode 100644 index 000000000000..8d5d46537334 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-tl/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Walang internet ang pag-tether"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Hindi makakonekta ang mga device"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"I-off ang pag-tether"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Naka-on ang Hotspot o pag-tether"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Posibleng magkaroon ng mga karagdagang singil habang nagro-roam"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-tr/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-tr/strings.xml new file mode 100644 index 000000000000..80cab33ac05e --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-tr/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering\'in internet bağlantısı yok"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Cihazlar bağlanamıyor"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Tethering\'i kapat"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot veya tethering açık"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Dolaşım sırasında ek ücretler uygulanabilir"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-uk/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-uk/strings.xml new file mode 100644 index 000000000000..c05932a5ae7f --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-uk/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Телефон, який використовується як модем, не підключений до Інтернету"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Не вдається підключити пристрої"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Вимкнути використання телефона як модема"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Увімкнено точку доступу або використання телефона як модема"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"У роумінгу може стягуватися додаткова плата"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-ur/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ur/strings.xml new file mode 100644 index 000000000000..d820eee8ba3c --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-ur/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"ٹیدرنگ میں انٹرنیٹ نہیں ہے"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"آلات منسلک نہیں ہو سکتے"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"ٹیدرنگ آف کریں"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"ہاٹ اسپاٹ یا ٹیدرنگ آن ہے"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"رومنگ کے دوران اضافی چارجز لاگو ہو سکتے ہیں"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-uz/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-uz/strings.xml new file mode 100644 index 000000000000..726148aaee5a --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-uz/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Modem internetga ulanmagan"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Qurilmalar ulanmadi"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Modem rejimini faolsizlantirish"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Hotspot yoki modem rejimi yoniq"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Rouming vaqtida qoʻshimcha haq olinishi mumkin"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-vi/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-vi/strings.xml new file mode 100644 index 000000000000..b7cb0456b673 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-vi/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Không có Internet để chia sẻ kết Internet"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Các thiết bị không thể kết nối"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Tắt tính năng chia sẻ Internet"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"Điểm phát sóng hoặc tính năng chia sẻ Internet đang bật"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Bạn có thể mất thêm phí dữ liệu khi chuyển vùng"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-zh-rCN/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-zh-rCN/strings.xml new file mode 100644 index 000000000000..af91afff9a4c --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-zh-rCN/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"共享网络未连接到互联网"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"设备无法连接"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"关闭网络共享"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"热点或网络共享已开启"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"漫游时可能会产生额外的费用"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-zh-rHK/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-zh-rHK/strings.xml new file mode 100644 index 000000000000..28e6b80c01a9 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-zh-rHK/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"無法透過網絡共享連線至互聯網"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"裝置無法連接"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"關閉網絡共享"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"熱點或網絡共享已開啟"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"漫遊時可能需要支付額外費用"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-zh-rTW/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-zh-rTW/strings.xml new file mode 100644 index 000000000000..05b90692ea7b --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-zh-rTW/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"無法透過數據連線連上網際網路"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"裝置無法連線"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"關閉數據連線"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"無線基地台或數據連線已開啟"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"使用漫遊服務可能須支付額外費用"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004-zu/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-zu/strings.xml new file mode 100644 index 000000000000..11eb66621971 --- /dev/null +++ b/packages/Tethering/res/values-mcc310-mnc004-zu/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="5030042590486713460">"Ukusebenzisa ifoni njengemodemu akunayo i-inthanethi"</string> + <string name="no_upstream_notification_message" msgid="3843613362272973447">"Amadivayisi awakwazi ukuxhumeka"</string> + <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"Vala ukusebenzisa ifoni njengemodemu"</string> + <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"I-hotspot noma ukusebenzisa ifoni njengemodemu kuvuliwe"</string> + <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"Kungaba nezinkokhelo ezengeziwe uma uzula"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc310-mnc004/strings.xml b/packages/Tethering/res/values-mcc310-mnc004/strings.xml index 9dadd49cf8a4..ce9ff6080717 100644 --- a/packages/Tethering/res/values-mcc310-mnc004/strings.xml +++ b/packages/Tethering/res/values-mcc310-mnc004/strings.xml @@ -25,6 +25,4 @@ <string name="upstream_roaming_notification_title">Hotspot or tethering is on</string> <!-- String for cellular roaming notification message [CHAR LIMIT=500] --> <string name="upstream_roaming_notification_message">Additional charges may apply while roaming</string> - <!-- String for cellular roaming notification continue button [CHAR LIMIT=200] --> - <string name="upstream_roaming_notification_continue_button">Continue</string> </resources> diff --git a/packages/Tethering/res/values-mcc310-mnc120/strings.xml b/packages/Tethering/res/values-mcc310-mnc120/strings.xml deleted file mode 100644 index 618df90c7105..000000000000 --- a/packages/Tethering/res/values-mcc310-mnc120/strings.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- String for tethered notification title with client number info. --> - <plurals name="tethered_notification_title_with_client_number"> - <item quantity="one"><xliff:g>%1$d</xliff:g> device connected.</item> - <item quantity="other"><xliff:g>%1$d</xliff:g> devices connected.</item> - </plurals> -</resources>
\ No newline at end of file diff --git a/packages/Tethering/res/values-mcc311-mnc480-af/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-af/strings.xml new file mode 100644 index 000000000000..9bfa5317a9e4 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-af/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Verbinding het nie internet nie"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Toestelle kan nie koppel nie"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Skakel verbinding af"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Warmkol of verbinding is aan"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Bykomende heffings kan geld terwyl jy swerf"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-am/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-am/strings.xml new file mode 100644 index 000000000000..5949dfa776d7 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-am/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"ማስተሳሰር ምንም በይነመረብ የለውም"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"መሣሪያዎችን ማገናኘት አይቻልም"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ማስተሳሰርን አጥፋ"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"መገናኛ ነጥብ ወይም ማስተሳሰር በርቷል"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"በሚያንዣብብበት ጊዜ ተጨማሪ ክፍያዎች ተፈጻሚ ሊሆኑ ይችላሉ"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-ar/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ar/strings.xml new file mode 100644 index 000000000000..8467f9b1f5cf --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-ar/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"ما مِن اتصال بالإنترنت خلال التوصيل"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"تعذّر اتصال الأجهزة"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"إيقاف التوصيل"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"نقطة الاتصال أو التوصيل مفعّلان"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"قد يتم تطبيق رسوم إضافية أثناء التجوال."</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-as/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-as/strings.xml new file mode 100644 index 000000000000..9776bd89da48 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-as/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"টে\'ডাৰিঙৰ ইণ্টাৰনেট নাই"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"ডিভাইচসমূহ সংযোগ কৰিব নোৱাৰি"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"টে\'ডাৰিং অফ কৰক"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"হটস্পট অথবা টে\'ডাৰিং অন আছে"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"ৰ\'মিঙত থাকিলে অতিৰিক্ত মাচুল প্ৰযোজ্য হ’ব পাৰে"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-az/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-az/strings.xml new file mode 100644 index 000000000000..e6d3eaf9f07c --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-az/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Modemin internetə girişi yoxdur"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Cihazları qoşmaq mümkün deyil"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Modemi deaktiv edin"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot və ya modem aktivdir"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Rouminq zamanı əlavə ödənişlər tətbiq edilə bilər"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-b+sr+Latn/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-b+sr+Latn/strings.xml new file mode 100644 index 000000000000..4c8a1df8eece --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-b+sr+Latn/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Privezivanje nema pristup internetu"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Povezivanje uređaja nije uspelo"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Isključi privezivanje"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Uključen je hotspot ili privezivanje"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Možda važe dodatni troškovi u romingu"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-be/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-be/strings.xml new file mode 100644 index 000000000000..edfa41e1ffd3 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-be/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Рэжым мадэма выкарыстоўваецца без доступу да інтэрнэту"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Не ўдалося падключыць прылады"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Выключыць рэжым мадэма"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Хот-спот або рэжым мадэма ўключаны"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Пры выкарыстанні роўмінгу можа спаганяцца дадатковая плата"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-bg/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-bg/strings.xml new file mode 100644 index 000000000000..f56398196f51 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-bg/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Тетърингът няма връзка с интернет"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Устройствата не могат да установят връзка"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Изключване на тетъринга"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Точката за достъп или тетърингът са включени"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Възможно е да ви бъдат начислени допълнителни такси при роуминг"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-bn/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-bn/strings.xml new file mode 100644 index 000000000000..d8ecd2e988f9 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-bn/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"টিথারিং করার জন্য কোনও ইন্টারনেট কানেকশন নেই"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"ডিভাইস কানেক্ট করতে পারছে না"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"টিথারিং বন্ধ করুন"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"হটস্পট বা টিথারিং চালু আছে"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"রোমিংয়ের সময় অতিরিক্ত চার্জ করা হতে পারে"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-bs/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-bs/strings.xml new file mode 100644 index 000000000000..b85fd5e28577 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-bs/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Povezivanje putem mobitela nema internet"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Uređaji se ne mogu povezati"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Isključi povezivanje putem mobitela"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Pristupna tačka ili povezivanje putem mobitela je uključeno"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Mogu nastati dodatni troškovi u romingu"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-ca/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ca/strings.xml new file mode 100644 index 000000000000..a3572151be6b --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-ca/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"La compartició de xarxa no té accés a Internet"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"No es poden connectar els dispositius"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Desactiva la compartició de xarxa"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"S\'ha activat el punt d\'accés Wi‑Fi o la compartició de xarxa"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"És possible que s\'apliquin costos addicionals en itinerància"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-cs/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-cs/strings.xml new file mode 100644 index 000000000000..91196be9e557 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-cs/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering nemá připojení k internetu"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Zařízení se nemůžou připojit"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Vypnout tethering"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Je zapnutý hotspot nebo tethering"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Při roamingu mohou být účtovány dodatečné poplatky"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-da/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-da/strings.xml new file mode 100644 index 000000000000..196890011d50 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-da/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Netdeling har ingen internetforbindelse"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Enheder kan ikke oprette forbindelse"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Deaktiver netdeling"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot eller netdeling er aktiveret"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Der opkræves muligvis yderligere gebyrer ved roaming"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-de/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-de/strings.xml new file mode 100644 index 000000000000..eb3f8c52c0c5 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-de/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering hat keinen Internetzugriff"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Geräte können sich nicht verbinden"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Tethering deaktivieren"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot oder Tethering ist aktiviert"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Für das Roaming können zusätzliche Gebühren anfallen"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-el/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-el/strings.xml new file mode 100644 index 000000000000..56c3d81b634e --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-el/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Η σύνδεση δεν έχει πρόσβαση στο διαδίκτυο"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Δεν είναι δυνατή η σύνδεση των συσκευών"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Απενεργοποιήστε τη σύνδεση"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Ενεργό σημείο πρόσβασης Wi-Fi ή ενεργή σύνδεση"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Ενδέχεται να ισχύουν επιπλέον χρεώσεις κατά την περιαγωγή."</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-en-rAU/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-en-rAU/strings.xml new file mode 100644 index 000000000000..dd1a1971cdd8 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-en-rAU/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering has no Internet"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Devices can’t connect"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Turn off tethering"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot or tethering is on"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Additional charges may apply while roaming"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-en-rCA/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-en-rCA/strings.xml new file mode 100644 index 000000000000..dd1a1971cdd8 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-en-rCA/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering has no Internet"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Devices can’t connect"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Turn off tethering"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot or tethering is on"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Additional charges may apply while roaming"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-en-rGB/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-en-rGB/strings.xml new file mode 100644 index 000000000000..dd1a1971cdd8 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-en-rGB/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering has no Internet"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Devices can’t connect"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Turn off tethering"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot or tethering is on"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Additional charges may apply while roaming"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-en-rIN/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-en-rIN/strings.xml new file mode 100644 index 000000000000..dd1a1971cdd8 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-en-rIN/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering has no Internet"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Devices can’t connect"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Turn off tethering"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot or tethering is on"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Additional charges may apply while roaming"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-en-rXC/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-en-rXC/strings.xml new file mode 100644 index 000000000000..d3347aae207d --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-en-rXC/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering has no internet"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Devices can’t connect"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Turn off tethering"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot or tethering is on"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Additional charges may apply while roaming"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-es-rUS/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-es-rUS/strings.xml new file mode 100644 index 000000000000..2f0504f07de7 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-es-rUS/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"La conexión mediante dispositivo móvil no tiene Internet"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"No se pueden conectar los dispositivos"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Desactivar conexión mediante dispositivo móvil"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Se activó el hotspot o la conexión mediante dispositivo móvil"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Es posible que se apliquen cargos adicionales por roaming"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-es/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-es/strings.xml new file mode 100644 index 000000000000..2d8f88242502 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-es/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"La conexión no se puede compartir, porque no hay acceso a Internet"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Los dispositivos no se pueden conectar"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Desactivar conexión compartida"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Punto de acceso o conexión compartida activados"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Puede que se apliquen cargos adicionales en itinerancia"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-et/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-et/strings.xml new file mode 100644 index 000000000000..8493c470710d --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-et/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Jagamisel puudub internetiühendus"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Seadmed ei saa ühendust luua"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Lülita jagamine välja"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Kuumkoht või jagamine on sisse lülitatud"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Rändluse kasutamisega võivad kaasneda lisatasud"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-eu/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-eu/strings.xml new file mode 100644 index 000000000000..33bccab3e88c --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-eu/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Konexioa partekatzeko aukerak ez du Interneteko konexiorik"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Ezin dira konektatu gailuak"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Desaktibatu konexioa partekatzeko aukera"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Wifi-gunea edo konexioa partekatzeko aukera aktibatuta dago"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Baliteke kostu gehigarriak ordaindu behar izatea ibiltaritzan"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-fa/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-fa/strings.xml new file mode 100644 index 000000000000..cf8a0cc27705 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-fa/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"«اشتراکگذاری اینترنت» به اینترنت دسترسی ندارد"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"دستگاهها متصل نمیشوند"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"خاموش کردن «اشتراکگذاری اینترنت»"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"«نقطه اتصال» یا «اشتراکگذاری اینترنت» روشن است"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"ممکن است درحین فراگردی تغییرات دیگر اعمال شود"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-fi/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-fi/strings.xml new file mode 100644 index 000000000000..6a3ab806db98 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-fi/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Ei jaettavaa internetyhteyttä"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Laitteet eivät voi muodostaa yhteyttä"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Laita yhteyden jakaminen pois päältä"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot tai yhteyden jakaminen on päällä"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Roaming voi aiheuttaa lisämaksuja"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-fr-rCA/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-fr-rCA/strings.xml new file mode 100644 index 000000000000..ffb9bf60472e --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-fr-rCA/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Le partage de connexion n\'est pas connecté à Internet"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Impossible de connecter les appareils"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Désactiver le partage de connexion"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Le point d\'accès ou le partage de connexion est activé"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"En itinérance, des frais supplémentaires peuvent s\'appliquer"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-fr/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-fr/strings.xml new file mode 100644 index 000000000000..768bce3f0ab1 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-fr/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Aucune connexion à Internet n\'est disponible pour le partage de connexion"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Impossible de connecter les appareils"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Désactiver le partage de connexion"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Le point d\'accès ou le partage de connexion est activé"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"En itinérance, des frais supplémentaires peuvent s\'appliquer"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-gl/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-gl/strings.xml new file mode 100644 index 000000000000..0c4195a7caf3 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-gl/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"A conexión compartida non ten Internet"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Non se puideron conectar os dispositivos"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Desactivar conexión compartida"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Está activada a zona wifi ou a conexión compartida"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Pódense aplicar cargos adicionais en itinerancia"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-gu/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-gu/strings.xml new file mode 100644 index 000000000000..e9d33a7db259 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-gu/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"ઇન્ટરનેટ શેર કરવાની સુવિધામાં ઇન્ટરનેટ નથી"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"ડિવાઇસ કનેક્ટ કરી શકાતા નથી"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ઇન્ટરનેટ શેર કરવાની સુવિધા બંધ કરો"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"હૉટસ્પૉટ અથવા ઇન્ટરનેટ શેર કરવાની સુવિધા ચાલુ છે"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"રોમિંગમાં વધારાના શુલ્ક લાગી શકે છે"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-hi/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-hi/strings.xml new file mode 100644 index 000000000000..aa418ac5d3bb --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-hi/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"टेदरिंग से इंटरनेट नहीं चल रहा"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"डिवाइस कनेक्ट नहीं हो पा रहे"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"टेदरिंग बंद करें"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"हॉटस्पॉट या टेदरिंग चालू है"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"रोमिंग के दौरान अतिरिक्त शुल्क लग सकता है"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-hr/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-hr/strings.xml new file mode 100644 index 000000000000..51c524afbc53 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-hr/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Modemsko povezivanje nema internet"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Uređaji se ne mogu povezati"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Isključivanje modemskog povezivanja"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Uključena je žarišna točka ili modemsko povezivanje"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"U roamingu su mogući dodatni troškovi"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-hu/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-hu/strings.xml new file mode 100644 index 000000000000..164e45edd142 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-hu/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Nincs internetkapcsolat az internet megosztásához"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Az eszközök nem tudnak csatlakozni"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Internetmegosztás kikapcsolása"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"A hotspot vagy az internetmegosztás be van kapcsolva"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Roaming során további díjak léphetnek fel"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-hy/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-hy/strings.xml new file mode 100644 index 000000000000..e76c0a4c80d5 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-hy/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Մոդեմի ռեժիմի կապը բացակայում է"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Չհաջողվեց միացնել սարքը"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Անջատել մոդեմի ռեժիմը"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Թեժ կետը կամ մոդեմի ռեժիմը միացված է"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Ռոումինգում կարող են լրացուցիչ վճարներ գանձվել"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-in/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-in/strings.xml new file mode 100644 index 000000000000..2b817f8abd17 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-in/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Tidak ada koneksi internet di tethering"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Perangkat tidak dapat terhubung"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Nonaktifkan tethering"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot atau tethering aktif"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Biaya tambahan mungkin berlaku saat roaming"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-is/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-is/strings.xml new file mode 100644 index 000000000000..a338d9c7cab8 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-is/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Tjóðrun er ekki með internettengingu"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Tæki geta ekki tengst"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Slökkva á tjóðrun"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Kveikt er á heitum reit eða tjóðrun"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Viðbótargjöld kunna að eiga við í reiki"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-it/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-it/strings.xml new file mode 100644 index 000000000000..77769c2ac56c --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-it/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Nessuna connessione a Internet per il tethering"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Impossibile connettere i dispositivi"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Disattiva il tethering"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot o tethering attivi"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Potrebbero essere applicati costi aggiuntivi durante il roaming"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-iw/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-iw/strings.xml new file mode 100644 index 000000000000..5267b5126435 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-iw/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"אי אפשר להפעיל את תכונת שיתוף האינטרנט בין מכשירים כי אין חיבור לאינטרנט"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"למכשירים אין אפשרות להתחבר"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"השבתה של שיתוף האינטרנט בין מכשירים"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"תכונת הנקודה לשיתוף אינטרנט או תכונת שיתוף האינטרנט בין מכשירים פועלת"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"ייתכנו חיובים נוספים בעת נדידה"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-ja/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ja/strings.xml new file mode 100644 index 000000000000..66a9a6dd35c2 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-ja/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"テザリングがインターネットに接続されていません"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"デバイスを接続できません"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"テザリングを OFF にする"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"アクセス ポイントまたはテザリングが ON です"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"ローミング時に追加料金が発生することがあります"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-ka/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ka/strings.xml new file mode 100644 index 000000000000..d8ad8808498f --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-ka/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"ტეტერინგს არ აქვს ინტერნეტზე წვდომა"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"მოწყობილობები ვერ ახერხებენ დაკავშირებას"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ტეტერინგის გამორთვა"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ჩართულია უსადენო ქსელი ან ტეტერინგი"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"როუმინგის გამოყენებისას შეიძლება ჩამოგეჭრათ დამატებითი საფასური"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-kk/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-kk/strings.xml new file mode 100644 index 000000000000..1ddd6b419b57 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-kk/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Тетеринг режимі интернет байланысынсыз пайдаланылуда"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Құрылғыларды байланыстыру мүмкін емес"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Тетерингіні өшіру"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Хотспот немесе тетеринг қосулы"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Роуминг кезінде қосымша ақы алынуы мүмкін."</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-km/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-km/strings.xml new file mode 100644 index 000000000000..cf5a1379ccc7 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-km/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"ការភ្ជាប់មិនមានអ៊ីនធឺណិតទេ"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"មិនអាចភ្ជាប់ឧបករណ៍បានទេ"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"បិទការភ្ជាប់"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ហតស្ប៉ត ឬការភ្ជាប់ត្រូវបានបើក"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"អាចមានការគិតថ្លៃបន្ថែម នៅពេលរ៉ូមីង"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-kn/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-kn/strings.xml new file mode 100644 index 000000000000..68ae68bc1998 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-kn/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"ಟೆಥರಿಂಗ್ ಯಾವುದೇ ಇಂಟರ್ನೆಟ್ ಕನೆಕ್ಷನ್ ಹೊಂದಿಲ್ಲ"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"ಸಾಧನಗಳನ್ನು ಕನೆಕ್ಟ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ಟೆಥರಿಂಗ್ ಆಫ್ ಮಾಡಿ"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ಹಾಟ್ಸ್ಪಾಟ್ ಅಥವಾ ಟೆಥರಿಂಗ್ ಆನ್ ಆಗಿದೆ"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"ರೋಮಿಂಗ್ನಲ್ಲಿರುವಾಗ ಹೆಚ್ಚುವರಿ ಶುಲ್ಕಗಳು ಅನ್ವಯವಾಗಬಹುದು"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-ko/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ko/strings.xml new file mode 100644 index 000000000000..17185ba2d063 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-ko/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"테더링으로 인터넷을 사용할 수 없음"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"기기에서 연결할 수 없음"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"테더링 사용 중지"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"핫스팟 또는 테더링 켜짐"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"로밍 중에는 추가 요금이 발생할 수 있습니다."</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-ky/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ky/strings.xml new file mode 100644 index 000000000000..6a9fb9810cc6 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-ky/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Модем режими Интернети жок колдонулууда"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Түзмөктөр туташпай жатат"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Модем режимин өчүрүү"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Байланыш түйүнү же модем режими күйүк"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Роумингде кошумча акы алынышы мүмкүн"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-lo/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-lo/strings.xml new file mode 100644 index 000000000000..bcc4b5762678 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-lo/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"ການປ່ອຍສັນຍານບໍ່ມີອິນເຕີເນັດ"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"ອຸປະກອນບໍ່ສາມາດເຊື່ອມຕໍ່ໄດ້"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ປິດການປ່ອຍສັນຍານ"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ເປີດໃຊ້ຮັອດສະປອດ ຫຼື ການປ່ອຍສັນຍານຢູ່"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"ອາດມີຄ່າໃຊ້ຈ່າຍເພີ່ມເຕີມໃນລະຫວ່າງການໂຣມມິງ"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-lt/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-lt/strings.xml new file mode 100644 index 000000000000..011c2c11fb88 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-lt/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Nėra įrenginio kaip modemo naudojimo interneto ryšio"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Nepavyko susieti įrenginių"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Išjungti įrenginio kaip modemo naudojimą"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Įjungtas viešosios interneto prieigos taškas arba įrenginio kaip modemo naudojimas"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Veikiant tarptinkliniam ryšiui gali būti taikomi papildomi mokesčiai"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-lv/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-lv/strings.xml new file mode 100644 index 000000000000..5cb2f3b7aac8 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-lv/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Piesaistei nav interneta savienojuma"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Nevar savienot ierīces"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Izslēgt piesaisti"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Ir ieslēgts tīklājs vai piesaiste"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Viesabonēšanas laikā var tikt piemērota papildu samaksa"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-mk/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-mk/strings.xml new file mode 100644 index 000000000000..4cbfd887c57e --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-mk/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Нема интернет преку мобилен"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Уредите не може да се поврзат"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Исклучи интернет преку мобилен"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Точката на пристап или интернетот преку мобилен е вклучен"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"При роаминг може да се наплатат дополнителни трошоци"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-ml/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ml/strings.xml new file mode 100644 index 000000000000..9cf4eaf34a97 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-ml/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"ടെതറിംഗിന് ഇന്റർനെറ്റ് ഇല്ല"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"ഉപകരണങ്ങൾ കണക്റ്റ് ചെയ്യാനാവില്ല"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ടെതറിംഗ് ഓഫാക്കുക"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ഹോട്ട്സ്പോട്ട് അല്ലെങ്കിൽ ടെതറിംഗ് ഓണാണ്"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"റോമിംഗ് ചെയ്യുമ്പോൾ അധിക നിരക്കുകൾ ബാധകമായേക്കാം"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-mn/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-mn/strings.xml new file mode 100644 index 000000000000..47c82c14d9d6 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-mn/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Модемд интернэт алга байна"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Төхөөрөмжүүд холбогдох боломжгүй байна"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Модем болгохыг унтраах"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Сүлжээний цэг эсвэл модем болгох асаалттай байна"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Роумингийн үеэр нэмэлт төлбөр нэхэмжилж болзошгүй"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-mr/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-mr/strings.xml new file mode 100644 index 000000000000..ad9e809ab27d --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-mr/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"टेदरिंगला इंटरनेट नाही"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"डिव्हाइस कनेक्ट होऊ शकत नाहीत"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"टेदरिंग बंद करा"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"हॉटस्पॉट किंवा टेदरिंग सुरू आहे"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"रोमिंगदरम्यान अतिरिक्त शुल्क लागू होऊ शकतात"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-ms/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ms/strings.xml new file mode 100644 index 000000000000..e708cb8717b3 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-ms/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Penambatan tiada Internet"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Peranti tidak dapat disambungkan"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Matikan penambatan"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Tempat liputan atau penambatan dihidupkan"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Caj tambahan mungkin digunakan semasa perayauan"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-my/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-my/strings.xml new file mode 100644 index 000000000000..ba5462250b05 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-my/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်းတွင် အင်တာနက် မရှိပါ"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"စက်များ ချိတ်ဆက်၍ မရပါ"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်း ပိတ်ရန်"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ဟော့စပေါ့ (သို့) မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်း ဖွင့်ထားသည်"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"ပြင်ပကွန်ရက်နှင့် ချိတ်ဆက်သည့်အခါ နောက်ထပ်ကျသင့်မှုများ ရှိနိုင်သည်"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-nb/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-nb/strings.xml new file mode 100644 index 000000000000..57db484a2543 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-nb/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Internettdeling har ikke internettilgang"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Enhetene kan ikke koble til"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Slå av internettdeling"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Wi-Fi-sone eller internettdeling er på"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Ytterligere kostnader kan påløpe under roaming"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-ne/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ne/strings.xml new file mode 100644 index 000000000000..617c50dd0ce8 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-ne/strings.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- no translation found for no_upstream_notification_title (611650570559011140) --> + <skip /> + <!-- no translation found for no_upstream_notification_message (6508394877641864863) --> + <skip /> + <!-- no translation found for no_upstream_notification_disable_button (7609346639290990508) --> + <skip /> + <!-- no translation found for upstream_roaming_notification_title (6032901176124830787) --> + <skip /> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"रोमिङ सेवा प्रयोग गर्दा अतिरिक्त शुल्क लाग्न सक्छ"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-nl/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-nl/strings.xml new file mode 100644 index 000000000000..b08133f4e592 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-nl/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering heeft geen internet"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Apparaten kunnen niet worden verbonden"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Tethering uitschakelen"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot of tethering is ingeschakeld"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Er kunnen extra kosten voor roaming in rekening worden gebracht."</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-or/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-or/strings.xml new file mode 100644 index 000000000000..1ad4ca354ad5 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-or/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"ଟିଥରିଂ ପାଇଁ କୌଣସି ଇଣ୍ଟର୍ନେଟ୍ ସଂଯୋଗ ନାହିଁ"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"ଡିଭାଇସଗୁଡ଼ିକ ସଂଯୋଗ କରାଯାଇପାରିବ ନାହିଁ"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ଟିଥରିଂ ବନ୍ଦ କରନ୍ତୁ"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ହଟସ୍ପଟ୍ କିମ୍ବା ଟିଥରିଂ ଚାଲୁ ଅଛି"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"ରୋମିଂରେ ଥିବା ସମୟରେ ଅତିରିକ୍ତ ଶୁଳ୍କ ଲାଗୁ ହୋଇପାରେ"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-pa/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-pa/strings.xml new file mode 100644 index 000000000000..88def563d85a --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-pa/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"ਟੈਦਰਿੰਗ ਕੋਲ ਇੰਟਰਨੈੱਟ ਪਹੁੰਚ ਨਹੀਂ ਹੈ"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"ਡੀਵਾਈਸ ਕਨੈਕਟ ਨਹੀਂ ਕੀਤੇ ਜਾ ਸਕਦੇ"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ਟੈਦਰਿੰਗ ਬੰਦ ਕਰੋ"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ਹੌਟਸਪੌਟ ਜਾਂ ਟੈਦਰਿੰਗ ਚਾਲੂ ਹੈ"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"ਰੋਮਿੰਗ ਦੌਰਾਨ ਵਧੀਕ ਖਰਚੇ ਲਾਗੂ ਹੋ ਸਕਦੇ ਹਨ"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-pl/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-pl/strings.xml new file mode 100644 index 000000000000..f9890abdc26b --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-pl/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering nie ma internetu"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Urządzenia nie mogą się połączyć"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Wyłącz tethering"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot lub tethering jest włączony"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Podczas korzystania z roamingu mogą zostać naliczone dodatkowe opłaty"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-pt-rBR/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-pt-rBR/strings.xml new file mode 100644 index 000000000000..ce3b88479f09 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-pt-rBR/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"O tethering não tem Internet"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Não é possível conectar os dispositivos"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Desativar o tethering"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Ponto de acesso ou tethering ativado"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Pode haver cobranças extras durante o roaming"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-pt-rPT/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-pt-rPT/strings.xml new file mode 100644 index 000000000000..7e883ea57682 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-pt-rPT/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"A ligação (à Internet) via telemóvel não tem Internet"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Não é possível ligar os dispositivos"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Desativar ligação (à Internet) via telemóvel"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"A zona Wi-Fi ou a ligação (à Internet) via telemóvel está ativada"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Podem aplicar-se custos adicionais em roaming."</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-pt/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-pt/strings.xml new file mode 100644 index 000000000000..ce3b88479f09 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-pt/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"O tethering não tem Internet"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Não é possível conectar os dispositivos"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Desativar o tethering"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Ponto de acesso ou tethering ativado"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Pode haver cobranças extras durante o roaming"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-ro/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ro/strings.xml new file mode 100644 index 000000000000..1009417316ed --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-ro/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Procesul de tethering nu are internet"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Dispozitivele nu se pot conecta"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Dezactivați procesul de tethering"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"S-a activat hotspotul sau tethering"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Se pot aplica taxe suplimentare pentru roaming"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-ru/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ru/strings.xml new file mode 100644 index 000000000000..88683bed95b8 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-ru/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Режим модема используется без доступа к Интернету"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Невозможно подключить устройства."</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Отключить режим модема"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Включены точка доступа или режим модема"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"За использование услуг связи в роуминге может взиматься дополнительная плата."</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-si/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-si/strings.xml new file mode 100644 index 000000000000..176bcdb797c6 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-si/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"ටෙදරින් හට අන්තර්ජාලය නැත"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"උපාංගවලට සම්බන්ධ විය නොහැකිය"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ටෙදරින් ක්රියාවිරහිත කරන්න"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"හොට්ස්පොට් හෝ ටෙදරින් ක්රියාත්මකයි"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"රෝමිං අතරතුර අමතර ගාස්තු අදාළ විය හැකිය"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-sk/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-sk/strings.xml new file mode 100644 index 000000000000..b9e2127fa879 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-sk/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering nemá internetové pripojenie"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Zariadenia sa nemôžu pripojiť"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Vypnúť tethering"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Je zapnutý hotspot alebo tethering"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Počas roamingu vám môžu byť účtované ďalšie poplatky"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-sl/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-sl/strings.xml new file mode 100644 index 000000000000..e8140e686a0c --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-sl/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Internetna povezava prek mobilnega telefona ni vzpostavljena"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Napravi se ne moreta povezati"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Izklopi internetno povezavo prek mobilnega telefona"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Dostopna točka ali internetna povezava prek mobilnega telefona je vklopljena"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Med gostovanjem lahko nastanejo dodatni stroški"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-sq/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-sq/strings.xml new file mode 100644 index 000000000000..61e698d6e8ab --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-sq/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Ndarja e internetit nuk ka internet"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Pajisjet nuk mund të lidhen"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Çaktivizo ndarjen e internetit"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Zona e qasjes për internet ose ndarja e internetit është aktive"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Mund të zbatohen tarifime shtesë kur je në roaming"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-sr/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-sr/strings.xml new file mode 100644 index 000000000000..b4c411c35475 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-sr/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Привезивање нема приступ интернету"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Повезивање уређаја није успело"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Искључи привезивање"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Укључен је хотспот или привезивање"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Можда важе додатни трошкови у ромингу"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-sv/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-sv/strings.xml new file mode 100644 index 000000000000..4f543e47b998 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-sv/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Det finns ingen internetanslutning för internetdelningen"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Enheterna kan inte anslutas"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Inaktivera internetdelning"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Surfzon eller internetdelning har aktiverats"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Ytterligare avgifter kan tillkomma vid roaming"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-sw/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-sw/strings.xml new file mode 100644 index 000000000000..ac347ab485e0 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-sw/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Kipengele cha kusambaza mtandao hakina intaneti"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Imeshindwa kuunganisha vifaa"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Zima kipengele cha kusambaza mtandao"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Umewasha kipengele cha kusambaza mtandao au mtandao pepe"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Huenda ukatozwa gharama za ziada ukitumia mitandao ya ng\'ambo"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-ta/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ta/strings.xml new file mode 100644 index 000000000000..0e437593ee87 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-ta/strings.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- no translation found for no_upstream_notification_title (611650570559011140) --> + <skip /> + <!-- no translation found for no_upstream_notification_message (6508394877641864863) --> + <skip /> + <!-- no translation found for no_upstream_notification_disable_button (7609346639290990508) --> + <skip /> + <!-- no translation found for upstream_roaming_notification_title (6032901176124830787) --> + <skip /> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"ரோமிங்கின்போது கூடுதல் கட்டணங்கள் விதிக்கப்படக்கூடும்"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-te/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-te/strings.xml new file mode 100644 index 000000000000..9360297dd807 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-te/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"టెథరింగ్ చేయడానికి ఇంటర్నెట్ కనెక్షన్ లేదు"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"పరికరాలు కనెక్ట్ అవ్వడం లేదు"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"టెథరింగ్ను ఆఫ్ చేయండి"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"హాట్స్పాట్ లేదా టెథరింగ్ ఆన్లో ఉంది"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"రోమింగ్లో ఉన్నప్పుడు అదనపు ఛార్జీలు వర్తించవచ్చు"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-th/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-th/strings.xml new file mode 100644 index 000000000000..9c4d7e08f2b6 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-th/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"การเชื่อมต่ออินเทอร์เน็ตผ่านมือถือไม่มีอินเทอร์เน็ต"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"อุปกรณ์เชื่อมต่อไม่ได้"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ปิดการเชื่อมต่ออินเทอร์เน็ตผ่านมือถือ"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ฮอตสปอตหรือการเชื่อมต่ออินเทอร์เน็ตผ่านมือถือเปิดอยู่"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"อาจมีค่าใช้จ่ายเพิ่มเติมขณะโรมมิ่ง"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-tl/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-tl/strings.xml new file mode 100644 index 000000000000..a7c78a593267 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-tl/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Walang internet ang pag-tether"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Hindi makakonekta ang mga device"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"I-off ang pag-tether"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Naka-on ang Hotspot o pag-tether"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Posibleng magkaroon ng mga karagdagang singil habang nagro-roam"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-tr/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-tr/strings.xml new file mode 100644 index 000000000000..93da2c3f7981 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-tr/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering\'in internet bağlantısı yok"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Cihazlar bağlanamıyor"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Tethering\'i kapat"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot veya tethering açık"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Dolaşım sırasında ek ücretler uygulanabilir"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-uk/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-uk/strings.xml new file mode 100644 index 000000000000..ee0dcd2c4b6a --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-uk/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Телефон, який використовується як модем, не підключений до Інтернету"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Не вдається підключити пристрої"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Вимкнути використання телефона як модема"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Увімкнено точку доступу або використання телефона як модема"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"У роумінгу може стягуватися додаткова плата"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-ur/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ur/strings.xml new file mode 100644 index 000000000000..41cd28eef9bd --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-ur/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"ٹیدرنگ میں انٹرنیٹ نہیں ہے"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"آلات منسلک نہیں ہو سکتے"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"ٹیدرنگ آف کریں"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"ہاٹ اسپاٹ یا ٹیدرنگ آن ہے"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"رومنگ کے دوران اضافی چارجز لاگو ہو سکتے ہیں"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-uz/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-uz/strings.xml new file mode 100644 index 000000000000..c847bc943bd4 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-uz/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Modem internetga ulanmagan"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Qurilmalar ulanmadi"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Modem rejimini faolsizlantirish"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Hotspot yoki modem rejimi yoniq"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Rouming vaqtida qoʻshimcha haq olinishi mumkin"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-vi/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-vi/strings.xml new file mode 100644 index 000000000000..a74326f09ec5 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-vi/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Không có Internet để chia sẻ kết Internet"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Các thiết bị không thể kết nối"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Tắt tính năng chia sẻ Internet"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"Điểm phát sóng hoặc tính năng chia sẻ Internet đang bật"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Bạn có thể mất thêm phí dữ liệu khi chuyển vùng"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-zh-rCN/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-zh-rCN/strings.xml new file mode 100644 index 000000000000..d7370036e351 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-zh-rCN/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"共享网络未连接到互联网"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"设备无法连接"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"关闭网络共享"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"热点或网络共享已开启"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"漫游时可能会产生额外的费用"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-zh-rHK/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-zh-rHK/strings.xml new file mode 100644 index 000000000000..f378a9dc2cfb --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-zh-rHK/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"無法透過網絡共享連線至互聯網"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"裝置無法連接"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"關閉網絡共享"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"熱點或網絡共享已開啟"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"漫遊時可能需要支付額外費用"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-zh-rTW/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-zh-rTW/strings.xml new file mode 100644 index 000000000000..ea01b943fbe8 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-zh-rTW/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"無法透過數據連線連上網際網路"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"裝置無法連線"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"關閉數據連線"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"無線基地台或數據連線已開啟"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"使用漫遊服務可能須支付額外費用"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480-zu/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-zu/strings.xml new file mode 100644 index 000000000000..32f6df56f154 --- /dev/null +++ b/packages/Tethering/res/values-mcc311-mnc480-zu/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="no_upstream_notification_title" msgid="611650570559011140">"Ukusebenzisa ifoni njengemodemu akunayo i-inthanethi"</string> + <string name="no_upstream_notification_message" msgid="6508394877641864863">"Amadivayisi awakwazi ukuxhumeka"</string> + <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"Vala ukusebenzisa ifoni njengemodemu"</string> + <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"I-hotspot noma ukusebenzisa ifoni njengemodemu kuvuliwe"</string> + <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"Kungaba nezinkokhelo ezengeziwe uma uzula"</string> +</resources> diff --git a/packages/Tethering/res/values-mcc311-mnc480/strings.xml b/packages/Tethering/res/values-mcc311-mnc480/strings.xml index 9dadd49cf8a4..ce9ff6080717 100644 --- a/packages/Tethering/res/values-mcc311-mnc480/strings.xml +++ b/packages/Tethering/res/values-mcc311-mnc480/strings.xml @@ -25,6 +25,4 @@ <string name="upstream_roaming_notification_title">Hotspot or tethering is on</string> <!-- String for cellular roaming notification message [CHAR LIMIT=500] --> <string name="upstream_roaming_notification_message">Additional charges may apply while roaming</string> - <!-- String for cellular roaming notification continue button [CHAR LIMIT=200] --> - <string name="upstream_roaming_notification_continue_button">Continue</string> </resources> diff --git a/packages/Tethering/res/values-mcc311-mnc490/strings.xml b/packages/Tethering/res/values-mcc311-mnc490/strings.xml deleted file mode 100644 index 618df90c7105..000000000000 --- a/packages/Tethering/res/values-mcc311-mnc490/strings.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2020 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- String for tethered notification title with client number info. --> - <plurals name="tethered_notification_title_with_client_number"> - <item quantity="one"><xliff:g>%1$d</xliff:g> device connected.</item> - <item quantity="other"><xliff:g>%1$d</xliff:g> devices connected.</item> - </plurals> -</resources>
\ No newline at end of file diff --git a/packages/Tethering/res/values-mk/strings.xml b/packages/Tethering/res/values-mk/strings.xml index 0fab8aa4761f..9ad9b9a58935 100644 --- a/packages/Tethering/res/values-mk/strings.xml +++ b/packages/Tethering/res/values-mk/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Поврзувањето или точката на пристап се активни"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Допрете за поставување."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Врзувањето е оневозможено"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Контактирајте со администраторот за детали"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Активно е врзување или точка на пристап"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Допрете за поставување."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Врзувањето е оневозможено"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Контактирајте со администраторот за детали"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Статус на точката на пристап и врзувањето"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-ml/strings.xml b/packages/Tethering/res/values-ml/strings.xml index fd7e556e3899..9db79ce220a4 100644 --- a/packages/Tethering/res/values-ml/strings.xml +++ b/packages/Tethering/res/values-ml/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"ടെതറിംഗ് അല്ലെങ്കിൽ ഹോട്ട്സ്പോട്ട് സജീവമാണ്"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"സജ്ജമാക്കാൻ ടാപ്പുചെയ്യുക."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"ടെതറിംഗ് പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"വിശദവിവരങ്ങൾക്ക് നിങ്ങളുടെ അഡ്മിനെ ബന്ധപ്പെടുക"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"ടെതറിംഗ് അല്ലെങ്കിൽ ഹോട്ട്സ്പോട്ട് സജീവമാണ്"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"സജ്ജീകരിക്കാൻ ടാപ്പ് ചെയ്യുക."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"ടെതറിംഗ് പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"വിശദാംശങ്ങൾക്ക് നിങ്ങളുടെ അഡ്മിനെ ബന്ധപ്പെടുക"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"ഹോട്ട്സ്പോട്ടിന്റെയും ടെതറിംഗിന്റെയും നില"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-mn/strings.xml b/packages/Tethering/res/values-mn/strings.xml index 4596577c5d95..42d1edbaceb9 100644 --- a/packages/Tethering/res/values-mn/strings.xml +++ b/packages/Tethering/res/values-mn/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Модем болгох эсвэл идэвхтэй цэг болгох"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Тохируулахын тулд товшино уу."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Модем болгох боломжгүй байна"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Дэлгэрэнгүй мэдээлэл авахын тулд админтайгаа холбогдоно уу"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Модем болгох эсвэл сүлжээний цэг идэвхтэй байна"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Тохируулахын тулд товшино уу."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Модем болгохыг идэвхгүй болгосон"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Дэлгэрэнгүй мэдээлэл авахын тулд админтайгаа холбогдоно уу"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Сүлжээний цэг болон модем болгох төлөв"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-mr/strings.xml b/packages/Tethering/res/values-mr/strings.xml index 85c9ade4feee..13995b6b8aa5 100644 --- a/packages/Tethering/res/values-mr/strings.xml +++ b/packages/Tethering/res/values-mr/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"टेदरिंग किंवा हॉटस्पॉट सक्रिय"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"सेट करण्यासाठी टॅप करा."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"टेदरिंग बंद आहे"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"तपशीलांसाठी तुमच्या प्रशासकाशी संपर्क साधा"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"टेदरिंग किंवा हॉटस्पॉट अॅक्टिव्ह आहे"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"सेट करण्यासाठी टॅप करा."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"टेदरिंग बंद केले आहे"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"तपशीलांसाठी तुमच्या ॲडमिनशी संपर्क साधा"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"हॉटस्पॉट आणि टेदरिंगची स्थिती"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-ms/strings.xml b/packages/Tethering/res/values-ms/strings.xml index ec6bdbda08e7..d6a67f37b1de 100644 --- a/packages/Tethering/res/values-ms/strings.xml +++ b/packages/Tethering/res/values-ms/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Penambatan atau titik panas aktif"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Ketik untuk membuat persediaan."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Penambatan dilumpuhkan"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Hubungi pentadbir anda untuk maklumat lanjut"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Penambatan atau tempat liputan aktif"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Ketik untuk membuat persediaan."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Penambatan dilumpuhkan"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Hubungi pentadbir anda untuk mendapatkan maklumat lanjut"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status tempat liputan & penambatan"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-my/strings.xml b/packages/Tethering/res/values-my/strings.xml index 83978b67d433..49f6b88a7514 100644 --- a/packages/Tethering/res/values-my/strings.xml +++ b/packages/Tethering/res/values-my/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"တဆင့်ပြန်လည်လွှင့်ခြင်း သို့မဟုတ် ဟော့စပေါ့ ဖွင့်ထားသည်"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"စနစ်ထည့်သွင်းရန် တို့ပါ။"</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"မိုဘိုင်းဖုန်းကို မိုဒမ်အဖြစ်သုံးခြင်းအား ပိတ်ထားသည်"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"အသေးစိတ်အချက်အလက်များအတွက် သင့်စီမံခန့်ခွဲသူကို ဆက်သွယ်ပါ"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်း သို့မဟုတ် ဟော့စပေါ့ ဖွင့်ထားသည်"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"စနစ်ထည့်သွင်းရန် တို့ပါ။"</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်းကို ပိတ်ထားသည်"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"အသေးစိတ်အတွက် သင့်စီမံခန့်ခွဲသူကို ဆက်သွယ်ပါ"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"ဟော့စပေါ့နှင့် မိုဘိုင်းဖုန်းသုံး ချိတ်ဆက်မျှဝေခြင်း အခြေအနေ"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-nb/strings.xml b/packages/Tethering/res/values-nb/strings.xml index 9abf32dd7bf1..9594e0a70a69 100644 --- a/packages/Tethering/res/values-nb/strings.xml +++ b/packages/Tethering/res/values-nb/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Internettdeling eller trådløs sone er aktiv"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Trykk for å konfigurere."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Internettdeling er slått av"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Ta kontakt med administratoren din for å få mer informasjon"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Internettdeling eller Wi-Fi-sone er aktiv"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Trykk for å konfigurere."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Internettdeling er slått av"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Ta kontakt med administratoren din for å få mer informasjon"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status for Wi-Fi-sone og internettdeling"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-ne/strings.xml b/packages/Tethering/res/values-ne/strings.xml index c8869298a546..72ae3a80a928 100644 --- a/packages/Tethering/res/values-ne/strings.xml +++ b/packages/Tethering/res/values-ne/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"टेथर गर्ने वा हटस्पट सक्रिय"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"सेटअप गर्न ट्याप गर्नुहोस्।"</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"टेदरिङलाई असक्षम पारिएको छ"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"विवरणहरूका लागि आफ्ना प्रशासकलाई सम्पर्क गर्नुहोस्"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"टेदरिङ वा हटस्पट सक्रिय छ"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"सेटअप गर्न ट्याप गर्नुहोस्।"</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"टेदरिङ सुविधा असक्षम पारिएको छ"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"विवरणहरूका लागि आफ्ना प्रशासकलाई सम्पर्क गर्नुहोस्"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"हटस्पट तथा टेदरिङको स्थिति"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-nl/strings.xml b/packages/Tethering/res/values-nl/strings.xml index 0ec4bff62154..18b2bbfc7670 100644 --- a/packages/Tethering/res/values-nl/strings.xml +++ b/packages/Tethering/res/values-nl/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering of hotspot actief"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Tik om in te stellen."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering is uitgeschakeld"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Neem contact op met je beheerder voor meer informatie"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering of hotspot actief"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Tik om in te stellen."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering is uitgeschakeld"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Neem contact op met je beheerder voor meer informatie"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status van hotspot en tethering"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-or/strings.xml b/packages/Tethering/res/values-or/strings.xml index 457685795a16..a15a6db42af6 100644 --- a/packages/Tethering/res/values-or/strings.xml +++ b/packages/Tethering/res/values-or/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"ଟିଥରିଙ୍ଗ କିମ୍ୱା ହଟସ୍ପଟ୍ ସକ୍ରିୟ ଅଛି"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"ସେଟଅପ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ।"</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"ଟିଥରିଙ୍ଗ ଅକ୍ଷମ କରାଯାଇଛି"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"ବିବରଣୀ ପାଇଁ ନିଜ ଆଡମିନ୍ଙ୍କ ସହ ଯୋଗାଯୋଗ କରନ୍ତୁ"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"ଟିଥେରିଂ କିମ୍ୱା ହଟସ୍ପଟ୍ ସକ୍ରିୟ ଅଛି"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"ସେଟ୍ ଅପ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ।"</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"ଟିଥେରିଂ ଅକ୍ଷମ କରାଯାଇଛି"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"ବିବରଣୀଗୁଡ଼ିକ ପାଇଁ ଆପଣଙ୍କ ଆଡମିନଙ୍କ ସହ ଯୋଗାଯୋଗ କରନ୍ତୁ"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"ହଟସ୍ପଟ୍ ଓ ଟିଥେରିଂ ସ୍ଥିତି"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-pa/strings.xml b/packages/Tethering/res/values-pa/strings.xml index deddf2ea27f7..a8235e423e47 100644 --- a/packages/Tethering/res/values-pa/strings.xml +++ b/packages/Tethering/res/values-pa/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"ਟੈਦਰਿੰਗ ਜਾਂ ਹੌਟਸਪੌਟ ਕਿਰਿਆਸ਼ੀਲ"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"ਸਥਾਪਤ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"ਟੈਦਰਿੰਗ ਨੂੰ ਅਯੋਗ ਬਣਾਇਆ ਗਿਆ ਹੈ"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"ਵੇਰਵਿਆਂ ਲਈ ਆਪਣੇ ਪ੍ਰਸ਼ਾਸਕ ਨੂੰ ਸੰਪਰਕ ਕਰੋ"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"ਟੈਦਰਿੰਗ ਜਾਂ ਹੌਟਸਪੌਟ ਕਿਰਿਆਸ਼ੀਲ"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"ਸੈੱਟਅੱਪ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"ਟੈਦਰਿੰਗ ਨੂੰ ਬੰਦ ਕੀਤਾ ਗਿਆ ਹੈ"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"ਵੇਰਵਿਆਂ ਲਈ ਆਪਣੇ ਪ੍ਰਸ਼ਾਸਕ ਨਾਲ ਸੰਪਰਕ ਕਰੋ"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"ਹੌਟਸਪੌਟ ਅਤੇ ਟੈਦਰਿੰਗ ਦੀ ਸਥਿਤੀ"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-pl/strings.xml b/packages/Tethering/res/values-pl/strings.xml index 48d8468935a1..ccb017d43fa8 100644 --- a/packages/Tethering/res/values-pl/strings.xml +++ b/packages/Tethering/res/values-pl/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Aktywny tethering lub punkt dostępu"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Kliknij, by skonfigurować."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering został wyłączony"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Aby uzyskać szczegółowe informacje, skontaktuj się z administratorem"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Aktywny tethering lub punkt dostępu"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Kliknij, by skonfigurować"</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering został wyłączony"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Aby uzyskać szczegółowe informacje, skontaktuj się z administratorem"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot i tethering – stan"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-pt-rBR/strings.xml b/packages/Tethering/res/values-pt-rBR/strings.xml index 32c22b8713b5..a0a4745f9394 100644 --- a/packages/Tethering/res/values-pt-rBR/strings.xml +++ b/packages/Tethering/res/values-pt-rBR/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Ponto de acesso ou tethering ativo"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Toque para configurar."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering desativado"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Fale com seu administrador para saber detalhes"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Ponto de acesso ou tethering ativo"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Toque para configurar."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering desativado"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Fale com seu administrador para saber detalhes"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status de ponto de acesso e tethering"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-pt-rPT/strings.xml b/packages/Tethering/res/values-pt-rPT/strings.xml index 641e22f44f25..e3f03fcc6934 100644 --- a/packages/Tethering/res/values-pt-rPT/strings.xml +++ b/packages/Tethering/res/values-pt-rPT/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Ligação ponto a ponto ou hotspot activos"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Toque para configurar."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"A ligação (à Internet) via telemóvel está desativada."</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Contacte o gestor para obter detalhes."</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Ligação (à Internet) via telemóvel ou zona Wi-Fi ativas"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Toque para configurar."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"A ligação (à Internet) via telemóvel está desativada."</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Contacte o administrador para obter detalhes."</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Estado da zona Wi-Fi e da ligação (à Internet) via telemóvel"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-pt/strings.xml b/packages/Tethering/res/values-pt/strings.xml index 32c22b8713b5..a0a4745f9394 100644 --- a/packages/Tethering/res/values-pt/strings.xml +++ b/packages/Tethering/res/values-pt/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Ponto de acesso ou tethering ativo"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Toque para configurar."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering desativado"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Fale com seu administrador para saber detalhes"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Ponto de acesso ou tethering ativo"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Toque para configurar."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering desativado"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Fale com seu administrador para saber detalhes"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status de ponto de acesso e tethering"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-ro/strings.xml b/packages/Tethering/res/values-ro/strings.xml index f861f733b4a1..5706a4a69c79 100644 --- a/packages/Tethering/res/values-ro/strings.xml +++ b/packages/Tethering/res/values-ro/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering sau hotspot activ"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Atingeți ca să configurați."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tetheringul este dezactivat"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Contactați administratorul pentru detalii"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering sau hotspot activ"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Atingeți ca să configurați."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tetheringul este dezactivat"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Contactați administratorul pentru detalii"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Starea hotspotului și a tetheringului"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-ru/strings.xml b/packages/Tethering/res/values-ru/strings.xml index 027cb410c54c..7cb6f7db3fc8 100644 --- a/packages/Tethering/res/values-ru/strings.xml +++ b/packages/Tethering/res/values-ru/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Включен режим модема"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Нажмите, чтобы настроить."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Включить режим модема нельзя"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Обратитесь к администратору, чтобы узнать подробности."</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Включен режим модема или точка доступа"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Нажмите, чтобы настроить."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Использование телефона в качестве модема запрещено"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Чтобы узнать подробности, обратитесь к администратору."</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Статус хот-спота и режима модема"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-si/strings.xml b/packages/Tethering/res/values-si/strings.xml index 7d8599f2c2d9..ec34c22de750 100644 --- a/packages/Tethering/res/values-si/strings.xml +++ b/packages/Tethering/res/values-si/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"ටෙදරින් හෝ හොට්ස්පොට් සක්රීයයි"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"පිහිටුවීමට තට්ටු කරන්න."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"ටෙදරින් අබල කර ඇත"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"විස්තර සඳහා ඔබගේ පරිපාලක අමතන්න"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"ටෙදරින් හෝ හොට්ස්පොට් සක්රීයයි"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"පිහිටුවීමට තට්ටු කරන්න."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"ටෙදරින් අබල කර ඇත"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"විස්තර සඳහා ඔබගේ පරිපාලක අමතන්න"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"හොට්ස්පොට් & ටෙදරින් තත්ත්වය"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-sk/strings.xml b/packages/Tethering/res/values-sk/strings.xml index a8fe297c0088..43e787c84f87 100644 --- a/packages/Tethering/res/values-sk/strings.xml +++ b/packages/Tethering/res/values-sk/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering alebo prístupový bod je aktívny"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Klepnutím prejdete na nastavenie."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering je deaktivovaný"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"O podrobnosti požiadajte svojho správcu"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering alebo prístupový bod je aktívny"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Klepnutím prejdete na nastavenie."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering je deaktivovaný"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"O podrobnosti požiadajte svojho správcu"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Stav hotspotu a tetheringu"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-sl/strings.xml b/packages/Tethering/res/values-sl/strings.xml index b5e5e3856f7b..59433626a115 100644 --- a/packages/Tethering/res/values-sl/strings.xml +++ b/packages/Tethering/res/values-sl/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Aktivna povezava z internetom ali dostopna točka sta aktivni"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Dotaknite se, če želite nastaviti."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Povezava z internetom prek mobilnega telefona je onemogočena"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Za podrobnosti se obrnite na skrbnika"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Povezava z internetom prek mobilnega telefona ali dostopna točka je aktivna"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Dotaknite se, če želite nastaviti."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Povezava z internetom prek mobilnega telefona je onemogočena"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Za podrobnosti se obrnite na skrbnika"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Stanje dostopne točke in povezave z internetom prek mobilnega telefona"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-sq/strings.xml b/packages/Tethering/res/values-sq/strings.xml index fdd4906cc51e..21e11558bb0b 100644 --- a/packages/Tethering/res/values-sq/strings.xml +++ b/packages/Tethering/res/values-sq/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Lidhja e çiftimit ose ajo e qasjes në zona publike interneti është aktive"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Trokit për ta konfiguruar."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Lidhja e çiftimit është çaktivizuar"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Kontakto me administratorin për detaje"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Ndarja e internetit ose zona e qasjes së internetit është aktive"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Trokit për ta konfiguruar."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Ndarja e internetit është çaktivizuar"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Kontakto me administratorin për detaje"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Statusi i zonës së qasjes dhe ndarjes së internetit"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-sr/strings.xml b/packages/Tethering/res/values-sr/strings.xml index 9fab34589724..e2e4dc6361d4 100644 --- a/packages/Tethering/res/values-sr/strings.xml +++ b/packages/Tethering/res/values-sr/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Активно повезивање са интернетом преко мобилног уређаја или хотспот"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Додирните да бисте подесили."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Привезивање је онемогућено"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Потражите детаље од администратора"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Привезивање или хотспот је активан"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Додирните да бисте подесили."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Привезивање је онемогућено"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Потражите детаље од администратора"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Статус хотспота и привезивања"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-sv/strings.xml b/packages/Tethering/res/values-sv/strings.xml index 10eeb0fe12e1..72702c28587d 100644 --- a/packages/Tethering/res/values-sv/strings.xml +++ b/packages/Tethering/res/values-sv/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Internetdelning eller surfzon aktiverad"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Tryck om du vill konfigurera."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Internetdelning har inaktiverats"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Kontakta administratören om du vill veta mer"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Internetdelning eller surfzon har aktiverats"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Tryck om du vill konfigurera."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Internetdelning har inaktiverats"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Kontakta administratören om du vill veta mer"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Trådlös surfzon och internetdelning har inaktiverats"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-sw/strings.xml b/packages/Tethering/res/values-sw/strings.xml index 335396307730..65e4aa8cebb0 100644 --- a/packages/Tethering/res/values-sw/strings.xml +++ b/packages/Tethering/res/values-sw/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Kushiriki au kusambaza intaneti kumewashwa"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Gusa ili uweke mipangilio."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Umezima kipengele cha kusambaza mtandao"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Wasiliana na msimamizi wako ili upate maelezo zaidi"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Kusambaza mtandao au mtandaopepe umewashwa"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Gusa ili uweke mipangilio."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Umezima kipengele cha kusambaza mtandao"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Wasiliana na msimamizi wako ili upate maelezo zaidi"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Mtandaopepe na hali ya kusambaza mtandao"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-ta/strings.xml b/packages/Tethering/res/values-ta/strings.xml index b1e5cc241376..4aba62d4ab46 100644 --- a/packages/Tethering/res/values-ta/strings.xml +++ b/packages/Tethering/res/values-ta/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"டெதெரிங்/ஹாட்ஸ்பாட் இயங்குகிறது"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"அமைக்க, தட்டவும்."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"இணைப்பு முறை முடக்கப்பட்டுள்ளது"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"விவரங்களுக்கு, உங்கள் நிர்வாகியைத் தொடர்புகொள்ளவும்"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"டெதெரிங் அல்லது ஹாட்ஸ்பாட் இயங்குகிறது"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"அமைக்க, தட்டவும்."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"டெதெரிங் முடக்கப்பட்டுள்ளது"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"விவரங்களுக்கு உங்கள் நிர்வாகியைத் தொடர்புகொள்ளவும்"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"ஹாட்ஸ்பாட் & டெதெரிங் நிலை"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-te/strings.xml b/packages/Tethering/res/values-te/strings.xml index aae40dee40db..1f917913416f 100644 --- a/packages/Tethering/res/values-te/strings.xml +++ b/packages/Tethering/res/values-te/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"టీథర్ చేయబడినది లేదా హాట్స్పాట్ సక్రియంగా ఉండేది"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"సెటప్ చేయడానికి నొక్కండి."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"టెథెరింగ్ నిలిపివేయబడింది"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"వివరాల కోసం మీ నిర్వాహకులను సంప్రదించండి"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"టెథరింగ్ లేదా హాట్స్పాట్ యాక్టివ్గా ఉంది"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"సెటప్ చేయడానికి ట్యాప్ చేయండి."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"టెథరింగ్ డిజేబుల్ చేయబడింది"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"వివరాల కోసం మీ అడ్మిన్ని సంప్రదించండి"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"హాట్స్పాట్ & టెథరింగ్ స్థితి"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-th/strings.xml b/packages/Tethering/res/values-th/strings.xml index 1b800565ad1f..44171c0db82f 100644 --- a/packages/Tethering/res/values-th/strings.xml +++ b/packages/Tethering/res/values-th/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"การปล่อยสัญญาณหรือฮอตสปอตทำงานอยู่"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"แตะเพื่อตั้งค่า"</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"ปิดใช้การเชื่อมต่ออินเทอร์เน็ตผ่านมือถือแล้ว"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"ติดต่อผู้ดูแลระบบเพื่อขอรายละเอียด"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"การเชื่อมต่ออินเทอร์เน็ตผ่านมือถือหรือฮอตสปอตทำงานอยู่"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"แตะเพื่อตั้งค่า"</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"ปิดใช้การเชื่อมต่ออินเทอร์เน็ตผ่านมือถือแล้ว"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"ติดต่อผู้ดูแลระบบเพื่อขอรายละเอียด"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"สถานะฮอตสปอตและการเชื่อมต่ออินเทอร์เน็ตผ่านมือถือ"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-tl/strings.xml b/packages/Tethering/res/values-tl/strings.xml index 12863f90e15a..7347dd3e6254 100644 --- a/packages/Tethering/res/values-tl/strings.xml +++ b/packages/Tethering/res/values-tl/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Pagsasama o aktibong hotspot"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"I-tap upang i-set up."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Naka-disable ang pag-tether"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Makipag-ugnayan sa iyong admin para sa mga detalye"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Aktibo ang pag-tether o hotspot"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"I-tap para i-set up."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Naka-disable ang pag-tether"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Makipag-ugnayan sa iyong admin para sa mga detalye"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Status ng hotspot at pag-tether"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-tr/strings.xml b/packages/Tethering/res/values-tr/strings.xml index bfcf1ace2cf7..32030f176574 100644 --- a/packages/Tethering/res/values-tr/strings.xml +++ b/packages/Tethering/res/values-tr/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering veya hotspot etkin"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Ayarlamak için dokunun."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering devre dışı bırakıldı"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Ayrıntılı bilgi için yöneticinize başvurun"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Tethering veya hotspot etkin"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Ayarlamak için dokunun."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Tethering devre dışı bırakıldı"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Ayrıntılı bilgi için yöneticinize başvurun"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot ve tethering durumu"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-uk/strings.xml b/packages/Tethering/res/values-uk/strings.xml index 8e159c072350..1ca89b3f7813 100644 --- a/packages/Tethering/res/values-uk/strings.xml +++ b/packages/Tethering/res/values-uk/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Прив\'язка чи точка дост. активна"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Торкніться, щоб налаштувати."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Використання телефона в режимі модема вимкнено"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Щоб дізнатися більше, зв’яжіться з адміністратором"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Модем чи точка доступу активні"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Натисніть, щоб налаштувати."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Використання телефона як модема вимкнено"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Щоб дізнатися більше, зв\'яжіться з адміністратором"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Статус точки доступу та модема"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-ur/strings.xml b/packages/Tethering/res/values-ur/strings.xml index 89195d4aae29..d72c7d419577 100644 --- a/packages/Tethering/res/values-ur/strings.xml +++ b/packages/Tethering/res/values-ur/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"ٹیدرنگ یا ہاٹ اسپاٹ فعال"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"سیٹ اپ کرنے کیلئے تھپتھپائیں۔"</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"ٹیدرنگ غیر فعال ہے"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"تفصیلات کے لئے اپنے منتظم سے رابطہ کریں"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"ٹیدرنگ یا ہاٹ اسپاٹ فعال"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"سیٹ اپ کرنے کیلئے تھپتھپائیں۔"</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"ٹیدرنگ غیر فعال ہے"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"تفصیلات کے لئے اپنے منتظم سے رابطہ کریں"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"ہاٹ اسپاٹ اور ٹیتھرنگ کا اسٹیٹس"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-uz/strings.xml b/packages/Tethering/res/values-uz/strings.xml index 0ac4d4a7410a..af3b2ebb3500 100644 --- a/packages/Tethering/res/values-uz/strings.xml +++ b/packages/Tethering/res/values-uz/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Modem rejimi yoniq"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Sozlash uchun bosing."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Modem rejimi faolsizlantirildi"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Tafsilotlari uchun administratoringizga murojaat qiling"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Modem rejimi yoki hotspot yoniq"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Sozlash uchun bosing."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Modem rejimi faolsizlantirildi"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Tafsilotlari uchun administratoringizga murojaat qiling"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Hotspot va modem rejimi holati"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-vi/strings.xml b/packages/Tethering/res/values-vi/strings.xml index 85a4db8aa5da..21a0735922c3 100644 --- a/packages/Tethering/res/values-vi/strings.xml +++ b/packages/Tethering/res/values-vi/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Chức năng điểm truy cập Internet hoặc điểm phát sóng đang hoạt động"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Nhấn để thiết lập."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Đã tắt tính năng chia sẻ kết nối"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Hãy liên hệ với quản trị viên của bạn để biết chi tiết"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Tính năng chia sẻ Internet hoặc điểm phát sóng đang hoạt động"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Hãy nhấn để thiết lập."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Đã tắt tính năng chia sẻ Internet"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Hãy liên hệ với quản trị viên của bạn để biết chi tiết"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"Trạng thái điểm phát sóng và chia sẻ Internet"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-zh-rCN/strings.xml b/packages/Tethering/res/values-zh-rCN/strings.xml index ff1fe039531c..98e3b4b46fdb 100644 --- a/packages/Tethering/res/values-zh-rCN/strings.xml +++ b/packages/Tethering/res/values-zh-rCN/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"网络共享或热点已启用"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"点按即可进行设置。"</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"网络共享已停用"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"请与您的管理员联系以了解详情"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"网络共享或热点已启用"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"点按即可设置。"</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"网络共享已停用"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"如需了解详情,请与您的管理员联系"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"热点和网络共享状态"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-zh-rHK/strings.xml b/packages/Tethering/res/values-zh-rHK/strings.xml index 0de39fac97f8..9cafd42dd43f 100644 --- a/packages/Tethering/res/values-zh-rHK/strings.xml +++ b/packages/Tethering/res/values-zh-rHK/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"已啟用網絡共享或熱點"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"輕按即可設定。"</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"網絡共享已停用"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"請聯絡您的管理員以瞭解詳情"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"網絡共享或熱點已啟用"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"輕按即可設定。"</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"網絡共享已停用"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"請聯絡您的管理員以瞭解詳情"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"熱點和網絡共享狀態"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-zh-rTW/strings.xml b/packages/Tethering/res/values-zh-rTW/strings.xml index 9a117bbca43f..9d738a76eb0e 100644 --- a/packages/Tethering/res/values-zh-rTW/strings.xml +++ b/packages/Tethering/res/values-zh-rTW/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"網路共用或無線基地台已啟用"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"輕觸即可進行設定。"</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"數據連線已停用"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"詳情請洽你的管理員"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"數據連線或無線基地台已啟用"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"輕觸即可進行設定。"</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"數據連線已停用"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"詳情請洽你的管理員"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"無線基地台與數據連線狀態"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values-zu/strings.xml b/packages/Tethering/res/values-zu/strings.xml index 8fe10d86cb03..f210f8726ee5 100644 --- a/packages/Tethering/res/values-zu/strings.xml +++ b/packages/Tethering/res/values-zu/strings.xml @@ -1,8 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="tethered_notification_title" msgid="3146694234398202601">"Ukusebenzisa njengemodemu noma i-hotspot ephathekayo kuvuliwe"</string> - <string name="tethered_notification_message" msgid="2113628520792055377">"Thepha ukuze usethe."</string> - <string name="disable_tether_notification_title" msgid="7526977944111313195">"Ukusebenzisa ifoni njengemodemu kukhutshaziwe"</string> - <string name="disable_tether_notification_message" msgid="2913366428516852495">"Xhumana nomphathi wakho ukuze uthole imininingwane"</string> + <string name="tethered_notification_title" msgid="6426563586025792944">"Ukusebenzisa njengemodemu noma i-hotspot ephathekayo kuvuliwe"</string> + <string name="tethered_notification_message" msgid="64800879503420696">"Thepha ukuze usethe."</string> + <string name="disable_tether_notification_title" msgid="3004509127903564191">"Ukusebenzisa ifoni njengemodemu kukhutshaziwe"</string> + <string name="disable_tether_notification_message" msgid="6717523799293901476">"Xhumana nomphathi wakho ukuze uthole imininingwane"</string> + <string name="notification_channel_tethering_status" msgid="2663463891530932727">"I-Hotspot nesimo sokusebenzisa ifoni njengemodemu"</string> + <string name="no_upstream_notification_title" msgid="1204601824631788482"></string> + <string name="no_upstream_notification_message" msgid="8586582938243032621"></string> + <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string> + <string name="upstream_roaming_notification_title" msgid="4772373823198997030"></string> + <string name="upstream_roaming_notification_message" msgid="3985577843181551650"></string> </resources> diff --git a/packages/Tethering/res/values/config.xml b/packages/Tethering/res/values/config.xml index 66fbefca9456..83c99d22fda7 100644 --- a/packages/Tethering/res/values/config.xml +++ b/packages/Tethering/res/values/config.xml @@ -158,51 +158,6 @@ <!-- ComponentName of the service used to run no ui tether provisioning. --> <string translatable="false" name="config_wifi_tether_enable">com.android.settings/.wifi.tether.TetherService</string> - <!-- Enable tethering notification --> - <!-- Icons for showing tether enable notification. - Each item should have two elements and be separated with ";". - - The first element is downstream types which is one of tethering. This element has to be - made by WIFI, USB, BT, and OR'd with the others. Use "|" to combine multiple downstream - types and use "," to separate each combinations. Such as - - USB|BT,WIFI|USB|BT - - The second element is icon for the item. This element has to be composed by - <package name>:drawable/<resource name>. Such as - - 1. com.android.networkstack.tethering:drawable/stat_sys_tether_general - 2. android:drawable/xxx - - So the entire string of each item would be - - USB|BT,WIFI|USB|BT;com.android.networkstack.tethering:drawable/stat_sys_tether_general - - NOTE: One config can be separated into two or more for readability. Such as - - WIFI|USB,WIFI|BT,USB|BT,WIFI|USB|BT;android:drawable/xxx - - can be separated into - - WIFI|USB;android:drawable/xxx - WIFI|BT;android:drawable/xxx - USB|BT;android:drawable/xxx - WIFI|USB|BT;android:drawable/xxx - - Notification will not show if the downstream type isn't listed in array. - Empty array means disable notifications. --> - <!-- In AOSP, hotspot is configured to no notification by default. Because status bar has showed - an icon on the right side already --> - <string-array translatable="false" name="tethering_notification_icons"> - <item>USB;com.android.networkstack.tethering:drawable/stat_sys_tether_usb</item> - <item>BT;com.android.networkstack.tethering:drawable/stat_sys_tether_bluetooth</item> - <item>WIFI|USB,WIFI|BT,USB|BT,WIFI|USB|BT;com.android.networkstack.tethering:drawable/stat_sys_tether_general</item> - </string-array> - <!-- String for tether enable notification title. --> - <string name="tethering_notification_title">@string/tethered_notification_title</string> - <!-- String for tether enable notification message. --> - <string name="tethering_notification_message">@string/tethered_notification_message</string> - <!-- No upstream notification is shown when there is a downstream but no upstream that is able to do the tethering. --> <!-- Delay(millisecond) to show no upstream notification after there's no Backhaul. Set delay to diff --git a/packages/Tethering/res/values/overlayable.xml b/packages/Tethering/res/values/overlayable.xml index bbba3f30a292..16ae8ade19da 100644 --- a/packages/Tethering/res/values/overlayable.xml +++ b/packages/Tethering/res/values/overlayable.xml @@ -32,44 +32,6 @@ <item type="string" name="config_mobile_hotspot_provision_response"/> <item type="integer" name="config_mobile_hotspot_provision_check_period"/> <item type="string" name="config_wifi_tether_enable"/> - <!-- Configuration values for TetheringNotificationUpdater --> - <!-- Icons for showing tether enable notification. - Each item should have two elements and be separated with ";". - - The first element is downstream types which is one of tethering. This element has to be - made by WIFI, USB, BT, and OR'd with the others. Use "|" to combine multiple downstream - types and use "," to separate each combinations. Such as - - USB|BT,WIFI|USB|BT - - The second element is icon for the item. This element has to be composed by - <package name>:drawable/<resource name>. Such as - - 1. com.android.networkstack.tethering:drawable/stat_sys_tether_general - 2. android:drawable/xxx - - So the entire string of each item would be - - USB|BT,WIFI|USB|BT;com.android.networkstack.tethering:drawable/stat_sys_tether_general - - NOTE: One config can be separated into two or more for readability. Such as - - WIFI|USB,WIFI|BT,USB|BT,WIFI|USB|BT;android:drawable/xxx - - can be separated into - - WIFI|USB;android:drawable/xxx - WIFI|BT;android:drawable/xxx - USB|BT;android:drawable/xxx - WIFI|USB|BT;android:drawable/xxx - - Notification will not show if the downstream type isn't listed in array. - Empty array means disable notifications. --> - <item type="array" name="tethering_notification_icons"/> - <!-- String for tether enable notification title. --> - <item type="string" name="tethering_notification_title"/> - <!-- String for tether enable notification message. --> - <item type="string" name="tethering_notification_message"/> <!-- Params from config.xml that can be overlaid --> </policy> </overlayable> diff --git a/packages/Tethering/res/values/strings.xml b/packages/Tethering/res/values/strings.xml index 4fa60d412566..d63c7c5063cc 100644 --- a/packages/Tethering/res/values/strings.xml +++ b/packages/Tethering/res/values/strings.xml @@ -19,9 +19,6 @@ <string name="tethered_notification_title">Tethering or hotspot active</string> <!-- String for tethered notification message [CHAR LIMIT=200] --> <string name="tethered_notification_message">Tap to set up.</string> - <!-- String for tethered notification title with client number info. --> - <plurals name="tethered_notification_title_with_client_number"> - </plurals> <!-- This notification is shown when tethering has been disabled on a user's device. The device is managed by the user's employer. Tethering can't be turned on unless the @@ -47,6 +44,4 @@ <string name="upstream_roaming_notification_title"></string> <!-- String for cellular roaming notification message [CHAR LIMIT=500] --> <string name="upstream_roaming_notification_message"></string> - <!-- String for cellular roaming notification continue button [CHAR LIMIT=200] --> - <string name="upstream_roaming_notification_continue_button"></string> </resources> diff --git a/packages/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java b/packages/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java index 85a23fb83fb2..55344fc75d65 100644 --- a/packages/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java +++ b/packages/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java @@ -142,7 +142,7 @@ public class OffloadHardwareInterface { public boolean initOffloadConfig() { IOffloadConfig offloadConfig; try { - offloadConfig = IOffloadConfig.getService(); + offloadConfig = IOffloadConfig.getService(true /*retry*/); } catch (RemoteException e) { mLog.e("getIOffloadConfig error " + e); return false; diff --git a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java index 05cf68efd755..4e16c49caa0e 100644 --- a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java +++ b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java @@ -1007,6 +1007,11 @@ public class Tethering { } @VisibleForTesting + boolean isTetheringActive() { + return mActiveTetheringRequests.size() > 0; + } + + @VisibleForTesting protected static class UserRestrictionActionListener { private final UserManager mUserManager; private final Tethering mWrapper; @@ -1043,13 +1048,14 @@ public class Tethering { return; } - // Restricted notification is shown when tethering function is disallowed on - // user's device. - mNotificationUpdater.notifyTetheringDisabledByRestriction(); - - // Untether from all downstreams since tethering is disallowed. - mWrapper.untetherAll(); + if (mWrapper.isTetheringActive()) { + // Restricted notification is shown when tethering function is disallowed on + // user's device. + mNotificationUpdater.notifyTetheringDisabledByRestriction(); + // Untether from all downstreams since tethering is disallowed. + mWrapper.untetherAll(); + } // TODO(b/148139325): send tetheringSupported on restriction change } } diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java index f490cc4719fa..d03deda37fdf 100644 --- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java +++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java @@ -17,9 +17,6 @@ package com.android.networkstack.tethering; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; -import static android.net.TetheringManager.TETHERING_BLUETOOTH; -import static android.net.TetheringManager.TETHERING_USB; -import static android.net.TetheringManager.TETHERING_WIFI; import static android.text.TextUtils.isEmpty; import android.app.Notification; @@ -39,10 +36,8 @@ import android.os.UserHandle; import android.provider.Settings; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; -import android.util.Log; import android.util.SparseArray; -import androidx.annotation.ArrayRes; import androidx.annotation.DrawableRes; import androidx.annotation.IntDef; import androidx.annotation.IntRange; @@ -77,9 +72,6 @@ public class TetheringNotificationUpdater { private static final boolean NO_NOTIFY = false; @VisibleForTesting static final int EVENT_SHOW_NO_UPSTREAM = 1; - // Id to update and cancel enable notification. Must be unique within the tethering app. - @VisibleForTesting - static final int ENABLE_NOTIFICATION_ID = 1000; // Id to update and cancel restricted notification. Must be unique within the tethering app. @VisibleForTesting static final int RESTRICTED_NOTIFICATION_ID = 1001; @@ -120,7 +112,6 @@ public class TetheringNotificationUpdater { @Retention(RetentionPolicy.SOURCE) @IntDef(value = { - ENABLE_NOTIFICATION_ID, RESTRICTED_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID @@ -223,7 +214,6 @@ public class TetheringNotificationUpdater { final boolean tetheringActiveChanged = (downstreamTypes == DOWNSTREAM_NONE) != (mDownstreamTypesMask == DOWNSTREAM_NONE); final boolean subIdChanged = subId != mActiveDataSubId; - final boolean downstreamChanged = downstreamTypes != mDownstreamTypesMask; final boolean upstreamChanged = noUpstream != mNoUpstream; final boolean roamingChanged = isRoaming != mRoaming; final boolean updateAll = tetheringActiveChanged || subIdChanged; @@ -232,19 +222,10 @@ public class TetheringNotificationUpdater { mNoUpstream = noUpstream; mRoaming = isRoaming; - if (updateAll || downstreamChanged) updateEnableNotification(); if (updateAll || upstreamChanged) updateNoUpstreamNotification(); if (updateAll || roamingChanged) updateRoamingNotification(); } - private void updateEnableNotification() { - final boolean tetheringInactive = mDownstreamTypesMask == DOWNSTREAM_NONE; - - if (tetheringInactive || setupNotification() == NO_NOTIFY) { - clearNotification(ENABLE_NOTIFICATION_ID); - } - } - private void updateNoUpstreamNotification() { final boolean tetheringInactive = mDownstreamTypesMask == DOWNSTREAM_NONE; @@ -286,7 +267,7 @@ public class TetheringNotificationUpdater { null /* options */); showNotification(R.drawable.stat_sys_tether_general, title, message, - RESTRICTED_NOTIFICATION_ID, pi, new Action[0]); + RESTRICTED_NOTIFICATION_ID, false /* ongoing */, pi, new Action[0]); } private void notifyTetheringNoUpstream() { @@ -307,65 +288,7 @@ public class TetheringNotificationUpdater { final Action action = new Action.Builder(NO_ICON_ID, disableButton, pi).build(); showNotification(R.drawable.stat_sys_tether_general, title, message, - NO_UPSTREAM_NOTIFICATION_ID, null /* pendingIntent */, action); - } - - /** - * Returns the downstream types mask which convert from given string. - * - * @param types This string has to be made by "WIFI", "USB", "BT", and OR'd with the others. - * - * @return downstream types mask value. - */ - @VisibleForTesting - @IntRange(from = 0, to = 7) - int getDownstreamTypesMask(@NonNull final String types) { - int downstreamTypesMask = DOWNSTREAM_NONE; - final String[] downstreams = types.split("\\|"); - for (String downstream : downstreams) { - if (USB_DOWNSTREAM.equals(downstream.trim())) { - downstreamTypesMask |= (1 << TETHERING_USB); - } else if (WIFI_DOWNSTREAM.equals(downstream.trim())) { - downstreamTypesMask |= (1 << TETHERING_WIFI); - } else if (BLUETOOTH_DOWNSTREAM.equals(downstream.trim())) { - downstreamTypesMask |= (1 << TETHERING_BLUETOOTH); - } - } - return downstreamTypesMask; - } - - /** - * Returns the icons {@link android.util.SparseArray} which get from given string-array resource - * id. - * - * @param id String-array resource id - * - * @return {@link android.util.SparseArray} with downstream types and icon id info. - */ - @NonNull - @VisibleForTesting - SparseArray<Integer> getIcons(@ArrayRes int id, @NonNull Resources res) { - final String[] array = res.getStringArray(id); - final SparseArray<Integer> icons = new SparseArray<>(); - for (String config : array) { - if (isEmpty(config)) continue; - - final String[] elements = config.split(";"); - if (elements.length != 2) { - Log.wtf(TAG, - "Unexpected format in Tethering notification configuration : " + config); - continue; - } - - final String[] types = elements[0].split(","); - for (String type : types) { - int mask = getDownstreamTypesMask(type); - if (mask == DOWNSTREAM_NONE) continue; - icons.put(mask, res.getIdentifier( - elements[1].trim(), null /* defType */, null /* defPackage */)); - } - } - return icons; + NO_UPSTREAM_NOTIFICATION_ID, true /* ongoing */, null /* pendingIntent */, action); } private boolean setupRoamingNotification() { @@ -387,7 +310,7 @@ public class TetheringNotificationUpdater { null /* options */); showNotification(R.drawable.stat_sys_tether_general, title, message, - ROAMING_NOTIFICATION_ID, pi, new Action[0]); + ROAMING_NOTIFICATION_ID, true /* ongoing */, pi, new Action[0]); return NOTIFY_DONE; } @@ -403,38 +326,15 @@ public class TetheringNotificationUpdater { return NOTIFY_DONE; } - private boolean setupNotification() { - final Resources res = getResourcesForSubId(mContext, mActiveDataSubId); - final SparseArray<Integer> downstreamIcons = - getIcons(R.array.tethering_notification_icons, res); - - final int iconId = downstreamIcons.get(mDownstreamTypesMask, NO_ICON_ID); - if (iconId == NO_ICON_ID) return NO_NOTIFY; - - final String title = res.getString(R.string.tethering_notification_title); - final String message = res.getString(R.string.tethering_notification_message); - if (isEmpty(title) || isEmpty(message)) return NO_NOTIFY; - - final PendingIntent pi = PendingIntent.getActivity( - mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */), - 0 /* requestCode */, - new Intent(Settings.ACTION_TETHER_SETTINGS), - Intent.FLAG_ACTIVITY_NEW_TASK, - null /* options */); - - showNotification(iconId, title, message, ENABLE_NOTIFICATION_ID, pi, new Action[0]); - return NOTIFY_DONE; - } - private void showNotification(@DrawableRes final int iconId, @NonNull final String title, - @NonNull final String message, @NotificationId final int id, @Nullable PendingIntent pi, - @NonNull final Action... actions) { + @NonNull final String message, @NotificationId final int id, final boolean ongoing, + @Nullable PendingIntent pi, @NonNull final Action... actions) { final Notification notification = new Notification.Builder(mContext, mChannel.getId()) .setSmallIcon(iconId) .setContentTitle(title) .setContentText(message) - .setOngoing(true) + .setOngoing(ongoing) .setColor(mContext.getColor( android.R.color.system_notification_accent_color)) .setVisibility(Notification.VISIBILITY_PUBLIC) diff --git a/packages/Tethering/tests/integration/Android.bp b/packages/Tethering/tests/integration/Android.bp index 6b751afdf58b..3305ed084481 100644 --- a/packages/Tethering/tests/integration/Android.bp +++ b/packages/Tethering/tests/integration/Android.bp @@ -69,6 +69,7 @@ android_test { test_config: "AndroidTest_Coverage.xml", defaults: ["libnetworkstackutilsjni_deps"], static_libs: [ + "NetworkStaticLibTestsLib", "NetworkStackTestsLib", "TetheringTestsLib", "TetheringIntegrationTestsLib", diff --git a/packages/Tethering/tests/unit/Android.bp b/packages/Tethering/tests/unit/Android.bp index 26517ceb72ae..08cfb30813ca 100644 --- a/packages/Tethering/tests/unit/Android.bp +++ b/packages/Tethering/tests/unit/Android.bp @@ -14,6 +14,26 @@ // limitations under the License. // +// Tests in this folder are included both in unit tests and CTS. +java_library { + name: "TetheringCommonTests", + srcs: [ + "common/**/*.java", + "common/**/*.kt" + ], + static_libs: [ + "androidx.test.rules", + "net-tests-utils", + ], + // TODO(b/147200698) change sdk_version to module-current and remove framework-minus-apex + sdk_version: "core_platform", + libs: [ + "framework-minus-apex", + "framework-tethering", + ], + visibility: ["//cts/tests/tests/tethering"], +} + java_defaults { name: "TetheringTestsDefaults", srcs: [ @@ -22,6 +42,7 @@ java_defaults { ], static_libs: [ "TetheringApiCurrentLib", + "TetheringCommonTests", "androidx.test.rules", "frameworks-base-testutils", "mockito-target-extended-minus-junit4", diff --git a/packages/Tethering/tests/unit/src/android/net/TetheredClientTest.kt b/packages/Tethering/tests/unit/common/android/net/TetheredClientTest.kt index a20a0dfd9c89..55c59dd08f41 100644 --- a/packages/Tethering/tests/unit/src/android/net/TetheredClientTest.kt +++ b/packages/Tethering/tests/unit/common/android/net/TetheredClientTest.kt @@ -33,7 +33,9 @@ private val TEST_MACADDR = MacAddress.fromBytes(byteArrayOf(12, 23, 34, 45, 56, private val TEST_OTHER_MACADDR = MacAddress.fromBytes(byteArrayOf(23, 34, 45, 56, 67, 78)) private val TEST_ADDR1 = makeLinkAddress("192.168.113.3", prefixLength = 24, expTime = 123L) private val TEST_ADDR2 = makeLinkAddress("fe80::1:2:3", prefixLength = 64, expTime = 456L) -private val TEST_ADDRINFO1 = AddressInfo(TEST_ADDR1, "test_hostname") +private val TEST_HOSTNAME = "test_hostname" +private val TEST_OTHER_HOSTNAME = "test_other_hostname" +private val TEST_ADDRINFO1 = AddressInfo(TEST_ADDR1, TEST_HOSTNAME) private val TEST_ADDRINFO2 = AddressInfo(TEST_ADDR2, null) private fun makeLinkAddress(addr: String, prefixLength: Int, expTime: Long) = LinkAddress( @@ -49,6 +51,7 @@ private fun makeLinkAddress(addr: String, prefixLength: Int, expTime: Long) = Li class TetheredClientTest { @Test fun testParceling() { + assertParcelSane(TEST_ADDRINFO1, fieldCount = 2) assertParcelSane(makeTestClient(), fieldCount = 3) } @@ -65,7 +68,7 @@ class TetheredClientTest { // Different hostname assertNotEquals(makeTestClient(), TetheredClient( TEST_MACADDR, - listOf(AddressInfo(TEST_ADDR1, "test_other_hostname"), TEST_ADDRINFO2), + listOf(AddressInfo(TEST_ADDR1, TEST_OTHER_HOSTNAME), TEST_ADDRINFO2), TETHERING_BLUETOOTH)) // Null hostname @@ -97,6 +100,21 @@ class TetheredClientTest { TETHERING_USB), client1.addAddresses(client2)) } + @Test + fun testGetters() { + assertEquals(TEST_MACADDR, makeTestClient().macAddress) + assertEquals(listOf(TEST_ADDRINFO1, TEST_ADDRINFO2), makeTestClient().addresses) + assertEquals(TETHERING_BLUETOOTH, makeTestClient().tetheringType) + } + + @Test + fun testAddressInfo_Getters() { + assertEquals(TEST_ADDR1, TEST_ADDRINFO1.address) + assertEquals(TEST_ADDR2, TEST_ADDRINFO2.address) + assertEquals(TEST_HOSTNAME, TEST_ADDRINFO1.hostname) + assertEquals(null, TEST_ADDRINFO2.hostname) + } + private fun makeTestClient() = TetheredClient( TEST_MACADDR, listOf(TEST_ADDRINFO1, TEST_ADDRINFO2), diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt index 04f31a7a2880..745468fdf378 100644 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt +++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringNotificationUpdaterTest.kt @@ -20,8 +20,6 @@ import android.app.Notification import android.app.NotificationManager import android.content.Context import android.content.res.Resources -import android.net.ConnectivityManager.TETHERING_BLUETOOTH -import android.net.ConnectivityManager.TETHERING_USB import android.net.ConnectivityManager.TETHERING_WIFI import android.os.Handler import android.os.HandlerThread @@ -29,14 +27,12 @@ import android.os.Looper import android.net.NetworkCapabilities import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING import android.os.UserHandle -import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID import android.telephony.TelephonyManager import androidx.test.filters.SmallTest import androidx.test.platform.app.InstrumentationRegistry import androidx.test.runner.AndroidJUnit4 import com.android.internal.util.test.BroadcastInterceptingContext import com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE -import com.android.networkstack.tethering.TetheringNotificationUpdater.ENABLE_NOTIFICATION_ID import com.android.networkstack.tethering.TetheringNotificationUpdater.EVENT_SHOW_NO_UPSTREAM import com.android.networkstack.tethering.TetheringNotificationUpdater.NO_UPSTREAM_NOTIFICATION_ID import com.android.networkstack.tethering.TetheringNotificationUpdater.RESTRICTED_NOTIFICATION_ID @@ -63,17 +59,9 @@ import org.mockito.Mockito.verifyZeroInteractions import org.mockito.MockitoAnnotations const val TEST_SUBID = 1 -const val WIFI_ICON_ID = 1 -const val USB_ICON_ID = 2 -const val BT_ICON_ID = 3 -const val GENERAL_ICON_ID = 4 const val WIFI_MASK = 1 shl TETHERING_WIFI -const val USB_MASK = 1 shl TETHERING_USB -const val BT_MASK = 1 shl TETHERING_BLUETOOTH -const val TITLE = "Tethering active" -const val MESSAGE = "Tap here to set up." -const val TEST_TITLE = "Hotspot active" -const val TEST_MESSAGE = "Tap to set up hotspot." +const val TEST_DISALLOW_TITLE = "Tether function is disallowed" +const val TEST_DISALLOW_MESSAGE = "Please contact your admin" const val TEST_NO_UPSTREAM_TITLE = "Hotspot has no internet access" const val TEST_NO_UPSTREAM_MESSAGE = "Device cannot connect to internet." const val TEST_NO_UPSTREAM_BUTTON = "Turn off hotspot" @@ -88,7 +76,6 @@ class TetheringNotificationUpdaterTest { @Mock private lateinit var mockContext: Context @Mock private lateinit var notificationManager: NotificationManager @Mock private lateinit var telephonyManager: TelephonyManager - @Mock private lateinit var defaultResources: Resources @Mock private lateinit var testResources: Resources // lateinit for these classes under test, as they should be reset to a different instance for @@ -97,11 +84,6 @@ class TetheringNotificationUpdaterTest { private lateinit var notificationUpdater: TetheringNotificationUpdater private lateinit var fakeTetheringThread: HandlerThread - private val ENABLE_ICON_CONFIGS = arrayOf( - "USB;android.test:drawable/usb", "BT;android.test:drawable/bluetooth", - "WIFI|BT;android.test:drawable/general", "WIFI|USB;android.test:drawable/general", - "USB|BT;android.test:drawable/general", "WIFI|USB|BT;android.test:drawable/general") - private val ROAMING_CAPABILITIES = NetworkCapabilities() private val HOME_CAPABILITIES = NetworkCapabilities().addCapability(NET_CAPABILITY_NOT_ROAMING) private val NOTIFICATION_ICON_ID = R.drawable.stat_sys_tether_general @@ -117,29 +99,19 @@ class TetheringNotificationUpdaterTest { private inner class WrappedNotificationUpdater(c: Context, looper: Looper) : TetheringNotificationUpdater(c, looper) { - override fun getResourcesForSubId(context: Context, subId: Int) = - when (subId) { - TEST_SUBID -> testResources - INVALID_SUBSCRIPTION_ID -> defaultResources - else -> super.getResourcesForSubId(context, subId) - } + override fun getResourcesForSubId(c: Context, subId: Int) = + if (subId == TEST_SUBID) testResources else super.getResourcesForSubId(c, subId) } private fun setupResources() { - doReturn(ENABLE_ICON_CONFIGS).`when`(defaultResources) - .getStringArray(R.array.tethering_notification_icons) - doReturn(arrayOf("WIFI;android.test:drawable/wifi")).`when`(testResources) - .getStringArray(R.array.tethering_notification_icons) doReturn(5).`when`(testResources) .getInteger(R.integer.delay_to_show_no_upstream_after_no_backhaul) doReturn(true).`when`(testResources) .getBoolean(R.bool.config_upstream_roaming_notification) - doReturn(TITLE).`when`(defaultResources).getString(R.string.tethering_notification_title) - doReturn(MESSAGE).`when`(defaultResources) - .getString(R.string.tethering_notification_message) - doReturn(TEST_TITLE).`when`(testResources).getString(R.string.tethering_notification_title) - doReturn(TEST_MESSAGE).`when`(testResources) - .getString(R.string.tethering_notification_message) + doReturn(TEST_DISALLOW_TITLE).`when`(testResources) + .getString(R.string.disable_tether_notification_title) + doReturn(TEST_DISALLOW_MESSAGE).`when`(testResources) + .getString(R.string.disable_tether_notification_message) doReturn(TEST_NO_UPSTREAM_TITLE).`when`(testResources) .getString(R.string.no_upstream_notification_title) doReturn(TEST_NO_UPSTREAM_MESSAGE).`when`(testResources) @@ -150,14 +122,6 @@ class TetheringNotificationUpdaterTest { .getString(R.string.upstream_roaming_notification_title) doReturn(TEST_ROAMING_MESSAGE).`when`(testResources) .getString(R.string.upstream_roaming_notification_message) - doReturn(USB_ICON_ID).`when`(defaultResources) - .getIdentifier(eq("android.test:drawable/usb"), any(), any()) - doReturn(BT_ICON_ID).`when`(defaultResources) - .getIdentifier(eq("android.test:drawable/bluetooth"), any(), any()) - doReturn(GENERAL_ICON_ID).`when`(defaultResources) - .getIdentifier(eq("android.test:drawable/general"), any(), any()) - doReturn(WIFI_ICON_ID).`when`(testResources) - .getIdentifier(eq("android.test:drawable/wifi"), any(), any()) } @Before @@ -206,119 +170,27 @@ class TetheringNotificationUpdaterTest { } @Test - fun testNotificationWithDownstreamChanged() { - // Wifi downstream. No notification. - notificationUpdater.onDownstreamChanged(WIFI_MASK) - verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID)) - - // Same downstream changed. Nothing happened. - notificationUpdater.onDownstreamChanged(WIFI_MASK) - verifyZeroInteractions(notificationManager) - - // Wifi and usb downstreams. Show enable notification - notificationUpdater.onDownstreamChanged(WIFI_MASK or USB_MASK) - verifyNotification(GENERAL_ICON_ID, TITLE, MESSAGE, ENABLE_NOTIFICATION_ID) - - // Usb downstream. Still show enable notification. - notificationUpdater.onDownstreamChanged(USB_MASK) - verifyNotification(USB_ICON_ID, TITLE, MESSAGE, ENABLE_NOTIFICATION_ID) - - // No downstream. No notification. - notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE) - verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID, - ROAMING_NOTIFICATION_ID)) - } - - @Test - fun testNotificationWithActiveDataSubscriptionIdChanged() { - // Usb downstream. Showed enable notification with default resource. - notificationUpdater.onDownstreamChanged(USB_MASK) - verifyNotification(USB_ICON_ID, TITLE, MESSAGE, ENABLE_NOTIFICATION_ID) - - // Same subId changed. Nothing happened. - notificationUpdater.onActiveDataSubscriptionIdChanged(INVALID_SUBSCRIPTION_ID) - verifyZeroInteractions(notificationManager) - - // Set test sub id. Clear notification with test resource. + fun testRestrictedNotification() { + // Set test sub id. notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID) - verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID, - ROAMING_NOTIFICATION_ID)) - - // Wifi downstream. Show enable notification with test resource. - notificationUpdater.onDownstreamChanged(WIFI_MASK) - verifyNotification(WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE, ENABLE_NOTIFICATION_ID) - - // No downstream. No notification. - notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE) - verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID, - ROAMING_NOTIFICATION_ID)) - } - - private fun assertIconNumbers(number: Int, configs: Array<String?>) { - doReturn(configs).`when`(defaultResources) - .getStringArray(R.array.tethering_notification_icons) - assertEquals(number, notificationUpdater.getIcons( - R.array.tethering_notification_icons, defaultResources).size()) - } - - @Test - fun testGetIcons() { - assertIconNumbers(0, arrayOfNulls<String>(0)) - assertIconNumbers(0, arrayOf(null, "")) - assertIconNumbers(3, arrayOf( - // These configurations are invalid with wrong strings or symbols. - ";", ",", "|", "|,;", "WIFI", "1;2", " U SB; ", "bt;", "WIFI;USB;BT", "WIFI|USB|BT", - "WIFI,BT,USB", " WIFI| | | USB, test:drawable/test", - // This configuration is valid with two downstream types (USB, BT). - "USB|,,,,,|BT;drawable/test ", - // This configuration is valid with one downstream types (WIFI). - " WIFI ; android.test:drawable/xxx ")) - } - - @Test - fun testGetDownstreamTypesMask() { - assertEquals(DOWNSTREAM_NONE, notificationUpdater.getDownstreamTypesMask("")) - assertEquals(DOWNSTREAM_NONE, notificationUpdater.getDownstreamTypesMask("1")) - assertEquals(DOWNSTREAM_NONE, notificationUpdater.getDownstreamTypesMask("WIFI_P2P")) - assertEquals(DOWNSTREAM_NONE, notificationUpdater.getDownstreamTypesMask("usb")) - assertEquals(WIFI_MASK, notificationUpdater.getDownstreamTypesMask(" WIFI ")) - assertEquals(USB_MASK, notificationUpdater.getDownstreamTypesMask("USB | B T")) - assertEquals(BT_MASK, notificationUpdater.getDownstreamTypesMask(" WIFI: | BT")) - assertEquals(WIFI_MASK or USB_MASK, - notificationUpdater.getDownstreamTypesMask("1|2|USB|WIFI|BLUETOOTH||")) - } - - @Test - fun testSetupRestrictedNotification() { - val title = context.resources.getString(R.string.disable_tether_notification_title) - val message = context.resources.getString(R.string.disable_tether_notification_message) - val disallowTitle = "Tether function is disallowed" - val disallowMessage = "Please contact your admin" - doReturn(title).`when`(defaultResources) - .getString(R.string.disable_tether_notification_title) - doReturn(message).`when`(defaultResources) - .getString(R.string.disable_tether_notification_message) - doReturn(disallowTitle).`when`(testResources) - .getString(R.string.disable_tether_notification_title) - doReturn(disallowMessage).`when`(testResources) - .getString(R.string.disable_tether_notification_message) + verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID)) // User restrictions on. Show restricted notification. notificationUpdater.notifyTetheringDisabledByRestriction() - verifyNotification(NOTIFICATION_ICON_ID, title, message, RESTRICTED_NOTIFICATION_ID) + verifyNotification(NOTIFICATION_ICON_ID, TEST_DISALLOW_TITLE, TEST_DISALLOW_MESSAGE, + RESTRICTED_NOTIFICATION_ID) // User restrictions off. Clear notification. notificationUpdater.tetheringRestrictionLifted() verifyNotificationCancelled(listOf(RESTRICTED_NOTIFICATION_ID)) - // Set test sub id. No notification. - notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID) - verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID, - ROAMING_NOTIFICATION_ID)) + // No downstream. + notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE) + verifyZeroInteractions(notificationManager) - // User restrictions on again. Show restricted notification with test resource. + // User restrictions on again. Show restricted notification. notificationUpdater.notifyTetheringDisabledByRestriction() - verifyNotification(NOTIFICATION_ICON_ID, disallowTitle, disallowMessage, + verifyNotification(NOTIFICATION_ICON_ID, TEST_DISALLOW_TITLE, TEST_DISALLOW_MESSAGE, RESTRICTED_NOTIFICATION_ID) } @@ -356,15 +228,14 @@ class TetheringNotificationUpdaterTest { } @Test - fun testNotificationWithUpstreamCapabilitiesChanged_NoUpstream() { - // Set test sub id. No notification. + fun testNoUpstreamNotification() { + // Set test sub id. notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID) - verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID, - ROAMING_NOTIFICATION_ID)) + verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID)) - // Wifi downstream. Show enable notification with test resource. + // Wifi downstream. notificationUpdater.onDownstreamChanged(WIFI_MASK) - verifyNotification(WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE, ENABLE_NOTIFICATION_ID) + verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID)) // There is no upstream. Show no upstream notification. notificationUpdater.onUpstreamCapabilitiesChanged(null) @@ -386,15 +257,14 @@ class TetheringNotificationUpdaterTest { verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE, NO_UPSTREAM_NOTIFICATION_ID) - // No downstream. No notification. + // No downstream. notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE) - verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID, - ROAMING_NOTIFICATION_ID)) + verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID)) - // Put up enable notification with wifi downstream and home capabilities. + // Wifi downstream and home capabilities. notificationUpdater.onDownstreamChanged(WIFI_MASK) notificationUpdater.onUpstreamCapabilitiesChanged(HOME_CAPABILITIES) - verifyNotification(WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE, ENABLE_NOTIFICATION_ID) + verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID)) // Set R.integer.delay_to_show_no_upstream_after_no_backhaul to -1 and change to no upstream // again. Don't put up no upstream notification. @@ -429,15 +299,14 @@ class TetheringNotificationUpdaterTest { } @Test - fun testNotificationWithUpstreamCapabilitiesChanged_Roaming() { - // Set test sub id. Clear notification. + fun testRoamingNotification() { + // Set test sub id. notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID) - verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID, - ROAMING_NOTIFICATION_ID)) + verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID)) - // Wifi downstream. Show enable notification with test resource. + // Wifi downstream. notificationUpdater.onDownstreamChanged(WIFI_MASK) - verifyNotification(WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE, ENABLE_NOTIFICATION_ID) + verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID)) // Upstream capabilities changed to roaming state. Show roaming notification. notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES) @@ -464,14 +333,16 @@ class TetheringNotificationUpdaterTest { verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE, NO_UPSTREAM_NOTIFICATION_ID) - // No downstream. No notification. + // No downstream. notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE) - verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID, - ROAMING_NOTIFICATION_ID)) + verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID)) - // Wifi downstream again. Show enable notification with test resource. + // Wifi downstream again. notificationUpdater.onDownstreamChanged(WIFI_MASK) - verifyNotification(WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE, ENABLE_NOTIFICATION_ID) + notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS) + verifyNotificationCancelled(listOf(ROAMING_NOTIFICATION_ID), false) + verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE, + NO_UPSTREAM_NOTIFICATION_ID) // Set R.bool.config_upstream_roaming_notification to false and change upstream // network to roaming state again. No roaming notification. diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java index cf0548304a78..0c86eeb6cd4d 100644 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java +++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java @@ -1079,12 +1079,12 @@ public class TetheringTest { } private void runUserRestrictionsChange( - boolean currentDisallow, boolean nextDisallow, String[] activeTetheringIfacesList, + boolean currentDisallow, boolean nextDisallow, boolean isTetheringActive, int expectedInteractionsWithShowNotification) throws Exception { final Bundle newRestrictions = new Bundle(); newRestrictions.putBoolean(UserManager.DISALLOW_CONFIG_TETHERING, nextDisallow); final Tethering mockTethering = mock(Tethering.class); - when(mockTethering.getTetheredIfaces()).thenReturn(activeTetheringIfacesList); + when(mockTethering.isTetheringActive()).thenReturn(isTetheringActive); when(mUserManager.getUserRestrictions()).thenReturn(newRestrictions); final Tethering.UserRestrictionActionListener ural = @@ -1100,63 +1100,63 @@ public class TetheringTest { } @Test - public void testDisallowTetheringWhenNoTetheringInterfaceIsActive() throws Exception { - final String[] emptyActiveIfacesList = new String[]{}; + public void testDisallowTetheringWhenTetheringIsNotActive() throws Exception { + final boolean isTetheringActive = false; final boolean currDisallow = false; final boolean nextDisallow = true; - final int expectedInteractionsWithShowNotification = 1; + final int expectedInteractionsWithShowNotification = 0; - runUserRestrictionsChange(currDisallow, nextDisallow, emptyActiveIfacesList, + runUserRestrictionsChange(currDisallow, nextDisallow, isTetheringActive, expectedInteractionsWithShowNotification); } @Test - public void testDisallowTetheringWhenAtLeastOneTetheringInterfaceIsActive() throws Exception { - final String[] nonEmptyActiveIfacesList = new String[]{TEST_WLAN_IFNAME}; + public void testDisallowTetheringWhenTetheringIsActive() throws Exception { + final boolean isTetheringActive = true; final boolean currDisallow = false; final boolean nextDisallow = true; final int expectedInteractionsWithShowNotification = 1; - runUserRestrictionsChange(currDisallow, nextDisallow, nonEmptyActiveIfacesList, + runUserRestrictionsChange(currDisallow, nextDisallow, isTetheringActive, expectedInteractionsWithShowNotification); } @Test - public void testAllowTetheringWhenNoTetheringInterfaceIsActive() throws Exception { - final String[] nonEmptyActiveIfacesList = new String[]{}; + public void testAllowTetheringWhenTetheringIsNotActive() throws Exception { + final boolean isTetheringActive = false; final boolean currDisallow = true; final boolean nextDisallow = false; final int expectedInteractionsWithShowNotification = 0; - runUserRestrictionsChange(currDisallow, nextDisallow, nonEmptyActiveIfacesList, + runUserRestrictionsChange(currDisallow, nextDisallow, isTetheringActive, expectedInteractionsWithShowNotification); } @Test - public void testAllowTetheringWhenAtLeastOneTetheringInterfaceIsActive() throws Exception { - final String[] nonEmptyActiveIfacesList = new String[]{TEST_WLAN_IFNAME}; + public void testAllowTetheringWhenTetheringIsActive() throws Exception { + final boolean isTetheringActive = true; final boolean currDisallow = true; final boolean nextDisallow = false; final int expectedInteractionsWithShowNotification = 0; - runUserRestrictionsChange(currDisallow, nextDisallow, nonEmptyActiveIfacesList, + runUserRestrictionsChange(currDisallow, nextDisallow, isTetheringActive, expectedInteractionsWithShowNotification); } @Test public void testDisallowTetheringUnchanged() throws Exception { - final String[] nonEmptyActiveIfacesList = new String[]{TEST_WLAN_IFNAME}; + final boolean isTetheringActive = true; final int expectedInteractionsWithShowNotification = 0; boolean currDisallow = true; boolean nextDisallow = true; - runUserRestrictionsChange(currDisallow, nextDisallow, nonEmptyActiveIfacesList, + runUserRestrictionsChange(currDisallow, nextDisallow, isTetheringActive, expectedInteractionsWithShowNotification); currDisallow = false; nextDisallow = false; - runUserRestrictionsChange(currDisallow, nextDisallow, nonEmptyActiveIfacesList, + runUserRestrictionsChange(currDisallow, nextDisallow, isTetheringActive, expectedInteractionsWithShowNotification); } diff --git a/packages/overlays/IconPackCircularLauncherOverlay/res/drawable/ic_corp.xml b/packages/overlays/IconPackCircularLauncherOverlay/res/drawable/ic_corp.xml index b0001462bfff..a05a38996ed8 100644 --- a/packages/overlays/IconPackCircularLauncherOverlay/res/drawable/ic_corp.xml +++ b/packages/overlays/IconPackCircularLauncherOverlay/res/drawable/ic_corp.xml @@ -1,29 +1,26 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2019 The Android Open Source Project +<!-- Copyright (C) 2020 The Android Open Source Project - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" android:height="24dp" - android:tint="?android:attr/textColorHint" - android:viewportHeight="24" android:viewportWidth="24" - android:width="24dp" > - <path - android:fillColor="@android:color/white" - android:pathData="M16,4c0-1.1-0.9-2-2-2h-4C8.9,2,8,2.9,8,4v2H2v12c0,1.7,1.3,3,3,3h14c1.7,0,3-1.3,3-3V6h-6V4z M9.5,4 c0-0.3,0.2-0.5,0.5-0.5h4c0.3,0,0.5,0.2,0.5,0.5v2h-5V4z M20.5,7.5V18c0,0.8-0.7,1.5-1.5,1.5H5c-0.8,0-1.5-0.7-1.5-1.5V7.5H20.5z" /> - <path - android:fillColor="@android:color/white" - android:pathData="M 12 12.3 C 12.6627416998 12.3 13.2 12.8372583002 13.2 13.5 C 13.2 14.1627416998 12.6627416998 14.7 12 14.7 C 11.3372583002 14.7 10.8 14.1627416998 10.8 13.5 C 10.8 12.8372583002 11.3372583002 12.3 12 12.3 Z" /> + android:viewportHeight="24"> + <path + android:fillColor="#FF000000" + android:pathData="M19.75,6H16c0,0 0,0 0,0c0,-2.05 -0.95,-4 -4,-4C8.96,2 8,3.97 8,6c0,0 0,0 0,0H4.25C3.01,6 2,7.01 2,8.25v10.5C2,19.99 3.01,21 4.25,21h15.5c1.24,0 2.25,-1.01 2.25,-2.25V8.25C22,7.01 20.99,6 19.75,6zM12,3.5c0.54,-0.01 2.5,-0.11 2.5,2.5c0,0 0,0 0,0h-5c0,0 0,0 0,0C9.5,3.39 11.45,3.48 12,3.5zM20.5,18.75c0,0.41 -0.34,0.75 -0.75,0.75H4.25c-0.41,0 -0.75,-0.34 -0.75,-0.75V8.25c0,-0.41 0.34,-0.75 0.75,-0.75h15.5c0.41,0 0.75,0.34 0.75,0.75V18.75z"/> + <path + android:fillColor="#FF000000" + android:pathData="M12,12c-0.05,0 -1.5,-0.09 -1.5,1.5c0,1.59 1.43,1.5 1.5,1.5c0.05,0 1.5,0.09 1.5,-1.5C13.5,11.91 12.07,12 12,12z"/> </vector>
\ No newline at end of file diff --git a/packages/overlays/IconPackCircularLauncherOverlay/res/drawable/ic_corp_off.xml b/packages/overlays/IconPackCircularLauncherOverlay/res/drawable/ic_corp_off.xml new file mode 100644 index 000000000000..a8102519361b --- /dev/null +++ b/packages/overlays/IconPackCircularLauncherOverlay/res/drawable/ic_corp_off.xml @@ -0,0 +1,26 @@ +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="#FF000000" + android:pathData="M19.5,19.5l-6,-6L12,12L7.5,7.5L6,6L2.81,2.81L1.75,3.87l2.17,2.17C2.83,6.2 2,7.12 2,8.25v10.5C2,19.99 3.01,21 4.25,21h14.63l1.25,1.25l1.06,-1.06l-0.44,-0.44L19.5,19.5zM4.25,19.5c-0.41,0 -0.75,-0.34 -0.75,-0.75V8.25c0,-0.41 0.34,-0.75 0.75,-0.75h1.13l5.27,5.27c-0.09,0.19 -0.15,0.42 -0.15,0.73c0,1.59 1.43,1.5 1.5,1.5c0.02,0 0.38,0.02 0.74,-0.14l4.64,4.64H4.25z"/> + <path + android:fillColor="#FF000000" + android:pathData="M9.62,7.5h10.13c0.41,0 0.75,0.34 0.75,0.75v10.13l1.28,1.28c0.13,-0.28 0.22,-0.58 0.22,-0.91V8.25C22,7.01 20.99,6 19.75,6H16c0,0 0,0 0,0c0,-2.05 -0.95,-4 -4,-4C9.01,2 8.04,3.9 8.01,5.89L9.62,7.5zM12,3.5c0.54,-0.01 2.5,-0.11 2.5,2.5c0,0 0,0 0,0h-5c0,0 0,0 0,0C9.5,3.39 11.45,3.48 12,3.5z"/> +</vector> diff --git a/packages/overlays/IconPackCircularLauncherOverlay/res/drawable/ic_screenshot.xml b/packages/overlays/IconPackCircularLauncherOverlay/res/drawable/ic_screenshot.xml new file mode 100644 index 000000000000..e8608a598fbf --- /dev/null +++ b/packages/overlays/IconPackCircularLauncherOverlay/res/drawable/ic_screenshot.xml @@ -0,0 +1,29 @@ +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="#FF000000" + android:pathData="M16.75,1h-9.5C6.01,1 5,2.01 5,3.25v17.5C5,21.99 6.01,23 7.25,23h9.5c1.24,0 2.25,-1.01 2.25,-2.25V3.25C19,2.01 17.99,1 16.75,1zM7.25,2.5h9.5c0.41,0 0.75,0.34 0.75,0.75v1h-11v-1C6.5,2.84 6.84,2.5 7.25,2.5zM17.5,5.75v12.5h-11V5.75H17.5zM16.75,21.5h-9.5c-0.41,0 -0.75,-0.34 -0.75,-0.75v-1h11v1C17.5,21.16 17.16,21.5 16.75,21.5z"/> + <path + android:fillColor="#FF000000" + android:pathData="M9.5,11V8.5H12V7H8.75C8.34,7 8,7.34 8,7.75V11H9.5z"/> + <path + android:fillColor="#FF000000" + android:pathData="M12,17h3.25c0.41,0 0.75,-0.34 0.75,-0.75V13h-1.5v2.5H12V17z"/> +</vector> diff --git a/packages/overlays/IconPackCircularLauncherOverlay/res/drawable/ic_select.xml b/packages/overlays/IconPackCircularLauncherOverlay/res/drawable/ic_select.xml new file mode 100644 index 000000000000..4e265fd9c5d5 --- /dev/null +++ b/packages/overlays/IconPackCircularLauncherOverlay/res/drawable/ic_select.xml @@ -0,0 +1,32 @@ +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="#FF000000" + android:pathData="M11.25,1h1.5v3h-1.5z"/> + <path + android:fillColor="#FF000000" + android:pathData="M15.5983,5.3402l2.1213,-2.1213l1.0606,1.0606l-2.1213,2.1213z"/> + <path + android:fillColor="#FF000000" + android:pathData="M5.2187,4.2803l1.0606,-1.0606l2.1213,2.1213l-1.0606,1.0606z"/> + <path + android:fillColor="#FF000000" + android:pathData="M15.5,12.5l-1.25,0V8.12C14.25,6.95 13.3,6 12.12,6S10,6.95 10,8.12v7.9L8.03,15.5c-0.39,-0.1 -1.23,-0.36 -2.56,0.97c-0.29,0.29 -0.29,0.75 -0.01,1.05l3.79,3.98c0,0 0,0 0,0.01c1.37,1.41 3.28,1.51 4.04,1.49l2.2,0c2.12,0.06 5.25,-1.01 5.25,-5.25C20.75,13.19 17.23,12.46 15.5,12.5zM15.5,21.5l-2.25,0c-0.44,0.01 -1.93,-0.02 -2.92,-1.03l-3.27,-3.43c0.17,-0.1 0.38,-0.13 0.58,-0.08l2.91,0.78c0.47,0.12 0.94,-0.23 0.94,-0.72V8.12c0,-0.34 0.28,-0.62 0.62,-0.62s0.62,0.28 0.62,0.62v5.12c0,0.41 0.33,0.75 0.75,0.75l2.05,0c1.26,-0.03 3.7,0.37 3.7,3.75C19.25,21.14 16.78,21.53 15.5,21.5z"/> +</vector> diff --git a/packages/overlays/IconPackCircularLauncherOverlay/res/drawable/ic_share.xml b/packages/overlays/IconPackCircularLauncherOverlay/res/drawable/ic_share.xml new file mode 100644 index 000000000000..726d1aa5e1c2 --- /dev/null +++ b/packages/overlays/IconPackCircularLauncherOverlay/res/drawable/ic_share.xml @@ -0,0 +1,39 @@ +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="#FF000000" + android:pathData="M17.88,3.5l0.06,0l0.04,0H18l0.04,0l0.02,0l0.06,0c1.38,0 1.38,1.01 1.38,1.5s0,1.5 -1.38,1.5l-0.06,0l-0.04,0H18l-0.04,0l-0.02,0l-0.06,0C16.5,6.5 16.5,5.49 16.5,5S16.5,3.5 17.88,3.5M17.88,2C17.33,2 15,2.15 15,5c0,2.85 2.31,3 2.88,3c0.06,0 0.11,0 0.12,0c0.01,0 0.05,0 0.12,0C18.67,8 21,7.85 21,5c0,-2.85 -2.31,-3 -2.88,-3C18.06,2 18.01,2 18,2C17.99,2 17.95,2 17.88,2L17.88,2z"/> + <path + android:fillColor="#FF000000" + android:pathData="M17.88,17.5l0.06,0l0.04,0H18l0.04,0l0.02,0l0.06,0c1.38,0 1.38,1.01 1.38,1.5s0,1.5 -1.38,1.5l-0.06,0l-0.04,0H18l-0.04,0l-0.02,0l-0.06,0c-1.38,0 -1.38,-1.01 -1.38,-1.5S16.5,17.5 17.88,17.5M17.88,16C17.33,16 15,16.15 15,19c0,2.85 2.31,3 2.88,3c0.06,0 0.11,0 0.12,0c0.01,0 0.05,0 0.12,0c0.56,0 2.88,-0.15 2.88,-3c0,-2.85 -2.31,-3 -2.88,-3c-0.06,0 -0.11,0 -0.12,0C17.99,16 17.95,16 17.88,16L17.88,16z"/> + <path + android:fillColor="#FF000000" + android:pathData="M5.88,10.5l0.06,0l0.04,0H6l0.04,0l0.02,0l0.06,0c1.38,0 1.38,1.01 1.38,1.5s0,1.5 -1.38,1.5l-0.06,0l-0.04,0H6l-0.04,0l-0.02,0l-0.06,0C4.5,13.5 4.5,12.49 4.5,12S4.5,10.5 5.88,10.5M5.88,9C5.33,9 3,9.15 3,12c0,2.85 2.31,3 2.88,3c0.06,0 0.11,0 0.12,0c0.01,0 0.05,0 0.12,0C6.67,15 9,14.85 9,12c0,-2.85 -2.31,-3 -2.88,-3C6.06,9 6.01,9 6,9C5.99,9 5.95,9 5.88,9L5.88,9z"/> + <path + android:pathData="M16.01,6.16L8,10.83" + android:strokeWidth="1.5" + android:fillColor="#00000000" + android:strokeColor="#000000"/> + <path + android:pathData="M16.06,17.87L8.19,13.28" + android:strokeWidth="1.5" + android:fillColor="#00000000" + android:strokeColor="#000000"/> +</vector> diff --git a/packages/overlays/IconPackFilledLauncherOverlay/res/drawable/ic_corp.xml b/packages/overlays/IconPackFilledLauncherOverlay/res/drawable/ic_corp.xml index 7139313a9ef9..0dfaf8188f8d 100644 --- a/packages/overlays/IconPackFilledLauncherOverlay/res/drawable/ic_corp.xml +++ b/packages/overlays/IconPackFilledLauncherOverlay/res/drawable/ic_corp.xml @@ -1,27 +1,23 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2019 The Android Open Source Project +<!-- Copyright (C) 2020 The Android Open Source Project - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" android:height="24dp" - android:tint="?android:attr/textColorHint" - android:viewportHeight="24" android:viewportWidth="24" - android:width="24dp" > - <path android:pathData="M 10 4 H 14 V 6 H 10 V 4 Z" /> - <path - android:fillColor="@android:color/white" - android:pathData="M20,6h-4V4c0-1.1-0.9-2-2-2h-4C8.9,2,8,2.9,8,4v2H4C2.9,6,2,6.9,2,8l0,11c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V8 C22,6.9,21.1,6,20,6z M12,15c-0.8,0-1.5-0.7-1.5-1.5S11.2,12,12,12s1.5,0.7,1.5,1.5S12.8,15,12,15z M14,6h-4V4h4V6z" /> -</vector>
\ No newline at end of file + android:viewportHeight="24"> + <path + android:fillColor="#FF000000" + android:pathData="M19,6h-3c0,-2.21 -1.79,-4 -4,-4S8,3.79 8,6H5C3.34,6 2,7.34 2,9v9c0,1.66 1.34,3 3,3h14c1.66,0 3,-1.34 3,-3V9C22,7.34 20.66,6 19,6zM12,15c-0.83,0 -1.5,-0.67 -1.5,-1.5S11.17,12 12,12s1.5,0.67 1.5,1.5S12.83,15 12,15zM10,6c0,-1.1 0.9,-2 2,-2s2,0.9 2,2H10z"/> +</vector> diff --git a/packages/overlays/IconPackFilledLauncherOverlay/res/drawable/ic_corp_off.xml b/packages/overlays/IconPackFilledLauncherOverlay/res/drawable/ic_corp_off.xml new file mode 100644 index 000000000000..b3f353a16943 --- /dev/null +++ b/packages/overlays/IconPackFilledLauncherOverlay/res/drawable/ic_corp_off.xml @@ -0,0 +1,26 @@ +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="#FF000000" + android:pathData="M21.81,18.98C21.92,18.67 22,18.35 22,18V9c0,-1.66 -1.34,-3 -3,-3h-3c0,-2.21 -1.79,-4 -4,-4c-1.95,0 -3.57,1.4 -3.92,3.24L21.81,18.98zM12,4c1.1,0 2,0.9 2,2h-4C10,4.9 10.9,4 12,4z"/> + <path + android:fillColor="#FF000000" + android:pathData="M20.56,20.55l-17,-17c0,0 0,0 0,0L3.45,3.44c-0.39,-0.39 -1.02,-0.39 -1.41,0s-0.39,1.02 0,1.41l1.53,1.53C2.64,6.89 2,7.87 2,9v9c0,1.66 1.34,3 3,3h13.18l0.96,0.96c0.2,0.2 0.45,0.29 0.71,0.29s0.51,-0.1 0.71,-0.29C20.94,21.57 20.94,20.94 20.56,20.55C20.56,20.55 20.56,20.55 20.56,20.55zM12,15c-0.83,0 -1.5,-0.67 -1.5,-1.5c0,-0.06 0.01,-0.11 0.02,-0.16l1.65,1.65C12.11,14.99 12.06,15 12,15z"/> +</vector> diff --git a/packages/overlays/IconPackFilledLauncherOverlay/res/drawable/ic_screenshot.xml b/packages/overlays/IconPackFilledLauncherOverlay/res/drawable/ic_screenshot.xml new file mode 100644 index 000000000000..1d291c93fb4d --- /dev/null +++ b/packages/overlays/IconPackFilledLauncherOverlay/res/drawable/ic_screenshot.xml @@ -0,0 +1,29 @@ +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="#FF000000" + android:pathData="M8.75,11c0.41,0 0.75,-0.34 0.75,-0.75V8.5h1.75C11.66,8.5 12,8.16 12,7.75C12,7.34 11.66,7 11.25,7H9C8.45,7 8,7.45 8,8v2.25C8,10.66 8.34,11 8.75,11z"/> + <path + android:fillColor="#FF000000" + android:pathData="M12.75,17H15c0.55,0 1,-0.45 1,-1v-2.25c0,-0.41 -0.34,-0.75 -0.75,-0.75s-0.75,0.34 -0.75,0.75v1.75h-1.75c-0.41,0 -0.75,0.34 -0.75,0.75C12,16.66 12.34,17 12.75,17z"/> + <path + android:fillColor="#FF000000" + android:pathData="M16,1H8C6.34,1 5,2.34 5,4v16c0,1.65 1.35,3 3,3h8c1.65,0 3,-1.35 3,-3V4C19,2.34 17.66,1 16,1zM17,18H7V6h10V18z"/> +</vector> diff --git a/packages/overlays/IconPackFilledLauncherOverlay/res/drawable/ic_select.xml b/packages/overlays/IconPackFilledLauncherOverlay/res/drawable/ic_select.xml new file mode 100644 index 000000000000..df4525d48777 --- /dev/null +++ b/packages/overlays/IconPackFilledLauncherOverlay/res/drawable/ic_select.xml @@ -0,0 +1,32 @@ +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="#FF000000" + android:pathData="M17.59,5.83l0.71,-0.71c0.39,-0.39 0.39,-1.02 0,-1.41l0,0c-0.39,-0.39 -1.02,-0.39 -1.41,0l-0.71,0.71c-0.39,0.39 -0.39,1.02 0,1.41C16.56,6.21 17.2,6.21 17.59,5.83z"/> + <path + android:fillColor="#FF000000" + android:pathData="M12,4c0.55,0 1,-0.45 1,-1V2c0,-0.55 -0.45,-1 -1,-1s-1,0.45 -1,1v1C11,3.55 11.45,4 12,4z"/> + <path + android:fillColor="#FF000000" + android:pathData="M6.42,5.83c0.39,0.39 1.02,0.39 1.41,0c0.39,-0.39 0.39,-1.02 0,-1.41L7.12,3.71c-0.39,-0.39 -1.02,-0.39 -1.41,0l0,0c-0.39,0.39 -0.39,1.02 0,1.41L6.42,5.83z"/> + <path + android:fillColor="#FF000000" + android:pathData="M17.95,14.43l-3.23,-1.61c-0.42,-0.21 -0.88,-0.32 -1.34,-0.32H13v-5C13,6.67 12.33,6 11.5,6C10.67,6 10,6.67 10,7.5v9.12c0,0.32 -0.29,0.55 -0.6,0.49l-2.84,-0.6c-0.37,-0.08 -0.76,0.04 -1.03,0.31c-0.43,0.44 -0.43,1.14 0.01,1.58l3.71,3.71C9.81,22.68 10.58,23 11.37,23h4.82c1.49,0 2.76,-1.1 2.97,-2.58l0.41,-2.89C19.76,16.26 19.1,15.01 17.95,14.43z"/> +</vector> diff --git a/packages/overlays/IconPackFilledLauncherOverlay/res/drawable/ic_share.xml b/packages/overlays/IconPackFilledLauncherOverlay/res/drawable/ic_share.xml new file mode 100644 index 000000000000..89ee5274e48f --- /dev/null +++ b/packages/overlays/IconPackFilledLauncherOverlay/res/drawable/ic_share.xml @@ -0,0 +1,39 @@ +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M18,5L6,12" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="#000000"/> + <path + android:pathData="M18,19L6,12" + android:strokeWidth="2" + android:fillColor="#00000000" + android:strokeColor="#000000"/> + <path + android:fillColor="#FF000000" + android:pathData="M18,5m-3,0a3,3 0,1 1,6 0a3,3 0,1 1,-6 0"/> + <path + android:fillColor="#FF000000" + android:pathData="M18,19m-3,0a3,3 0,1 1,6 0a3,3 0,1 1,-6 0"/> + <path + android:fillColor="#FF000000" + android:pathData="M6,12m-3,0a3,3 0,1 1,6 0a3,3 0,1 1,-6 0"/> +</vector> diff --git a/packages/overlays/IconPackRoundedLauncherOverlay/res/drawable/ic_corp.xml b/packages/overlays/IconPackRoundedLauncherOverlay/res/drawable/ic_corp.xml index 38f515fdb22d..be31fb9171bf 100644 --- a/packages/overlays/IconPackRoundedLauncherOverlay/res/drawable/ic_corp.xml +++ b/packages/overlays/IconPackRoundedLauncherOverlay/res/drawable/ic_corp.xml @@ -1,29 +1,26 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2019 The Android Open Source Project +<!-- Copyright (C) 2020 The Android Open Source Project - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" android:height="24dp" - android:tint="?android:attr/textColorHint" - android:viewportHeight="24" android:viewportWidth="24" - android:width="24dp" > - <path - android:fillColor="@android:color/white" - android:pathData="M20,6h-4V4c0-1.1-0.9-2-2-2h-4C8.9,2,8,2.9,8,4v2H4C2.9,6,2,6.9,2,8v11c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V8 C22,6.9,21.1,6,20,6z M9.5,4c0-0.3,0.2-0.5,0.5-0.5h4c0.3,0,0.5,0.2,0.5,0.5v2h-5V4z M20.5,19c0,0.3-0.2,0.5-0.5,0.5H4 c-0.3,0-0.5-0.2-0.5-0.5V8c0-0.3,0.2-0.5,0.5-0.5h16c0.3,0,0.5,0.2,0.5,0.5V19z" /> - <path - android:fillColor="@android:color/white" - android:pathData="M 12 12.3 C 12.6627416998 12.3 13.2 12.8372583002 13.2 13.5 C 13.2 14.1627416998 12.6627416998 14.7 12 14.7 C 11.3372583002 14.7 10.8 14.1627416998 10.8 13.5 C 10.8 12.8372583002 11.3372583002 12.3 12 12.3 Z" /> -</vector>
\ No newline at end of file + android:viewportHeight="24"> + <path + android:fillColor="#FF000000" + android:pathData="M20.59,6H16V3.41L14.59,2H9.41L8,3.41V6H3.41L2,7.41v12.17L3.41,21h17.17L22,19.59V7.41L20.59,6zM9.5,3.5h5V6h-5V3.5zM20.5,19.5h-17v-12h17V19.5z"/> + <path + android:fillColor="#FF000000" + android:pathData="M12,13.5m-1.5,0a1.5,1.5 0,1 1,3 0a1.5,1.5 0,1 1,-3 0"/> +</vector> diff --git a/packages/overlays/IconPackRoundedLauncherOverlay/res/drawable/ic_corp_off.xml b/packages/overlays/IconPackRoundedLauncherOverlay/res/drawable/ic_corp_off.xml new file mode 100644 index 000000000000..8d298f7cbc97 --- /dev/null +++ b/packages/overlays/IconPackRoundedLauncherOverlay/res/drawable/ic_corp_off.xml @@ -0,0 +1,26 @@ +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="#FF000000" + android:pathData="M19.5,19.5l-6,-6L12,12L7.5,7.5L6,6L2.81,2.81L1.75,3.87L3.88,6H3.41L2,7.41v12.17L3.41,21h15.46l1.25,1.25l1.06,-1.06l-0.4,-0.4L19.5,19.5zM3.5,19.5v-12h1.88l5.3,5.3c-0.11,0.21 -0.18,0.44 -0.18,0.7c0,0.83 0.67,1.5 1.5,1.5c0.25,0 0.49,-0.07 0.7,-0.18l4.68,4.68H3.5z"/> + <path + android:fillColor="#FF000000" + android:pathData="M9.62,7.5H20.5v10.88l1.35,1.35L22,19.59V7.41L20.59,6H16V3.41L14.59,2H9.41L8,3.41v2.46L9.62,7.5zM9.5,3.5h5V6h-5V3.5z"/> +</vector> diff --git a/packages/Tethering/res/values-mcc312-mnc530/strings.xml b/packages/overlays/IconPackRoundedLauncherOverlay/res/drawable/ic_screenshot.xml index 618df90c7105..ed90b85c8b87 100644 --- a/packages/Tethering/res/values-mcc312-mnc530/strings.xml +++ b/packages/overlays/IconPackRoundedLauncherOverlay/res/drawable/ic_screenshot.xml @@ -1,4 +1,3 @@ -<?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2020 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); @@ -13,10 +12,18 @@ See the License for the specific language governing permissions and limitations under the License. --> -<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- String for tethered notification title with client number info. --> - <plurals name="tethered_notification_title_with_client_number"> - <item quantity="one"><xliff:g>%1$d</xliff:g> device connected.</item> - <item quantity="other"><xliff:g>%1$d</xliff:g> devices connected.</item> - </plurals> -</resources>
\ No newline at end of file +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="#FF000000" + android:pathData="M17.59,1H6.41L5,2.41v19.17L6.41,23h11.17L19,21.59V2.41L17.59,1zM17.5,2.5v1.75h-11V2.5H17.5zM17.5,5.75v12.5h-11V5.75H17.5zM6.5,21.5v-1.75h11v1.75H6.5z"/> + <path + android:fillColor="#FF000000" + android:pathData="M9.5,11l0,-2.5l2.5,0l0,-1.5l-4,0l0,4z"/> + <path + android:fillColor="#FF000000" + android:pathData="M12,17l4,0l0,-4l-1.5,0l0,2.5l-2.5,0z"/> +</vector> diff --git a/packages/overlays/IconPackRoundedLauncherOverlay/res/drawable/ic_select.xml b/packages/overlays/IconPackRoundedLauncherOverlay/res/drawable/ic_select.xml new file mode 100644 index 000000000000..b699a4444d76 --- /dev/null +++ b/packages/overlays/IconPackRoundedLauncherOverlay/res/drawable/ic_select.xml @@ -0,0 +1,32 @@ +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="#FF000000" + android:pathData="M16.19,12.5h-1.94V8.12C14.25,6.95 13.3,6 12.12,6S10,6.95 10,8.12v7.9l-3.22,-0.86l-1.82,1.82L10.68,23h8.6l1.57,-7.96L16.19,12.5zM18.04,21.5h-6.72l-4.27,-4.49l0.18,-0.18l4.28,1.14V8.12c0,-0.34 0.28,-0.62 0.62,-0.62s0.62,0.28 0.62,0.62V14h3.06l3.35,1.83L18.04,21.5z"/> + <path + android:fillColor="#FF000000" + android:pathData="M11.25,1h1.5v3h-1.5z"/> + <path + android:fillColor="#FF000000" + android:pathData="M15.5983,5.3402l2.1213,-2.1213l1.0606,1.0606l-2.1213,2.1213z"/> + <path + android:fillColor="#FF000000" + android:pathData="M5.2187,4.2803l1.0606,-1.0606l2.1213,2.1213l-1.0606,1.0606z"/> +</vector> diff --git a/packages/overlays/IconPackRoundedLauncherOverlay/res/drawable/ic_share.xml b/packages/overlays/IconPackRoundedLauncherOverlay/res/drawable/ic_share.xml new file mode 100644 index 000000000000..36dd3baa8994 --- /dev/null +++ b/packages/overlays/IconPackRoundedLauncherOverlay/res/drawable/ic_share.xml @@ -0,0 +1,39 @@ +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="#FF000000" + android:pathData="M18,3.5c0.83,0 1.5,0.67 1.5,1.5S18.83,6.5 18,6.5S16.5,5.83 16.5,5S17.17,3.5 18,3.5M18,2c-1.66,0 -3,1.34 -3,3s1.34,3 3,3s3,-1.34 3,-3S19.66,2 18,2L18,2z"/> + <path + android:fillColor="#FF000000" + android:pathData="M18,17.5c0.83,0 1.5,0.67 1.5,1.5s-0.67,1.5 -1.5,1.5s-1.5,-0.67 -1.5,-1.5S17.17,17.5 18,17.5M18,16c-1.66,0 -3,1.34 -3,3s1.34,3 3,3s3,-1.34 3,-3S19.66,16 18,16L18,16z"/> + <path + android:fillColor="#FF000000" + android:pathData="M6,10.5c0.83,0 1.5,0.67 1.5,1.5S6.83,13.5 6,13.5S4.5,12.83 4.5,12S5.17,10.5 6,10.5M6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3s3,-1.34 3,-3S7.66,9 6,9L6,9z"/> + <path + android:pathData="M16.01,6.16L8,10.83" + android:strokeWidth="1.5" + android:fillColor="#00000000" + android:strokeColor="#000000"/> + <path + android:pathData="M16.06,17.87L8.19,13.28" + android:strokeWidth="1.5" + android:fillColor="#00000000" + android:strokeColor="#000000"/> +</vector> diff --git a/services/Android.bp b/services/Android.bp index 730b9a5dc3e3..6d637bedeef7 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -112,7 +112,6 @@ droidstubs { name: "services-stubs.sources", srcs: [":services-all-sources"], installable: false, - api_tag_name: "SYSTEM_SERVER", args: " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.SYSTEM_SERVER\\)" + " --hide-annotation android.annotation.Hide" + " --hide InternalClasses" + // com.android.* classes are okay in this interface diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index 132b6927badd..da9bdf3262d5 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -1023,8 +1023,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ try { mMainHandler.post(PooledLambda.obtainRunnable((nonArg) -> { final ScreenshotGraphicBuffer screenshotBuffer = LocalServices - .getService(DisplayManagerInternal.class) - .screenshotWithoutSecureLayer(displayId); + .getService(DisplayManagerInternal.class).userScreenshot(displayId); if (screenshotBuffer != null) { sendScreenshotSuccess(screenshotBuffer, callback); } else { diff --git a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java index b9e3050275ad..a1fc3fa1857d 100644 --- a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java +++ b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java @@ -16,6 +16,8 @@ package com.android.server.accessibility; +import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_ACCESSIBILITY_ACTIONS; + import android.accessibilityservice.AccessibilityService; import android.app.PendingIntent; import android.app.RemoteAction; @@ -399,7 +401,8 @@ public class SystemActionPerformer { ScreenshotHelper screenshotHelper = (mScreenshotHelperSupplier != null) ? mScreenshotHelperSupplier.get() : new ScreenshotHelper(mContext); screenshotHelper.takeScreenshot(android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN, - true, true, new Handler(Looper.getMainLooper()), null); + true, true, SCREENSHOT_ACCESSIBILITY_ACTIONS, + new Handler(Looper.getMainLooper()), null); return true; } } diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index b27c5d54a6fb..35089d6f5de7 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -117,6 +117,7 @@ import java.util.Optional; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; +import java.util.function.Function; /** * A session for a given activity. @@ -3079,19 +3080,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final boolean isWhitelisted = mService .isWhitelistedForAugmentedAutofillLocked(mComponentName); - final String historyItem = - "aug:id=" + id + " u=" + uid + " m=" + mode - + " a=" + ComponentName.flattenToShortString(mComponentName) - + " f=" + mCurrentViewId - + " s=" + remoteService.getComponentName() - + " w=" + isWhitelisted; - mService.getMaster().logRequestLocked(historyItem); - if (!isWhitelisted) { if (sVerbose) { Slog.v(TAG, "triggerAugmentedAutofillLocked(): " + ComponentName.flattenToShortString(mComponentName) + " not whitelisted "); } + logAugmentedAutofillRequestLocked(mode, remoteService.getComponentName(), + mCurrentViewId, isWhitelisted, /*isInline*/null); return null; } @@ -3116,24 +3111,27 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final AutofillId focusedId = mCurrentViewId; + final Function<InlineSuggestionsResponse, Boolean> inlineSuggestionsResponseCallback = + response -> { + synchronized (mLock) { + return mInlineSessionController.onInlineSuggestionsResponseLocked( + focusedId, response); + } + }; final Consumer<InlineSuggestionsRequest> requestAugmentedAutofill = (inlineSuggestionsRequest) -> { - remoteService.onRequestAutofillLocked(id, mClient, taskId, mComponentName, - AutofillId.withoutSession(focusedId), - currentValue, inlineSuggestionsRequest, - /*inlineSuggestionsCallback=*/ - response -> { - synchronized (mLock) { - return mInlineSessionController - .onInlineSuggestionsResponseLocked( - focusedId, response); - } - }, - /*onErrorCallback=*/ () -> { - synchronized (mLock) { - cancelAugmentedAutofillLocked(); - } - }, mService.getRemoteInlineSuggestionRenderServiceLocked()); + synchronized (mLock) { + logAugmentedAutofillRequestLocked(mode, remoteService.getComponentName(), + focusedId, isWhitelisted, inlineSuggestionsRequest != null); + remoteService.onRequestAutofillLocked(id, mClient, taskId, mComponentName, + AutofillId.withoutSession(focusedId), currentValue, + inlineSuggestionsRequest, inlineSuggestionsResponseCallback, + /*onErrorCallback=*/ () -> { + synchronized (mLock) { + cancelAugmentedAutofillLocked(); + } + }, mService.getRemoteInlineSuggestionRenderServiceLocked()); + } }; // When the inline suggestion render service is available, there are 2 cases when @@ -3150,9 +3148,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (sDebug) Slog.d(TAG, "Create inline request for augmented autofill"); remoteRenderService.getInlineSuggestionsRendererInfo(new RemoteCallback( (extras) -> { - mInlineSessionController.onCreateInlineSuggestionsRequestLocked( - focusedId, /*requestConsumer=*/ requestAugmentedAutofill, - extras); + synchronized (mLock) { + mInlineSessionController.onCreateInlineSuggestionsRequestLocked( + focusedId, /*requestConsumer=*/ requestAugmentedAutofill, + extras); + } }, mHandler)); } else { requestAugmentedAutofill.accept( @@ -3165,6 +3165,20 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } @GuardedBy("mLock") + private void logAugmentedAutofillRequestLocked(int mode, + ComponentName augmentedRemoteServiceName, AutofillId focusedId, boolean isWhitelisted, + Boolean isInline) { + final String historyItem = + "aug:id=" + id + " u=" + uid + " m=" + mode + + " a=" + ComponentName.flattenToShortString(mComponentName) + + " f=" + focusedId + + " s=" + augmentedRemoteServiceName + + " w=" + isWhitelisted + + " i=" + isInline; + mService.getMaster().logRequestLocked(historyItem); + } + + @GuardedBy("mLock") private void cancelAugmentedAutofillLocked() { final RemoteAugmentedAutofillService remoteService = mService .getRemoteAugmentedAutofillServiceLocked(); diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index ce651100699f..4009cafd04fb 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -603,8 +603,7 @@ class AlarmManagerService extends SystemService { } pw.print(KEY_APP_STANDBY_RESTRICTED_QUOTA); pw.print("="); - TimeUtils.formatDuration(APP_STANDBY_RESTRICTED_QUOTA, pw); - pw.println(); + pw.println(APP_STANDBY_RESTRICTED_QUOTA); pw.print(KEY_APP_STANDBY_RESTRICTED_WINDOW); pw.print("="); TimeUtils.formatDuration(APP_STANDBY_RESTRICTED_WINDOW, pw); diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 2bbf27849005..97a5cfe6006d 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -1146,7 +1146,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { && registrationLimit >= 1 && numRecordsForPid >= registrationLimit) { String errorMsg = "Pid " + callingPid + " has exceeded the number of permissible" - + "registered listeners. Ignoring request to add."; + + " registered listeners. Ignoring request to add."; loge(errorMsg); if (mConfigurationProvider .isRegistrationLimitEnabledInPlatformCompat(callingUid)) { @@ -1157,7 +1157,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { // Log the warning independently of the dynamically set limit -- apps shouldn't be // doing this regardless of whether we're throwing them an exception for it. Rlog.w(TAG, "Pid " + callingPid + " has exceeded half the number of permissible" - + "registered listeners. Now at " + numRecordsForPid); + + " registered listeners. Now at " + numRecordsForPid); } r = new Record(); diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java index 58c388ed08c6..b09d74180c4b 100644 --- a/services/core/java/com/android/server/UiModeManagerService.java +++ b/services/core/java/com/android/server/UiModeManagerService.java @@ -122,7 +122,6 @@ final class UiModeManagerService extends SystemService { private boolean mVrHeadset; private boolean mComputedNightMode; private int mCarModeEnableFlags; - private boolean mSetupWizardComplete; // flag set by resource, whether to enable Car dock launch when starting car mode. private boolean mEnableCarDockLaunch = true; @@ -164,12 +163,6 @@ final class UiModeManagerService extends SystemService { mConfiguration.setToDefaults(); } - @VisibleForTesting - protected UiModeManagerService(Context context, boolean setupWizardComplete) { - this(context); - mSetupWizardComplete = setupWizardComplete; - } - private static Intent buildHomeIntent(String category) { Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(category); @@ -283,25 +276,6 @@ final class UiModeManagerService extends SystemService { } }; - private final ContentObserver mSetupWizardObserver = new ContentObserver(mHandler) { - @Override - public void onChange(boolean selfChange, Uri uri) { - synchronized (mLock) { - // setup wizard is done now so we can unblock - if (setupWizardCompleteForCurrentUser() && !selfChange) { - mSetupWizardComplete = true; - getContext().getContentResolver() - .unregisterContentObserver(mSetupWizardObserver); - // update night mode - Context context = getContext(); - updateNightModeFromSettingsLocked(context, context.getResources(), - UserHandle.getCallingUserId()); - updateLocked(0, 0); - } - } - } - }; - private final ContentObserver mDarkThemeObserver = new ContentObserver(mHandler) { @Override public void onChange(boolean selfChange, Uri uri) { @@ -319,13 +293,6 @@ final class UiModeManagerService extends SystemService { } @Override - public void onSwitchUser(int userHandle) { - super.onSwitchUser(userHandle); - getContext().getContentResolver().unregisterContentObserver(mSetupWizardObserver); - verifySetupWizardCompleted(); - } - - @Override public void onBootPhase(int phase) { if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { synchronized (mLock) { @@ -351,6 +318,8 @@ final class UiModeManagerService extends SystemService { context.registerReceiver(mBatteryReceiver, batteryFilter); IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_USER_SWITCHED); + context.registerReceiver(mSettingsRestored, + new IntentFilter(Intent.ACTION_SETTING_RESTORED)); context.registerReceiver(new UserSwitchedReceiver(), filter, null, mHandler); updateConfigurationLocked(); applyConfigurationExternallyLocked(); @@ -361,9 +330,6 @@ final class UiModeManagerService extends SystemService { @Override public void onStart() { final Context context = getContext(); - // If setup isn't complete for this user listen for completion so we can unblock - // being able to send a night mode configuration change event - verifySetupWizardCompleted(); final Resources res = context.getResources(); mDefaultUiModeType = res.getInteger( @@ -438,20 +404,6 @@ final class UiModeManagerService extends SystemService { return mConfiguration; } - // Records whether setup wizard has happened or not and adds an observer for this user if not. - private void verifySetupWizardCompleted() { - final Context context = getContext(); - final int userId = UserHandle.getCallingUserId(); - if (!setupWizardCompleteForCurrentUser()) { - mSetupWizardComplete = false; - context.getContentResolver().registerContentObserver( - Secure.getUriFor( - Secure.USER_SETUP_COMPLETE), false, mSetupWizardObserver, userId); - } else { - mSetupWizardComplete = true; - } - } - private boolean setupWizardCompleteForCurrentUser() { return Secure.getIntForUser(getContext().getContentResolver(), Secure.USER_SETUP_COMPLETE, 0, UserHandle.getCallingUserId()) == 1; @@ -480,28 +432,20 @@ final class UiModeManagerService extends SystemService { final int defaultNightMode = res.getInteger( com.android.internal.R.integer.config_defaultNightMode); int oldNightMode = mNightMode; - if (mSetupWizardComplete) { - mNightMode = Secure.getIntForUser(context.getContentResolver(), - Secure.UI_NIGHT_MODE, defaultNightMode, userId); - mOverrideNightModeOn = Secure.getIntForUser(context.getContentResolver(), - Secure.UI_NIGHT_MODE_OVERRIDE_ON, 0, userId) != 0; - mOverrideNightModeOff = Secure.getIntForUser(context.getContentResolver(), - Secure.UI_NIGHT_MODE_OVERRIDE_OFF, 0, userId) != 0; - mCustomAutoNightModeStartMilliseconds = LocalTime.ofNanoOfDay( - Secure.getLongForUser(context.getContentResolver(), - Secure.DARK_THEME_CUSTOM_START_TIME, - DEFAULT_CUSTOM_NIGHT_START_TIME.toNanoOfDay() / 1000L, userId) * 1000); - mCustomAutoNightModeEndMilliseconds = LocalTime.ofNanoOfDay( - Secure.getLongForUser(context.getContentResolver(), - Secure.DARK_THEME_CUSTOM_END_TIME, - DEFAULT_CUSTOM_NIGHT_END_TIME.toNanoOfDay() / 1000L, userId) * 1000); - } else { - mNightMode = defaultNightMode; - mCustomAutoNightModeEndMilliseconds = DEFAULT_CUSTOM_NIGHT_END_TIME; - mCustomAutoNightModeStartMilliseconds = DEFAULT_CUSTOM_NIGHT_START_TIME; - mOverrideNightModeOn = false; - mOverrideNightModeOff = false; - } + mNightMode = Secure.getIntForUser(context.getContentResolver(), + Secure.UI_NIGHT_MODE, defaultNightMode, userId); + mOverrideNightModeOn = Secure.getIntForUser(context.getContentResolver(), + Secure.UI_NIGHT_MODE_OVERRIDE_ON, 0, userId) != 0; + mOverrideNightModeOff = Secure.getIntForUser(context.getContentResolver(), + Secure.UI_NIGHT_MODE_OVERRIDE_OFF, 0, userId) != 0; + mCustomAutoNightModeStartMilliseconds = LocalTime.ofNanoOfDay( + Secure.getLongForUser(context.getContentResolver(), + Secure.DARK_THEME_CUSTOM_START_TIME, + DEFAULT_CUSTOM_NIGHT_START_TIME.toNanoOfDay() / 1000L, userId) * 1000); + mCustomAutoNightModeEndMilliseconds = LocalTime.ofNanoOfDay( + Secure.getLongForUser(context.getContentResolver(), + Secure.DARK_THEME_CUSTOM_END_TIME, + DEFAULT_CUSTOM_NIGHT_END_TIME.toNanoOfDay() / 1000L, userId) * 1000); return oldNightMode != mNightMode; } @@ -644,10 +588,6 @@ final class UiModeManagerService extends SystemService { Slog.e(TAG, "Night mode locked, requires MODIFY_DAY_NIGHT_MODE permission"); return; } - if (!mSetupWizardComplete) { - Slog.d(TAG, "Night mode cannot be changed before setup wizard completes."); - return; - } switch (mode) { case UiModeManager.MODE_NIGHT_NO: case UiModeManager.MODE_NIGHT_YES: diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 21760cdf02eb..419389f7abef 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -119,7 +119,6 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; import java.util.Comparator; -import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.function.Predicate; @@ -1753,8 +1752,8 @@ public final class ActiveServices { private void updateServiceForegroundLocked(ProcessRecord proc, boolean oomAdj) { boolean anyForeground = false; int fgServiceTypes = 0; - for (int i = proc.services.size() - 1; i >= 0; i--) { - ServiceRecord sr = proc.services.valueAt(i); + for (int i = proc.numberOfRunningServices() - 1; i >= 0; i--) { + ServiceRecord sr = proc.getRunningServiceAt(i); if (sr.isForeground || sr.fgRequired) { anyForeground = true; fgServiceTypes |= sr.foregroundServiceType; @@ -1765,8 +1764,8 @@ public final class ActiveServices { private void updateWhitelistManagerLocked(ProcessRecord proc) { proc.whitelistManager = false; - for (int i=proc.services.size()-1; i>=0; i--) { - ServiceRecord sr = proc.services.valueAt(i); + for (int i = proc.numberOfRunningServices() - 1; i >= 0; i--) { + ServiceRecord sr = proc.getRunningServiceAt(i); if (sr.whitelistManager) { proc.whitelistManager = true; break; @@ -1802,8 +1801,8 @@ public final class ActiveServices { } boolean anyClientActivities = false; - for (int i=proc.services.size()-1; i>=0 && !anyClientActivities; i--) { - ServiceRecord sr = proc.services.valueAt(i); + for (int i = proc.numberOfRunningServices() - 1; i >= 0 && !anyClientActivities; i--) { + ServiceRecord sr = proc.getRunningServiceAt(i); ArrayMap<IBinder, ArrayList<ConnectionRecord>> connections = sr.getConnections(); for (int conni = connections.size() - 1; conni >= 0 && !anyClientActivities; conni--) { ArrayList<ConnectionRecord> clist = connections.valueAt(conni); @@ -2995,7 +2994,7 @@ public final class ActiveServices { r.setProcess(app); r.restartTime = r.lastActivity = SystemClock.uptimeMillis(); - final boolean newService = app.services.add(r); + final boolean newService = app.startService(r); bumpServiceExecutingLocked(r, execInFg, "create"); mAm.updateLruProcessLocked(app, false, null); updateServiceForegroundLocked(r.app, /* oomAdj= */ false); @@ -3036,7 +3035,7 @@ public final class ActiveServices { // Cleanup. if (newService) { - app.services.remove(r); + app.stopService(r); r.setProcess(null); } @@ -3362,7 +3361,7 @@ public final class ActiveServices { synchronized (r.stats.getBatteryStats()) { r.stats.stopLaunchedLocked(); } - r.app.services.remove(r); + r.app.stopService(r); r.app.updateBoundClientUids(); if (r.whitelistManager) { updateWhitelistManagerLocked(r.app); @@ -3652,7 +3651,7 @@ public final class ActiveServices { } if (finishing) { if (r.app != null && !r.app.isPersistent()) { - r.app.services.remove(r); + r.app.stopService(r); r.app.updateBoundClientUids(); if (r.whitelistManager) { updateWhitelistManagerLocked(r.app); @@ -3748,7 +3747,7 @@ public final class ActiveServices { didSomething = true; Slog.i(TAG, " Force stopping service " + service); if (service.app != null && !service.app.isPersistent()) { - service.app.services.remove(service); + service.app.stopService(service); service.app.updateBoundClientUids(); if (service.whitelistManager) { updateWhitelistManagerLocked(service.app); @@ -3861,24 +3860,22 @@ public final class ActiveServices { if (false) { // XXX we are letting the client link to the service for // death notifications. - if (app.services.size() > 0) { - Iterator<ServiceRecord> it = app.services.iterator(); - while (it.hasNext()) { - ServiceRecord r = it.next(); - ArrayMap<IBinder, ArrayList<ConnectionRecord>> connections = r.getConnections(); - for (int conni=connections.size()-1; conni>=0; conni--) { - ArrayList<ConnectionRecord> cl = connections.valueAt(conni); - for (int i=0; i<cl.size(); i++) { - ConnectionRecord c = cl.get(i); - if (c.binding.client != app) { - try { - //c.conn.connected(r.className, null); - } catch (Exception e) { - // todo: this should be asynchronous! - Slog.w(TAG, "Exception thrown disconnected servce " - + r.shortInstanceName - + " from app " + app.processName, e); - } + int numberOfRunningServices = app.numberOfRunningServices(); + for (int sIndex = 0; sIndex < numberOfRunningServices; sIndex++) { + ServiceRecord r = app.getRunningServiceAt(sIndex); + ArrayMap<IBinder, ArrayList<ConnectionRecord>> connections = r.getConnections(); + for (int conni = connections.size() - 1; conni >= 0; conni--) { + ArrayList<ConnectionRecord> cl = connections.valueAt(conni); + for (int i = 0; i < cl.size(); i++) { + ConnectionRecord c = cl.get(i); + if (c.binding.client != app) { + try { + //c.conn.connected(r.className, null); + } catch (Exception e) { + // todo: this should be asynchronous! + Slog.w(TAG, "Exception thrown disconnected servce " + + r.shortInstanceName + + " from app " + app.processName, e); } } } @@ -3897,13 +3894,13 @@ public final class ActiveServices { app.whitelistManager = false; // Clear app state from services. - for (int i = app.services.size() - 1; i >= 0; i--) { - ServiceRecord sr = app.services.valueAt(i); + for (int i = app.numberOfRunningServices() - 1; i >= 0; i--) { + ServiceRecord sr = app.getRunningServiceAt(i); synchronized (sr.stats.getBatteryStats()) { sr.stats.stopLaunchedLocked(); } if (sr.app != app && sr.app != null && !sr.app.isPersistent()) { - sr.app.services.remove(sr); + sr.app.stopService(sr); sr.app.updateBoundClientUids(); } sr.setProcess(null); @@ -3962,13 +3959,13 @@ public final class ActiveServices { ServiceMap smap = getServiceMapLocked(app.userId); // Now do remaining service cleanup. - for (int i=app.services.size()-1; i>=0; i--) { - ServiceRecord sr = app.services.valueAt(i); + for (int i = app.numberOfRunningServices() - 1; i >= 0; i--) { + ServiceRecord sr = app.getRunningServiceAt(i); // Unless the process is persistent, this process record is going away, // so make sure the service is cleaned out of it. if (!app.isPersistent()) { - app.services.removeAt(i); + app.stopService(sr); app.updateBoundClientUids(); } @@ -4018,7 +4015,7 @@ public final class ActiveServices { } if (!allowRestart) { - app.services.clear(); + app.stopAllServices(); app.clearBoundClientUids(); // Make sure there are no more restarting services for this process. @@ -4920,8 +4917,8 @@ public final class ActiveServices { if (pr.uid != uid) { continue; } - for (int j = pr.services.size() - 1; j >= 0; j--) { - ServiceRecord r = pr.services.valueAt(j); + for (int j = pr.numberOfRunningServices() - 1; j >= 0; j--) { + ServiceRecord r = pr.getRunningServiceAt(j); if (!r.isForeground) { continue; } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 3fdf541566a5..5d7590d7a458 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -18172,7 +18172,7 @@ public class ActivityManagerService extends IActivityManager.Stub for (int i = mProcessList.mRemovedProcesses.size() - 1; i >= 0; i--) { final ProcessRecord app = mProcessList.mRemovedProcesses.get(i); if (!app.hasActivitiesOrRecentTasks() - && app.curReceivers.isEmpty() && app.services.size() == 0) { + && app.curReceivers.isEmpty() && app.numberOfRunningServices() == 0) { Slog.i( TAG, "Exiting empty application process " + app.toShortString() + " (" @@ -20045,8 +20045,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (uid == mTargetUid && isTargetOp(code)) { final long identity = Binder.clearCallingIdentity(); try { - return mAppOpsService.noteProxyOperation(code, Process.SHELL_UID, - "com.android.shell", null, uid, packageName, featureId, + return superImpl.apply(code, Process.SHELL_UID, "com.android.shell", featureId, shouldCollectAsyncNotedOp, message); } finally { Binder.restoreCallingIdentity(identity); diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java index b1fc0296518b..50d2cab0af81 100644 --- a/services/core/java/com/android/server/am/AppErrors.java +++ b/services/core/java/com/android/server/am/AppErrors.java @@ -702,10 +702,10 @@ class AppErrors { } // Bump up the crash count of any services currently running in the proc. - for (int i = app.services.size() - 1; i >= 0; i--) { + for (int i = app.numberOfRunningServices() - 1; i >= 0; i--) { // Any services running in the application need to be placed // back in the pending list. - ServiceRecord sr = app.services.valueAt(i); + ServiceRecord sr = app.getRunningServiceAt(i); // If the service was restarted a while ago, then reset crash count, else increment it. if (now > sr.restartTime + ProcessList.MIN_CRASH_INTERVAL) { sr.crashCount = 1; diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index dbad562c0271..b647818e3f7a 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -1013,9 +1013,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub @Override public void noteNetworkInterfaceType(String iface, int networkType) { enforceCallingPermission(); - synchronized (mStats) { - mStats.noteNetworkInterfaceTypeLocked(iface, networkType); - } + mStats.noteNetworkInterfaceType(iface, networkType); } @Override diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 2d6ef81faf1c..ad858533c430 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -837,7 +837,8 @@ public final class OomAdjuster { break; } - if (app.isolated && app.services.size() <= 0 && app.isolatedEntryPoint == null) { + if (app.isolated && app.numberOfRunningServices() <= 0 + && app.isolatedEntryPoint == null) { // If this is an isolated process, there are no services // running in it, and it's not a special process with a // custom entry point, then the process is no longer @@ -1446,12 +1447,12 @@ public final class OomAdjuster { } int capabilityFromFGS = 0; // capability from foreground service. - for (int is = app.services.size() - 1; + for (int is = app.numberOfRunningServices() - 1; is >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND || procState > PROCESS_STATE_TOP); is--) { - ServiceRecord s = app.services.valueAt(is); + ServiceRecord s = app.getRunningServiceAt(is); if (s.startRequested) { app.hasStartedServices = true; if (procState > PROCESS_STATE_SERVICE) { diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index e28464a27dc4..c9ee47278796 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -2181,6 +2181,17 @@ public final class ProcessList { return result; } + private boolean needsStorageDataIsolation(StorageManagerInternal storageManagerInternal, + ProcessRecord app) { + return mVoldAppDataIsolationEnabled && UserHandle.isApp(app.uid) + && !storageManagerInternal.isExternalStorageService(app.uid) + // Special mounting mode doesn't need to have data isolation as they won't + // access /mnt/user anyway. + && app.mountMode != Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE + && app.mountMode != Zygote.MOUNT_EXTERNAL_PASS_THROUGH + && app.mountMode != Zygote.MOUNT_EXTERNAL_INSTALLER; + } + private Process.ProcessStartResult startProcess(HostingRecord hostingRecord, String entryPoint, ProcessRecord app, int uid, int[] gids, int runtimeFlags, int zygotePolicyFlags, int mountExternal, String seInfo, String requiredAbi, String instructionSet, @@ -2237,8 +2248,7 @@ public final class ProcessList { int userId = UserHandle.getUserId(uid); StorageManagerInternal storageManagerInternal = LocalServices.getService( StorageManagerInternal.class); - if (mVoldAppDataIsolationEnabled && UserHandle.isApp(app.uid) - && !storageManagerInternal.isExternalStorageService(uid)) { + if (needsStorageDataIsolation(storageManagerInternal, app)) { bindMountAppStorageDirs = true; if (pkgDataInfoMap == null || !storageManagerInternal.prepareStorageDirs(userId, pkgDataInfoMap.keySet(), diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 61ebc361b6af..a1ec07cda8a8 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -261,9 +261,9 @@ class ProcessRecord implements WindowProcessListener { // Controller for error dialogs private final ErrorDialogController mDialogController = new ErrorDialogController(); // Controller for driving the process state on the window manager side. - final private WindowProcessController mWindowProcessController; + private final WindowProcessController mWindowProcessController; // all ServiceRecord running in this process - final ArraySet<ServiceRecord> services = new ArraySet<>(); + private final ArraySet<ServiceRecord> mServices = new ArraySet<>(); // services that are currently executing code (need to remain foreground). final ArraySet<ServiceRecord> executingServices = new ArraySet<>(); // All ConnectionRecord this process holds @@ -577,10 +577,10 @@ class ProcessRecord implements WindowProcessListener { pw.println(Arrays.toString(isolatedEntryPointArgs)); } mWindowProcessController.dump(pw, prefix); - if (services.size() > 0) { + if (mServices.size() > 0) { pw.print(prefix); pw.println("Services:"); - for (int i=0; i<services.size(); i++) { - pw.print(prefix); pw.print(" - "); pw.println(services.valueAt(i)); + for (int i = 0; i < mServices.size(); i++) { + pw.print(prefix); pw.print(" - "); pw.println(mServices.valueAt(i)); } } if (executingServices.size() > 0) { @@ -735,6 +735,60 @@ class ProcessRecord implements WindowProcessListener { } } + /** + * Records a service as running in the process. Note that this method does not actually start + * the service, but records the service as started for bookkeeping. + * + * @return true if the service was added, false otherwise. + */ + boolean startService(ServiceRecord record) { + if (record == null) { + return false; + } + boolean added = mServices.add(record); + if (added && record.serviceInfo != null) { + mWindowProcessController.onServiceStarted(record.serviceInfo); + } + return added; + } + + /** + * Records a service as stopped. Note that like {@link #startService(ServiceRecord)} this method + * does not actually stop the service, but records the service as stopped for bookkeeping. + * + * @return true if the service was removed, false otherwise. + */ + boolean stopService(ServiceRecord record) { + return mServices.remove(record); + } + + /** + * The same as calling {@link #stopService(ServiceRecord)} on all current running services. + */ + void stopAllServices() { + mServices.clear(); + } + + /** + * Returns the number of services added with {@link #startService(ServiceRecord)} and not yet + * removed by a call to {@link #stopService(ServiceRecord)} or {@link #stopAllServices()}. + * + * @see #startService(ServiceRecord) + * @see #stopService(ServiceRecord) + */ + int numberOfRunningServices() { + return mServices.size(); + } + + /** + * Returns the service at the specified {@code index}. + * + * @see #numberOfRunningServices() + */ + ServiceRecord getRunningServiceAt(int index) { + return mServices.valueAt(index); + } + void setCached(boolean cached) { if (mCached != cached) { mCached = cached; @@ -768,9 +822,9 @@ class ProcessRecord implements WindowProcessListener { return true; } - final int servicesSize = services.size(); + final int servicesSize = mServices.size(); for (int i = 0; i < servicesSize; i++) { - ServiceRecord r = services.valueAt(i); + ServiceRecord r = mServices.valueAt(i); if (r.isForeground) { return true; } @@ -1289,16 +1343,16 @@ class ProcessRecord implements WindowProcessListener { } void updateBoundClientUids() { - if (services.isEmpty()) { + if (mServices.isEmpty()) { clearBoundClientUids(); return; } // grab a set of clientUids of all connections of all services ArraySet<Integer> boundClientUids = new ArraySet<>(); - final int K = services.size(); - for (int j = 0; j < K; j++) { + final int serviceCount = mServices.size(); + for (int j = 0; j < serviceCount; j++) { ArrayMap<IBinder, ArrayList<ConnectionRecord>> conns = - services.valueAt(j).getConnections(); + mServices.valueAt(j).getConnections(); final int N = conns.size(); for (int conni = 0; conni < N; conni++) { ArrayList<ConnectionRecord> c = conns.valueAt(conni); diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 546025a2498f..85c41907e333 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -1877,8 +1877,10 @@ class UserController implements Handler.Callback { builder.append("; this requires "); builder.append(INTERACT_ACROSS_USERS_FULL); if (allowMode != ALLOW_FULL_ONLY) { - builder.append(" or "); - builder.append(INTERACT_ACROSS_USERS); + if (allowMode == ALLOW_NON_FULL || isSameProfileGroup) { + builder.append(" or "); + builder.append(INTERACT_ACROSS_USERS); + } if (isSameProfileGroup && allowMode == ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE) { builder.append(" or "); diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index c2c79d361996..032ad63a8570 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -30,6 +30,7 @@ import android.media.AudioRoutesInfo; import android.media.AudioSystem; import android.media.IAudioRoutesObserver; import android.media.IStrategyPreferredDeviceDispatcher; +import android.media.MediaMetrics; import android.os.Binder; import android.os.Handler; import android.os.IBinder; @@ -668,6 +669,13 @@ import java.io.PrintWriter; } AudioService.sForceUseLogger.log( new AudioServiceEvents.ForceUseEvent(useCase, config, eventSource)); + new MediaMetrics.Item(MediaMetrics.Name.AUDIO_FORCE_USE + MediaMetrics.SEPARATOR + + AudioSystem.forceUseUsageToString(useCase)) + .set(MediaMetrics.Property.EVENT, "onSetForceUse") + .set(MediaMetrics.Property.FORCE_USE_DUE_TO, eventSource) + .set(MediaMetrics.Property.FORCE_USE_MODE, + AudioSystem.forceUseConfigToString(config)) + .record(); AudioSystem.setForceUse(useCase, config); } diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index c17ed3e292ef..3e97a1e136c6 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -33,6 +33,7 @@ import android.media.AudioRoutesInfo; import android.media.AudioSystem; import android.media.IAudioRoutesObserver; import android.media.IStrategyPreferredDeviceDispatcher; +import android.media.MediaMetrics; import android.os.Binder; import android.os.RemoteCallbackList; import android.os.RemoteException; @@ -64,10 +65,69 @@ public class AudioDeviceInventory { // lock to synchronize all access to mConnectedDevices and mApmConnectedDevices private final Object mDevicesLock = new Object(); + //Audio Analytics ids. + private static final String mMetricsId = "audio.device."; + // List of connected devices // Key for map created from DeviceInfo.makeDeviceListKey() @GuardedBy("mDevicesLock") - private final LinkedHashMap<String, DeviceInfo> mConnectedDevices = new LinkedHashMap<>(); + private final LinkedHashMap<String, DeviceInfo> mConnectedDevices = new LinkedHashMap<>() { + @Override + public DeviceInfo put(String key, DeviceInfo value) { + final DeviceInfo result = super.put(key, value); + record("put", true /* connected */, key, value); + return result; + } + + @Override + public DeviceInfo putIfAbsent(String key, DeviceInfo value) { + final DeviceInfo result = super.putIfAbsent(key, value); + if (result == null) { + record("putIfAbsent", true /* connected */, key, value); + } + return result; + } + + @Override + public DeviceInfo remove(Object key) { + final DeviceInfo result = super.remove(key); + if (result != null) { + record("remove", false /* connected */, (String) key, result); + } + return result; + } + + @Override + public boolean remove(Object key, Object value) { + final boolean result = super.remove(key, value); + if (result) { + record("remove", false /* connected */, (String) key, (DeviceInfo) value); + } + return result; + } + + // Not overridden + // clear + // compute + // computeIfAbsent + // computeIfPresent + // merge + // putAll + // replace + // replaceAll + private void record(String event, boolean connected, String key, DeviceInfo value) { + // DeviceInfo - int mDeviceType; + // DeviceInfo - int mDeviceCodecFormat; + new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE + + MediaMetrics.SEPARATOR + AudioSystem.getDeviceName(value.mDeviceType)) + .set(MediaMetrics.Property.ADDRESS, value.mDeviceAddress) + .set(MediaMetrics.Property.EVENT, event) + .set(MediaMetrics.Property.NAME, value.mDeviceName) + .set(MediaMetrics.Property.STATE, connected + ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED) + .record(); + } + }; // List of devices actually connected to AudioPolicy (through AudioSystem), only one // by device type, which is used as the key, value is the DeviceInfo generated key. @@ -236,6 +296,16 @@ public class AudioDeviceInventory { + " codec=" + a2dpCodec + " vol=" + a2dpVolume)); + new MediaMetrics.Item(mMetricsId + "a2dp") + .set(MediaMetrics.Property.ADDRESS, address) + .set(MediaMetrics.Property.ENCODING, AudioSystem.audioFormatToString(a2dpCodec)) + .set(MediaMetrics.Property.EVENT, "onSetA2dpSinkConnectionState") + .set(MediaMetrics.Property.INDEX, a2dpVolume) + .set(MediaMetrics.Property.STATE, + state == BluetoothProfile.STATE_CONNECTED + ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED) + .record(); + synchronized (mDevicesLock) { final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, btDevice.getAddress()); @@ -284,6 +354,15 @@ public class AudioDeviceInventory { final DeviceInfo di = mConnectedDevices.get(key); boolean isConnected = di != null; + new MediaMetrics.Item(mMetricsId + "onSetA2dpSourceConnectionState") + .set(MediaMetrics.Property.ADDRESS, address) + .set(MediaMetrics.Property.DEVICE, + AudioSystem.getDeviceName(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP)) + .set(MediaMetrics.Property.STATE, + state == BluetoothProfile.STATE_CONNECTED + ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED) + .record(); + if (isConnected && state != BluetoothProfile.STATE_CONNECTED) { makeA2dpSrcUnavailable(address); } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) { @@ -301,6 +380,17 @@ public class AudioDeviceInventory { AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( "onSetHearingAidConnectionState addr=" + address)); + new MediaMetrics.Item(mMetricsId + "onSetHearingAidConnectionState") + .set(MediaMetrics.Property.ADDRESS, address) + .set(MediaMetrics.Property.DEVICE, + AudioSystem.getDeviceName(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP)) + .set(MediaMetrics.Property.STATE, + state == BluetoothProfile.STATE_CONNECTED + ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED) + .set(MediaMetrics.Property.STREAM_TYPE, + AudioSystem.streamToString(streamType)) + .record(); + synchronized (mDevicesLock) { final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID, btDevice.getAddress()); @@ -317,10 +407,15 @@ public class AudioDeviceInventory { } @GuardedBy("AudioDeviceBroker.mDeviceStateLock") - /*package*/ void onBluetoothA2dpActiveDeviceChange( + /*package*/ void onBluetoothA2dpActiveDeviceChange( @NonNull BtHelper.BluetoothA2dpDeviceInfo btInfo, int event) { + MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + + "onBluetoothA2dpActiveDeviceChange") + .set(MediaMetrics.Property.EVENT, BtHelper.a2dpDeviceEventToString(event)); + final BluetoothDevice btDevice = btInfo.getBtDevice(); if (btDevice == null) { + mmi.set(MediaMetrics.Property.EARLY_RETURN, "btDevice null").record(); return; } if (AudioService.DEBUG_DEVICES) { @@ -341,6 +436,8 @@ public class AudioDeviceInventory { if (mDeviceBroker.hasScheduledA2dpSinkConnectionState(btDevice)) { AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( "A2dp config change ignored (scheduled connection change)")); + mmi.set(MediaMetrics.Property.EARLY_RETURN, "A2dp config change ignored") + .record(); return; } final String key = DeviceInfo.makeDeviceListKey( @@ -348,9 +445,16 @@ public class AudioDeviceInventory { final DeviceInfo di = mConnectedDevices.get(key); if (di == null) { Log.e(TAG, "invalid null DeviceInfo in onBluetoothA2dpActiveDeviceChange"); + mmi.set(MediaMetrics.Property.EARLY_RETURN, "null DeviceInfo").record(); return; } + mmi.set(MediaMetrics.Property.ADDRESS, address) + .set(MediaMetrics.Property.ENCODING, + AudioSystem.audioFormatToString(a2dpCodec)) + .set(MediaMetrics.Property.INDEX, a2dpVolume) + .set(MediaMetrics.Property.NAME, di.mDeviceName); + if (event == BtHelper.EVENT_ACTIVE_DEVICE_CHANGE) { // Device is connected if (a2dpVolume != -1) { @@ -388,6 +492,7 @@ public class AudioDeviceInventory { + address + " codec=" + a2dpCodec).printLog(TAG)); } } + mmi.record(); } /*package*/ void onMakeA2dpDeviceUnavailableNow(String address, int a2dpCodec) { @@ -399,6 +504,9 @@ public class AudioDeviceInventory { /*package*/ void onReportNewRoutes() { int n = mRoutesObservers.beginBroadcast(); if (n > 0) { + new MediaMetrics.Item(mMetricsId + "onReportNewRoutes") + .set(MediaMetrics.Property.OBSERVERS, n) + .record(); AudioRoutesInfo routes; synchronized (mCurAudioRoutes) { routes = new AudioRoutesInfo(mCurAudioRoutes); @@ -428,6 +536,13 @@ public class AudioDeviceInventory { AudioDeviceInventory.WiredDeviceConnectionState wdcs) { AudioService.sDeviceLogger.log(new AudioServiceEvents.WiredDevConnectEvent(wdcs)); + MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + + "onSetWiredDeviceConnectionState") + .set(MediaMetrics.Property.ADDRESS, wdcs.mAddress) + .set(MediaMetrics.Property.DEVICE, AudioSystem.getDeviceName(wdcs.mType)) + .set(MediaMetrics.Property.STATE, + wdcs.mState == AudioService.CONNECTION_STATE_DISCONNECTED + ? MediaMetrics.Value.DISCONNECTED : MediaMetrics.Value.CONNECTED); synchronized (mDevicesLock) { if ((wdcs.mState == AudioService.CONNECTION_STATE_DISCONNECTED) && DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET.contains(wdcs.mType)) { @@ -438,6 +553,8 @@ public class AudioDeviceInventory { if (!handleDeviceConnection(wdcs.mState == AudioService.CONNECTION_STATE_CONNECTED, wdcs.mType, wdcs.mAddress, wdcs.mName)) { // change of connection state failed, bailout + mmi.set(MediaMetrics.Property.EARLY_RETURN, "change of connection state failed") + .record(); return; } if (wdcs.mState != AudioService.CONNECTION_STATE_DISCONNECTED) { @@ -453,15 +570,20 @@ public class AudioDeviceInventory { sendDeviceConnectionIntent(wdcs.mType, wdcs.mState, wdcs.mAddress, wdcs.mName); updateAudioRoutes(wdcs.mType, wdcs.mState); } + mmi.record(); } /*package*/ void onToggleHdmi() { + MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + "onToggleHdmi") + .set(MediaMetrics.Property.DEVICE, + AudioSystem.getDeviceName(AudioSystem.DEVICE_OUT_HDMI)); synchronized (mDevicesLock) { // Is HDMI connected? final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HDMI, ""); final DeviceInfo di = mConnectedDevices.get(key); if (di == null) { Log.e(TAG, "invalid null DeviceInfo in onToggleHdmi"); + mmi.set(MediaMetrics.Property.EARLY_RETURN, "invalid null DeviceInfo").record(); return; } // Toggle HDMI to retrigger broadcast with proper formats. @@ -472,6 +594,7 @@ public class AudioDeviceInventory { AudioSystem.DEVICE_STATE_AVAILABLE, "", "", "android"); // reconnect } + mmi.record(); } /*package*/ void onSaveSetPreferredDevice(int strategy, @NonNull AudioDeviceAttributes device) { @@ -535,6 +658,12 @@ public class AudioDeviceInventory { + Integer.toHexString(device) + " address:" + address + " name:" + deviceName + ")"); } + MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + "handleDeviceConnection") + .set(MediaMetrics.Property.ADDRESS, address) + .set(MediaMetrics.Property.DEVICE, AudioSystem.getDeviceName(device)) + .set(MediaMetrics.Property.MODE, connect + ? MediaMetrics.Value.CONNECT : MediaMetrics.Value.DISCONNECT) + .set(MediaMetrics.Property.NAME, deviceName); synchronized (mDevicesLock) { final String deviceKey = DeviceInfo.makeDeviceListKey(device, address); if (AudioService.DEBUG_DEVICES) { @@ -550,13 +679,18 @@ public class AudioDeviceInventory { AudioSystem.DEVICE_STATE_AVAILABLE, address, deviceName, AudioSystem.AUDIO_FORMAT_DEFAULT); if (res != AudioSystem.AUDIO_STATUS_OK) { - Slog.e(TAG, "not connecting device 0x" + Integer.toHexString(device) - + " due to command error " + res); + final String reason = "not connecting device 0x" + Integer.toHexString(device) + + " due to command error " + res; + Slog.e(TAG, reason); + mmi.set(MediaMetrics.Property.EARLY_RETURN, reason) + .set(MediaMetrics.Property.STATE, MediaMetrics.Value.DISCONNECTED) + .record(); return false; } mConnectedDevices.put(deviceKey, new DeviceInfo( device, deviceName, address, AudioSystem.AUDIO_FORMAT_DEFAULT)); mDeviceBroker.postAccessoryPlugMediaUnmute(device); + mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.CONNECTED).record(); return true; } else if (!connect && isConnected) { mAudioSystem.setDeviceConnectionState(device, @@ -564,11 +698,13 @@ public class AudioDeviceInventory { AudioSystem.AUDIO_FORMAT_DEFAULT); // always remove even if disconnection failed mConnectedDevices.remove(deviceKey); + mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.CONNECTED).record(); return true; } Log.w(TAG, "handleDeviceConnection() failed, deviceKey=" + deviceKey + ", deviceSpec=" + di + ", connect=" + connect); } + mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.DISCONNECTED).record(); return false; } @@ -582,6 +718,8 @@ public class AudioDeviceInventory { toRemove.add(deviceInfo.mDeviceAddress); } }); + new MediaMetrics.Item(mMetricsId + "disconnectA2dp") + .record(); if (toRemove.size() > 0) { final int delay = checkSendBecomingNoisyIntentInt( AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, @@ -602,6 +740,8 @@ public class AudioDeviceInventory { toRemove.add(deviceInfo.mDeviceAddress); } }); + new MediaMetrics.Item(mMetricsId + "disconnectA2dpSink") + .record(); toRemove.stream().forEach(deviceAddress -> makeA2dpSrcUnavailable(deviceAddress)); } } @@ -615,6 +755,8 @@ public class AudioDeviceInventory { toRemove.add(deviceInfo.mDeviceAddress); } }); + new MediaMetrics.Item(mMetricsId + "disconnectHearingAid") + .record(); if (toRemove.size() > 0) { final int delay = checkSendBecomingNoisyIntentInt( AudioSystem.DEVICE_OUT_HEARING_AID, 0, AudioSystem.DEVICE_NONE); @@ -743,6 +885,8 @@ public class AudioDeviceInventory { final int res = mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, AudioSystem.DEVICE_STATE_AVAILABLE, address, name, a2dpCodec); + // TODO: log in MediaMetrics once distinction between connection failure and + // double connection is made. if (res != AudioSystem.AUDIO_STATUS_OK) { AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( "APM failed to make available A2DP device addr=" + address @@ -771,7 +915,12 @@ public class AudioDeviceInventory { @GuardedBy("mDevicesLock") private void makeA2dpDeviceUnavailableNow(String address, int a2dpCodec) { + MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + "a2dp." + address) + .set(MediaMetrics.Property.ENCODING, AudioSystem.audioFormatToString(a2dpCodec)) + .set(MediaMetrics.Property.EVENT, "makeA2dpDeviceUnavailableNow"); + if (address == null) { + mmi.set(MediaMetrics.Property.EARLY_RETURN, "address null").record(); return; } final String deviceToRemoveKey = @@ -783,6 +932,9 @@ public class AudioDeviceInventory { // removing A2DP device not currently used by AudioPolicy, log but don't act on it AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent( "A2DP device " + address + " made unavailable, was not used")).printLog(TAG)); + mmi.set(MediaMetrics.Property.EARLY_RETURN, + "A2DP device made unavailable, was not used") + .record(); return; } @@ -804,6 +956,7 @@ public class AudioDeviceInventory { mApmConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP); // Remove A2DP routes as well setCurrentAudioRouteNameIfPossible(null); + mmi.record(); } @GuardedBy("mDevicesLock") @@ -862,6 +1015,14 @@ public class AudioDeviceInventory { mDeviceBroker.postApplyVolumeOnDevice(streamType, AudioSystem.DEVICE_OUT_HEARING_AID, "makeHearingAidDeviceAvailable"); setCurrentAudioRouteNameIfPossible(name); + new MediaMetrics.Item(mMetricsId + "makeHearingAidDeviceAvailable") + .set(MediaMetrics.Property.ADDRESS, address != null ? address : "") + .set(MediaMetrics.Property.DEVICE, + AudioSystem.getDeviceName(AudioSystem.DEVICE_OUT_HEARING_AID)) + .set(MediaMetrics.Property.NAME, name) + .set(MediaMetrics.Property.STREAM_TYPE, + AudioSystem.streamToString(streamType)) + .record(); } @GuardedBy("mDevicesLock") @@ -873,6 +1034,11 @@ public class AudioDeviceInventory { DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID, address)); // Remove Hearing Aid routes as well setCurrentAudioRouteNameIfPossible(null); + new MediaMetrics.Item(mMetricsId + "makeHearingAidDeviceUnavailable") + .set(MediaMetrics.Property.ADDRESS, address != null ? address : "") + .set(MediaMetrics.Property.DEVICE, + AudioSystem.getDeviceName(AudioSystem.DEVICE_OUT_HEARING_AID)) + .record(); } @GuardedBy("mDevicesLock") @@ -919,10 +1085,18 @@ public class AudioDeviceInventory { @GuardedBy("mDevicesLock") private int checkSendBecomingNoisyIntentInt(int device, @AudioService.ConnectionState int state, int musicDevice) { + MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + + "checkSendBecomingNoisyIntentInt") + .set(MediaMetrics.Property.DEVICE, AudioSystem.getDeviceName(device)) + .set(MediaMetrics.Property.STATE, + state == AudioService.CONNECTION_STATE_CONNECTED + ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED); if (state != AudioService.CONNECTION_STATE_DISCONNECTED) { + mmi.set(MediaMetrics.Property.DELAY_MS, 0).record(); // OK to return return 0; } if (!BECOMING_NOISY_INTENT_DEVICES_SET.contains(device)) { + mmi.set(MediaMetrics.Property.DELAY_MS, 0).record(); // OK to return return 0; } int delay = 0; @@ -950,12 +1124,14 @@ public class AudioDeviceInventory { // the pausing of some apps that are playing remotely AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent( "dropping ACTION_AUDIO_BECOMING_NOISY")).printLog(TAG)); + mmi.set(MediaMetrics.Property.DELAY_MS, 0).record(); // OK to return return 0; } mDeviceBroker.postBroadcastBecomingNoisy(); delay = AudioService.BECOMING_NOISY_DELAY_MS; } + mmi.set(MediaMetrics.Property.DELAY_MS, delay).record(); return delay; } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index f840f2d359d5..7cac376ea7ae 100755 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -88,6 +88,7 @@ import android.media.IStrategyPreferredDeviceDispatcher; import android.media.IVolumeController; import android.media.MediaExtractor; import android.media.MediaFormat; +import android.media.MediaMetrics; import android.media.PlayerBase; import android.media.VolumePolicy; import android.media.audiofx.AudioEffect; @@ -1880,6 +1881,16 @@ public class AudioService extends IAudioService.Stub synchronized (mExtVolumeControllerLock) { extVolCtlr = mExtVolumeController; } + new MediaMetrics.Item(mMetricsId + "adjustSuggestedStreamVolume") + .setUid(Binder.getCallingUid()) + .set(MediaMetrics.Property.CALLING_PACKAGE, callingPackage) + .set(MediaMetrics.Property.CLIENT_NAME, caller) + .set(MediaMetrics.Property.DIRECTION, direction > 0 + ? MediaMetrics.Value.UP : MediaMetrics.Value.DOWN) + .set(MediaMetrics.Property.EXTERNAL, extVolCtlr != null + ? MediaMetrics.Value.YES : MediaMetrics.Value.NO) + .set(MediaMetrics.Property.FLAGS, flags) + .record(); if (extVolCtlr != null) { sendMsg(mAudioHandler, MSG_NOTIFY_VOL_EVENT, SENDMSG_QUEUE, direction, 0 /*ignored*/, @@ -3141,21 +3152,32 @@ public class AudioService extends IAudioService.Stub if (uid == android.os.Process.SYSTEM_UID) { uid = UserHandle.getUid(userId, UserHandle.getAppId(uid)); } + MediaMetrics.Item mmi = new MediaMetrics.Item(MediaMetrics.Name.AUDIO_MIC) + .setUid(uid) + .set(MediaMetrics.Property.CALLING_PACKAGE, callingPackage) + .set(MediaMetrics.Property.EVENT, "setMicrophoneMute") + .set(MediaMetrics.Property.REQUEST, on + ? MediaMetrics.Value.MUTE : MediaMetrics.Value.UNMUTE); + // If OP_MUTE_MICROPHONE is set, disallow unmuting. if (!on && mAppOps.noteOp(AppOpsManager.OP_MUTE_MICROPHONE, uid, callingPackage) != AppOpsManager.MODE_ALLOWED) { + mmi.set(MediaMetrics.Property.EARLY_RETURN, "disallow unmuting").record(); return; } if (!checkAudioSettingsPermission("setMicrophoneMute()")) { + mmi.set(MediaMetrics.Property.EARLY_RETURN, "!checkAudioSettingsPermission").record(); return; } if (userId != UserHandle.getCallingUserId() && mContext.checkCallingOrSelfPermission( android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) != PackageManager.PERMISSION_GRANTED) { + mmi.set(MediaMetrics.Property.EARLY_RETURN, "permission").record(); return; } mMicMuteFromApi = on; + mmi.record(); // record now, the no caller check will set the mute state. setMicrophoneMuteNoCallerCheck(userId); } @@ -3167,6 +3189,12 @@ public class AudioService extends IAudioService.Stub return; } mMicMuteFromSwitch = on; + new MediaMetrics.Item(MediaMetrics.Name.AUDIO_MIC) + .setUid(userId) + .set(MediaMetrics.Property.EVENT, "setMicrophoneMuteFromSwitch") + .set(MediaMetrics.Property.REQUEST, on + ? MediaMetrics.Value.MUTE : MediaMetrics.Value.UNMUTE) + .record(); setMicrophoneMuteNoCallerCheck(userId); } @@ -3207,6 +3235,17 @@ public class AudioService extends IAudioService.Stub Log.e(TAG, "Error changing mic mute state to " + muted + " current:" + mMicMuteFromSystemCached); } + + new MediaMetrics.Item(MediaMetrics.Name.AUDIO_MIC) + .setUid(userId) + .set(MediaMetrics.Property.EVENT, "setMicrophoneMuteNoCallerCheck") + .set(MediaMetrics.Property.MUTE, mMicMuteFromSystemCached + ? MediaMetrics.Value.ON : MediaMetrics.Value.OFF) + .set(MediaMetrics.Property.REQUEST, muted + ? MediaMetrics.Value.MUTE : MediaMetrics.Value.UNMUTE) + .set(MediaMetrics.Property.STATUS, ret) + .record(); + try { // send the intent even if there was a failure to change the actual mute state: // the AudioManager.setMicrophoneMute API doesn't have a return value to @@ -3941,10 +3980,20 @@ public class AudioService extends IAudioService.Stub } // for logging only + final int uid = Binder.getCallingUid(); + final int pid = Binder.getCallingPid(); final String eventSource = new StringBuilder("setSpeakerphoneOn(").append(on) - .append(") from u/pid:").append(Binder.getCallingUid()).append("/") - .append(Binder.getCallingPid()).toString(); + .append(") from u/pid:").append(uid).append("/") + .append(pid).toString(); final boolean stateChanged = mDeviceBroker.setSpeakerphoneOn(on, eventSource); + new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE + + MediaMetrics.SEPARATOR + "setSpeakerphoneOn") + .setUid(uid) + .setPid(pid) + .set(MediaMetrics.Property.STATE, on + ? MediaMetrics.Value.ON : MediaMetrics.Value.OFF) + .record(); + if (stateChanged) { final long ident = Binder.clearCallingIdentity(); try { @@ -3975,9 +4024,19 @@ public class AudioService extends IAudioService.Stub } // for logging only + final int uid = Binder.getCallingUid(); + final int pid = Binder.getCallingPid(); final String eventSource = new StringBuilder("setBluetoothScoOn(").append(on) - .append(") from u/pid:").append(Binder.getCallingUid()).append("/") - .append(Binder.getCallingPid()).toString(); + .append(") from u/pid:").append(uid).append("/").append(pid).toString(); + + //bt sco + new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE + + MediaMetrics.SEPARATOR + "setBluetoothScoOn") + .setUid(uid) + .setPid(pid) + .set(MediaMetrics.Property.STATE, on + ? MediaMetrics.Value.ON : MediaMetrics.Value.OFF) + .record(); mDeviceBroker.setBluetoothScoOn(on, eventSource); } @@ -3993,9 +4052,20 @@ public class AudioService extends IAudioService.Stub /** @see AudioManager#setBluetoothA2dpOn(boolean) */ public void setBluetoothA2dpOn(boolean on) { // for logging only + final int uid = Binder.getCallingUid(); + final int pid = Binder.getCallingPid(); final String eventSource = new StringBuilder("setBluetoothA2dpOn(").append(on) - .append(") from u/pid:").append(Binder.getCallingUid()).append("/") - .append(Binder.getCallingPid()).toString(); + .append(") from u/pid:").append(uid).append("/") + .append(pid).toString(); + + new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE + + MediaMetrics.SEPARATOR + "setBluetoothA2dpOn") + .setUid(uid) + .setPid(pid) + .set(MediaMetrics.Property.STATE, on + ? MediaMetrics.Value.ON : MediaMetrics.Value.OFF) + .record(); + mDeviceBroker.setBluetoothA2dpOn_Async(on, eventSource); } @@ -4006,31 +4076,59 @@ public class AudioService extends IAudioService.Stub /** @see AudioManager#startBluetoothSco() */ public void startBluetoothSco(IBinder cb, int targetSdkVersion) { + final int uid = Binder.getCallingUid(); + final int pid = Binder.getCallingPid(); final int scoAudioMode = (targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2) ? BtHelper.SCO_MODE_VIRTUAL_CALL : BtHelper.SCO_MODE_UNDEFINED; final String eventSource = new StringBuilder("startBluetoothSco()") - .append(") from u/pid:").append(Binder.getCallingUid()).append("/") - .append(Binder.getCallingPid()).toString(); + .append(") from u/pid:").append(uid).append("/") + .append(pid).toString(); + + new MediaMetrics.Item(MediaMetrics.Name.AUDIO_BLUETOOTH) + .setUid(uid) + .setPid(pid) + .set(MediaMetrics.Property.EVENT, "startBluetoothSco") + .set(MediaMetrics.Property.SCO_AUDIO_MODE, + BtHelper.scoAudioModeToString(scoAudioMode)) + .record(); startBluetoothScoInt(cb, scoAudioMode, eventSource); + } /** @see AudioManager#startBluetoothScoVirtualCall() */ public void startBluetoothScoVirtualCall(IBinder cb) { + final int uid = Binder.getCallingUid(); + final int pid = Binder.getCallingPid(); final String eventSource = new StringBuilder("startBluetoothScoVirtualCall()") - .append(") from u/pid:").append(Binder.getCallingUid()).append("/") - .append(Binder.getCallingPid()).toString(); + .append(") from u/pid:").append(uid).append("/") + .append(pid).toString(); + + new MediaMetrics.Item(MediaMetrics.Name.AUDIO_BLUETOOTH) + .setUid(uid) + .setPid(pid) + .set(MediaMetrics.Property.EVENT, "startBluetoothScoVirtualCall") + .set(MediaMetrics.Property.SCO_AUDIO_MODE, + BtHelper.scoAudioModeToString(BtHelper.SCO_MODE_VIRTUAL_CALL)) + .record(); startBluetoothScoInt(cb, BtHelper.SCO_MODE_VIRTUAL_CALL, eventSource); } void startBluetoothScoInt(IBinder cb, int scoAudioMode, @NonNull String eventSource) { + MediaMetrics.Item mmi = new MediaMetrics.Item(MediaMetrics.Name.AUDIO_BLUETOOTH) + .set(MediaMetrics.Property.EVENT, "startBluetoothScoInt") + .set(MediaMetrics.Property.SCO_AUDIO_MODE, + BtHelper.scoAudioModeToString(scoAudioMode)); + if (!checkAudioSettingsPermission("startBluetoothSco()") || !mSystemReady) { + mmi.set(MediaMetrics.Property.EARLY_RETURN, "permission or systemReady").record(); return; } synchronized (mDeviceBroker.mSetModeLock) { mDeviceBroker.startBluetoothScoForClient_Sync(cb, scoAudioMode, eventSource); } + mmi.record(); } /** @see AudioManager#stopBluetoothSco() */ @@ -4039,12 +4137,21 @@ public class AudioService extends IAudioService.Stub !mSystemReady) { return; } + final int uid = Binder.getCallingUid(); + final int pid = Binder.getCallingPid(); final String eventSource = new StringBuilder("stopBluetoothSco()") - .append(") from u/pid:").append(Binder.getCallingUid()).append("/") - .append(Binder.getCallingPid()).toString(); + .append(") from u/pid:").append(uid).append("/") + .append(pid).toString(); synchronized (mDeviceBroker.mSetModeLock) { mDeviceBroker.stopBluetoothScoForClient_Sync(cb, eventSource); } + new MediaMetrics.Item(MediaMetrics.Name.AUDIO_BLUETOOTH) + .setUid(uid) + .setPid(pid) + .set(MediaMetrics.Property.EVENT, "stopBluetoothSco") + .set(MediaMetrics.Property.SCO_AUDIO_MODE, + BtHelper.scoAudioModeToString(BtHelper.SCO_MODE_UNDEFINED)) + .record(); } @@ -4806,6 +4913,14 @@ public class AudioService extends IAudioService.Stub && state != CONNECTION_STATE_DISCONNECTED) { throw new IllegalArgumentException("Invalid state " + state); } + new MediaMetrics.Item(mMetricsId + "setWiredDeviceConnectionState") + .set(MediaMetrics.Property.ADDRESS, address) + .set(MediaMetrics.Property.CLIENT_NAME, caller) + .set(MediaMetrics.Property.DEVICE, AudioSystem.getDeviceName(type)) + .set(MediaMetrics.Property.NAME, name) + .set(MediaMetrics.Property.STATE, + state == CONNECTION_STATE_CONNECTED ? "connected" : "disconnected") + .record(); mDeviceBroker.setWiredDeviceConnectionState(type, state, address, name, caller); } @@ -5266,7 +5381,32 @@ public class AudioService extends IAudioService.Stub private String mVolumeIndexSettingName; private int mObservedDevices; - private final SparseIntArray mIndexMap = new SparseIntArray(8); + private final SparseIntArray mIndexMap = new SparseIntArray(8) { + @Override + public void put(int key, int value) { + super.put(key, value); + record("put", key, value); + } + @Override + public void setValueAt(int index, int value) { + super.setValueAt(index, value); + record("setValueAt", keyAt(index), value); + } + + // Record all changes in the VolumeStreamState + private void record(String event, int key, int value) { + final String device = key == AudioSystem.DEVICE_OUT_DEFAULT ? "default" + : AudioSystem.getOutputDeviceName(key); + new MediaMetrics.Item(MediaMetrics.Name.AUDIO_VOLUME + MediaMetrics.SEPARATOR + + AudioSystem.streamToString(mStreamType) + + "." + device) + .set(MediaMetrics.Property.EVENT, event) + .set(MediaMetrics.Property.INDEX, value) + .set(MediaMetrics.Property.MIN_INDEX, mIndexMin) + .set(MediaMetrics.Property.MAX_INDEX, mIndexMax) + .record(); + } + }; private final Intent mVolumeChanged; private final Intent mStreamDevicesChanged; @@ -5949,6 +6089,13 @@ public class AudioService extends IAudioService.Stub + eventSource); break; } + new MediaMetrics.Item(MediaMetrics.Name.AUDIO_FORCE_USE + + MediaMetrics.SEPARATOR + AudioSystem.forceUseUsageToString(useCase)) + .set(MediaMetrics.Property.EVENT, "setForceUse") + .set(MediaMetrics.Property.FORCE_USE_DUE_TO, eventSource) + .set(MediaMetrics.Property.FORCE_USE_MODE, + AudioSystem.forceUseConfigToString(config)) + .record(); sForceUseLogger.log( new AudioServiceEvents.ForceUseEvent(useCase, config, eventSource)); AudioSystem.setForceUse(useCase, config); @@ -6450,23 +6597,42 @@ public class AudioService extends IAudioService.Stub public int requestAudioFocus(AudioAttributes aa, int durationHint, IBinder cb, IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags, IAudioPolicyCallback pcb, int sdk) { + final int uid = Binder.getCallingUid(); + MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + "focus") + .setUid(uid) + //.putInt("durationHint", durationHint) + .set(MediaMetrics.Property.CALLING_PACKAGE, callingPackageName) + .set(MediaMetrics.Property.CLIENT_NAME, clientId) + .set(MediaMetrics.Property.EVENT, "requestAudioFocus") + .set(MediaMetrics.Property.FLAGS, flags); + // permission checks if (aa != null && !isValidAudioAttributesUsage(aa)) { - Log.w(TAG, "Request using unsupported usage."); + final String reason = "Request using unsupported usage"; + Log.w(TAG, reason); + mmi.set(MediaMetrics.Property.EARLY_RETURN, reason) + .record(); return AudioManager.AUDIOFOCUS_REQUEST_FAILED; } if ((flags & AudioManager.AUDIOFOCUS_FLAG_LOCK) == AudioManager.AUDIOFOCUS_FLAG_LOCK) { if (AudioSystem.IN_VOICE_COMM_FOCUS_ID.equals(clientId)) { if (PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission( android.Manifest.permission.MODIFY_PHONE_STATE)) { - Log.e(TAG, "Invalid permission to (un)lock audio focus", new Exception()); + final String reason = "Invalid permission to (un)lock audio focus"; + Log.e(TAG, reason, new Exception()); + mmi.set(MediaMetrics.Property.EARLY_RETURN, reason) + .record(); return AudioManager.AUDIOFOCUS_REQUEST_FAILED; } } else { // only a registered audio policy can be used to lock focus synchronized (mAudioPolicies) { if (!mAudioPolicies.containsKey(pcb.asBinder())) { - Log.e(TAG, "Invalid unregistered AudioPolicy to (un)lock audio focus"); + final String reason = + "Invalid unregistered AudioPolicy to (un)lock audio focus"; + Log.e(TAG, reason); + mmi.set(MediaMetrics.Property.EARLY_RETURN, reason) + .record(); return AudioManager.AUDIOFOCUS_REQUEST_FAILED; } } @@ -6474,25 +6640,40 @@ public class AudioService extends IAudioService.Stub } if (callingPackageName == null || clientId == null || aa == null) { - Log.e(TAG, "Invalid null parameter to request audio focus"); + final String reason = "Invalid null parameter to request audio focus"; + Log.e(TAG, reason); + mmi.set(MediaMetrics.Property.EARLY_RETURN, reason) + .record(); return AudioManager.AUDIOFOCUS_REQUEST_FAILED; } - + mmi.record(); return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd, clientId, callingPackageName, flags, sdk, - forceFocusDuckingForAccessibility(aa, durationHint, Binder.getCallingUid())); + forceFocusDuckingForAccessibility(aa, durationHint, uid)); } public int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId, AudioAttributes aa, String callingPackageName) { + MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + "focus") + .set(MediaMetrics.Property.CALLING_PACKAGE, callingPackageName) + .set(MediaMetrics.Property.CLIENT_NAME, clientId) + .set(MediaMetrics.Property.EVENT, "abandonAudioFocus"); + if (aa != null && !isValidAudioAttributesUsage(aa)) { Log.w(TAG, "Request using unsupported usage."); + mmi.set(MediaMetrics.Property.EARLY_RETURN, "unsupported usage").record(); + return AudioManager.AUDIOFOCUS_REQUEST_FAILED; } + mmi.record(); return mMediaFocusControl.abandonAudioFocus(fd, clientId, aa, callingPackageName); } public void unregisterAudioFocusClient(String clientId) { + new MediaMetrics.Item(mMetricsId + "focus") + .set(MediaMetrics.Property.CLIENT_NAME, clientId) + .set(MediaMetrics.Property.EVENT, "unregisterAudioFocusClient") + .record(); mMediaFocusControl.unregisterAudioFocusClient(clientId); } @@ -7066,6 +7247,12 @@ public class AudioService extends IAudioService.Stub } } + /** + * Audio Analytics ids. + */ + private static final String mMetricsId = MediaMetrics.Name.AUDIO_SERVICE + + MediaMetrics.SEPARATOR; + private static String safeMediaVolumeStateToString(int state) { switch(state) { case SAFE_MEDIA_VOLUME_NOT_CONFIGURED: return "SAFE_MEDIA_VOLUME_NOT_CONFIGURED"; diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java index add620e74df0..591356746e38 100644 --- a/services/core/java/com/android/server/audio/AudioServiceEvents.java +++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java @@ -19,6 +19,7 @@ package com.android.server.audio; import android.media.AudioAttributes; import android.media.AudioManager; import android.media.AudioSystem; +import android.media.MediaMetrics; import com.android.server.audio.AudioDeviceInventory.WiredDeviceConnectionState; @@ -120,6 +121,7 @@ public class AudioServiceEvents { mCaller = caller; mGroupName = null; mAudioAttributes = null; + logMetricEvent(); } /** used for VOL_SET_HEARING_AID_VOL*/ @@ -132,6 +134,7 @@ public class AudioServiceEvents { mCaller = null; mGroupName = null; mAudioAttributes = null; + logMetricEvent(); } /** used for VOL_SET_AVRCP_VOL */ @@ -144,6 +147,7 @@ public class AudioServiceEvents { mCaller = null; mGroupName = null; mAudioAttributes = null; + logMetricEvent(); } /** used for VOL_VOICE_ACTIVITY_HEARING_AID */ @@ -156,6 +160,7 @@ public class AudioServiceEvents { mCaller = null; mGroupName = null; mAudioAttributes = null; + logMetricEvent(); } /** used for VOL_MODE_CHANGE_HEARING_AID */ @@ -168,6 +173,7 @@ public class AudioServiceEvents { mCaller = null; mGroupName = null; mAudioAttributes = null; + logMetricEvent(); } /** used for VOL_SET_GROUP_VOL */ @@ -179,6 +185,102 @@ public class AudioServiceEvents { mCaller = caller; mGroupName = group; mAudioAttributes = aa; + logMetricEvent(); + } + + + /** + * Audio Analytics unique Id. + */ + private static final String mMetricsId = MediaMetrics.Name.AUDIO_VOLUME_EVENT; + + /** + * Log mediametrics event + */ + private void logMetricEvent() { + switch (mOp) { + case VOL_ADJUST_SUGG_VOL: + case VOL_ADJUST_VOL_UID: + case VOL_ADJUST_STREAM_VOL: { + String eventName; + switch (mOp) { + case VOL_ADJUST_SUGG_VOL: + eventName = "adjustSuggestedStreamVolume"; + break; + case VOL_ADJUST_STREAM_VOL: + eventName = "adjustStreamVolume"; + break; + case VOL_ADJUST_VOL_UID: + eventName = "adjustStreamVolumeForUid"; + break; + default: + return; // not possible, just return here + } + new MediaMetrics.Item(mMetricsId) + .set(MediaMetrics.Property.CALLING_PACKAGE, mCaller) + .set(MediaMetrics.Property.DIRECTION, mVal1 > 0 ? "up" : "down") + .set(MediaMetrics.Property.EVENT, eventName) + .set(MediaMetrics.Property.FLAGS, mVal2) + .set(MediaMetrics.Property.STREAM_TYPE, + AudioSystem.streamToString(mStream)) + .record(); + return; + } + case VOL_SET_STREAM_VOL: + new MediaMetrics.Item(mMetricsId) + .set(MediaMetrics.Property.CALLING_PACKAGE, mCaller) + .set(MediaMetrics.Property.EVENT, "setStreamVolume") + .set(MediaMetrics.Property.FLAGS, mVal2) + .set(MediaMetrics.Property.INDEX, mVal1) + .set(MediaMetrics.Property.STREAM_TYPE, + AudioSystem.streamToString(mStream)) + .record(); + return; + case VOL_SET_HEARING_AID_VOL: + new MediaMetrics.Item(mMetricsId) + .set(MediaMetrics.Property.EVENT, "setHearingAidVolume") + .set(MediaMetrics.Property.GAIN_DB, (double) mVal2) + .set(MediaMetrics.Property.INDEX, mVal1) + .record(); + return; + case VOL_SET_AVRCP_VOL: + new MediaMetrics.Item(mMetricsId) + .set(MediaMetrics.Property.EVENT, "setAvrcpVolume") + .set(MediaMetrics.Property.INDEX, mVal1) + .record(); + return; + case VOL_VOICE_ACTIVITY_HEARING_AID: + new MediaMetrics.Item(mMetricsId) + .set(MediaMetrics.Property.EVENT, "voiceActivityHearingAid") + .set(MediaMetrics.Property.INDEX, mVal1) + .set(MediaMetrics.Property.STATE, + mVal2 == 1 ? "active" : "inactive") + .set(MediaMetrics.Property.STREAM_TYPE, + AudioSystem.streamToString(mStream)) + .record(); + return; + case VOL_MODE_CHANGE_HEARING_AID: + new MediaMetrics.Item(mMetricsId) + .set(MediaMetrics.Property.EVENT, "modeChangeHearingAid") + .set(MediaMetrics.Property.INDEX, mVal1) + .set(MediaMetrics.Property.MODE, AudioSystem.modeToString(mVal2)) + .set(MediaMetrics.Property.STREAM_TYPE, + AudioSystem.streamToString(mStream)) + .record(); + return; + case VOL_SET_GROUP_VOL: + new MediaMetrics.Item(mMetricsId) + .set(MediaMetrics.Property.ATTRIBUTES, mAudioAttributes.toString()) + .set(MediaMetrics.Property.CALLING_PACKAGE, mCaller) + .set(MediaMetrics.Property.EVENT, "setVolumeIndexForAttributes") + .set(MediaMetrics.Property.FLAGS, mVal2) + .set(MediaMetrics.Property.GROUP, mGroupName) + .set(MediaMetrics.Property.INDEX, mVal1) + .record(); + return; + default: + return; + } } @Override diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index 93d1bede9de8..accb90cc3c0c 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -113,6 +113,24 @@ public class BtHelper { private static final int BT_HEARING_AID_GAIN_MIN = -128; + /** + * Returns a string representation of the scoAudioMode. + */ + public static String scoAudioModeToString(int scoAudioMode) { + switch (scoAudioMode) { + case SCO_MODE_UNDEFINED: + return "SCO_MODE_UNDEFINED"; + case SCO_MODE_VIRTUAL_CALL: + return "SCO_MODE_VIRTUAL_CALL"; + case SCO_MODE_RAW: + return "SCO_MODE_RAW"; + case SCO_MODE_VR: + return "SCO_MODE_VR"; + default: + return "SCO_MODE_(" + scoAudioMode + ")"; + } + } + //---------------------------------------------------------------------- /*package*/ static class BluetoothA2dpDeviceInfo { private final @NonNull BluetoothDevice mBtDevice; @@ -965,4 +983,27 @@ public class BtHelper { return AudioSystem.AUDIO_FORMAT_DEFAULT; } } + + /** + * Returns the String equivalent of the btCodecType. + * + * This uses an "ENCODING_" prefix for consistency with Audio; + * we could alternately use the "SOURCE_CODEC_TYPE_" prefix from Bluetooth. + */ + public static String bluetoothCodecToEncodingString(int btCodecType) { + switch (btCodecType) { + case BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC: + return "ENCODING_SBC"; + case BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC: + return "ENCODING_AAC"; + case BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX: + return "ENCODING_APTX"; + case BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD: + return "ENCODING_APTX_HD"; + case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC: + return "ENCODING_LDAC"; + default: + return "ENCODING_BT_CODEC_TYPE(" + btCodecType + ")"; + } + } } diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java index bfab9919258c..26281b739436 100755 --- a/services/core/java/com/android/server/audio/MediaFocusControl.java +++ b/services/core/java/com/android/server/audio/MediaFocusControl.java @@ -25,6 +25,7 @@ import android.media.AudioFocusInfo; import android.media.AudioManager; import android.media.AudioSystem; import android.media.IAudioFocusDispatcher; +import android.media.MediaMetrics; import android.media.audiopolicy.IAudioPolicyCallback; import android.os.Binder; import android.os.Build; @@ -144,6 +145,8 @@ public class MediaFocusControl implements PlayerFocusEnforcer { private static final AudioEventLogger mEventLogger = new AudioEventLogger(50, "focus commands as seen by MediaFocusControl"); + private static final String mMetricsId = MediaMetrics.Name.AUDIO_FOCUS; + /*package*/ void noFocusForSuspendedApp(@NonNull String packageName, int uid) { synchronized (mAudioFocusLock) { final Iterator<FocusRequester> stackIterator = mFocusStack.iterator(); @@ -818,6 +821,17 @@ public class MediaFocusControl implements PlayerFocusEnforcer { protected int requestAudioFocus(@NonNull AudioAttributes aa, int focusChangeHint, IBinder cb, IAudioFocusDispatcher fd, @NonNull String clientId, @NonNull String callingPackageName, int flags, int sdk, boolean forceDuck) { + new MediaMetrics.Item(mMetricsId) + .setUid(Binder.getCallingUid()) + .set(MediaMetrics.Property.CALLING_PACKAGE, callingPackageName) + .set(MediaMetrics.Property.CLIENT_NAME, clientId) + .set(MediaMetrics.Property.EVENT, "requestAudioFocus") + .set(MediaMetrics.Property.FLAGS, flags) + .set(MediaMetrics.Property.FOCUS_CHANGE_HINT, + AudioManager.audioFocusToString(focusChangeHint)) + //.set(MediaMetrics.Property.SDK, sdk) + .record(); + mEventLogger.log((new AudioEventLogger.StringEvent( "requestAudioFocus() from uid/pid " + Binder.getCallingUid() + "/" + Binder.getCallingPid() @@ -982,6 +996,13 @@ public class MediaFocusControl implements PlayerFocusEnforcer { * */ protected int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId, AudioAttributes aa, String callingPackageName) { + new MediaMetrics.Item(mMetricsId) + .setUid(Binder.getCallingUid()) + .set(MediaMetrics.Property.CALLING_PACKAGE, callingPackageName) + .set(MediaMetrics.Property.CLIENT_NAME, clientId) + .set(MediaMetrics.Property.EVENT, "abandonAudioFocus") + .record(); + // AudioAttributes are currently ignored, to be used for zones / a11y mEventLogger.log((new AudioEventLogger.StringEvent( "abandonAudioFocus() from uid/pid " + Binder.getCallingUid() diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java index f4d7f9ac5a5e..36d69c93c1cb 100644 --- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java +++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java @@ -89,10 +89,8 @@ class AutomaticBrightnessController { private final BrightnessMappingStrategy mBrightnessMapper; // The minimum and maximum screen brightnesses. - private final int mScreenBrightnessRangeMinimum; - private final int mScreenBrightnessRangeMaximum; - private final float mScreenBrightnessRangeMinimumFloat; - private final float mScreenBrightnessRangeMaximumFloat; + private final float mScreenBrightnessRangeMinimum; + private final float mScreenBrightnessRangeMaximum; // How much to scale doze brightness by (should be (0, 1.0]). private final float mDozeScaleFactor; @@ -156,7 +154,6 @@ class AutomaticBrightnessController { // The screen brightness threshold at which to brighten or darken the screen. private float mScreenBrighteningThreshold; private float mScreenDarkeningThreshold; - // The most recent light sample. private float mLastObservedLux; @@ -177,8 +174,9 @@ class AutomaticBrightnessController { // We preserve this value even when we stop using the light sensor so // that we can quickly revert to the previous auto-brightness level // while the light sensor warms up. - // Use -1 if there is no current auto-brightness value available. - private int mScreenAutoBrightness = PowerManager.BRIGHTNESS_INVALID; + // Use PowerManager.BRIGHTNESS_INVALID_FLOAT if there is no current auto-brightness value + // available. + private float mScreenAutoBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; // The current display policy. This is useful, for example, for knowing when we're dozing, // where the light sensor may not be available. @@ -188,7 +186,7 @@ class AutomaticBrightnessController { // for the initial state of the sample. private boolean mBrightnessAdjustmentSamplePending; private float mBrightnessAdjustmentSampleOldLux; - private int mBrightnessAdjustmentSampleOldBrightness; + private float mBrightnessAdjustmentSampleOldBrightness; // When the short term model is invalidated, we don't necessarily reset it (i.e. clear the // user's adjustment) immediately, but wait for a drastic enough change in the ambient light. @@ -243,13 +241,8 @@ class AutomaticBrightnessController { mCallbacks = callbacks; mSensorManager = sensorManager; mBrightnessMapper = mapper; - mScreenBrightnessRangeMinimum = - BrightnessSynchronizer.brightnessFloatToInt(mContext, brightnessMin); - mScreenBrightnessRangeMaximum = - com.android.internal.BrightnessSynchronizer.brightnessFloatToInt( - mContext, brightnessMax); - mScreenBrightnessRangeMinimumFloat = brightnessMin; - mScreenBrightnessRangeMaximumFloat = brightnessMax; + mScreenBrightnessRangeMinimum = brightnessMin; + mScreenBrightnessRangeMaximum = brightnessMax; mLightSensorWarmUpTimeConfig = lightSensorWarmUpTime; mDozeScaleFactor = dozeScaleFactor; mNormalLightSensorRate = lightSensorRate; @@ -299,12 +292,12 @@ class AutomaticBrightnessController { return true; } - public int getAutomaticScreenBrightness() { + public float getAutomaticScreenBrightness() { if (!mAmbientLuxValid) { - return -1; + return PowerManager.BRIGHTNESS_INVALID_FLOAT; } if (mDisplayPolicy == DisplayPowerRequest.POLICY_DOZE) { - return Math.round(mScreenAutoBrightness * mDozeScaleFactor); + return mScreenAutoBrightness * mDozeScaleFactor; } return mScreenAutoBrightness; } @@ -385,7 +378,7 @@ class AutomaticBrightnessController { private boolean setScreenBrightnessByUser(float brightness) { if (!mAmbientLuxValid) { - // If we don't have a valid ambient lux then we don't have a valid brightness anyways, + // If we don't have a valid ambient lux then we don't have a valid brightness anyway, // and we can't use this data to add a new control point to the short-term model. return false; } @@ -486,7 +479,7 @@ class AutomaticBrightnessController { } else if (mLightSensorEnabled) { mLightSensorEnabled = false; mAmbientLuxValid = !mResetAmbientLuxAfterWarmUpConfig; - mScreenAutoBrightness = PowerManager.BRIGHTNESS_INVALID; + mScreenAutoBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; mRecentLightSamples = 0; mAmbientLightRingBuffer.clear(); mCurrentLightSensorRate = -1; @@ -735,29 +728,28 @@ class AutomaticBrightnessController { float value = mBrightnessMapper.getBrightness(mAmbientLux, mForegroundAppPackageName, mForegroundAppCategory); - int newScreenAutoBrightness = BrightnessSynchronizer.brightnessFloatToInt( - mContext, clampScreenBrightnessFloat(value)); + float newScreenAutoBrightness = clampScreenBrightness(value); // If screenAutoBrightness is set, we should have screen{Brightening,Darkening}Threshold, // in which case we ignore the new screen brightness if it doesn't differ enough from the // previous one. - if (mScreenAutoBrightness != -1 + if (!Float.isNaN(mScreenAutoBrightness) && !isManuallySet && newScreenAutoBrightness > mScreenDarkeningThreshold && newScreenAutoBrightness < mScreenBrighteningThreshold) { if (mLoggingEnabled) { - Slog.d(TAG, "ignoring newScreenAutoBrightness: " + mScreenDarkeningThreshold - + " < " + newScreenAutoBrightness + " < " + mScreenBrighteningThreshold); + Slog.d(TAG, "ignoring newScreenAutoBrightness: " + + mScreenDarkeningThreshold + " < " + newScreenAutoBrightness + + " < " + mScreenBrighteningThreshold); } return; } - - if (mScreenAutoBrightness != newScreenAutoBrightness) { + if (!BrightnessSynchronizer.floatEquals(mScreenAutoBrightness, + newScreenAutoBrightness)) { if (mLoggingEnabled) { - Slog.d(TAG, "updateAutoBrightness: " + - "mScreenAutoBrightness=" + mScreenAutoBrightness + ", " + - "newScreenAutoBrightness=" + newScreenAutoBrightness); + Slog.d(TAG, "updateAutoBrightness: " + + "mScreenAutoBrightness=" + mScreenAutoBrightness + ", " + + "newScreenAutoBrightness=" + newScreenAutoBrightness); } - mScreenAutoBrightness = newScreenAutoBrightness; mScreenBrighteningThreshold = clampScreenBrightness( mScreenBrightnessThresholds.getBrighteningThreshold(newScreenAutoBrightness)); @@ -770,19 +762,12 @@ class AutomaticBrightnessController { } } - // Clamps values with float range [1.0-255.0] - // TODO(brightnessfloat): convert everything that uses this to float system + // Clamps values with float range [0.0-1.0] private float clampScreenBrightness(float value) { return MathUtils.constrain(value, mScreenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum); } - // Clamps values with float range [0.0-1.0] - private float clampScreenBrightnessFloat(float value) { - return MathUtils.constrain(value, - mScreenBrightnessRangeMinimumFloat, mScreenBrightnessRangeMaximumFloat); - } - private void prepareBrightnessAdjustmentSample() { if (!mBrightnessAdjustmentSamplePending) { mBrightnessAdjustmentSamplePending = true; @@ -806,12 +791,13 @@ class AutomaticBrightnessController { private void collectBrightnessAdjustmentSample() { if (mBrightnessAdjustmentSamplePending) { mBrightnessAdjustmentSamplePending = false; - if (mAmbientLuxValid && mScreenAutoBrightness >= 0) { + if (mAmbientLuxValid && (mScreenAutoBrightness >= PowerManager.BRIGHTNESS_MIN + || mScreenAutoBrightness == PowerManager.BRIGHTNESS_OFF_FLOAT)) { if (mLoggingEnabled) { - Slog.d(TAG, "Auto-brightness adjustment changed by user: " + - "lux=" + mAmbientLux + ", " + - "brightness=" + mScreenAutoBrightness + ", " + - "ring=" + mAmbientLightRingBuffer); + Slog.d(TAG, "Auto-brightness adjustment changed by user: " + + "lux=" + mAmbientLux + ", " + + "brightness=" + mScreenAutoBrightness + ", " + + "ring=" + mAmbientLightRingBuffer); } EventLog.writeEvent(EventLogTags.AUTO_BRIGHTNESS_ADJ, diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index b4f7cdbd5694..02d499fbd81f 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -28,6 +28,8 @@ import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SHOUL import static android.hardware.display.DisplayViewport.VIEWPORT_EXTERNAL; import static android.hardware.display.DisplayViewport.VIEWPORT_INTERNAL; import static android.hardware.display.DisplayViewport.VIEWPORT_VIRTUAL; +import static android.view.Surface.ROTATION_270; +import static android.view.Surface.ROTATION_90; import android.Manifest; import android.annotation.NonNull; @@ -1363,8 +1365,7 @@ public final class DisplayManagerService extends SystemService { return null; } - private SurfaceControl.ScreenshotGraphicBuffer screenshotInternal(int displayId, - boolean captureSecureLayer) { + private SurfaceControl.ScreenshotGraphicBuffer systemScreenshotInternal(int displayId) { synchronized (mSyncRoot) { final IBinder token = getDisplayToken(displayId); if (token == null) { @@ -1376,15 +1377,42 @@ public final class DisplayManagerService extends SystemService { } final DisplayInfo displayInfo = logicalDisplay.getDisplayInfoLocked(); - if (captureSecureLayer) { - return SurfaceControl.screenshotToBufferWithSecureLayersUnsafe(token, new Rect(), - displayInfo.getNaturalWidth(), displayInfo.getNaturalHeight(), - false /* useIdentityTransform */, 0 /* rotation */); - } else { - return SurfaceControl.screenshotToBuffer(token, new Rect(), - displayInfo.getNaturalWidth(), displayInfo.getNaturalHeight(), - false /* useIdentityTransform */, 0 /* rotation */); + return SurfaceControl.screenshotToBufferWithSecureLayersUnsafe(token, new Rect(), + displayInfo.getNaturalWidth(), displayInfo.getNaturalHeight(), + false /* useIdentityTransform */, 0 /* rotation */); + } + } + + private SurfaceControl.ScreenshotGraphicBuffer userScreenshotInternal(int displayId) { + synchronized (mSyncRoot) { + final IBinder token = getDisplayToken(displayId); + if (token == null) { + return null; + } + final LogicalDisplay logicalDisplay = mLogicalDisplays.get(displayId); + if (logicalDisplay == null) { + return null; + } + + final DisplayInfo displayInfo = logicalDisplay.getDisplayInfoLocked(); + // Takes screenshot based on current device orientation. + final Display display = DisplayManagerGlobal.getInstance() + .getRealDisplay(displayId); + if (display == null) { + return null; } + final Point displaySize = new Point(); + display.getRealSize(displaySize); + + int rotation = displayInfo.rotation; + // TODO (b/153382624) : This workaround solution would be removed after + // SurfaceFlinger fixes the inconsistency with rotation direction issue. + if (rotation == ROTATION_90 || rotation == ROTATION_270) { + rotation = (rotation == ROTATION_90) ? ROTATION_270 : ROTATION_90; + } + + return SurfaceControl.screenshotToBuffer(token, new Rect(), displaySize.x, + displaySize.y, false /* useIdentityTransform */, rotation /* rotation */); } } @@ -2502,13 +2530,13 @@ public final class DisplayManagerService extends SystemService { } @Override - public SurfaceControl.ScreenshotGraphicBuffer screenshot(int displayId) { - return screenshotInternal(displayId, true); + public SurfaceControl.ScreenshotGraphicBuffer systemScreenshot(int displayId) { + return systemScreenshotInternal(displayId); } @Override - public SurfaceControl.ScreenshotGraphicBuffer screenshotWithoutSecureLayer(int displayId) { - return screenshotInternal(displayId, false); + public SurfaceControl.ScreenshotGraphicBuffer userScreenshot(int displayId) { + return userScreenshotInternal(displayId); } @Override diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 48e30bf42c2d..bafeb77c55e6 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -948,8 +948,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (Float.isNaN(brightnessState)) { float newAutoBrightnessAdjustment = autoBrightnessAdjustment; if (autoBrightnessEnabled) { - brightnessState = BrightnessSynchronizer.brightnessIntToFloat( - mContext, mAutomaticBrightnessController.getAutomaticScreenBrightness()); + brightnessState = mAutomaticBrightnessController.getAutomaticScreenBrightness(); newAutoBrightnessAdjustment = mAutomaticBrightnessController.getAutomaticScreenBrightnessAdjustment(); } diff --git a/services/core/java/com/android/server/display/HysteresisLevels.java b/services/core/java/com/android/server/display/HysteresisLevels.java index f0a505d4d818..2b565698ff8c 100644 --- a/services/core/java/com/android/server/display/HysteresisLevels.java +++ b/services/core/java/com/android/server/display/HysteresisLevels.java @@ -64,8 +64,8 @@ public class HysteresisLevels { * Return the brightening hysteresis threshold for the given value level. */ public float getBrighteningThreshold(float value) { - float brightConstant = getReferenceLevel(value, mBrighteningThresholds); - float brightThreshold = value * (1.0f + brightConstant); + final float brightConstant = getReferenceLevel(value, mBrighteningThresholds); + final float brightThreshold = value * (1.0f + brightConstant); if (DEBUG) { Slog.d(TAG, "bright hysteresis constant=" + brightConstant + ", threshold=" + brightThreshold + ", value=" + value); @@ -77,8 +77,8 @@ public class HysteresisLevels { * Return the darkening hysteresis threshold for the given value level. */ public float getDarkeningThreshold(float value) { - float darkConstant = getReferenceLevel(value, mDarkeningThresholds); - float darkThreshold = value * (1.0f - darkConstant); + final float darkConstant = getReferenceLevel(value, mDarkeningThresholds); + final float darkThreshold = value * (1.0f - darkConstant); if (DEBUG) { Slog.d(TAG, "dark hysteresis constant=: " + darkConstant + ", threshold=" + darkThreshold + ", value=" + value); diff --git a/services/core/java/com/android/server/emergency/EmergencyAffordanceService.java b/services/core/java/com/android/server/emergency/EmergencyAffordanceService.java index 1cf27ffd1903..cc7915cc3534 100644 --- a/services/core/java/com/android/server/emergency/EmergencyAffordanceService.java +++ b/services/core/java/com/android/server/emergency/EmergencyAffordanceService.java @@ -20,90 +20,80 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.os.Binder; +import android.os.Build; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.os.Message; import android.provider.Settings; -import android.telephony.CellInfo; -import android.telephony.CellInfoGsm; -import android.telephony.CellInfoLte; -import android.telephony.CellInfoWcdma; -import android.telephony.CellLocation; -import android.telephony.PhoneStateListener; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; +import android.text.TextUtils; +import android.util.Slog; +import com.android.internal.util.DumpUtils; +import com.android.internal.util.IndentingPrintWriter; import com.android.server.SystemService; +import java.io.FileDescriptor; +import java.io.PrintWriter; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; /** - * A service that listens to connectivity and SIM card changes and determines if the emergency mode - * should be enabled + * A service that listens to connectivity and SIM card changes and determines if the emergency + * affordance should be enabled. */ public class EmergencyAffordanceService extends SystemService { private static final String TAG = "EmergencyAffordanceService"; + private static final boolean DBG = false; - private static final int NUM_SCANS_UNTIL_ABORT = 4; + private static final String SERVICE_NAME = "emergency_affordance"; private static final int INITIALIZE_STATE = 1; - private static final int CELL_INFO_STATE_CHANGED = 2; - private static final int SUBSCRIPTION_CHANGED = 3; - /** - * Global setting, whether the last scan of the sim cards reveal that a sim was inserted that - * requires the emergency affordance. The value is a boolean (1 or 0). - * @hide + * @param arg1 slot Index + * @param arg2 0 + * @param obj ISO country code */ - private static final String EMERGENCY_SIM_INSERTED_SETTING = "emergency_sim_inserted_before"; - - private final Context mContext; - private final ArrayList<Integer> mEmergencyCallMccNumbers; + private static final int NETWORK_COUNTRY_CHANGED = 2; + private static final int SUBSCRIPTION_CHANGED = 3; + private static final int UPDATE_AIRPLANE_MODE_STATUS = 4; - private final Object mLock = new Object(); + // Global Settings to override emergency affordance country ISO for debugging. + // Available only on debug build. The value is a country ISO string in lower case (eg. "us"). + private static final String EMERGENCY_AFFORDANCE_OVERRIDE_ISO = + "emergency_affordance_override_iso"; - private TelephonyManager mTelephonyManager; + private final Context mContext; + // Country ISOs that require affordance + private final ArrayList<String> mEmergencyCallCountryIsos; private SubscriptionManager mSubscriptionManager; - private boolean mEmergencyAffordanceNeeded; + private TelephonyManager mTelephonyManager; private MyHandler mHandler; - private int mScansCompleted; - private PhoneStateListener mPhoneStateListener = new PhoneStateListener() { - @Override - public void onCellInfoChanged(List<CellInfo> cellInfo) { - if (!isEmergencyAffordanceNeeded()) { - requestCellScan(); - } - } + private boolean mAnySimNeedsEmergencyAffordance; + private boolean mAnyNetworkNeedsEmergencyAffordance; + private boolean mEmergencyAffordanceNeeded; + private boolean mAirplaneModeEnabled; + private boolean mVoiceCapable; - @Override - public void onCellLocationChanged(CellLocation location) { - if (!isEmergencyAffordanceNeeded()) { - requestCellScan(); - } - } - }; - private BroadcastReceiver mAirplaneModeReceiver = new BroadcastReceiver() { + private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - if (Settings.Global.getInt(context.getContentResolver(), - Settings.Global.AIRPLANE_MODE_ON, 0) == 0) { - startScanning(); - requestCellScan(); + if (TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED.equals(intent.getAction())) { + String countryCode = intent.getStringExtra(TelephonyManager.EXTRA_NETWORK_COUNTRY); + int slotId = intent.getIntExtra(SubscriptionManager.EXTRA_SLOT_INDEX, + SubscriptionManager.INVALID_SIM_SLOT_INDEX); + mHandler.obtainMessage( + NETWORK_COUNTRY_CHANGED, slotId, 0, countryCode).sendToTarget(); + } else if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(intent.getAction())) { + mHandler.obtainMessage(UPDATE_AIRPLANE_MODE_STATUS).sendToTarget(); } } }; - private boolean mSimNeedsEmergencyAffordance; - private boolean mNetworkNeedsEmergencyAffordance; - private boolean mVoiceCapable; - - private void requestCellScan() { - mHandler.obtainMessage(CELL_INFO_STATE_CHANGED).sendToTarget(); - } private SubscriptionManager.OnSubscriptionsChangedListener mSubscriptionChangedListener = new SubscriptionManager.OnSubscriptionsChangedListener() { @@ -116,207 +106,200 @@ public class EmergencyAffordanceService extends SystemService { public EmergencyAffordanceService(Context context) { super(context); mContext = context; - int[] numbers = context.getResources().getIntArray( - com.android.internal.R.array.config_emergency_mcc_codes); - mEmergencyCallMccNumbers = new ArrayList<>(numbers.length); - for (int i = 0; i < numbers.length; i++) { - mEmergencyCallMccNumbers.add(numbers[i]); + String[] isos = context.getResources().getStringArray( + com.android.internal.R.array.config_emergency_iso_country_codes); + mEmergencyCallCountryIsos = new ArrayList<>(isos.length); + for (String iso : isos) { + mEmergencyCallCountryIsos.add(iso); } - } - private void updateEmergencyAffordanceNeeded() { - synchronized (mLock) { - mEmergencyAffordanceNeeded = mVoiceCapable && (mSimNeedsEmergencyAffordance || - mNetworkNeedsEmergencyAffordance); - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.EMERGENCY_AFFORDANCE_NEEDED, - mEmergencyAffordanceNeeded ? 1 : 0); - if (mEmergencyAffordanceNeeded) { - stopScanning(); + if (Build.IS_DEBUGGABLE) { + String overrideIso = Settings.Global.getString( + mContext.getContentResolver(), EMERGENCY_AFFORDANCE_OVERRIDE_ISO); + if (!TextUtils.isEmpty(overrideIso)) { + if (DBG) Slog.d(TAG, "Override ISO to " + overrideIso); + mEmergencyCallCountryIsos.clear(); + mEmergencyCallCountryIsos.add(overrideIso); } } } - private void stopScanning() { - synchronized (mLock) { - mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE); - mScansCompleted = 0; - } - } - - private boolean isEmergencyAffordanceNeeded() { - synchronized (mLock) { - return mEmergencyAffordanceNeeded; - } - } - @Override public void onStart() { + if (DBG) Slog.i(TAG, "onStart"); + publishBinderService(SERVICE_NAME, new BinderService()); } @Override public void onBootPhase(int phase) { if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { - mTelephonyManager = mContext.getSystemService(TelephonyManager.class); - mVoiceCapable = mTelephonyManager.isVoiceCapable(); - if (!mVoiceCapable) { - updateEmergencyAffordanceNeeded(); - return; - } - mSubscriptionManager = SubscriptionManager.from(mContext); - HandlerThread thread = new HandlerThread(TAG); - thread.start(); - mHandler = new MyHandler(thread.getLooper()); - mHandler.obtainMessage(INITIALIZE_STATE).sendToTarget(); - startScanning(); - IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED); - mContext.registerReceiver(mAirplaneModeReceiver, filter); - mSubscriptionManager.addOnSubscriptionsChangedListener(mSubscriptionChangedListener); + if (DBG) Slog.i(TAG, "onBootPhase"); + handleThirdPartyBootPhase(); } } - private void startScanning() { - mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CELL_INFO - | PhoneStateListener.LISTEN_CELL_LOCATION); - } - /** Handler to do the heavier work on */ private class MyHandler extends Handler { - public MyHandler(Looper l) { super(l); } @Override public void handleMessage(Message msg) { + if (DBG) Slog.d(TAG, "handleMessage: " + msg.what); switch (msg.what) { case INITIALIZE_STATE: handleInitializeState(); break; - case CELL_INFO_STATE_CHANGED: - handleUpdateCellInfo(); + case NETWORK_COUNTRY_CHANGED: + final String countryIso = (String) msg.obj; + final int slotId = msg.arg1; + handleNetworkCountryChanged(countryIso, slotId); break; case SUBSCRIPTION_CHANGED: handleUpdateSimSubscriptionInfo(); break; + case UPDATE_AIRPLANE_MODE_STATUS: + handleUpdateAirplaneModeStatus(); + break; + default: + Slog.e(TAG, "Unexpected message received: " + msg.what); } } } private void handleInitializeState() { - if (handleUpdateSimSubscriptionInfo()) { - return; - } - if (handleUpdateCellInfo()) { + if (DBG) Slog.d(TAG, "handleInitializeState"); + handleUpdateAirplaneModeStatus(); + handleUpdateSimSubscriptionInfo(); + updateNetworkCountry(); + updateEmergencyAffordanceNeeded(); + } + + private void handleThirdPartyBootPhase() { + if (DBG) Slog.d(TAG, "handleThirdPartyBootPhase"); + mTelephonyManager = mContext.getSystemService(TelephonyManager.class); + mVoiceCapable = mTelephonyManager.isVoiceCapable(); + if (!mVoiceCapable) { + updateEmergencyAffordanceNeeded(); return; } - updateEmergencyAffordanceNeeded(); + + HandlerThread thread = new HandlerThread(TAG); + thread.start(); + mHandler = new MyHandler(thread.getLooper()); + + mSubscriptionManager = SubscriptionManager.from(mContext); + mSubscriptionManager.addOnSubscriptionsChangedListener(mSubscriptionChangedListener); + + IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED); + filter.addAction(TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED); + mContext.registerReceiver(mBroadcastReceiver, filter); + + mHandler.obtainMessage(INITIALIZE_STATE).sendToTarget(); + } + + private void handleUpdateAirplaneModeStatus() { + mAirplaneModeEnabled = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.AIRPLANE_MODE_ON, 0) == 1; + if (DBG) Slog.d(TAG, "APM status updated to " + mAirplaneModeEnabled); } - private boolean handleUpdateSimSubscriptionInfo() { - boolean neededBefore = simNeededAffordanceBefore(); - boolean neededNow = neededBefore; + private void handleUpdateSimSubscriptionInfo() { List<SubscriptionInfo> activeSubscriptionInfoList = mSubscriptionManager.getActiveSubscriptionInfoList(); + if (DBG) Slog.d(TAG, "handleUpdateSimSubscriptionInfo: " + activeSubscriptionInfoList); if (activeSubscriptionInfoList == null) { - setSimNeedsEmergencyAffordance(neededNow); - return neededNow; + return; } + + boolean needsAffordance = false; for (SubscriptionInfo info : activeSubscriptionInfoList) { - int mcc = info.getMcc(); - if (mccRequiresEmergencyAffordance(mcc)) { - neededNow = true; + if (isoRequiresEmergencyAffordance(info.getCountryIso())) { + needsAffordance = true; break; - } else if (mcc != 0 && mcc != Integer.MAX_VALUE){ - // a Sim with a different mcc code was found - neededNow = false; - } - String simOperator = mTelephonyManager - .createForSubscriptionId(info.getSubscriptionId()).getSimOperator(); - mcc = 0; - if (simOperator != null && simOperator.length() >= 3) { - mcc = Integer.parseInt(simOperator.substring(0, 3)); - } - if (mcc != 0) { - if (mccRequiresEmergencyAffordance(mcc)) { - neededNow = true; - break; - } else { - // a Sim with a different mcc code was found - neededNow = false; - } } } - setSimNeedsEmergencyAffordance(neededNow); - return neededNow; + + mAnySimNeedsEmergencyAffordance = needsAffordance; + updateEmergencyAffordanceNeeded(); } - private void setSimNeedsEmergencyAffordance(boolean simNeedsEmergencyAffordance) { - if (simNeededAffordanceBefore() != simNeedsEmergencyAffordance) { - Settings.Global.putInt(mContext.getContentResolver(), - EMERGENCY_SIM_INSERTED_SETTING, - simNeedsEmergencyAffordance ? 1 : 0); + private void handleNetworkCountryChanged(String countryIso, int slotId) { + if (DBG) { + Slog.d(TAG, "handleNetworkCountryChanged: countryIso=" + countryIso + + ", slotId=" + slotId); } - if (simNeedsEmergencyAffordance != mSimNeedsEmergencyAffordance) { - mSimNeedsEmergencyAffordance = simNeedsEmergencyAffordance; - updateEmergencyAffordanceNeeded(); + + if (TextUtils.isEmpty(countryIso) && mAirplaneModeEnabled) { + Slog.w(TAG, "Ignore empty countryIso report when APM is on."); + return; } - } - private boolean simNeededAffordanceBefore() { - return Settings.Global.getInt(mContext.getContentResolver(), - EMERGENCY_SIM_INSERTED_SETTING, 0) != 0; + updateNetworkCountry(); + + updateEmergencyAffordanceNeeded(); } - private boolean handleUpdateCellInfo() { - List<CellInfo> cellInfos = mTelephonyManager.getAllCellInfo(); - if (cellInfos == null) { - return false; - } - boolean stopScanningAfterScan = false; - for (CellInfo cellInfo : cellInfos) { - int mcc = 0; - if (cellInfo instanceof CellInfoGsm) { - mcc = ((CellInfoGsm) cellInfo).getCellIdentity().getMcc(); - } else if (cellInfo instanceof CellInfoLte) { - mcc = ((CellInfoLte) cellInfo).getCellIdentity().getMcc(); - } else if (cellInfo instanceof CellInfoWcdma) { - mcc = ((CellInfoWcdma) cellInfo).getCellIdentity().getMcc(); - } - if (mccRequiresEmergencyAffordance(mcc)) { - setNetworkNeedsEmergencyAffordance(true); - return true; - } else if (mcc != 0 && mcc != Integer.MAX_VALUE) { - // we found an mcc that isn't in the list, abort - stopScanningAfterScan = true; + private void updateNetworkCountry() { + boolean needsAffordance = false; + + final int activeModems = mTelephonyManager.getActiveModemCount(); + for (int i = 0; i < activeModems; i++) { + String countryIso = mTelephonyManager.getNetworkCountryIso(i); + if (DBG) Slog.d(TAG, "UpdateNetworkCountry: slotId=" + i + " countryIso=" + countryIso); + if (isoRequiresEmergencyAffordance(countryIso)) { + needsAffordance = true; + break; } } - if (stopScanningAfterScan) { - stopScanning(); - } else { - onCellScanFinishedUnsuccessful(); - } - setNetworkNeedsEmergencyAffordance(false); - return false; + + mAnyNetworkNeedsEmergencyAffordance = needsAffordance; + + updateEmergencyAffordanceNeeded(); } - private void setNetworkNeedsEmergencyAffordance(boolean needsAffordance) { - synchronized (mLock) { - mNetworkNeedsEmergencyAffordance = needsAffordance; - updateEmergencyAffordanceNeeded(); - } + private boolean isoRequiresEmergencyAffordance(String iso) { + return mEmergencyCallCountryIsos.contains(iso); } - private void onCellScanFinishedUnsuccessful() { - synchronized (mLock) { - mScansCompleted++; - if (mScansCompleted >= NUM_SCANS_UNTIL_ABORT) { - stopScanning(); - } + private void updateEmergencyAffordanceNeeded() { + if (DBG) { + Slog.d(TAG, "updateEmergencyAffordanceNeeded: mEmergencyAffordanceNeeded=" + + mEmergencyAffordanceNeeded + ", mVoiceCapable=" + mVoiceCapable + + ", mAnySimNeedsEmergencyAffordance=" + mAnySimNeedsEmergencyAffordance + + ", mAnyNetworkNeedsEmergencyAffordance=" + + mAnyNetworkNeedsEmergencyAffordance); + } + boolean lastAffordanceNeeded = mEmergencyAffordanceNeeded; + + mEmergencyAffordanceNeeded = mVoiceCapable + && (mAnySimNeedsEmergencyAffordance || mAnyNetworkNeedsEmergencyAffordance); + + if (lastAffordanceNeeded != mEmergencyAffordanceNeeded) { + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.EMERGENCY_AFFORDANCE_NEEDED, + mEmergencyAffordanceNeeded ? 1 : 0); } } - private boolean mccRequiresEmergencyAffordance(int mcc) { - return mEmergencyCallMccNumbers.contains(mcc); + private void dumpInternal(IndentingPrintWriter ipw) { + ipw.println("EmergencyAffordanceService (dumpsys emergency_affordance) state:\n"); + ipw.println("mEmergencyAffordanceNeeded=" + mEmergencyAffordanceNeeded); + ipw.println("mVoiceCapable=" + mVoiceCapable); + ipw.println("mAnySimNeedsEmergencyAffordance=" + mAnySimNeedsEmergencyAffordance); + ipw.println("mAnyNetworkNeedsEmergencyAffordance=" + mAnyNetworkNeedsEmergencyAffordance); + ipw.println("mEmergencyCallCountryIsos=" + String.join(",", mEmergencyCallCountryIsos)); + } + + private final class BinderService extends Binder { + @Override + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) { + return; + } + + dumpInternal(new IndentingPrintWriter(pw, " ")); + } } } diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java index 44e973ecedfa..393e8db96c01 100644 --- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java +++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java @@ -117,6 +117,8 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { private static final String ALLOWED_INSTALLER_DELIMITER = ","; private static final String INSTALLER_PACKAGE_CERT_DELIMITER = "\\|"; + public static final boolean DEBUG_INTEGRITY_COMPONENT = false; + private static final Set<String> PACKAGE_INSTALLER = new HashSet<>( Arrays.asList( @@ -262,14 +264,18 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { int verificationId = intent.getIntExtra(EXTRA_VERIFICATION_ID, -1); try { - Slog.i(TAG, "Received integrity verification intent " + intent.toString()); - Slog.i(TAG, "Extras " + intent.getExtras()); + if (DEBUG_INTEGRITY_COMPONENT) { + Slog.d(TAG, "Received integrity verification intent " + intent.toString()); + Slog.d(TAG, "Extras " + intent.getExtras()); + } String installerPackageName = getInstallerPackageName(intent); // Skip integrity verification if the verifier is doing the install. if (!integrityCheckIncludesRuleProvider() && isRuleProvider(installerPackageName)) { - Slog.i(TAG, "Verifier doing the install. Skipping integrity check."); + if (DEBUG_INTEGRITY_COMPONENT) { + Slog.i(TAG, "Verifier doing the install. Skipping integrity check."); + } mPackageManagerInternal.setIntegrityVerificationResult( verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW); return; @@ -303,19 +309,23 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { AppInstallMetadata appInstallMetadata = builder.build(); - Slog.i( - TAG, - "To be verified: " - + appInstallMetadata - + " installers " - + getAllowedInstallers(packageInfo)); + if (DEBUG_INTEGRITY_COMPONENT) { + Slog.i( + TAG, + "To be verified: " + + appInstallMetadata + + " installers " + + getAllowedInstallers(packageInfo)); + } IntegrityCheckResult result = mEvaluationEngine.evaluate(appInstallMetadata); - Slog.i( - TAG, - "Integrity check result: " - + result.getEffect() - + " due to " - + result.getMatchedRules()); + if (DEBUG_INTEGRITY_COMPONENT) { + Slog.i( + TAG, + "Integrity check result: " + + result.getEffect() + + " due to " + + result.getMatchedRules()); + } FrameworkStatsLog.write( FrameworkStatsLog.INTEGRITY_CHECK_RESULT_REPORTED, @@ -424,7 +434,7 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { .getPackageInfo(installer, PackageManager.GET_SIGNING_CERTIFICATES); return getCertificateFingerprint(installerInfo); } catch (PackageManager.NameNotFoundException e) { - Slog.i(TAG, "Installer package " + installer + " not found."); + Slog.w(TAG, "Installer package " + installer + " not found."); return Collections.emptyList(); } } @@ -653,28 +663,39 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { private String getCallingRulePusherPackageName(int callingUid) { // Obtain the system apps that are whitelisted in config_integrityRuleProviderPackages. List<String> allowedRuleProviders = getAllowedRuleProviderSystemApps(); - Slog.i(TAG, String.format( - "Rule provider system app list contains: %s", allowedRuleProviders)); + if (DEBUG_INTEGRITY_COMPONENT) { + Slog.i(TAG, String.format( + "Rule provider system app list contains: %s", allowedRuleProviders)); + } // Identify the package names in the caller list. List<String> callingPackageNames = getPackageListForUid(callingUid); - Slog.i(TAG, String.format("Calling packages are: ", callingPackageNames)); + if (DEBUG_INTEGRITY_COMPONENT) { + Slog.i(TAG, String.format("Calling packages are: ", callingPackageNames)); + } // Find the intersection between the allowed and calling packages. Ideally, we will have // at most one package name here. But if we have more, it is fine. - List<String> allowedCallingPackages = - callingPackageNames - .stream() - .filter(packageName -> allowedRuleProviders.contains(packageName)) - .collect(Collectors.toList()); - Slog.i(TAG, String.format("Calling rule pusher packages are: ", allowedCallingPackages)); - + List<String> allowedCallingPackages = new ArrayList<>(); + for (String packageName : callingPackageNames) { + if (allowedRuleProviders.contains(packageName)) { + allowedCallingPackages.add(packageName); + } + } + if (DEBUG_INTEGRITY_COMPONENT) { + Slog.i(TAG, + String.format("Calling rule pusher packages are: ", allowedCallingPackages)); + } return allowedCallingPackages.isEmpty() ? null : allowedCallingPackages.get(0); } private boolean isRuleProvider(String installerPackageName) { - return getAllowedRuleProviderSystemApps().stream() - .anyMatch(ruleProvider -> ruleProvider.equals(installerPackageName)); + for (String ruleProvider : getAllowedRuleProviderSystemApps()) { + if (ruleProvider.matches(installerPackageName)) { + return true; + } + } + return false; } private List<String> getAllowedRuleProviderSystemApps() { @@ -682,13 +703,18 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { Arrays.asList( mContext.getResources() .getStringArray(R.array.config_integrityRuleProviderPackages)); - - Slog.i(TAG, String.format("Rule provider list contains: %s", integrityRuleProviders)); + if (DEBUG_INTEGRITY_COMPONENT) { + Slog.i(TAG, String.format("Rule provider list contains: %s", integrityRuleProviders)); + } // Filter out the rule provider packages that are not system apps. - return integrityRuleProviders.stream() - .filter(this::isSystemApp) - .collect(Collectors.toList()); + List<String> systemAppRuleProviders = new ArrayList<>(); + for (String ruleProvider: integrityRuleProviders) { + if (isSystemApp(ruleProvider)) { + systemAppRuleProviders.add(ruleProvider); + } + } + return systemAppRuleProviders; } private boolean isSystemApp(String packageName) { diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index d432fc83b52a..3e6d6f5fe192 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -819,7 +819,9 @@ public class PreferencesHelper implements RankingConfig { } if (fromTargetApp) { channel.setLockscreenVisibility(r.visibility); - channel.setAllowBubbles(existing != null && existing.canBubble()); + channel.setAllowBubbles(existing != null + ? existing.getAllowBubbles() + : NotificationChannel.DEFAULT_ALLOW_BUBBLE); } clearLockedFieldsLocked(channel); channel.setImportanceLockedByOEM(r.oemLockedImportance); @@ -1704,7 +1706,7 @@ public class PreferencesHelper implements RankingConfig { if (original.canShowBadge() != update.canShowBadge()) { update.lockFields(NotificationChannel.USER_LOCKED_SHOW_BADGE); } - if (original.canBubble() != update.canBubble()) { + if (original.getAllowBubbles() != update.getAllowBubbles()) { update.lockFields(NotificationChannel.USER_LOCKED_ALLOW_BUBBLE); } } diff --git a/services/core/java/com/android/server/notification/ShortcutHelper.java b/services/core/java/com/android/server/notification/ShortcutHelper.java index 1d4843822931..96da649350b0 100644 --- a/services/core/java/com/android/server/notification/ShortcutHelper.java +++ b/services/core/java/com/android/server/notification/ShortcutHelper.java @@ -21,7 +21,6 @@ import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC; import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED; import android.annotation.NonNull; -import android.content.Intent; import android.content.IntentFilter; import android.content.pm.LauncherApps; import android.content.pm.ShortcutInfo; @@ -41,9 +40,18 @@ import java.util.List; /** * Helper for querying shortcuts. */ -class ShortcutHelper { +public class ShortcutHelper { private static final String TAG = "ShortcutHelper"; + private static final IntentFilter SHARING_FILTER = new IntentFilter(); + static { + try { + SHARING_FILTER.addDataType("*/*"); + } catch (IntentFilter.MalformedMimeTypeException e) { + Slog.e(TAG, "Bad mime type", e); + } + } + /** * Listener to call when a shortcut we're tracking has been removed. */ @@ -54,7 +62,6 @@ class ShortcutHelper { private LauncherApps mLauncherAppsService; private ShortcutListener mShortcutListener; private ShortcutServiceInternal mShortcutServiceInternal; - private IntentFilter mSharingFilter; // Key: packageName Value: <shortcutId, notifId> private HashMap<String, HashMap<String, String>> mActiveShortcutBubbles = new HashMap<>(); @@ -122,12 +129,6 @@ class ShortcutHelper { ShortcutServiceInternal shortcutServiceInternal) { mLauncherAppsService = launcherApps; mShortcutListener = listener; - mSharingFilter = new IntentFilter(); - try { - mSharingFilter.addDataType("*/*"); - } catch (IntentFilter.MalformedMimeTypeException e) { - Slog.e(TAG, "Bad mime type", e); - } mShortcutServiceInternal = shortcutServiceInternal; } @@ -142,7 +143,21 @@ class ShortcutHelper { } /** - * Only returns shortcut info if it's found and if it's {@link ShortcutInfo#isLongLived()}. + * Returns whether the given shortcut info is a conversation shortcut. + */ + public static boolean isConversationShortcut( + ShortcutInfo shortcutInfo, ShortcutServiceInternal mShortcutServiceInternal, + int callingUserId) { + if (shortcutInfo == null || !shortcutInfo.isLongLived() || !shortcutInfo.isEnabled()) { + return false; + } + return mShortcutServiceInternal.isSharingShortcut(callingUserId, "android", + shortcutInfo.getPackage(), shortcutInfo.getId(), shortcutInfo.getUserId(), + SHARING_FILTER); + } + + /** + * Only returns shortcut info if it's found and if it's a conversation shortcut. */ ShortcutInfo getValidShortcutInfo(String shortcutId, String packageName, UserHandle user) { if (mLauncherAppsService == null) { @@ -161,11 +176,7 @@ class ShortcutHelper { ShortcutInfo info = shortcuts != null && shortcuts.size() > 0 ? shortcuts.get(0) : null; - if (info == null || !info.isLongLived() || !info.isEnabled()) { - return null; - } - if (mShortcutServiceInternal.isSharingShortcut(user.getIdentifier(), - "android", packageName, shortcutId, user.getIdentifier(), mSharingFilter)) { + if (isConversationShortcut(info, mShortcutServiceInternal, user.getIdentifier())) { return info; } return null; diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java index 7c47cf0450d4..d36d038058f1 100644 --- a/services/core/java/com/android/server/pm/AppsFilter.java +++ b/services/core/java/com/android/server/pm/AppsFilter.java @@ -612,7 +612,7 @@ public class AppsFilter { } final int insert = ~loc; System.arraycopy(appIds, insert, buffer, 0, whitelistSize - insert); - appIds[insert] = existingUid; + appIds[insert] = existingAppId; System.arraycopy(buffer, 0, appIds, insert + 1, whitelistSize - insert); whitelistSize++; } diff --git a/services/core/java/com/android/server/pm/DataLoaderManagerService.java b/services/core/java/com/android/server/pm/DataLoaderManagerService.java index ae9c38498b10..062e9e15b519 100644 --- a/services/core/java/com/android/server/pm/DataLoaderManagerService.java +++ b/services/core/java/com/android/server/pm/DataLoaderManagerService.java @@ -38,6 +38,8 @@ import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.server.SystemService; +import libcore.io.IoUtils; + import java.util.List; /** @@ -64,31 +66,52 @@ public class DataLoaderManagerService extends SystemService { publishBinderService(Context.DATA_LOADER_MANAGER_SERVICE, mBinderService); } + private static void closeQuietly(FileSystemControlParcel control) { + if (control == null || control.incremental == null) { + return; + } + IoUtils.closeQuietly(control.incremental.cmd); + IoUtils.closeQuietly(control.incremental.pendingReads); + IoUtils.closeQuietly(control.incremental.log); + } + final class DataLoaderManagerBinderService extends IDataLoaderManager.Stub { @Override public boolean initializeDataLoader(int dataLoaderId, DataLoaderParamsParcel params, FileSystemControlParcel control, IDataLoaderStatusListener listener) { - synchronized (mLock) { - if (mServiceConnections.get(dataLoaderId) != null) { - Slog.e(TAG, "Data loader of ID=" + dataLoaderId + " already exists."); + DataLoaderServiceConnection connection = null; + try { + synchronized (mLock) { + if (mServiceConnections.get(dataLoaderId) != null) { + Slog.e(TAG, "Data loader of ID=" + dataLoaderId + " already exists."); + return false; + } + } + ComponentName componentName = + new ComponentName(params.packageName, params.className); + ComponentName dataLoaderComponent = resolveDataLoaderComponentName(componentName); + if (dataLoaderComponent == null) { return false; } - } - ComponentName componentName = new ComponentName(params.packageName, params.className); - ComponentName dataLoaderComponent = resolveDataLoaderComponentName(componentName); - if (dataLoaderComponent == null) { - return false; - } - // Binds to the specific data loader service - DataLoaderServiceConnection connection = - new DataLoaderServiceConnection(dataLoaderId, params, control, listener); - Intent intent = new Intent(); - intent.setComponent(dataLoaderComponent); - if (!mContext.bindServiceAsUser(intent, connection, Context.BIND_AUTO_CREATE, - UserHandle.of(UserHandle.getCallingUserId()))) { - Slog.e(TAG, "Failed to bind to data loader binder service."); - mContext.unbindService(connection); - return false; + // Binds to the specific data loader service + connection = + new DataLoaderServiceConnection(dataLoaderId, params, + control, listener); + control = null; // now connection manages it + Intent intent = new Intent(); + intent.setComponent(dataLoaderComponent); + if (!mContext.bindServiceAsUser(intent, connection, Context.BIND_AUTO_CREATE, + UserHandle.of(UserHandle.getCallingUserId()))) { + Slog.e(TAG, "Failed to bind to data loader binder service."); + mContext.unbindService(connection); + return false; + } + connection = null; + } finally { + DataLoaderManagerService.closeQuietly(control); + if (connection != null) { + connection.close(); + } } return true; } @@ -173,7 +196,7 @@ public class DataLoaderManagerService extends SystemService { } } - class DataLoaderServiceConnection implements ServiceConnection { + class DataLoaderServiceConnection implements ServiceConnection, AutoCloseable { final int mId; final DataLoaderParamsParcel mParams; final FileSystemControlParcel mControl; @@ -204,15 +227,34 @@ public class DataLoaderManagerService extends SystemService { @Override public void onServiceDisconnected(ComponentName arg0) { - if (mListener != null) { - try { - mListener.onStatusChanged(mId, IDataLoaderStatusListener.DATA_LOADER_DESTROYED); - } catch (RemoteException ignored) { - } - } + Slog.i(TAG, "DataLoader " + mId + " disconnected, but will try to recover"); + callListener(IDataLoaderStatusListener.DATA_LOADER_DESTROYED); remove(); } + @Override + public void onBindingDied(ComponentName name) { + Slog.i(TAG, "DataLoader " + mId + " died"); + callListener(IDataLoaderStatusListener.DATA_LOADER_DESTROYED); + mContext.unbindService(this); + close(); + remove(); + } + + @Override + public void onNullBinding(ComponentName name) { + Slog.i(TAG, "DataLoader " + mId + " failed to start"); + callListener(IDataLoaderStatusListener.DATA_LOADER_DESTROYED); + mContext.unbindService(this); + close(); + remove(); + } + + @Override + public void close() { + DataLoaderManagerService.closeQuietly(mControl); + } + IDataLoader getDataLoader() { return mDataLoader; } @@ -223,6 +265,8 @@ public class DataLoaderManagerService extends SystemService { } catch (RemoteException ignored) { } mContext.unbindService(this); + close(); + remove(); } private void remove() { @@ -230,5 +274,14 @@ public class DataLoaderManagerService extends SystemService { mServiceConnections.remove(mId); } } + + private void callListener(int status) { + if (mListener != null) { + try { + mListener.onStatusChanged(mId, status); + } catch (RemoteException ignored) { + } + } + } } } diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 4cfd1ab73c9e..3367cd556b2b 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -979,6 +979,9 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements @Override public void bypassNextStagedInstallerCheck(boolean value) { + if (!isCalledBySystemOrShell(Binder.getCallingUid())) { + throw new SecurityException("Caller not allowed to bypass staged installer check"); + } mBypassNextStagedInstallerCheck = value; } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 7adafe3ed658..28987ed8e7b3 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -11203,8 +11203,16 @@ public class PackageManagerService extends IPackageManager.Stub boolean needToDeriveAbi = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0; if (!needToDeriveAbi) { if (pkgSetting != null) { - primaryCpuAbiFromSettings = pkgSetting.primaryCpuAbiString; - secondaryCpuAbiFromSettings = pkgSetting.secondaryCpuAbiString; + // TODO(b/154610922): if it is not first boot or upgrade, we should directly use + // API info from existing package setting. However, stub packages currently do not + // preserve ABI info, thus the special condition check here. Remove the special + // check after we fix the stub generation. + if (pkgSetting.pkg != null && pkgSetting.pkg.isStub()) { + needToDeriveAbi = true; + } else { + primaryCpuAbiFromSettings = pkgSetting.primaryCpuAbiString; + secondaryCpuAbiFromSettings = pkgSetting.secondaryCpuAbiString; + } } else { // Re-scanning a system package after uninstalling updates; need to derive ABI needToDeriveAbi = true; @@ -20826,8 +20834,11 @@ public class PackageManagerService extends IPackageManager.Stub final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY; final SparseArray<int[]> broadcastWhitelist; synchronized (mLock) { - broadcastWhitelist = isInstantApp ? null : mAppsFilter.getVisibilityWhitelist( - getPackageSettingInternal(packageName, Process.SYSTEM_UID), + PackageSetting setting = getPackageSettingInternal(packageName, Process.SYSTEM_UID); + if (setting == null) { + return; + } + broadcastWhitelist = isInstantApp ? null : mAppsFilter.getVisibilityWhitelist(setting, userIds, mSettings.mPackages); } sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, flags, null, null, diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index c716fce8a86d..4561d2e7a8b2 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -104,6 +104,7 @@ import com.android.internal.os.BackgroundThread; import com.android.internal.util.DumpUtils; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.FrameworkStatsLog; +import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.internal.widget.LockPatternUtils; @@ -4516,6 +4517,8 @@ public class UserManagerService extends IUserManager.Stub { switch(cmd) { case "list": return runList(pw, shell); + case "list-missing-system-packages": + return runListMissingSystemPackages(pw, shell); default: return shell.handleDefaultCommands(cmd); } @@ -4582,6 +4585,30 @@ public class UserManagerService extends IUserManager.Stub { } } + private int runListMissingSystemPackages(PrintWriter pw, Shell shell) { + boolean verbose = false; + boolean force = false; + String opt; + while ((opt = shell.getNextOption()) != null) { + switch (opt) { + case "-v": + verbose = true; + break; + case "--force": + force = true; + break; + default: + pw.println("Invalid option: " + opt); + return -1; + } + } + + try (IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ")) { + mSystemPackageInstaller.dumpMissingSystemPackages(ipw, force, verbose); + } + return 0; + } + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return; @@ -5154,6 +5181,9 @@ public class UserManagerService extends IUserManager.Stub { pw.println(""); pw.println(" list [-v] [-all]"); pw.println(" Prints all users on the system."); + pw.println(" list-missing-system-packages [-v] [--force]"); + pw.println(" Prints all system packages that were not explicitly configured to be " + + "installed."); } } diff --git a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java index 85c2306f6d89..cd1087f5fcd7 100644 --- a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java +++ b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java @@ -22,15 +22,16 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; -import android.content.pm.PackageParser; import android.content.res.Resources; import android.os.SystemProperties; import android.os.UserHandle; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.Pair; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.IndentingPrintWriter; import com.android.server.LocalServices; import com.android.server.SystemConfig; import com.android.server.pm.parsing.pkg.AndroidPackage; @@ -38,7 +39,9 @@ import com.android.server.pm.parsing.pkg.AndroidPackage; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Map; import java.util.Set; @@ -263,37 +266,85 @@ class UserSystemPackageInstaller { if (!isLogMode(mode) && !isEnforceMode(mode)) { return; } - Slog.v(TAG, "Checking that all system packages are whitelisted."); + final List<Pair<Boolean, String>> warnings = checkSystemPackagesWhitelistWarnings(mode); + final int size = warnings.size(); + if (size == 0) { + Slog.v(TAG, "checkWhitelistedSystemPackages(mode=" + mode + "): no warnings"); + return; + } + + if (isImplicitWhitelistMode(mode) && !isLogMode(mode)) { + // Only shows whether all whitelisted packages are indeed on the system. + for (int i = 0; i < size; i++) { + final Pair<Boolean, String> pair = warnings.get(i); + final boolean isSevere = pair.first; + if (!isSevere) { + final String msg = pair.second; + Slog.w(TAG, msg); + } + } + return; + } + + Slog.v(TAG, "checkWhitelistedSystemPackages(mode=" + mode + "): " + size + " warnings"); + boolean doWtf = !isImplicitWhitelistMode(mode); + for (int i = 0; i < size; i++) { + final Pair<Boolean, String> pair = warnings.get(i); + final boolean isSevere = pair.first; + final String msg = pair.second; + if (isSevere) { + if (doWtf) { + Slog.wtf(TAG, msg); + } else { + Slog.e(TAG, msg); + } + } else { + Slog.w(TAG, msg); + } + } + } + + // TODO: method below was created to refactor the one-time logging logic so it can be used on + // dump / cmd as well. It could to be further refactored (for example, creating a new + // structure for the warnings so it doesn't need a Pair). + /** + * Gets warnings for system user whitelisting. + * + * @return list of warnings, where {@code Pair.first} is the severity ({@code true} for WTF, + * {@code false} for WARN) and {@code Pair.second} the message. + */ + @NonNull + private List<Pair<Boolean, String>> checkSystemPackagesWhitelistWarnings( + @PackageWhitelistMode int mode) { final Set<String> allWhitelistedPackages = getWhitelistedSystemPackages(); - PackageManagerInternal pmInt = LocalServices.getService(PackageManagerInternal.class); + final List<Pair<Boolean, String>> warnings = new ArrayList<>(); + final PackageManagerInternal pmInt = LocalServices.getService(PackageManagerInternal.class); // Check whether all whitelisted packages are indeed on the system. + final String notPresentFmt = "%s is whitelisted but not present."; + final String notSystemFmt = "%s is whitelisted and present but not a system package."; for (String pkgName : allWhitelistedPackages) { - AndroidPackage pkg = pmInt.getPackage(pkgName); + final AndroidPackage pkg = pmInt.getPackage(pkgName); if (pkg == null) { - Slog.w(TAG, pkgName + " is whitelisted but not present."); + warnings.add(new Pair<>(false, String.format(notPresentFmt, pkgName))); } else if (!pkg.isSystem()) { - Slog.w(TAG, pkgName + " is whitelisted and present but not a system package."); + warnings.add(new Pair<>(false, String.format(notSystemFmt, pkgName))); } } // Check whether all system packages are indeed whitelisted. - if (isImplicitWhitelistMode(mode) && !isLogMode(mode)) { - return; - } - final boolean doWtf = isEnforceMode(mode); + final String logMessageFmt = "System package %s is not whitelisted using " + + "'install-in-user-type' in SystemConfig for any user types!"; + final boolean isSevere = isEnforceMode(mode); pmInt.forEachPackage(pkg -> { - if (pkg.isSystem() && !allWhitelistedPackages.contains(pkg.getManifestPackageName())) { - final String msg = "System package " + pkg.getManifestPackageName() - + " is not whitelisted using 'install-in-user-type' in SystemConfig " - + "for any user types!"; - if (doWtf) { - Slog.wtf(TAG, msg); - } else { - Slog.e(TAG, msg); - } + if (!pkg.isSystem()) return; + final String pkgName = pkg.getManifestPackageName(); + if (!allWhitelistedPackages.contains(pkgName)) { + warnings.add(new Pair<>(isSevere, String.format(logMessageFmt, pkgName))); } }); + + return warnings; } /** Whether to only install system packages in new users for which they are whitelisted. */ @@ -602,32 +653,45 @@ class UserSystemPackageInstaller { } void dump(PrintWriter pw) { - final String prefix = " "; - final String prefix2 = prefix + prefix; + try (IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ")) { + dumpIndented(ipw); + } + } + + private void dumpIndented(IndentingPrintWriter pw) { final int mode = getWhitelistMode(); pw.println("Whitelisted packages per user type"); - pw.print(prefix); pw.print("Mode: "); + + pw.increaseIndent(); + pw.print("Mode: "); pw.print(mode); pw.print(isEnforceMode(mode) ? " (enforced)" : ""); pw.print(isLogMode(mode) ? " (logged)" : ""); pw.print(isImplicitWhitelistMode(mode) ? " (implicit)" : ""); pw.print(isIgnoreOtaMode(mode) ? " (ignore OTAs)" : ""); pw.println(); + pw.decreaseIndent(); - pw.print(prefix); pw.println("Legend"); + pw.increaseIndent(); + pw.println("Legend"); + pw.increaseIndent(); for (int idx = 0; idx < mUserTypes.length; idx++) { - pw.print(prefix2); pw.println(idx + " -> " + mUserTypes[idx]); + pw.println(idx + " -> " + mUserTypes[idx]); } + pw.decreaseIndent(); pw.decreaseIndent(); + pw.increaseIndent(); final int size = mWhitelistedPackagesForUserTypes.size(); if (size == 0) { - pw.print(prefix); pw.println("No packages"); + pw.println("No packages"); + pw.decreaseIndent(); return; } - pw.print(prefix); pw.print(size); pw.println(" packages:"); + pw.print(size); pw.println(" packages:"); + pw.increaseIndent(); for (int pkgIdx = 0; pkgIdx < size; pkgIdx++) { final String pkgName = mWhitelistedPackagesForUserTypes.keyAt(pkgIdx); - pw.print(prefix2); pw.print(pkgName); pw.print(": "); + pw.print(pkgName); pw.print(": "); final long userTypesBitSet = mWhitelistedPackagesForUserTypes.valueAt(pkgIdx); for (int idx = 0; idx < mUserTypes.length; idx++) { if ((userTypesBitSet & (1 << idx)) != 0) { @@ -636,5 +700,40 @@ class UserSystemPackageInstaller { } pw.println(); } + pw.decreaseIndent(); pw.decreaseIndent(); + + pw.increaseIndent(); + dumpMissingSystemPackages(pw, /* force= */ true, /* verbose= */ true); + pw.decreaseIndent(); + } + + void dumpMissingSystemPackages(IndentingPrintWriter pw, boolean force, boolean verbose) { + final int mode = getWhitelistMode(); + final boolean show = force || (isEnforceMode(mode) && !isImplicitWhitelistMode(mode)); + if (!show) return; + + final List<Pair<Boolean, String>> warnings = checkSystemPackagesWhitelistWarnings(mode); + final int size = warnings.size(); + + if (size == 0) { + if (verbose) { + pw.println("All system packages are accounted for"); + } + return; + } + + if (verbose) { + pw.print(size); pw.println(" warnings for system user:"); + pw.increaseIndent(); + } + for (int i = 0; i < size; i++) { + final Pair<Boolean, String> pair = warnings.get(i); + final String lvl = pair.first ? "WTF" : "WARN"; + final String msg = pair.second; + pw.print(lvl); pw.print(": "); pw.println(msg); + } + if (verbose) { + pw.decreaseIndent(); + } } } diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java index 780b2347287a..4a1a6a766726 100644 --- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java +++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java @@ -31,6 +31,7 @@ import android.content.pm.parsing.component.ParsedActivity; import android.content.pm.parsing.component.ParsedInstrumentation; import android.content.pm.parsing.component.ParsedProvider; import android.content.pm.parsing.component.ParsedService; +import android.os.incremental.IncrementalManager; import android.text.TextUtils; import com.android.internal.content.NativeLibraryHelper; @@ -141,8 +142,15 @@ public class AndroidPackageUtils { public static boolean canHaveOatDir(AndroidPackage pkg, boolean isUpdatedSystemApp) { // The following app types CANNOT have oat directory - // - non-updated system apps - return !pkg.isSystem() || isUpdatedSystemApp; + // - non-updated system apps, + // - incrementally installed apps. + if (pkg.isSystem() && !isUpdatedSystemApp) { + return false; + } + if (IncrementalManager.isIncrementalPath(pkg.getCodePath())) { + return false; + } + return true; } public static boolean hasComponentClassName(AndroidPackage pkg, String className) { diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 4624e9ea0209..2f84a99774f4 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -62,6 +62,8 @@ import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType; +import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD; +import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_OTHER; import static android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN; import static android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION; import static android.view.WindowManagerGlobal.ADD_OKAY; @@ -1328,6 +1330,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { mScreenshotChordVolumeDownKeyConsumed = true; cancelPendingPowerKeyAction(); mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN); + mScreenshotRunnable.setScreenshotSource(SCREENSHOT_KEY_CHORD); mHandler.postDelayed(mScreenshotRunnable, getScreenshotChordLongPressDelay()); } } @@ -1411,14 +1414,19 @@ public class PhoneWindowManager implements WindowManagerPolicy { private class ScreenshotRunnable implements Runnable { private int mScreenshotType = TAKE_SCREENSHOT_FULLSCREEN; + private int mScreenshotSource = SCREENSHOT_KEY_OTHER; public void setScreenshotType(int screenshotType) { mScreenshotType = screenshotType; } + public void setScreenshotSource(int screenshotSource) { + mScreenshotSource = screenshotSource; + } + @Override public void run() { - mDefaultDisplayPolicy.takeScreenshot(mScreenshotType); + mDefaultDisplayPolicy.takeScreenshot(mScreenshotType, mScreenshotSource); } } @@ -2693,6 +2701,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { int type = event.isShiftPressed() ? TAKE_SCREENSHOT_SELECTED_REGION : TAKE_SCREENSHOT_FULLSCREEN; mScreenshotRunnable.setScreenshotType(type); + mScreenshotRunnable.setScreenshotSource(SCREENSHOT_KEY_OTHER); mHandler.post(mScreenshotRunnable); return -1; } @@ -2709,6 +2718,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } else if (keyCode == KeyEvent.KEYCODE_SYSRQ) { if (down && repeatCount == 0) { mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN); + mScreenshotRunnable.setScreenshotSource(SCREENSHOT_KEY_OTHER); mHandler.post(mScreenshotRunnable); } return -1; diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index d89605a9ddbd..da07223686d7 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -395,14 +395,6 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { return false; } - /** - * Returns true if the window has a letterbox and any part of that letterbox overlaps with - * the given {@code rect}. - */ - default boolean isLetterboxedOverlappingWith(Rect rect) { - return false; - } - /** @return the current windowing mode of this window. */ int getWindowingMode(); diff --git a/services/core/java/com/android/server/power/AttentionDetector.java b/services/core/java/com/android/server/power/AttentionDetector.java index 1ab6adee2320..b69c45070487 100644 --- a/services/core/java/com/android/server/power/AttentionDetector.java +++ b/services/core/java/com/android/server/power/AttentionDetector.java @@ -17,7 +17,6 @@ package com.android.server.power; import static android.provider.DeviceConfig.NAMESPACE_ATTENTION_MANAGER_SERVICE; -import static android.provider.Settings.Secure.ADAPTIVE_SLEEP; import android.Manifest; import android.app.ActivityManager; @@ -192,9 +191,6 @@ public class AttentionDetector { } if (!isAttentionServiceSupported() || !serviceHasSufficientPermissions()) { - // Turns off adaptive sleep in settings for all users if attention service is not - // available. The setting itself should also be grayed out in this case. - Settings.Secure.putInt(mContentResolver, ADAPTIVE_SLEEP, 0); return nextScreenDimming; } diff --git a/services/core/java/com/android/server/soundtrigger_middleware/OWNERS b/services/core/java/com/android/server/soundtrigger_middleware/OWNERS new file mode 100644 index 000000000000..e5d037003ac4 --- /dev/null +++ b/services/core/java/com/android/server/soundtrigger_middleware/OWNERS @@ -0,0 +1,2 @@ +ytai@google.com +elaurent@google.com diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java b/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java new file mode 100644 index 000000000000..54ad1d268e56 --- /dev/null +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/CasResource.java @@ -0,0 +1,152 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.tv.tunerresourcemanager; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * A Cas resource object used by the Tuner Resource Manager to record the cas + * information. + * + * @hide + */ +public final class CasResource { + + private final int mSystemId; + + private int mMaxSessionNum; + + private int mAvailableSessionNum; + + /** + * The owner clients' ids when part of the Cas is occupied. + */ + private Map<Integer, Integer> mOwnerClientIdsToSessionNum = new HashMap<>(); + + private CasResource(Builder builder) { + this.mSystemId = builder.mSystemId; + this.mMaxSessionNum = builder.mMaxSessionNum; + this.mAvailableSessionNum = builder.mMaxSessionNum; + } + + public int getSystemId() { + return mSystemId; + } + + public int getMaxSessionNum() { + return mMaxSessionNum; + } + + public int getUsedSessionNum() { + return (mMaxSessionNum - mAvailableSessionNum); + } + + public boolean isFullyUsed() { + return mAvailableSessionNum == 0; + } + + /** + * Update max session number. + * + * @param maxSessionNum the new max session num. + */ + public void updateMaxSessionNum(int maxSessionNum) { + mAvailableSessionNum = Math.max( + 0, mAvailableSessionNum + (maxSessionNum - mMaxSessionNum)); + mMaxSessionNum = maxSessionNum; + } + + /** + * Set an owner for the cas + * + * @param ownerId the client id of the owner. + */ + public void setOwner(int ownerId) { + int sessionNum = mOwnerClientIdsToSessionNum.get(ownerId) == null + ? 1 : (mOwnerClientIdsToSessionNum.get(ownerId) + 1); + mOwnerClientIdsToSessionNum.put(ownerId, sessionNum); + mAvailableSessionNum--; + } + + /** + * Remove an owner of the Cas. + * + * @param ownerId the removing client id of the owner. + */ + public void removeOwner(int ownerId) { + mAvailableSessionNum += mOwnerClientIdsToSessionNum.get(ownerId); + mOwnerClientIdsToSessionNum.remove(ownerId); + } + + public Set<Integer> getOwnerClientIds() { + return mOwnerClientIdsToSessionNum.keySet(); + } + + @Override + public String toString() { + return "CasResource[systemId=" + this.mSystemId + + ", isFullyUsed=" + (this.mAvailableSessionNum == 0) + + ", maxSessionNum=" + this.mMaxSessionNum + + ", ownerClients=" + ownersMapToString() + "]"; + } + + /** + * Builder class for {@link CasResource}. + */ + public static class Builder { + + private int mSystemId; + private int mMaxSessionNum; + + Builder(int systemId) { + this.mSystemId = systemId; + } + + /** + * Builder for {@link CasResource}. + * + * @param maxSessionNum the max session num the current Cas has. + */ + public Builder maxSessionNum(int maxSessionNum) { + this.mMaxSessionNum = maxSessionNum; + return this; + } + + /** + * Build a {@link CasResource}. + * + * @return {@link CasResource}. + */ + public CasResource build() { + CasResource cas = new CasResource(this); + return cas; + } + } + + private String ownersMapToString() { + StringBuilder string = new StringBuilder("{"); + for (int clienId : mOwnerClientIdsToSessionNum.keySet()) { + string.append(" clientId=") + .append(clienId) + .append(", owns session num=") + .append(mOwnerClientIdsToSessionNum.get(clienId)) + .append(","); + } + return string.append("}").toString(); + } +} diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java index 4cdc17292ac4..2b0fe8a2602b 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java @@ -27,6 +27,7 @@ import java.util.Set; public final class ClientProfile { public static final int INVALID_GROUP_ID = -1; + public static final int INVALID_RESOURCE_ID = -1; /** * Client id sent to the client when registering with @@ -56,7 +57,6 @@ public final class ClientProfile { * also lose their resources. */ private int mGroupId = INVALID_GROUP_ID; - /** * Optional nice value for TRM to reduce client’s priority. */ @@ -73,6 +73,11 @@ public final class ClientProfile { private Set<Integer> mUsingLnbIds = new HashSet<>(); /** + * List of the Cas system ids that are used by the current client. + */ + private int mUsingCasSystemId = INVALID_RESOURCE_ID; + + /** * Optional arbitrary priority value given by the client. * * <p>This value can override the default priorotiy calculated from @@ -172,11 +177,32 @@ public final class ClientProfile { } /** + * Set when the client starts to use a Cas system. + * + * @param casSystemId cas being used. + */ + public void useCas(int casSystemId) { + mUsingCasSystemId = casSystemId; + } + + public int getInUseCasSystemId() { + return mUsingCasSystemId; + } + + /** + * Called when the client released a Cas System. + */ + public void releaseCas() { + mUsingCasSystemId = INVALID_RESOURCE_ID; + } + + /** * Called to reclaim all the resources being used by the current client. */ public void reclaimAllResources() { mUsingFrontendIds.clear(); mUsingLnbIds.clear(); + mUsingCasSystemId = INVALID_RESOURCE_ID; } @Override diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java index 7231813dd949..2f70840cfc8b 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java @@ -65,6 +65,8 @@ public class TunerResourceManagerService extends SystemService { private Map<Integer, FrontendResource> mFrontendResources = new HashMap<>(); // Map of the current available lnb resources private Map<Integer, LnbResource> mLnbResources = new HashMap<>(); + // Map of the current available Cas resources + private Map<Integer, CasResource> mCasResources = new HashMap<>(); @GuardedBy("mLock") private Map<Integer, ResourcesReclaimListenerRecord> mListeners = new HashMap<>(); @@ -158,10 +160,8 @@ public class TunerResourceManagerService extends SystemService { @Override public void updateCasInfo(int casSystemId, int maxSessionNum) { enforceTrmAccessPermission("updateCasInfo"); - if (DEBUG) { - Slog.d(TAG, - "updateCasInfo(casSystemId=" + casSystemId - + ", maxSessionNum=" + maxSessionNum + ")"); + synchronized (mLock) { + updateCasInfoInternal(casSystemId, maxSessionNum); } } @@ -185,11 +185,11 @@ public class TunerResourceManagerService extends SystemService { throw new RemoteException("frontendHandle can't be null"); } synchronized (mLock) { - try { - return requestFrontendInternal(request, frontendHandle); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + if (!checkClientExists(request.getClientId())) { + throw new RemoteException("Request frontend from unregistered client:" + + request.getClientId()); } + return requestFrontendInternal(request, frontendHandle); } } @@ -211,32 +211,45 @@ public class TunerResourceManagerService extends SystemService { throw new RemoteException("demuxHandle can't be null"); } synchronized (mLock) { + if (!checkClientExists(request.getClientId())) { + throw new RemoteException("Request demux from unregistered client:" + + request.getClientId()); + } return requestDemuxInternal(request, demuxHandle); } } @Override public boolean requestDescrambler(@NonNull TunerDescramblerRequest request, - @NonNull int[] descrambleHandle) throws RemoteException { + @NonNull int[] descramblerHandle) throws RemoteException { enforceDescramblerAccessPermission("requestDescrambler"); enforceTrmAccessPermission("requestDescrambler"); - if (descrambleHandle == null) { - throw new RemoteException("descrambleHandle can't be null"); + if (descramblerHandle == null) { + throw new RemoteException("descramblerHandle can't be null"); } synchronized (mLock) { - return requestDescramblerInternal(request, descrambleHandle); + if (!checkClientExists(request.getClientId())) { + throw new RemoteException("Request descrambler from unregistered client:" + + request.getClientId()); + } + return requestDescramblerInternal(request, descramblerHandle); } } @Override - public boolean requestCasSession( - @NonNull CasSessionRequest request, @NonNull int[] sessionResourceHandle) { + public boolean requestCasSession(@NonNull CasSessionRequest request, + @NonNull int[] casSessionHandle) throws RemoteException { enforceTrmAccessPermission("requestCasSession"); - if (DEBUG) { - Slog.d(TAG, "requestCasSession(request=" + request + ")"); + if (casSessionHandle == null) { + throw new RemoteException("casSessionHandle can't be null"); + } + synchronized (mLock) { + if (!checkClientExists(request.getClientId())) { + throw new RemoteException("Request cas from unregistered client:" + + request.getClientId()); + } + return requestCasSessionInternal(request, casSessionHandle); } - - return true; } @Override @@ -248,11 +261,11 @@ public class TunerResourceManagerService extends SystemService { throw new RemoteException("lnbHandle can't be null"); } synchronized (mLock) { - try { - return requestLnbInternal(request, lnbHandle); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + if (!checkClientExists(request.getClientId())) { + throw new RemoteException("Request lnb from unregistered client:" + + request.getClientId()); } + return requestLnbInternal(request, lnbHandle); } } @@ -264,15 +277,20 @@ public class TunerResourceManagerService extends SystemService { frontendHandle)) { throw new RemoteException("frontendHandle can't be invalid"); } - int frontendId = getResourceIdFromHandle(frontendHandle); - FrontendResource fe = getFrontendResource(frontendId); - if (fe == null) { - throw new RemoteException("Releasing frontend does not exist."); - } - if (fe.getOwnerClientId() != clientId) { - throw new RemoteException("Client is not the current owner of the releasing fe."); - } synchronized (mLock) { + if (!checkClientExists(clientId)) { + throw new RemoteException("Release frontend from unregistered client:" + + clientId); + } + int frontendId = getResourceIdFromHandle(frontendHandle); + FrontendResource fe = getFrontendResource(frontendId); + if (fe == null) { + throw new RemoteException("Releasing frontend does not exist."); + } + if (fe.getOwnerClientId() != clientId) { + throw new RemoteException( + "Client is not the current owner of the releasing fe."); + } releaseFrontendInternal(fe); } } @@ -296,10 +314,26 @@ public class TunerResourceManagerService extends SystemService { } @Override - public void releaseCasSession(int sessionResourceId, int clientId) { + public void releaseCasSession(int casSessionHandle, int clientId) throws RemoteException { enforceTrmAccessPermission("releaseCasSession"); - if (DEBUG) { - Slog.d(TAG, "releaseCasSession(sessionResourceId=" + sessionResourceId + ")"); + if (!validateResourceHandle( + TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, casSessionHandle)) { + throw new RemoteException("casSessionHandle can't be invalid"); + } + synchronized (mLock) { + if (!checkClientExists(clientId)) { + throw new RemoteException("Release cas from unregistered client:" + clientId); + } + int casSystemId = getClientProfile(clientId).getInUseCasSystemId(); + CasResource cas = getCasResource(casSystemId); + if (cas == null) { + throw new RemoteException("Releasing cas does not exist."); + } + if (!cas.getOwnerClientIds().contains(clientId)) { + throw new RemoteException( + "Client is not the current owner of the releasing cas."); + } + releaseCasSessionInternal(cas, clientId); } } @@ -310,6 +344,9 @@ public class TunerResourceManagerService extends SystemService { if (!validateResourceHandle(TunerResourceManager.TUNER_RESOURCE_TYPE_LNB, lnbHandle)) { throw new RemoteException("lnbHandle can't be invalid"); } + if (!checkClientExists(clientId)) { + throw new RemoteException("Release lnb from unregistered client:" + clientId); + } int lnbId = getResourceIdFromHandle(lnbHandle); LnbResource lnb = getLnbResource(lnbId); if (lnb == null) { @@ -465,17 +502,42 @@ public class TunerResourceManagerService extends SystemService { } @VisibleForTesting - protected boolean requestFrontendInternal(TunerFrontendRequest request, int[] frontendHandle) - throws RemoteException { + protected void updateCasInfoInternal(int casSystemId, int maxSessionNum) { + if (DEBUG) { + Slog.d(TAG, + "updateCasInfo(casSystemId=" + casSystemId + + ", maxSessionNum=" + maxSessionNum + ")"); + } + // If maxSessionNum is 0, removing the Cas Resource. + if (maxSessionNum == 0) { + removeCasResource(casSystemId); + return; + } + // If the Cas exists, updates the Cas Resource accordingly. + CasResource cas = getCasResource(casSystemId); + if (cas != null) { + if (cas.getUsedSessionNum() > maxSessionNum) { + // Sort and release the short number of Cas resources. + int releasingCasResourceNum = cas.getUsedSessionNum() - maxSessionNum; + releaseLowerPriorityClientCasResources(releasingCasResourceNum); + } + cas.updateMaxSessionNum(maxSessionNum); + return; + } + // Add the new Cas Resource. + cas = new CasResource.Builder(casSystemId) + .maxSessionNum(maxSessionNum) + .build(); + addCasResource(cas); + } + + @VisibleForTesting + protected boolean requestFrontendInternal(TunerFrontendRequest request, int[] frontendHandle) { if (DEBUG) { Slog.d(TAG, "requestFrontend(request=" + request + ")"); } frontendHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE; - if (!checkClientExists(request.getClientId())) { - Slog.e(TAG, "Request frontend from unregistered client:" + request.getClientId()); - return false; - } ClientProfile requestClient = getClientProfile(request.getClientId()); int grantingFrontendId = -1; int inUseLowestPriorityFrId = -1; @@ -496,7 +558,7 @@ public class TunerResourceManagerService extends SystemService { } else if (grantingFrontendId < 0) { // Record the frontend id with the lowest client priority among all the // in use frontends when no available frontend has been found. - int priority = getOwnerClientPriority(fr); + int priority = getOwnerClientPriority(fr.getOwnerClientId()); if (currentLowestPriority > priority) { inUseLowestPriorityFrId = fr.getId(); currentLowestPriority = priority; @@ -530,17 +592,12 @@ public class TunerResourceManagerService extends SystemService { } @VisibleForTesting - protected boolean requestLnbInternal(TunerLnbRequest request, int[] lnbHandle) - throws RemoteException { + protected boolean requestLnbInternal(TunerLnbRequest request, int[] lnbHandle) { if (DEBUG) { Slog.d(TAG, "requestLnb(request=" + request + ")"); } lnbHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE; - if (!checkClientExists(request.getClientId())) { - Slog.e(TAG, "Request lnb from unregistered client:" + request.getClientId()); - return false; - } ClientProfile requestClient = getClientProfile(request.getClientId()); int grantingLnbId = -1; int inUseLowestPriorityLnbId = -1; @@ -554,7 +611,7 @@ public class TunerResourceManagerService extends SystemService { } else { // Record the lnb id with the lowest client priority among all the // in use lnb when no available lnb has been found. - int priority = getOwnerClientPriority(lnb); + int priority = getOwnerClientPriority(lnb.getOwnerClientId()); if (currentLowestPriority > priority) { inUseLowestPriorityLnbId = lnb.getId(); currentLowestPriority = priority; @@ -588,7 +645,55 @@ public class TunerResourceManagerService extends SystemService { } @VisibleForTesting - void releaseFrontendInternal(FrontendResource fe) { + protected boolean requestCasSessionInternal(CasSessionRequest request, int[] casSessionHandle) { + if (DEBUG) { + Slog.d(TAG, "requestCasSession(request=" + request + ")"); + } + CasResource cas = getCasResource(request.getCasSystemId()); + // Unregistered Cas System is treated as having unlimited sessions. + if (cas == null) { + cas = new CasResource.Builder(request.getCasSystemId()) + .maxSessionNum(Integer.MAX_VALUE) + .build(); + addCasResource(cas); + } + casSessionHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE; + ClientProfile requestClient = getClientProfile(request.getClientId()); + int lowestPriorityOwnerId = -1; + // Priority max value is 1000 + int currentLowestPriority = MAX_CLIENT_PRIORITY + 1; + if (!cas.isFullyUsed()) { + casSessionHandle[0] = generateResourceHandle( + TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, cas.getSystemId()); + updateCasClientMappingOnNewGrant(request.getCasSystemId(), request.getClientId()); + return true; + } + for (int ownerId : cas.getOwnerClientIds()) { + // Record the client id with lowest priority that is using the current Cas system. + int priority = getOwnerClientPriority(ownerId); + if (currentLowestPriority > priority) { + lowestPriorityOwnerId = ownerId; + currentLowestPriority = priority; + } + } + + // When all the Cas sessions are occupied, reclaim the lowest priority client if the + // request client has higher priority. + if (lowestPriorityOwnerId > -1 && (requestClient.getPriority() > currentLowestPriority)) { + if (!reclaimResource(lowestPriorityOwnerId, + TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION)) { + return false; + } + casSessionHandle[0] = generateResourceHandle( + TunerResourceManager.TUNER_RESOURCE_TYPE_CAS_SESSION, cas.getSystemId()); + updateCasClientMappingOnNewGrant(request.getCasSystemId(), request.getClientId()); + return true; + } + return false; + } + + @VisibleForTesting + protected void releaseFrontendInternal(FrontendResource fe) { if (DEBUG) { Slog.d(TAG, "releaseFrontend(id=" + fe.getId() + ")"); } @@ -596,7 +701,7 @@ public class TunerResourceManagerService extends SystemService { } @VisibleForTesting - void releaseLnbInternal(LnbResource lnb) { + protected void releaseLnbInternal(LnbResource lnb) { if (DEBUG) { Slog.d(TAG, "releaseLnb(lnbId=" + lnb.getId() + ")"); } @@ -604,7 +709,15 @@ public class TunerResourceManagerService extends SystemService { } @VisibleForTesting - boolean requestDemuxInternal(TunerDemuxRequest request, int[] demuxHandle) { + protected void releaseCasSessionInternal(CasResource cas, int ownerClientId) { + if (DEBUG) { + Slog.d(TAG, "releaseCasSession(sessionResourceId=" + cas.getSystemId() + ")"); + } + updateCasClientMappingOnRelease(cas, ownerClientId); + } + + @VisibleForTesting + protected boolean requestDemuxInternal(TunerDemuxRequest request, int[] demuxHandle) { if (DEBUG) { Slog.d(TAG, "requestDemux(request=" + request + ")"); } @@ -614,7 +727,8 @@ public class TunerResourceManagerService extends SystemService { } @VisibleForTesting - boolean requestDescramblerInternal(TunerDescramblerRequest request, int[] descramblerHandle) { + protected boolean requestDescramblerInternal( + TunerDescramblerRequest request, int[] descramblerHandle) { if (DEBUG) { Slog.d(TAG, "requestDescrambler(request=" + request + ")"); } @@ -742,14 +856,28 @@ public class TunerResourceManagerService extends SystemService { ownerProfile.releaseLnb(releasingLnb.getId()); } + private void updateCasClientMappingOnNewGrant(int grantingId, int ownerClientId) { + CasResource grantingCas = getCasResource(grantingId); + ClientProfile ownerProfile = getClientProfile(ownerClientId); + grantingCas.setOwner(ownerClientId); + ownerProfile.useCas(grantingId); + } + + private void updateCasClientMappingOnRelease( + @NonNull CasResource releasingCas, int ownerClientId) { + ClientProfile ownerProfile = getClientProfile(ownerClientId); + releasingCas.removeOwner(ownerClientId); + ownerProfile.releaseCas(); + } + /** * Get the owner client's priority from the resource id. * - * @param resource a in use tuner resource. + * @param clientId the owner client id. * @return the priority of the owner client of the resource. */ - private int getOwnerClientPriority(TunerResourceBasic resource) { - return getClientProfile(resource.getOwnerClientId()).getPriority(); + private int getOwnerClientPriority(int clientId) { + return getClientProfile(clientId).getPriority(); } @VisibleForTesting @@ -783,6 +911,9 @@ public class TunerResourceManagerService extends SystemService { private void removeFrontendResource(int removingId) { FrontendResource fe = getFrontendResource(removingId); + if (fe == null) { + return; + } if (fe.isInUse()) { releaseFrontendInternal(fe); } @@ -811,6 +942,9 @@ public class TunerResourceManagerService extends SystemService { private void removeLnbResource(int removingId) { LnbResource lnb = getLnbResource(removingId); + if (lnb == null) { + return; + } if (lnb.isInUse()) { releaseLnbInternal(lnb); } @@ -819,6 +953,39 @@ public class TunerResourceManagerService extends SystemService { @VisibleForTesting @Nullable + protected CasResource getCasResource(int systemId) { + return mCasResources.get(systemId); + } + + @VisibleForTesting + protected Map<Integer, CasResource> getCasResources() { + return mCasResources; + } + + private void addCasResource(CasResource newCas) { + // Update resource list and available id list + mCasResources.put(newCas.getSystemId(), newCas); + } + + private void removeCasResource(int removingId) { + CasResource cas = getCasResource(removingId); + if (cas == null) { + return; + } + for (int ownerId : cas.getOwnerClientIds()) { + getClientProfile(ownerId).releaseCas(); + } + mCasResources.remove(removingId); + } + + private void releaseLowerPriorityClientCasResources(int releasingCasResourceNum) { + // TODO: Sort with a treemap + + // select the first num client to release + } + + @VisibleForTesting + @Nullable protected ClientProfile getClientProfile(int clientId) { return mClientProfiles.get(clientId); } @@ -830,12 +997,7 @@ public class TunerResourceManagerService extends SystemService { } private void removeClientProfile(int clientId) { - for (int id : getClientProfile(clientId).getInUseFrontendIds()) { - getFrontendResource(id).removeOwner(); - for (int groupMemberId : getFrontendResource(id).getExclusiveGroupMemberFeIds()) { - getFrontendResource(groupMemberId).removeOwner(); - } - } + reclaimingResourcesFromClient(getClientProfile(clientId)); mClientProfiles.remove(clientId); mListeners.remove(clientId); } @@ -847,6 +1009,9 @@ public class TunerResourceManagerService extends SystemService { for (Integer lnbId : profile.getInUseLnbIds()) { getLnbResource(lnbId).removeOwner(); } + if (profile.getInUseCasSystemId() != ClientProfile.INVALID_RESOURCE_ID) { + getCasResource(profile.getInUseCasSystemId()).removeOwner(profile.getId()); + } profile.reclaimAllResources(); } diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index abccf99579b7..785ca908b703 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -45,6 +45,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.util.ArraySet; +import android.util.IntArray; import android.util.Slog; import android.util.SparseArray; import android.util.TypedValue; @@ -66,6 +67,7 @@ import com.android.server.policy.WindowManagerPolicy; import com.android.server.wm.WindowManagerInternal.MagnificationCallbacks; import com.android.server.wm.WindowManagerInternal.WindowsForAccessibilityCallback; +import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -127,14 +129,13 @@ final class AccessibilityController { */ public boolean setWindowsForAccessibilityCallbackLocked(int displayId, WindowsForAccessibilityCallback callback) { - if (callback != null) { - final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId); - if (dc == null) { - return false; - } + final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId); + if (dc == null) { + return false; + } - final Display display = dc.getDisplay(); - if (display.getType() == Display.TYPE_VIRTUAL && dc.getParentWindow() != null) { + if (callback != null) { + if (isEmbeddedDisplay(dc)) { // If this display is an embedded one, its window observer should have been set from // window manager after setting its parent window. But if its window observer is // empty, that means this mapping didn't be set, and needs to do this again. @@ -151,13 +152,22 @@ final class AccessibilityController { mWindowsForAccessibilityObserver.put(displayId, new WindowsForAccessibilityObserver(mService, displayId, callback)); } else { + if (isEmbeddedDisplay(dc)) { + // If this display is an embedded one, its window observer should be removed along + // with the window observer of its parent display removed because the window + // observer of the embedded display and its parent display is the same, and would + // be removed together when stopping the window tracking of its parent display. So + // here don't need to do removing window observer of the embedded display again. + return true; + } final WindowsForAccessibilityObserver windowsForA11yObserver = mWindowsForAccessibilityObserver.get(displayId); - if (windowsForA11yObserver == null) { + if (windowsForA11yObserver == null) { throw new IllegalStateException( "Windows for accessibility callback of display " + displayId + " already cleared!"); } + removeObserverOfEmbeddedDisplay(windowsForA11yObserver); mWindowsForAccessibilityObserver.remove(displayId); } return true; @@ -331,6 +341,7 @@ final class AccessibilityController { mWindowsForAccessibilityObserver.get(parentDisplayId); if (windowsForA11yObserver != null) { + windowsForA11yObserver.addEmbeddedDisplay(embeddedDisplayId); // Replaces the observer of embedded display to the one of parent display mWindowsForAccessibilityObserver.put(embeddedDisplayId, windowsForA11yObserver); } @@ -341,6 +352,33 @@ final class AccessibilityController { windowState.getTransformationMatrix(sTempFloats, outMatrix); } + void dump(PrintWriter pw, String prefix) { + for (int i = 0; i < mDisplayMagnifiers.size(); i++) { + final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.valueAt(i); + if (displayMagnifier != null) { + displayMagnifier.dump(pw, prefix + + "Magnification display# " + mDisplayMagnifiers.keyAt(i)); + } + } + } + + private void removeObserverOfEmbeddedDisplay(WindowsForAccessibilityObserver + observerOfParentDisplay) { + final IntArray embeddedDisplayIdList = + observerOfParentDisplay.getAndClearEmbeddedDisplayIdList(); + + for (int index = 0; index < embeddedDisplayIdList.size(); index++) { + final int embeddedDisplayId = embeddedDisplayIdList.get(index); + mWindowsForAccessibilityObserver.remove(embeddedDisplayId); + } + } + + private static boolean isEmbeddedDisplay(DisplayContent dc) { + final Display display = dc.getDisplay(); + + return display.getType() == Display.TYPE_VIRTUAL && dc.getParentWindow() != null; + } + /** * This class encapsulates the functionality related to display magnification. */ @@ -551,6 +589,10 @@ final class AccessibilityController { mMagnifedViewport.drawWindowIfNeededLocked(t); } + void dump(PrintWriter pw, String prefix) { + mMagnifedViewport.dump(pw, prefix); + } + private final class MagnifiedViewport { private final SparseArray<WindowState> mTempWindowStates = @@ -820,6 +862,10 @@ final class AccessibilityController { }, false /* traverseTopToBottom */ ); } + void dump(PrintWriter pw, String prefix) { + mWindow.dump(pw, prefix); + } + private final class ViewportWindow { private static final String SURFACE_TITLE = "Magnification Overlay"; @@ -985,6 +1031,14 @@ final class AccessibilityController { mSurface.release(); } + void dump(PrintWriter pw, String prefix) { + pw.println(prefix + + " mBounds= " + mBounds + + " mDirtyRect= " + mDirtyRect + + " mWidth= " + mSurfaceControl.getWidth() + + " mHeight= " + mSurfaceControl.getHeight()); + } + private final class AnimationController extends Handler { private static final String PROPERTY_NAME_ALPHA = "alpha"; @@ -1152,6 +1206,8 @@ final class AccessibilityController { private final long mRecurringAccessibilityEventsIntervalMillis; + private final IntArray mEmbeddedDisplayIdList = new IntArray(0); + public WindowsForAccessibilityObserver(WindowManagerService windowManagerService, int displayId, WindowsForAccessibilityCallback callback) { @@ -1176,6 +1232,21 @@ final class AccessibilityController { } } + IntArray getAndClearEmbeddedDisplayIdList() { + final IntArray returnedArray = new IntArray(mEmbeddedDisplayIdList.size()); + returnedArray.addAll(mEmbeddedDisplayIdList); + mEmbeddedDisplayIdList.clear(); + + return returnedArray; + } + + void addEmbeddedDisplay(int displayId) { + if (displayId == mDisplayId) { + return; + } + mEmbeddedDisplayIdList.add(displayId); + } + /** * Check if windows have changed, and send them to the accessibility subsystem if they have. * diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 2648c86d3c6a..130da2dfe9c5 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -1188,12 +1188,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final boolean inPictureInPictureMode = inPinnedWindowingMode() && targetStackBounds != null; if (inPictureInPictureMode != mLastReportedPictureInPictureMode || forceUpdate) { - // Picture-in-picture mode changes also trigger a multi-window mode change as well, so - // update that here in order. Set the last reported MW state to the same as the PiP - // state since we haven't yet actually resized the task (these callbacks need to - // preceed the configuration change from the resiez. + // Picture-in-picture mode change normal triggers also multi-window mode change + // except transitions between pip and split screen mode, so update that here in order. + // Set the last reported MW state to the same as the PiP state since we haven't yet + // actually resized the task (these callbacks need to proceed the configuration change + // from the resize). // TODO(110009072): Once we move these callbacks to the client, remove all logic related // to forcing the update of the picture-in-picture mode as a part of the PiP animation. + final boolean shouldScheduleMultiWindowModeChange = + mLastReportedMultiWindowMode != inMultiWindowMode(); mLastReportedPictureInPictureMode = inPictureInPictureMode; mLastReportedMultiWindowMode = inPictureInPictureMode; final Configuration newConfig = new Configuration(); @@ -1204,7 +1207,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A task.computeConfigResourceOverrides(newConfig, task.getParent().getConfiguration()); } schedulePictureInPictureModeChanged(newConfig); - scheduleMultiWindowModeChanged(newConfig); + if (shouldScheduleMultiWindowModeChange) { + scheduleMultiWindowModeChanged(newConfig); + } } } @@ -1414,11 +1419,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } /** - * @return {@code true} if there is a letterbox and any part of that letterbox overlaps with - * the given {@code rect}. + * @see Letterbox#notIntersectsOrFullyContains(Rect) */ - boolean isLetterboxOverlappingWith(Rect rect) { - return mLetterbox != null && mLetterbox.isOverlappingWith(rect); + boolean letterboxNotIntersectsOrFullyContains(Rect rect) { + return mLetterbox == null || mLetterbox.notIntersectsOrFullyContains(rect); } static class Token extends IApplicationToken.Stub { @@ -2672,7 +2676,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (isState(PAUSED) && mStackSupervisor.getKeyguardController().isKeyguardLocked() && getStack().topActivityOccludesKeyguard()) { - getStack().ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, + getDisplay().ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, false /* preserveWindows */, false /* notifyClients */); } @@ -3137,7 +3141,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A commitVisibility(false /* visible */, true /* performLayout */); getDisplayContent().mOpeningApps.remove(this); - getDisplayContent().mChangingContainers.remove(this); getDisplayContent().mUnknownAppVisibilityController.appRemovedOrHidden(this); mWmService.mTaskSnapshotController.onAppRemoved(this); mStackSupervisor.getActivityMetricsLogger().notifyActivityRemoved(this); @@ -6130,19 +6133,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A @Override void cancelAnimation() { - cancelAnimationOnly(); - clearThumbnail(); - mSurfaceFreezer.unfreeze(getPendingTransaction()); - } - - /** - * This only cancels the animation. It doesn't do other teardown like cleaning-up thumbnail - * or interim leashes. - * <p> - * Used when canceling in preparation for starting a new animation. - */ - void cancelAnimationOnly() { super.cancelAnimation(); + clearThumbnail(); } @VisibleForTesting diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 0754a348740b..8edc84fc8f37 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -168,7 +168,6 @@ class ActivityStarter { // The task display area to launch the activity onto, barring any strong reason to do otherwise. private TaskDisplayArea mPreferredTaskDisplayArea; - // The windowing mode to apply to the root task, if possible private int mPreferredWindowingMode; private Task mInTask; @@ -1560,9 +1559,6 @@ class ActivityStarter { if (!mAvoidMoveToFront && mDoResume) { mTargetStack.getStack().moveToFront("reuseOrNewTask", targetTask); if (mOptions != null) { - if (mPreferredWindowingMode != WINDOWING_MODE_UNDEFINED) { - mTargetStack.setWindowingMode(mPreferredWindowingMode); - } if (mOptions.getTaskAlwaysOnTop()) { mTargetStack.setAlwaysOnTop(true); } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 736bc9f8bc22..02f6a696f598 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -5400,14 +5400,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final boolean inputMethodExists = !(config.keyboard == Configuration.KEYBOARD_NOKEYS && config.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH && config.navigation == Configuration.NAVIGATION_NONAV); - int modeType = config.uiMode & Configuration.UI_MODE_TYPE_MASK; - final boolean uiModeSupportsDialogs = (modeType != Configuration.UI_MODE_TYPE_CAR - && !(modeType == Configuration.UI_MODE_TYPE_WATCH && Build.IS_USER) - && modeType != Configuration.UI_MODE_TYPE_TELEVISION - && modeType != Configuration.UI_MODE_TYPE_VR_HEADSET); final boolean hideDialogsSet = Settings.Global.getInt(mContext.getContentResolver(), HIDE_ERROR_DIALOGS, 0) != 0; - mShowDialogs = inputMethodExists && uiModeSupportsDialogs && !hideDialogsSet; + mShowDialogs = inputMethodExists + && ActivityTaskManager.currentUiModeSupportsErrorDialogs(mContext) + && !hideDialogsSet; } private void updateFontScaleIfNeeded(@UserIdInt int userId) { @@ -6607,7 +6604,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } return; } - process.mIsImeProcess = true; process.registerDisplayConfigurationListener(displayContent); } } diff --git a/services/core/java/com/android/server/wm/BarController.java b/services/core/java/com/android/server/wm/BarController.java index 57cdb0b77bdb..8b14095874e3 100644 --- a/services/core/java/com/android/server/wm/BarController.java +++ b/services/core/java/com/android/server/wm/BarController.java @@ -168,7 +168,7 @@ public class BarController { } boolean isTransparentAllowed(WindowState win) { - return win == null || !win.isLetterboxedOverlappingWith(mContentFrame); + return win == null || win.letterboxNotIntersectsOrFullyContains(mContentFrame); } boolean setBarShowingLw(final boolean show) { diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index e26163247020..8f7fc9e354a0 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -5534,4 +5534,34 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } } } + + /** + * Returns the number of window tokens without surface on this display. A {@link WindowToken} + * won't have its {@link SurfaceControl} until a window is added to a {@link WindowToken}. + * The purpose of this method is to accumulate non-window containing {@link WindowToken}s and + * limit the usage if the count exceeds a number. + * + * @param callingUid app calling uid + * @return the number of window tokens without surface on this display + * @see WindowToken#addWindow(WindowState) + */ + int getWindowTokensWithoutSurfaceCount(int callingUid) { + List<WindowToken> tokens = new ArrayList<>(mTokenMap.values()); + int count = 0; + for (int i = tokens.size() - 1; i >= 0; i--) { + final WindowToken token = tokens.get(i); + if (callingUid != token.getOwnerUid()) { + continue; + } + // Skip if token is an Activity + if (token.asActivityRecord() != null) { + continue; + } + if (token.mSurfaceControl != null) { + continue; + } + count++; + } + return count; + } } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index e9d3d56ee283..8aace212d094 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -261,6 +261,8 @@ public class DisplayPolicy { @Px private int mRightGestureInset; + private boolean mNavButtonForcedVisible; + StatusBarManagerInternal getStatusBarManagerInternal() { synchronized (mServiceAcquireLock) { if (mStatusBarManagerInternal == null) { @@ -1046,12 +1048,14 @@ public class DisplayPolicy { // calculate inset. if (navigationBarPosition(displayFrames.mDisplayWidth, displayFrames.mDisplayHeight, - displayFrames.mRotation) == NAV_BAR_BOTTOM) { + displayFrames.mRotation) == NAV_BAR_BOTTOM + && !mNavButtonForcedVisible) { + sTmpRect.set(displayFrames.mUnrestricted); sTmpRect.intersectUnchecked(displayFrames.mDisplayCutoutSafe); inOutFrame.top = sTmpRect.bottom - getNavigationBarHeight(displayFrames.mRotation, - mDisplayContent.getConfiguration().uiMode); + mDisplayContent.getConfiguration().uiMode); } }, @@ -2810,6 +2814,8 @@ public class DisplayPolicy { mNavBarOpacityMode = res.getInteger(R.integer.config_navBarOpacityMode); mLeftGestureInset = mGestureNavigationSettingsObserver.getLeftSensitivity(res); mRightGestureInset = mGestureNavigationSettingsObserver.getRightSensitivity(res); + mNavButtonForcedVisible = + mGestureNavigationSettingsObserver.areNavigationButtonForcedVisible(); mNavigationBarLetsThroughTaps = res.getBoolean(R.bool.config_navBarTapThrough); mNavigationBarAlwaysShowOnSideGesture = res.getBoolean(R.bool.config_navBarAlwaysShowOnSideEdgeGesture); @@ -3783,13 +3789,14 @@ public class DisplayPolicy { * @param screenshotType The type of screenshot, for example either * {@link WindowManager#TAKE_SCREENSHOT_FULLSCREEN} or * {@link WindowManager#TAKE_SCREENSHOT_SELECTED_REGION} + * @param source Where the screenshot originated from (see WindowManager.ScreenshotSource) */ - public void takeScreenshot(int screenshotType) { + public void takeScreenshot(int screenshotType, int source) { if (mScreenshotHelper != null) { mScreenshotHelper.takeScreenshot(screenshotType, mStatusBar != null && mStatusBar.isVisibleLw(), mNavigationBar != null && mNavigationBar.isVisibleLw(), - mHandler, null /* completionConsumer */); + source, mHandler, null /* completionConsumer */); } } diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index 61a199a816df..5a9bf809fa4a 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -126,13 +126,22 @@ class InsetsPolicy { mPolicy.getStatusBarManagerInternal().showTransient(mDisplayContent.getDisplayId(), mShowingTransientTypes.toArray()); updateBarControlTarget(mFocusedWin); - InsetsState state = new InsetsState(mStateController.getRawInsetsState()); - startAnimation(true /* show */, () -> { + + // The leashes can be created while updating bar control target. The surface transaction + // of the new leashes might not be applied yet. The callback posted here ensures we can + // get the valid leashes because the surface transaction will be applied in the next + // animation frame which will be triggered if a new leash is created. + mDisplayContent.mWmService.mAnimator.getChoreographer().postFrameCallback(time -> { synchronized (mDisplayContent.mWmService.mGlobalLock) { - mStateController.notifyInsetsChanged(); + final InsetsState state = new InsetsState(mStateController.getRawInsetsState()); + startAnimation(true /* show */, () -> { + synchronized (mDisplayContent.mWmService.mGlobalLock) { + mStateController.notifyInsetsChanged(); + } + }, state); + mStateController.onInsetsModified(mDummyControlTarget, state); } - }, state); - mStateController.onInsetsModified(mDummyControlTarget, state); + }); } } diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index 1ca82ceeb570..351743f962b9 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -66,6 +66,7 @@ class InsetsSourceProvider { private TriConsumer<DisplayFrames, WindowState, Rect> mFrameProvider; private TriConsumer<DisplayFrames, WindowState, Rect> mImeFrameProvider; private final Rect mImeOverrideFrame = new Rect(); + private boolean mIsLeashReadyForDispatching; /** The visibility override from the current controlling window. */ private boolean mClientVisible; @@ -266,9 +267,14 @@ class InsetsSourceProvider { if (getSource().getType() == ITYPE_IME) { setClientVisible(InsetsState.getDefaultVisibility(mSource.getType())); } - final Transaction t = mDisplayContent.mWmService.mTransactionFactory.get(); + final Transaction t = mDisplayContent.getPendingTransaction(); mWin.startAnimation(t, mAdapter, !mClientVisible /* hidden */, ANIMATION_TYPE_INSETS_CONTROL, null /* animationFinishedCallback */); + + // The leash was just created. We cannot dispatch it until its surface transaction is + // applied. Otherwise, the client's operation to the leash might be overwritten by us. + mIsLeashReadyForDispatching = false; + final SurfaceControl leash = mAdapter.mCapturedLeash; final long frameNumber = mFinishSeamlessRotateFrameNumber; mFinishSeamlessRotateFrameNumber = -1; @@ -281,9 +287,6 @@ class InsetsSourceProvider { t.deferTransactionUntil(mWin.getSurfaceControl(), barrier, frameNumber); t.deferTransactionUntil(leash, barrier, frameNumber); } - // Applying the transaction here can prevent the client from applying its transaction sooner - // than us which makes us overwrite the client's operation to the leash. - t.apply(); mControlTarget = target; mControl = new InsetsSourceControl(mSource.getType(), leash, new Point(mWin.getWindowFrames().mFrame.left, mWin.getWindowFrames().mFrame.top)); @@ -313,6 +316,10 @@ class InsetsSourceProvider { return true; } + void onSurfaceTransactionApplied() { + mIsLeashReadyForDispatching = true; + } + private void setClientVisible(boolean clientVisible) { if (mClientVisible == clientVisible) { return; @@ -334,6 +341,13 @@ class InsetsSourceProvider { InsetsSourceControl getControl(InsetsControlTarget target) { if (target == mControlTarget) { + if (!mIsLeashReadyForDispatching && mControl != null) { + // The surface transaction of preparing leash is not applied yet. We don't send it + // to the client in case that the client applies its transaction sooner than ours + // that we could unexpectedly overwrite the surface state. + return new InsetsSourceControl(mControl.getType(), null /* leash */, + mControl.getSurfacePosition()); + } return mControl; } if (target == mFakeControlTarget) { diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java index 4ac319ddf6ce..765f98065dd0 100644 --- a/services/core/java/com/android/server/wm/InsetsStateController.java +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -407,6 +407,10 @@ class InsetsStateController { return; } mDisplayContent.mWmService.mAnimator.addAfterPrepareSurfacesRunnable(() -> { + for (int i = mProviders.size() - 1; i >= 0; i--) { + final InsetsSourceProvider provider = mProviders.valueAt(i); + provider.onSurfaceTransactionApplied(); + } for (int i = mPendingControlChanged.size() - 1; i >= 0; i--) { final InsetsControlTarget controlTarget = mPendingControlChanged.valueAt(i); controlTarget.notifyInsetsControlChanged(); diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java index 6721ef836e4e..00a4be3d1e35 100644 --- a/services/core/java/com/android/server/wm/Letterbox.java +++ b/services/core/java/com/android/server/wm/Letterbox.java @@ -77,10 +77,10 @@ public class Letterbox { mOuter.set(outer); mInner.set(inner); - mTop.layout(outer.left, outer.top, inner.right, inner.top, surfaceOrigin); - mLeft.layout(outer.left, inner.top, inner.left, outer.bottom, surfaceOrigin); - mBottom.layout(inner.left, inner.bottom, outer.right, outer.bottom, surfaceOrigin); - mRight.layout(inner.right, outer.top, outer.right, inner.bottom, surfaceOrigin); + mTop.layout(outer.left, outer.top, outer.right, inner.top, surfaceOrigin); + mLeft.layout(outer.left, outer.top, inner.left, outer.bottom, surfaceOrigin); + mBottom.layout(outer.left, inner.bottom, outer.right, outer.bottom, surfaceOrigin); + mRight.layout(inner.right, outer.top, outer.right, outer.bottom, surfaceOrigin); } @@ -101,17 +101,29 @@ public class Letterbox { } /** - * Returns true if any part of the letterbox overlaps with the given {@code rect}. + * Returns {@code true} if the letterbox does not overlap with the bar, or the letterbox can + * fully cover the window frame. + * + * @param rect The area of the window frame. */ - public boolean isOverlappingWith(Rect rect) { + boolean notIntersectsOrFullyContains(Rect rect) { + int emptyCount = 0; + int noOverlappingCount = 0; for (LetterboxSurface surface : mSurfaces) { - if (surface.isOverlappingWith(rect)) { + final Rect surfaceRect = surface.mLayoutFrameGlobal; + if (surfaceRect.isEmpty()) { + // empty letterbox + emptyCount++; + } else if (!Rect.intersects(surfaceRect, rect)) { + // no overlapping + noOverlappingCount++; + } else if (surfaceRect.contains(rect)) { + // overlapping and covered return true; } } - return false; + return (emptyCount + noOverlappingCount) == mSurfaces.length; } - /** * Hides the letterbox. * @@ -282,17 +294,6 @@ public class Letterbox { return Math.max(0, mLayoutFrameGlobal.height()); } - /** - * Returns if the given {@code rect} overlaps with this letterbox piece. - * @param rect the area to check for overlap in global coordinates - */ - public boolean isOverlappingWith(Rect rect) { - if (mLayoutFrameGlobal.isEmpty()) { - return false; - } - return Rect.intersects(rect, mLayoutFrameGlobal); - } - public void applySurfaceChanges(SurfaceControl.Transaction t) { if (mSurfaceFrameRelative.equals(mLayoutFrameRelative)) { // Nothing changed. diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java index 9d44cad70281..86e081854597 100644 --- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java +++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java @@ -212,7 +212,7 @@ class ScreenRotationAnimation { final Surface surface = mService.mSurfaceFactory.get(); surface.copyFrom(mScreenshotLayer); SurfaceControl.ScreenshotGraphicBuffer gb = - mService.mDisplayManagerInternal.screenshot(displayId); + mService.mDisplayManagerInternal.systemScreenshot(displayId); if (gb != null) { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "ScreenRotationAnimation#getMedianBorderLuma"); diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index df5cfee6c01c..93c6b6e7b83c 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -1943,12 +1943,15 @@ class Task extends WindowContainer<WindowContainer> { final int prevWinMode = getWindowingMode(); mTmpPrevBounds.set(getBounds()); final boolean wasInMultiWindowMode = inMultiWindowMode(); + final boolean wasInPictureInPicture = inPinnedWindowingMode(); super.onConfigurationChanged(newParentConfig); // Only need to update surface size here since the super method will handle updating // surface position. updateSurfaceSize(getPendingTransaction()); - if (wasInMultiWindowMode != inMultiWindowMode()) { + if (wasInPictureInPicture != inPinnedWindowingMode()) { + mStackSupervisor.scheduleUpdatePictureInPictureModeIfNeeded(this, getStack()); + } else if (wasInMultiWindowMode != inMultiWindowMode()) { mStackSupervisor.scheduleUpdateMultiWindowMode(this); } @@ -1992,7 +1995,8 @@ class Task extends WindowContainer<WindowContainer> { if (mWmService.mDisableTransitionAnimation || !isVisible() || getDisplayContent().mAppTransition.isTransitionSet() - || getSurfaceControl() == null) { + || getSurfaceControl() == null + || !isLeafTask()) { return false; } // Only do an animation into and out-of freeform mode for now. Other mode @@ -2281,16 +2285,18 @@ class Task extends WindowContainer<WindowContainer> { } density *= DisplayMetrics.DENSITY_DEFAULT_SCALE; - // If bounds have been overridden at this level, restrict config resources to these bounds - // rather than the parent because the overridden bounds can be larger than the parent. - boolean hasOverrideBounds = false; + // The bounds may have been overridden at this level. If the parent cannot cover these + // bounds, the configuration is still computed according to the override bounds. + final boolean insideParentBounds; + final Rect parentBounds = parentConfig.windowConfiguration.getBounds(); final Rect resolvedBounds = inOutConfig.windowConfiguration.getBounds(); if (resolvedBounds == null || resolvedBounds.isEmpty()) { - mTmpFullBounds.set(parentConfig.windowConfiguration.getBounds()); + mTmpFullBounds.set(parentBounds); + insideParentBounds = true; } else { mTmpFullBounds.set(resolvedBounds); - hasOverrideBounds = true; + insideParentBounds = parentBounds.contains(resolvedBounds); } Rect outAppBounds = inOutConfig.windowConfiguration.getAppBounds(); @@ -2299,30 +2305,30 @@ class Task extends WindowContainer<WindowContainer> { outAppBounds = inOutConfig.windowConfiguration.getAppBounds(); } // Non-null compatibility insets means the activity prefers to keep its original size, so - // the out bounds doesn't need to be restricted by the parent. - final boolean insideParentBounds = compatInsets == null; - if (insideParentBounds && windowingMode != WINDOWING_MODE_FREEFORM) { - Rect parentAppBounds; - if (hasOverrideBounds) { - // Since we overrode the bounds, restrict appBounds to display non-decor rather - // than parent. Otherwise, it won't match the overridden bounds. - final TaskDisplayArea displayArea = getDisplayArea(); - parentAppBounds = displayArea != null - ? displayArea.getConfiguration().windowConfiguration.getAppBounds() : null; + // the out bounds doesn't need to be restricted by the parent or current display. + final boolean customContainerPolicy = compatInsets != null; + if (!customContainerPolicy && windowingMode != WINDOWING_MODE_FREEFORM) { + final Rect containingAppBounds; + if (insideParentBounds) { + containingAppBounds = parentConfig.windowConfiguration.getAppBounds(); } else { - parentAppBounds = parentConfig.windowConfiguration.getAppBounds(); + // Restrict appBounds to display non-decor rather than parent because the override + // bounds are beyond the parent. Otherwise, it won't match the overridden bounds. + final TaskDisplayArea displayArea = getDisplayArea(); + containingAppBounds = displayArea != null + ? displayArea.getWindowConfiguration().getAppBounds() : null; } - if (parentAppBounds != null && !parentAppBounds.isEmpty()) { - outAppBounds.intersect(parentAppBounds); + if (containingAppBounds != null && !containingAppBounds.isEmpty()) { + outAppBounds.intersect(containingAppBounds); } } if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED || inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) { - if (insideParentBounds && WindowConfiguration.isFloating(windowingMode)) { + if (!customContainerPolicy && WindowConfiguration.isFloating(windowingMode)) { mTmpNonDecorBounds.set(mTmpFullBounds); mTmpStableBounds.set(mTmpFullBounds); - } else if (insideParentBounds + } else if (!customContainerPolicy && (overrideDisplayInfo != null || getDisplayContent() != null)) { final DisplayInfo di = overrideDisplayInfo != null ? overrideDisplayInfo @@ -2340,7 +2346,7 @@ class Task extends WindowContainer<WindowContainer> { if (rotation == ROTATION_UNDEFINED) { rotation = parentConfig.windowConfiguration.getRotation(); } - if (rotation != ROTATION_UNDEFINED && compatInsets != null) { + if (rotation != ROTATION_UNDEFINED && customContainerPolicy) { mTmpNonDecorBounds.set(mTmpFullBounds); mTmpStableBounds.set(mTmpFullBounds); compatInsets.getBoundsByRotation(mTmpBounds, rotation); @@ -2358,13 +2364,13 @@ class Task extends WindowContainer<WindowContainer> { if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) { final int overrideScreenWidthDp = (int) (mTmpStableBounds.width() / density); - inOutConfig.screenWidthDp = (insideParentBounds && !hasOverrideBounds) + inOutConfig.screenWidthDp = (insideParentBounds && !customContainerPolicy) ? Math.min(overrideScreenWidthDp, parentConfig.screenWidthDp) : overrideScreenWidthDp; } if (inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) { final int overrideScreenHeightDp = (int) (mTmpStableBounds.height() / density); - inOutConfig.screenHeightDp = (insideParentBounds && !hasOverrideBounds) + inOutConfig.screenHeightDp = (insideParentBounds && !customContainerPolicy) ? Math.min(overrideScreenHeightDp, parentConfig.screenHeightDp) : overrideScreenHeightDp; } @@ -2897,22 +2903,12 @@ class Task extends WindowContainer<WindowContainer> { if (!isRootTask) { adjustBoundsForDisplayChangeIfNeeded(dc); } - final DisplayContent prevDc = mDisplayContent; super.onDisplayChanged(dc); if (!isRootTask) { final int displayId = (dc != null) ? dc.getDisplayId() : INVALID_DISPLAY; mWmService.mAtmService.getTaskChangeNotificationController().notifyTaskDisplayChanged( mTaskId, displayId); } - if (prevDc != null && prevDc.mChangingContainers.remove(this)) { - // This gets called *after* this has been reparented to the new display. - // That reparenting resulted in this window changing modes (eg. FREEFORM -> FULLSCREEN), - // so this token is now "frozen" while waiting for the animation to start on prevDc - // (which will be cancelled since the window is no-longer a child). However, since this - // is no longer a child of prevDc, this won't be notified of the cancelled animation, - // so we need to cancel the change transition here. - mSurfaceFreezer.unfreeze(getPendingTransaction()); - } } boolean isResizeable(boolean checkSupportsPip) { @@ -4435,19 +4431,20 @@ class Task extends WindowContainer<WindowContainer> { // Let the old organizer know it has lost control. sendTaskVanished(); mTaskOrganizer = organizer; - sendTaskAppeared(); - onTaskOrganizerChanged(); - return true; - } - void taskOrganizerUnregistered() { - mTaskOrganizer = null; - mTaskAppearedSent = false; - mLastTaskOrganizerWindowingMode = -1; - onTaskOrganizerChanged(); - if (mCreatedByOrganizer) { - removeImmediately(); + if (mTaskOrganizer != null) { + sendTaskAppeared(); + } else { + // No longer managed by any organizer. + mTaskAppearedSent = false; + mLastTaskOrganizerWindowingMode = -1; + setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, false /* set */); + if (mCreatedByOrganizer) { + removeImmediately(); + } } + + return true; } /** @@ -4484,14 +4481,6 @@ class Task extends WindowContainer<WindowContainer> { return result; } - private void onTaskOrganizerChanged() { - if (mTaskOrganizer == null) { - // If this task is no longer controlled by a task organizer, then reset the force hidden - // state - setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, false /* set */); - } - } - @Override void setSurfaceControl(SurfaceControl sc) { super.setSurfaceControl(sc); diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index 25791c762e42..3dc672396c29 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -123,6 +123,10 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> { private final RootWindowContainer.FindTaskResult mTmpFindTaskResult = new RootWindowContainer.FindTaskResult(); + // Indicates whether the Assistant should show on top of the Dream (respectively, above + // everything else on screen). Otherwise, it will be put under always-on-top stacks. + private final boolean mAssistantOnTopOfDream; + /** * If this is the same as {@link #getFocusedStack} then the activity on the top of the focused * stack has been resumed. If stacks are changing position this will hold the old stack until @@ -148,6 +152,9 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> { mDisplayContent = displayContent; mRootWindowContainer = service.mRoot; mAtmService = service.mAtmService; + + mAssistantOnTopOfDream = mWmService.mContext.getResources().getBoolean( + com.android.internal.R.bool.config_assistantOnTopOfDream); } /** @@ -327,55 +334,80 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> { } /** - * When stack is added or repositioned, find a proper position for it. - * This will make sure that pinned stack always stays on top. - * @param requestedPosition Position requested by caller. - * @param stack Stack to be added or positioned. - * @param adding Flag indicates whether we're adding a new stack or positioning an existing. - * @return The proper position for the stack. + * Assigns a priority number to stack types. This priority defines an order between the types + * of stacks that are added to the task display area. + * + * Higher priority number indicates that the stack should have a higher z-order. + * + * @return the priority of the stack */ - private int findPositionForStack(int requestedPosition, ActivityStack stack, - boolean adding) { - if (stack.isActivityTypeDream()) { - return POSITION_TOP; - } - - if (stack.inPinnedWindowingMode()) { - return POSITION_TOP; - } - - final int topChildPosition = mChildren.size() - 1; - int belowAlwaysOnTopPosition = POSITION_BOTTOM; - for (int i = topChildPosition; i >= 0; --i) { - // Since a stack could be repositioned while being one of the child, return - // current index if that's the same stack we are positioning and it is always on - // top. - final boolean sameStack = mChildren.get(i) == stack; - if ((sameStack && stack.isAlwaysOnTop()) - || (!sameStack && !mChildren.get(i).isAlwaysOnTop())) { - belowAlwaysOnTopPosition = i; + private int getPriority(ActivityStack stack) { + if (mAssistantOnTopOfDream && stack.isActivityTypeAssistant()) return 4; + if (stack.isActivityTypeDream()) return 3; + if (stack.inPinnedWindowingMode()) return 2; + if (stack.isAlwaysOnTop()) return 1; + return 0; + } + + private int findMinPositionForStack(ActivityStack stack) { + int minPosition = POSITION_BOTTOM; + for (int i = 0; i < mChildren.size(); ++i) { + if (getPriority(getStackAt(i)) < getPriority(stack)) { + minPosition = i; + } else { break; } } - // The max possible position we can insert the stack at. - int maxPosition = POSITION_TOP; - // The min possible position we can insert the stack at. - int minPosition = POSITION_BOTTOM; - if (stack.isAlwaysOnTop()) { - if (hasPinnedTask()) { - // Always-on-top stacks go below the pinned stack. - maxPosition = mChildren.indexOf(mRootPinnedTask) - 1; + // Since a stack could be repositioned while still being one of the children, we check + // if this always-on-top stack already exists and if so, set the minPosition to its + // previous position. + final int currentIndex = getIndexOf(stack); + if (currentIndex > minPosition) { + minPosition = currentIndex; } - // Always-on-top stacks need to be above all other stacks. - minPosition = belowAlwaysOnTopPosition - != POSITION_BOTTOM ? belowAlwaysOnTopPosition : topChildPosition; - } else { - // Other stacks need to be below the always-on-top stacks. - maxPosition = belowAlwaysOnTopPosition - != POSITION_BOTTOM ? belowAlwaysOnTopPosition : 0; } + return minPosition; + } + + private int findMaxPositionForStack(ActivityStack stack) { + for (int i = mChildren.size() - 1; i >= 0; --i) { + final ActivityStack curr = getStackAt(i); + // Since a stack could be repositioned while still being one of the children, we check + // if 'curr' is the same stack and skip it if so + final boolean sameStack = curr == stack; + if (getPriority(curr) <= getPriority(stack) && !sameStack) { + return i; + } + } + return 0; + } + + /** + * When stack is added or repositioned, find a proper position for it. + * + * The order is defined as: + * - Dream is on top of everything + * - PiP is directly below the Dream + * - always-on-top stacks are directly below PiP; new always-on-top stacks are added above + * existing ones + * - other non-always-on-top stacks come directly below always-on-top stacks; new + * non-always-on-top stacks are added directly below always-on-top stacks and above existing + * non-always-on-top stacks + * - if {@link #mAssistantOnTopOfDream} is enabled, then Assistant is on top of everything + * (including the Dream); otherwise, it is a normal non-always-on-top stack + * + * @param requestedPosition Position requested by caller. + * @param stack Stack to be added or positioned. + * @param adding Flag indicates whether we're adding a new stack or positioning an existing. + * @return The proper position for the stack. + */ + private int findPositionForStack(int requestedPosition, ActivityStack stack, boolean adding) { + // The max possible position we can insert the stack at. + int maxPosition = findMaxPositionForStack(stack); + // The min possible position we can insert the stack at. + int minPosition = findMinPositionForStack(stack); // Cap the requested position to something reasonable for the previous position check // below. diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index adc50bf70446..306c100e651c 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -218,18 +218,24 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } void dispose() { - releaseTasks(); + // Move organizer from managing specific windowing modes for (int i = mTaskOrganizersForWindowingMode.size() - 1; i >= 0; --i) { mTaskOrganizersForWindowingMode.valueAt(i).remove(mOrganizer.getBinder()); } - } - private void releaseTasks() { - for (int i = mOrganizedTasks.size() - 1; i >= 0; i--) { - final Task t = mOrganizedTasks.get(i); - removeTask(t); - t.taskOrganizerUnregistered(); + // Update tasks currently managed by this organizer to the next one available if + // possible. + while (!mOrganizedTasks.isEmpty()) { + final Task t = mOrganizedTasks.get(0); + t.updateTaskOrganizerState(true /* forceUpdate */); + if (mOrganizedTasks.contains(t)) { + removeTask(t); + } } + + // Remove organizer state after removing tasks so we get a chance to send + // onTaskVanished. + mTaskOrganizerStates.remove(asBinder()); } void unlinkDeath() { @@ -313,16 +319,11 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { new TaskOrganizerState(organizer, uid)); } - if (orgs.size() == 1) { - // Only in the case where this is the root task organizer for the given - // windowing mode, we add report all existing tasks in that mode to the new - // task organizer. - mService.mRootWindowContainer.forAllTasks((task) -> { - if (task.getWindowingMode() == windowingMode) { - task.updateTaskOrganizerState(true /* forceUpdate */); - } - }); - } + mService.mRootWindowContainer.forAllTasks((task) -> { + if (task.getWindowingMode() == windowingMode) { + task.updateTaskOrganizerState(true /* forceUpdate */); + } + }); } } finally { Binder.restoreCallingIdentity(origId); @@ -335,7 +336,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { final long origId = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { - final TaskOrganizerState state = mTaskOrganizerStates.remove(organizer.asBinder()); + final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder()); if (state == null) { return; } @@ -367,7 +368,9 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { void onTaskVanished(ITaskOrganizer organizer, Task task) { final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder()); - state.removeTask(task); + if (state != null) { + state.removeTask(task); + } } @Override diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java index 203ca25ecf6e..5f3c63352015 100644 --- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java +++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS; @@ -45,6 +46,7 @@ class WallpaperWindowToken extends WindowToken { DisplayContent dc, boolean ownerCanManageAppTokens) { super(service, token, TYPE_WALLPAPER, explicit, dc, ownerCanManageAppTokens); dc.mWallpaperController.addWallpaperToken(this); + setWindowingMode(WINDOWING_MODE_FULLSCREEN); } @Override diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index f3e2992d5913..591bc5475cd7 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -526,6 +526,11 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< */ @CallSuper void removeImmediately() { + final DisplayContent dc = getDisplayContent(); + if (dc != null) { + mSurfaceFreezer.unfreeze(getPendingTransaction()); + dc.mChangingContainers.remove(this); + } while (!mChildren.isEmpty()) { final E child = mChildren.peekLast(); child.removeImmediately(); @@ -718,6 +723,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< * @param dc The display this container is on after changes. */ void onDisplayChanged(DisplayContent dc) { + if (mDisplayContent != null && mDisplayContent.mChangingContainers.remove(this)) { + // Cancel any change transition queued-up for this container on the old display. + mSurfaceFreezer.unfreeze(getPendingTransaction()); + } mDisplayContent = dc; if (dc != null && dc != this) { dc.getPendingTransaction().merge(mPendingTransaction); @@ -2033,6 +2042,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< void cancelAnimation() { mSurfaceAnimator.cancelAnimation(); + mSurfaceFreezer.unfreeze(getPendingTransaction()); } @Override diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index a5014145aa60..8eb4b2659cc0 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -23,6 +23,7 @@ import static android.Manifest.permission.MANAGE_APP_TOKENS; import static android.Manifest.permission.READ_FRAME_BUFFER; import static android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS; import static android.Manifest.permission.RESTRICTED_VR_ACCESS; +import static android.Manifest.permission.STATUS_BAR_SERVICE; import static android.Manifest.permission.WRITE_SECURE_SETTINGS; import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY; import static android.app.ActivityManagerInternal.ALLOW_NON_FULL; @@ -74,6 +75,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED; import static android.view.WindowManagerGlobal.ADD_OKAY; +import static android.view.WindowManagerGlobal.ADD_TOO_MANY_TOKENS; import static android.view.WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY; import static android.view.WindowManagerGlobal.RELAYOUT_RES_BLAST_SYNC; import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED; @@ -413,6 +415,12 @@ public class WindowManagerService extends IWindowManager.Stub private static final int ANIMATION_COMPLETED_TIMEOUT_MS = 5000; + /** The maximum count of window tokens without surface that an app can register. */ + private static final int MAXIMUM_WINDOW_TOKEN_COUNT_WITHOUT_SURFACE = 5; + + /** System UI can create more window context... */ + private static final int SYSTEM_UI_MULTIPLIER = 2; + // TODO(b/143053092): Remove the settings if it becomes stable. private static final String FIXED_ROTATION_TRANSFORM_SETTING_NAME = "fixed_rotation_transform"; boolean mIsFixedRotationTransformEnabled; @@ -2446,17 +2454,15 @@ public class WindowManagerService extends IWindowManager.Stub if (controls != null) { final int length = Math.min(controls.length, outControls.length); for (int i = 0; i < length; i++) { - final InsetsSourceControl control = controls[i]; - - // Check if we are sending invalid leashes. - final SurfaceControl leash = control != null ? control.getLeash() : null; - if (leash != null && !leash.isValid()) { - Slog.wtf(TAG, leash + " is not valid before sending to " + win, - leash.getReleaseStack()); - } - - outControls[i] = win.isClientLocal() && control != null - ? new InsetsSourceControl(control) : control; + // We will leave the critical section before returning the leash to the client, + // so we need to copy the leash to prevent others release the one that we are + // about to return. + // TODO: We will have an extra copy if the client is not local. + // For now, we rely on GC to release it. + // Maybe we can modify InsetsSourceControl.writeToParcel so it can release + // the extra leash as soon as possible. + outControls[i] = controls[i] != null + ? new InsetsSourceControl(controls[i]) : null; } } } @@ -2596,10 +2602,30 @@ public class WindowManagerService extends IWindowManager.Stub @Override public int addWindowTokenWithOptions(IBinder binder, int type, int displayId, Bundle options, String packageName) { + if (tokenCountExceed()) { + return ADD_TOO_MANY_TOKENS; + } return addWindowTokenWithOptions(binder, type, displayId, options, packageName, true /* fromClientToken */); } + private boolean tokenCountExceed() { + final int callingUid = Binder.getCallingUid(); + // Don't check if caller is from system server. + if (callingUid == myPid()) { + return false; + } + final int limit = + (checkCallingPermission(STATUS_BAR_SERVICE, "addWindowTokenWithOptions")) + ? MAXIMUM_WINDOW_TOKEN_COUNT_WITHOUT_SURFACE * SYSTEM_UI_MULTIPLIER + : MAXIMUM_WINDOW_TOKEN_COUNT_WITHOUT_SURFACE; + synchronized (mGlobalLock) { + int[] count = new int[1]; + mRoot.forAllDisplays(d -> count[0] += d.getWindowTokensWithoutSurfaceCount(callingUid)); + return count[0] >= limit; + } + } + private int addWindowTokenWithOptions(IBinder binder, int type, int displayId, Bundle options, String packageName, boolean fromClientToken) { final boolean callerCanManageAppTokens = @@ -6113,15 +6139,6 @@ public class WindowManagerService extends IWindowManager.Stub pw.print(" mInputMethodInputTarget in display# "); pw.print(displayId); pw.print(' '); pw.println(inputMethodInputTarget); } - if (mAccessibilityController != null) { - final Region magnificationRegion = new Region(); - mAccessibilityController.getMagnificationRegionLocked(displayId, - magnificationRegion); - pw.print(" mMagnificationRegion in display# "); - pw.print(displayId); - pw.print(' '); - pw.println(magnificationRegion); - } }); pw.print(" mInTouchMode="); pw.println(mInTouchMode); pw.print(" mLastDisplayFreezeDuration="); @@ -6137,6 +6154,9 @@ public class WindowManagerService extends IWindowManager.Stub mInputManagerCallback.dump(pw, " "); mTaskSnapshotController.dump(pw, " "); + if (mAccessibilityController != null) { + mAccessibilityController.dump(pw, " "); + } if (dumpAll) { final WindowState imeWindow = mRoot.getCurrentInputMethodWindow(); @@ -7658,15 +7678,8 @@ public class WindowManagerService extends IWindowManager.Stub Slog.w(TAG, "Cannot find window which accessibility connection is added to"); return; } - try (SurfaceControl.Transaction t = new SurfaceControl.Transaction()) { - t.setMetadata( - state.mSurfaceControl, - SurfaceControl.METADATA_ACCESSIBILITY_ID, - accessibilityWindowId); - t.apply(); - } finally { - SurfaceControl.closeTransaction(); - } + mTransaction.setMetadata(state.mSurfaceControl, + SurfaceControl.METADATA_ACCESSIBILITY_ID, accessibilityWindowId).apply(); } } diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 194ed3ec3b0e..41bd70726e71 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -40,6 +40,7 @@ import static com.android.server.wm.ActivityTaskManagerService.INSTRUMENTATION_K import static com.android.server.wm.ActivityTaskManagerService.KEY_DISPATCHING_TIMEOUT_MS; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE; +import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityThread; @@ -50,6 +51,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; +import android.content.pm.ServiceInfo; import android.content.res.Configuration; import android.os.Build; import android.os.Message; @@ -165,7 +167,8 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio // Thread currently set for VR scheduling int mVrThreadTid; - boolean mIsImeProcess; + // Whether this process has ever started a service with the BIND_INPUT_METHOD permission. + private volatile boolean mHasImeService; // all activities running in the process private final ArrayList<ActivityRecord> mActivities = new ArrayList<>(); @@ -187,6 +190,8 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio // Registered display id as a listener to override config change private int mDisplayId; private ActivityRecord mConfigActivityRecord; + // Whether the activity config override is allowed for this process. + private volatile boolean mIsActivityConfigOverrideAllowed = true; /** * Activities that hosts some UI drawn by the current process. The activities live * in another process. This is used to check if the process is currently showing anything @@ -201,9 +206,6 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio /** Whether our process is currently running a {@link IRemoteAnimationRunner} */ private boolean mRunningRemoteAnimation; - /** Whether this process is owned by the System UI package. */ - final boolean mIsSysUiPackage; - public WindowProcessController(@NonNull ActivityTaskManagerService atm, ApplicationInfo info, String name, int uid, int userId, Object owner, WindowProcessListener listener) { mInfo = info; @@ -215,8 +217,13 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio mAtm = atm; mDisplayId = INVALID_DISPLAY; - mIsSysUiPackage = info.packageName.equals( + boolean isSysUiPackage = info.packageName.equals( mAtm.getSysUiServiceComponentLocked().getPackageName()); + if (isSysUiPackage || mUid == Process.SYSTEM_UID) { + // This is a system owned process and should not use an activity config. + // TODO(b/151161907): Remove after support for display-independent (raw) SysUi configs. + mIsActivityConfigOverrideAllowed = false; + } onConfigurationChanged(atm.getGlobalConfiguration()); } @@ -1095,9 +1102,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio * always track the configuration of the non-finishing activity last added to the process. */ private void updateActivityConfigurationListener() { - if (mIsSysUiPackage || mUid == Process.SYSTEM_UID) { - // This is a system owned process and should not use an activity config. - // TODO(b/151161907): Remove after support for display-independent (raw) SysUi configs. + if (!mIsActivityConfigOverrideAllowed) { return; } @@ -1132,7 +1137,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio final Configuration config = getConfiguration(); if (mLastReportedConfiguration.diff(config) == 0) { // Nothing changed. - if (Build.IS_DEBUGGABLE && mIsImeProcess) { + if (Build.IS_DEBUGGABLE && mHasImeService) { // TODO (b/135719017): Temporary log for debugging IME service. Slog.w(TAG_CONFIGURATION, "Current config: " + config + " unchanged for IME proc " + mName); @@ -1156,7 +1161,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio private void dispatchConfigurationChange(Configuration config) { if (mThread == null) { - if (Build.IS_DEBUGGABLE && mIsImeProcess) { + if (Build.IS_DEBUGGABLE && mHasImeService) { // TODO (b/135719017): Temporary log for debugging IME service. Slog.w(TAG_CONFIGURATION, "Unable to send config for IME proc " + mName + ": no app thread"); @@ -1166,7 +1171,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio if (DEBUG_CONFIGURATION) { Slog.v(TAG_CONFIGURATION, "Sending to proc " + mName + " new config " + config); } - if (Build.IS_DEBUGGABLE && mIsImeProcess) { + if (Build.IS_DEBUGGABLE && mHasImeService) { // TODO (b/135719017): Temporary log for debugging IME service. Slog.v(TAG_CONFIGURATION, "Sending to IME proc " + mName + " new config " + config); } @@ -1286,6 +1291,35 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio } } + /** + * Called to notify {@link WindowProcessController} of a started service. + * + * @param serviceInfo information describing the started service. + */ + public void onServiceStarted(ServiceInfo serviceInfo) { + String permission = serviceInfo.permission; + if (permission == null) { + return; + } + + // TODO: Audit remaining services for disabling activity override (Wallpaper, Dream, etc). + switch (permission) { + case Manifest.permission.BIND_INPUT_METHOD: + mHasImeService = true; + // Fall-through + case Manifest.permission.BIND_ACCESSIBILITY_SERVICE: + case Manifest.permission.BIND_VOICE_INTERACTION: + // We want to avoid overriding the config of these services with that of the + // activity as it could lead to incorrect display metrics. For ex, IME services + // expect their config to match the config of the display with the IME window + // showing. + mIsActivityConfigOverrideAllowed = false; + break; + default: + break; + } + } + @HotPath(caller = HotPath.OOM_ADJUSTMENT) public void onTopProcChanged() { synchronized (mAtm.mGlobalLockWithoutBoost) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index ef690e1db396..bbe5d942874d 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -3660,9 +3660,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return mActivityRecord.getBounds().equals(mTmpRect); } - @Override - public boolean isLetterboxedOverlappingWith(Rect rect) { - return mActivityRecord != null && mActivityRecord.isLetterboxOverlappingWith(rect); + /** + * @see Letterbox#notIntersectsOrFullyContains(Rect) + */ + boolean letterboxNotIntersectsOrFullyContains(Rect rect) { + return mActivityRecord == null + || mActivityRecord.letterboxNotIntersectsOrFullyContains(rect); } boolean isDragResizeChanged() { diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 99577077d65d..bfe3b2890bc1 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -884,8 +884,8 @@ class WindowStateAnimator { clipRect = mTmpClipRect; } - if (mSurfaceResized && (mAttrType == TYPE_BASE_APPLICATION) && - (task != null) && (task.getMainWindowSizeChangeTransaction() != null)) { + if (w.mInRelayout && (mAttrType == TYPE_BASE_APPLICATION) && (task != null) + && (task.getMainWindowSizeChangeTransaction() != null)) { mSurfaceController.deferTransactionUntil(mWin.getClientViewRootSurface(), mWin.getFrameNumber()); SurfaceControl.mergeToGlobalTransaction(task.getMainWindowSizeChangeTransaction()); @@ -1476,6 +1476,7 @@ class WindowStateAnimator { if (dumpAll) { pw.print(prefix); pw.print("mDrawState="); pw.print(drawStateToString()); pw.print(prefix); pw.print(" mLastHidden="); pw.println(mLastHidden); + pw.print(prefix); pw.print("mEnterAnimationPending=" + mEnterAnimationPending); pw.print(prefix); pw.print("mSystemDecorRect="); mSystemDecorRect.printShortString(pw); pw.print(" mLastClipRect="); mLastClipRect.printShortString(pw); diff --git a/services/core/jni/com_android_server_tv_TvUinputBridge.cpp b/services/core/jni/com_android_server_tv_TvUinputBridge.cpp index 6e2e2c54518b..99deab4fd652 100644 --- a/services/core/jni/com_android_server_tv_TvUinputBridge.cpp +++ b/services/core/jni/com_android_server_tv_TvUinputBridge.cpp @@ -106,7 +106,7 @@ static int getGamepadkeyCode(int32_t androidKeyCode) { static const GamepadAxis* getGamepadAxis(int32_t androidAxisCode) { std::unordered_map<int32_t, int>::iterator it = gamepadAndroidAxisToIndexMap.find(androidAxisCode); - if (it == gamepadAndroidToLinuxKeyMap.end()) { + if (it == gamepadAndroidAxisToIndexMap.end()) { return nullptr; } return &GAMEPAD_AXES[it->second]; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 22b0b62a819d..5d5e424db017 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -161,8 +161,11 @@ import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentValues; import android.content.Context; +import android.content.IIntentReceiver; +import android.content.IIntentSender; import android.content.Intent; import android.content.IntentFilter; +import android.content.IntentSender; import android.content.PermissionChecker; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; @@ -171,6 +174,7 @@ import android.content.pm.CrossProfileAppsInternal; import android.content.pm.IPackageDataObserver; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; +import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManagerInternal; @@ -399,11 +403,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private static final long EXPIRATION_GRACE_PERIOD_MS = 5 * MS_PER_DAY; // 5 days, in ms private static final long MANAGED_PROFILE_MAXIMUM_TIME_OFF_THRESHOLD = 3 * MS_PER_DAY; + /** When to warn the user about the approaching work profile off deadline: 1 day before */ + private static final long MANAGED_PROFILE_OFF_WARNING_PERIOD = 1 * MS_PER_DAY; private static final String ACTION_EXPIRED_PASSWORD_NOTIFICATION = "com.android.server.ACTION_EXPIRED_PASSWORD_NOTIFICATION"; - private static final String ACTION_PROFILE_OFF_DEADLINE = + @VisibleForTesting + static final String ACTION_PROFILE_OFF_DEADLINE = "com.android.server.ACTION_PROFILE_OFF_DEADLINE"; private static final String ATTR_PERMISSION_PROVIDER = "permission-provider"; @@ -645,6 +652,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private static final boolean ENABLE_LOCK_GUARD = true; + /** Profile off deadline is not set or more than MANAGED_PROFILE_OFF_WARNING_PERIOD away. */ + private static final int PROFILE_OFF_DEADLINE_DEFAULT = 0; + /** Profile off deadline is closer than MANAGED_PROFILE_OFF_WARNING_PERIOD. */ + private static final int PROFILE_OFF_DEADLINE_WARNING = 1; + /** Profile off deadline reached, notify the user that personal apps blocked. */ + private static final int PROFILE_OFF_DEADLINE_REACHED = 2; + interface Stats { int LOCK_GUARD_GUARD = 0; @@ -922,11 +936,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { mUserData.remove(userHandle); } handlePackagesChanged(null /* check all admins */, userHandle); + updatePersonalAppsSuspensionOnUserStart(userHandle); } else if (Intent.ACTION_USER_STOPPED.equals(action)) { sendDeviceOwnerUserCommand(DeviceAdminReceiver.ACTION_USER_STOPPED, userHandle); if (isManagedProfile(userHandle)) { Slog.d(LOG_TAG, "Managed profile was stopped"); - updatePersonalAppSuspension(userHandle, false /* profileIsOn */); + updatePersonalAppsSuspension(userHandle, false /* unlocked */); } } else if (Intent.ACTION_USER_SWITCHED.equals(action)) { sendDeviceOwnerUserCommand(DeviceAdminReceiver.ACTION_USER_SWITCHED, userHandle); @@ -936,7 +951,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } if (isManagedProfile(userHandle)) { Slog.d(LOG_TAG, "Managed profile became unlocked"); - updatePersonalAppSuspension(userHandle, true /* profileIsOn */); + updatePersonalAppsSuspension(userHandle, true /* unlocked */); } } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) { handlePackagesChanged(null /* check all admins */, userHandle); @@ -963,7 +978,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Slog.i(LOG_TAG, "Profile off deadline alarm was triggered"); final int userId = getManagedUserId(UserHandle.USER_SYSTEM); if (userId >= 0) { - updatePersonalAppSuspension(userId, mUserManager.isUserUnlocked(userId)); + updatePersonalAppsSuspension(userId, mUserManager.isUserUnlocked(userId)); } else { Slog.wtf(LOG_TAG, "Got deadline alarm for nonexistent profile"); } @@ -2482,6 +2497,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public void runCryptoSelfTest() { CryptoTestHelper.runAndLogSelfTest(); } + + public String[] getPersonalAppsForSuspension(int userId) { + return new PersonalAppsSuspensionHelper( + mContext.createContextAsUser(UserHandle.of(userId), 0 /* flags */)) + .getPersonalAppsForSuspension(); + } + + public long systemCurrentTimeMillis() { + return System.currentTimeMillis(); + } } /** @@ -2704,6 +2729,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { clearDeviceOwnerLocked(doAdmin, doUserId); Slog.i(LOG_TAG, "Removing admin artifacts..."); removeAdminArtifacts(doAdminReceiver, doUserId); + Slog.i(LOG_TAG, "Uninstalling the DO..."); + uninstallOrDisablePackage(doAdminComponent.getPackageName(), doUserId); Slog.i(LOG_TAG, "Migration complete."); // Note: KeyChain keys are not removed and will remain accessible for the apps that have @@ -2715,6 +2742,47 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { .write(); } + private void uninstallOrDisablePackage(String packageName, int userHandle) { + final ApplicationInfo appInfo; + try { + appInfo = mIPackageManager.getApplicationInfo( + packageName, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, userHandle); + } catch (RemoteException e) { + // Shouldn't happen. + return; + } + if (appInfo == null) { + Slog.wtf(LOG_TAG, "Failed to get package info for " + packageName); + return; + } + if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { + Slog.i(LOG_TAG, String.format( + "Package %s is pre-installed, marking disabled until used", packageName)); + mContext.getPackageManager().setApplicationEnabledSetting(packageName, + PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, 0 /* flags */); + return; + } + + final IIntentSender.Stub mLocalSender = new IIntentSender.Stub() { + @Override + public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken, + IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) { + final int status = intent.getIntExtra( + PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE); + if (status == PackageInstaller.STATUS_SUCCESS) { + Slog.i(LOG_TAG, String.format( + "Package %s uninstalled for user %d", packageName, userHandle)); + } else { + Slog.e(LOG_TAG, String.format( + "Failed to uninstall %s; status: %d", packageName, status)); + } + } + }; + + final PackageInstaller pi = mInjector.getPackageManager(userHandle).getPackageInstaller(); + pi.uninstall(packageName, 0 /* flags */, new IntentSender((IIntentSender) mLocalSender)); + } + private void moveDoPoliciesToProfileParentAdmin(ActiveAdmin doAdmin, ActiveAdmin parentAdmin) { // The following policies can be already controlled via parent instance, skip if so. if (parentAdmin.mPasswordPolicy.quality == PASSWORD_QUALITY_UNSPECIFIED) { @@ -3998,10 +4066,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { applyManagedProfileRestrictionIfDeviceOwnerLocked(); } maybeStartSecurityLogMonitorOnActivityManagerReady(); - final int userId = getManagedUserId(UserHandle.USER_SYSTEM); - if (userId >= 0) { - updatePersonalAppSuspension(userId, false /* running */); - } break; case SystemService.PHASE_BOOT_COMPLETED: ensureDeviceOwnerUserStarted(); // TODO Consider better place to do this. @@ -4009,6 +4073,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } + private void updatePersonalAppsSuspensionOnUserStart(int userHandle) { + final int profileUserHandle = getManagedUserId(userHandle); + if (profileUserHandle >= 0) { + // Given that the parent user has just started, profile should be locked. + updatePersonalAppsSuspension(profileUserHandle, false /* unlocked */); + } else { + suspendPersonalAppsInternal(userHandle, false); + } + } + private void onLockSettingsReady() { getUserData(UserHandle.USER_SYSTEM); loadOwners(); @@ -15846,11 +15920,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } - final int suspendedState = suspended - ? PERSONAL_APPS_SUSPENDED_EXPLICITLY - : PERSONAL_APPS_NOT_SUSPENDED; - mInjector.binderWithCleanCallingIdentity( - () -> applyPersonalAppsSuspension(callingUserId, suspendedState)); + mInjector.binderWithCleanCallingIdentity(() -> updatePersonalAppsSuspension( + callingUserId, mUserManager.isUserUnlocked(callingUserId))); DevicePolicyEventLogger .createEvent(DevicePolicyEnums.SET_PERSONAL_APPS_SUSPENDED) @@ -15860,44 +15931,54 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } /** - * Checks whether there is a policy that requires personal apps to be suspended and if so, - * applies it. - * @param running whether the profile is currently considered running. + * Checks whether personal apps should be suspended according to the policy and applies the + * change if needed. + * + * @param unlocked whether the profile is currently running unlocked. */ - private void updatePersonalAppSuspension(int profileUserId, boolean running) { - final int suspensionState; + private void updatePersonalAppsSuspension(int profileUserId, boolean unlocked) { + final boolean suspended; synchronized (getLockObject()) { final ActiveAdmin profileOwner = getProfileOwnerAdminLocked(profileUserId); if (profileOwner != null) { - final boolean deadlineReached = - updateProfileOffDeadlineLocked(profileUserId, profileOwner, running); - suspensionState = makeSuspensionReasons( - profileOwner.mSuspendPersonalApps, deadlineReached); - Slog.d(LOG_TAG, - String.format("New personal apps suspension state: %d", suspensionState)); + final int deadlineState = + updateProfileOffDeadlineLocked(profileUserId, profileOwner, unlocked); + suspended = profileOwner.mSuspendPersonalApps + || deadlineState == PROFILE_OFF_DEADLINE_REACHED; + Slog.d(LOG_TAG, String.format("Personal apps suspended: %b, deadline state: %d", + suspended, deadlineState)); + updateProfileOffDeadlineNotificationLocked(profileUserId, profileOwner, + unlocked ? PROFILE_OFF_DEADLINE_DEFAULT : deadlineState); } else { - suspensionState = PERSONAL_APPS_NOT_SUSPENDED; + suspended = false; } } - applyPersonalAppsSuspension(profileUserId, suspensionState); + final int parentUserId = getProfileParentId(profileUserId); + suspendPersonalAppsInternal(parentUserId, suspended); } /** * Checks work profile time off policy, scheduling personal apps suspension via alarm if * necessary. - * @return whether the apps should be suspended based on maximum time off policy. + * @return profile deadline state */ - private boolean updateProfileOffDeadlineLocked( + private int updateProfileOffDeadlineLocked( int profileUserId, ActiveAdmin profileOwner, boolean unlocked) { - final long now = System.currentTimeMillis(); + final long now = mInjector.systemCurrentTimeMillis(); if (profileOwner.mProfileOffDeadline != 0 && now > profileOwner.mProfileOffDeadline) { // Profile off deadline is already reached. Slog.i(LOG_TAG, "Profile off deadline has been reached."); - return true; + return PROFILE_OFF_DEADLINE_REACHED; } boolean shouldSaveSettings = false; - if (profileOwner.mProfileOffDeadline != 0 + if (profileOwner.mSuspendPersonalApps) { + // When explicit suspension is active, deadline shouldn't be set. + if (profileOwner.mProfileOffDeadline != 0) { + profileOwner.mProfileOffDeadline = 0; + shouldSaveSettings = true; + } + } else if (profileOwner.mProfileOffDeadline != 0 && (profileOwner.mProfileMaximumTimeOffMillis == 0 || unlocked)) { // There is a deadline but either there is no policy or the profile is unlocked -> clear // the deadline. @@ -15913,52 +15994,51 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { shouldSaveSettings = true; } - updateProfileOffAlarm(profileOwner.mProfileOffDeadline); - if (shouldSaveSettings) { saveSettingsLocked(profileUserId); } - return false; - } - private void updateProfileOffAlarm(long profileOffDeadline) { + final long alarmTime; + final int deadlineState; + if (profileOwner.mProfileOffDeadline == 0) { + alarmTime = 0; + deadlineState = PROFILE_OFF_DEADLINE_DEFAULT; + } else if (profileOwner.mProfileOffDeadline - now < MANAGED_PROFILE_OFF_WARNING_PERIOD) { + // The deadline is close, upon the alarm personal apps should be suspended. + alarmTime = profileOwner.mProfileOffDeadline; + deadlineState = PROFILE_OFF_DEADLINE_WARNING; + } else { + // The deadline is quite far, upon the alarm we should warn the user first, so the + // alarm is scheduled earlier than the actual deadline. + alarmTime = profileOwner.mProfileOffDeadline - MANAGED_PROFILE_OFF_WARNING_PERIOD; + deadlineState = PROFILE_OFF_DEADLINE_DEFAULT; + } + final AlarmManager am = mInjector.getAlarmManager(); final PendingIntent pi = mInjector.pendingIntentGetBroadcast( mContext, REQUEST_PROFILE_OFF_DEADLINE, new Intent(ACTION_PROFILE_OFF_DEADLINE), PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT); - am.cancel(pi); - if (profileOffDeadline != 0) { - Slog.i(LOG_TAG, "Profile off deadline alarm is set."); - am.set(AlarmManager.RTC, profileOffDeadline, pi); - } else { - Slog.i(LOG_TAG, "Profile off deadline alarm is removed."); - } - } - - private void applyPersonalAppsSuspension( - int profileUserId, @PersonalAppsSuspensionReason int suspensionState) { - final boolean suspended = getUserData(UserHandle.USER_SYSTEM).mAppsSuspended; - final boolean shouldSuspend = suspensionState != PERSONAL_APPS_NOT_SUSPENDED; - if (suspended != shouldSuspend) { - suspendPersonalAppsInternal(shouldSuspend, UserHandle.USER_SYSTEM); - } - if (suspensionState == PERSONAL_APPS_SUSPENDED_PROFILE_TIMEOUT) { - sendPersonalAppsSuspendedNotification(profileUserId); + if (alarmTime == 0) { + Slog.i(LOG_TAG, "Profile off deadline alarm is removed."); + am.cancel(pi); } else { - clearPersonalAppsSuspendedNotification(); + Slog.i(LOG_TAG, "Profile off deadline alarm is set."); + am.set(AlarmManager.RTC, alarmTime, pi); } + + return deadlineState; } - private void suspendPersonalAppsInternal(boolean suspended, int userId) { + private void suspendPersonalAppsInternal(int userId, boolean suspended) { + if (getUserData(userId).mAppsSuspended == suspended) { + return; + } Slog.i(LOG_TAG, String.format("%s personal apps for user %d", suspended ? "Suspending" : "Unsuspending", userId)); mInjector.binderWithCleanCallingIdentity(() -> { try { - final String[] appsToSuspend = - new PersonalAppsSuspensionHelper( - mContext.createContextAsUser(UserHandle.of(userId), 0 /* flags */)) - .getPersonalAppsForSuspension(); + final String[] appsToSuspend = mInjector.getPersonalAppsForSuspension(userId); final String[] failedPackages = mIPackageManager.setPackagesSuspendedAsUser( appsToSuspend, suspended, null, null, null, PLATFORM_PACKAGE_NAME, userId); if (!ArrayUtils.isEmpty(failedPackages)) { @@ -15977,37 +16057,38 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } - private void clearPersonalAppsSuspendedNotification() { - mInjector.binderWithCleanCallingIdentity(() -> - mInjector.getNotificationManager().cancel( - SystemMessage.NOTE_PERSONAL_APPS_SUSPENDED)); - } + private void updateProfileOffDeadlineNotificationLocked(int profileUserId, + @Nullable ActiveAdmin profileOwner, int notificationState) { - private void sendPersonalAppsSuspendedNotification(int userId) { - final String profileOwnerPackageName; - final long maxTimeOffDays; - synchronized (getLockObject()) { - profileOwnerPackageName = mOwners.getProfileOwnerComponent(userId).getPackageName(); - final ActiveAdmin poAdmin = getProfileOwnerAdminLocked(userId); - maxTimeOffDays = TimeUnit.MILLISECONDS.toDays(poAdmin.mProfileMaximumTimeOffMillis); + if (notificationState == PROFILE_OFF_DEADLINE_DEFAULT) { + mInjector.getNotificationManager().cancel(SystemMessage.NOTE_PERSONAL_APPS_SUSPENDED); + return; } + final String profileOwnerPackageName = profileOwner.info.getPackageName(); + final long maxTimeOffDays = + TimeUnit.MILLISECONDS.toDays(profileOwner.mProfileMaximumTimeOffMillis); + final Intent intent = new Intent(DevicePolicyManager.ACTION_CHECK_POLICY_COMPLIANCE); intent.setPackage(profileOwnerPackageName); final PendingIntent pendingIntent = mInjector.pendingIntentGetActivityAsUser(mContext, - 0 /* requestCode */, intent, PendingIntent.FLAG_UPDATE_CURRENT, null /* options */, - UserHandle.of(userId)); + 0 /* requestCode */, intent, PendingIntent.FLAG_UPDATE_CURRENT, + null /* options */, UserHandle.of(profileUserId)); + + // TODO(b/149075510): Only the first of the notifications should be dismissible. + final String title = mContext.getString( + notificationState == PROFILE_OFF_DEADLINE_WARNING + ? R.string.personal_apps_suspended_tomorrow_title + : R.string.personal_apps_suspended_title); final Notification notification = new Notification.Builder(mContext, SystemNotificationChannels.DEVICE_ADMIN) .setSmallIcon(android.R.drawable.stat_sys_warning) .setOngoing(true) - .setContentTitle( - mContext.getString( - R.string.personal_apps_suspended_title)) + .setContentTitle(title) .setContentText(mContext.getString( - R.string.personal_apps_suspended_text, maxTimeOffDays)) + R.string.personal_apps_suspended_text, maxTimeOffDays)) .setColor(mContext.getColor(R.color.system_notification_accent_color)) .setContentIntent(pendingIntent) .build(); @@ -16039,7 +16120,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } mInjector.binderWithCleanCallingIdentity( - () -> updatePersonalAppSuspension(userId, mUserManager.isUserUnlocked())); + () -> updatePersonalAppsSuspension(userId, mUserManager.isUserUnlocked())); DevicePolicyEventLogger .createEvent(DevicePolicyEnums.SET_MANAGED_PROFILE_MAXIMUM_TIME_OFF) diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java b/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java index d9db17eba887..da716eaed82b 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java @@ -51,6 +51,10 @@ import java.util.Set; public class PersonalAppsSuspensionHelper { private static final String LOG_TAG = DevicePolicyManagerService.LOG_TAG; + // Flags to get all packages even if the user is still locked. + private static final int PACKAGE_QUERY_FLAGS = + PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE; + private final Context mContext; private final PackageManager mPackageManager; @@ -67,7 +71,7 @@ public class PersonalAppsSuspensionHelper { */ String[] getPersonalAppsForSuspension() { final List<PackageInfo> installedPackageInfos = - mPackageManager.getInstalledPackages(0 /* flags */); + mPackageManager.getInstalledPackages(PACKAGE_QUERY_FLAGS); final Set<String> result = new ArraySet<>(); for (final PackageInfo packageInfo : installedPackageInfos) { final ApplicationInfo info = packageInfo.applicationInfo; @@ -97,7 +101,7 @@ public class PersonalAppsSuspensionHelper { final Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_HOME); final List<ResolveInfo> matchingActivities = - mPackageManager.queryIntentActivities(intent, 0); + mPackageManager.queryIntentActivities(intent, PACKAGE_QUERY_FLAGS); for (final ResolveInfo resolveInfo : matchingActivities) { if (resolveInfo.activityInfo == null || TextUtils.isEmpty(resolveInfo.activityInfo.packageName)) { @@ -107,7 +111,7 @@ public class PersonalAppsSuspensionHelper { final String packageName = resolveInfo.activityInfo.packageName; try { final ApplicationInfo applicationInfo = - mPackageManager.getApplicationInfo(packageName, 0); + mPackageManager.getApplicationInfo(packageName, PACKAGE_QUERY_FLAGS); if (applicationInfo.isSystemApp() || applicationInfo.isUpdatedSystemApp()) { result.add(packageName); } @@ -147,7 +151,8 @@ public class PersonalAppsSuspensionHelper { private String getSettingsPackageName() { final Intent intent = new Intent(Settings.ACTION_SETTINGS); intent.addCategory(Intent.CATEGORY_DEFAULT); - final ResolveInfo resolveInfo = mPackageManager.resolveActivity(intent, /* flags= */ 0); + final ResolveInfo resolveInfo = + mPackageManager.resolveActivity(intent, PACKAGE_QUERY_FLAGS); if (resolveInfo != null) { return resolveInfo.activityInfo.packageName; } @@ -164,7 +169,7 @@ public class PersonalAppsSuspensionHelper { intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER); intentToResolve.setPackage(packageName); final List<ResolveInfo> resolveInfos = - mPackageManager.queryIntentActivities(intentToResolve, /* flags= */ 0); + mPackageManager.queryIntentActivities(intentToResolve, PACKAGE_QUERY_FLAGS); return resolveInfos != null && !resolveInfos.isEmpty(); } diff --git a/services/incremental/Android.bp b/services/incremental/Android.bp index b13d33054e19..de639c5d0760 100644 --- a/services/incremental/Android.bp +++ b/services/incremental/Android.bp @@ -19,6 +19,21 @@ cc_defaults { proto: { type: "lite", }, + tidy: true, + tidy_checks: [ + "android-*", + "cert-*", + "clang-analyzer-security*", + "-cert-err34-c", + "clang-analyzer-security*", + // Disabling due to many unavoidable warnings from POSIX API usage. + "-google-runtime-int", + "-google-explicit-constructor", + // do not define variadic C function - JNI headers + "-cert-dcl50-cpp", + // operator=() does not handle self-assignment properly - all protobuf-generated classes + "-cert-oop54-cpp", + ], } cc_defaults { @@ -62,6 +77,7 @@ filegroup { srcs: [ "incremental_service.c", "IncrementalService.cpp", + "IncrementalServiceValidation.cpp", "BinderIncrementalService.cpp", "path.cpp", "ServiceWrappers.cpp", diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp index fc8c6feac22b..847667427593 100644 --- a/services/incremental/BinderIncrementalService.cpp +++ b/services/incremental/BinderIncrementalService.cpp @@ -18,6 +18,7 @@ #include <android-base/logging.h> #include <android-base/no_destructor.h> +#include <android/os/IVold.h> #include <binder/IResultReceiver.h> #include <binder/PermissionCache.h> #include <incfs.h> @@ -117,11 +118,12 @@ binder::Status BinderIncrementalService::openStorage(const std::string& path, } binder::Status BinderIncrementalService::createStorage( - const std::string& path, const DataLoaderParamsParcel& params, + const std::string& path, const content::pm::DataLoaderParamsParcel& params, const ::android::sp<::android::content::pm::IDataLoaderStatusListener>& listener, int32_t createMode, int32_t* _aidl_return) { *_aidl_return = - mImpl.createStorage(path, const_cast<DataLoaderParamsParcel&&>(params), listener, + mImpl.createStorage(path, const_cast<content::pm::DataLoaderParamsParcel&&>(params), + listener, android::incremental::IncrementalService::CreateOptions( createMode)); return ok(); @@ -238,11 +240,8 @@ binder::Status BinderIncrementalService::isFileRangeLoaded(int32_t storageId, binder::Status BinderIncrementalService::getMetadataByPath(int32_t storageId, const std::string& path, std::vector<uint8_t>* _aidl_return) { - auto fid = mImpl.nodeFor(storageId, path); - if (fid != kIncFsInvalidFileId) { - auto metadata = mImpl.getMetadata(storageId, fid); - _aidl_return->assign(metadata.begin(), metadata.end()); - } + auto metadata = mImpl.getMetadata(storageId, path); + _aidl_return->assign(metadata.begin(), metadata.end()); return ok(); } diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp index f423119d240a..a6c45cc6c26f 100644 --- a/services/incremental/IncrementalService.cpp +++ b/services/incremental/IncrementalService.cpp @@ -18,32 +18,26 @@ #include "IncrementalService.h" -#include <android-base/file.h> #include <android-base/logging.h> #include <android-base/no_destructor.h> #include <android-base/properties.h> #include <android-base/stringprintf.h> -#include <android-base/strings.h> -#include <android/content/pm/IDataLoaderStatusListener.h> -#include <android/os/IVold.h> -#include <binder/BinderService.h> +#include <binder/AppOpsManager.h> #include <binder/Nullable.h> -#include <binder/ParcelFileDescriptor.h> #include <binder/Status.h> #include <sys/stat.h> #include <uuid/uuid.h> #include <charconv> #include <ctime> -#include <filesystem> #include <iterator> #include <span> #include <type_traits> +#include "IncrementalServiceValidation.h" #include "Metadata.pb.h" using namespace std::literals; -using namespace android::content::pm; namespace fs = std::filesystem; constexpr const char* kDataUsageStats = "android.permission.LOADER_USAGE_STATS"; @@ -51,10 +45,13 @@ constexpr const char* kOpUsage = "android:loader_usage_stats"; namespace android::incremental { +using content::pm::DataLoaderParamsParcel; +using content::pm::FileSystemControlParcel; +using content::pm::IDataLoader; + namespace { -using IncrementalFileSystemControlParcel = - ::android::os::incremental::IncrementalFileSystemControlParcel; +using IncrementalFileSystemControlParcel = os::incremental::IncrementalFileSystemControlParcel; struct Constants { static constexpr auto backing = "backing_store"sv; @@ -105,10 +102,13 @@ static std::string toMountKey(std::string_view path) { if (path::isAbsolute(path)) { path.remove_prefix(1); } + if (path.size() > 16) { + path = path.substr(0, 16); + } std::string res(path); - std::replace(res.begin(), res.end(), '/', '_'); - std::replace(res.begin(), res.end(), '@', '_'); - return std::string(constants().mountKeyPrefix) + res; + std::replace_if( + res.begin(), res.end(), [](char c) { return c == '/' || c == '@'; }, '_'); + return std::string(constants().mountKeyPrefix) += res; } static std::pair<std::string, std::string> makeMountDir(std::string_view incrementalDir, @@ -125,8 +125,26 @@ static std::pair<std::string, std::string> makeMountDir(std::string_view increme return {}; } +template <class Map> +typename Map::const_iterator findParentPath(const Map& map, std::string_view path) { + const auto nextIt = map.upper_bound(path); + if (nextIt == map.begin()) { + return map.end(); + } + const auto suspectIt = std::prev(nextIt); + if (!path::startsWith(path, suspectIt->first)) { + return map.end(); + } + return suspectIt; +} + +static base::unique_fd dup(base::borrowed_fd fd) { + const auto res = fcntl(fd.get(), F_DUPFD_CLOEXEC, 0); + return base::unique_fd(res); +} + template <class ProtoMessage, class Control> -static ProtoMessage parseFromIncfs(const IncFsWrapper* incfs, Control&& control, +static ProtoMessage parseFromIncfs(const IncFsWrapper* incfs, const Control& control, std::string_view path) { auto md = incfs->getMetadata(control, path); ProtoMessage message; @@ -155,20 +173,18 @@ std::string makeBindMdName() { } } // namespace -const bool IncrementalService::sEnablePerfLogging = - android::base::GetBoolProperty("incremental.perflogging", false); - IncrementalService::IncFsMount::~IncFsMount() { if (dataLoaderStub) { dataLoaderStub->cleanupResources(); dataLoaderStub = {}; } + control.close(); LOG(INFO) << "Unmounting and cleaning up mount " << mountId << " with root '" << root << '\''; for (auto&& [target, _] : bindPoints) { - LOG(INFO) << "\tbind: " << target; + LOG(INFO) << " bind: " << target; incrementalService.mVold->unmountIncFs(target); } - LOG(INFO) << "\troot: " << root; + LOG(INFO) << " root: " << root; incrementalService.mVold->unmountIncFs(path::join(root, constants().mount)); cleanupFilesystem(root); } @@ -193,8 +209,19 @@ auto IncrementalService::IncFsMount::makeStorage(StorageId id) -> StorageMap::it return storages.end(); } -static std::unique_ptr<DIR, decltype(&::closedir)> openDir(const char* path) { - return {::opendir(path), ::closedir}; +template <class Func> +static auto makeCleanup(Func&& f) { + auto deleter = [f = std::move(f)](auto) { f(); }; + // &f is a dangling pointer here, but we actually never use it as deleter moves it in. + return std::unique_ptr<Func, decltype(deleter)>(&f, std::move(deleter)); +} + +static std::unique_ptr<DIR, decltype(&::closedir)> openDir(const char* dir) { + return {::opendir(dir), ::closedir}; +} + +static auto openDir(std::string_view dir) { + return openDir(path::c_str(dir)); } static int rmDirContent(const char* path) { @@ -206,7 +233,7 @@ static int rmDirContent(const char* path) { if (entry->d_name == "."sv || entry->d_name == ".."sv) { continue; } - auto fullPath = android::base::StringPrintf("%s/%s", path, entry->d_name); + auto fullPath = base::StringPrintf("%s/%s", path, entry->d_name); if (entry->d_type == DT_DIR) { if (const auto err = rmDirContent(fullPath.c_str()); err != 0) { PLOG(WARNING) << "Failed to delete " << fullPath << " content"; @@ -256,7 +283,8 @@ IncrementalService::IncrementalService(ServiceManagerWrapper&& sm, std::string_v runJobProcessing(); }); - mountExistingImages(); + const auto mountedRootNames = adoptMountedInstances(); + mountExistingImages(mountedRootNames); } IncrementalService::~IncrementalService() { @@ -268,15 +296,7 @@ IncrementalService::~IncrementalService() { mJobProcessor.join(); } -inline const char* toString(TimePoint t) { - using SystemClock = std::chrono::system_clock; - time_t time = SystemClock::to_time_t( - SystemClock::now() + - std::chrono::duration_cast<SystemClock::duration>(t - Clock::now())); - return std::ctime(&time); -} - -inline const char* toString(IncrementalService::BindKind kind) { +static const char* toString(IncrementalService::BindKind kind) { switch (kind) { case IncrementalService::BindKind::Temporary: return "Temporary"; @@ -291,38 +311,48 @@ void IncrementalService::onDump(int fd) { std::unique_lock l(mLock); - dprintf(fd, "Mounts (%d):\n", int(mMounts.size())); + dprintf(fd, "Mounts (%d): {\n", int(mMounts.size())); for (auto&& [id, ifs] : mMounts) { - const IncFsMount& mnt = *ifs.get(); - dprintf(fd, "\t[%d]:\n", id); - dprintf(fd, "\t\tmountId: %d\n", mnt.mountId); - dprintf(fd, "\t\troot: %s\n", mnt.root.c_str()); - dprintf(fd, "\t\tnextStorageDirNo: %d\n", mnt.nextStorageDirNo.load()); - if (mnt.dataLoaderStub) { - mnt.dataLoaderStub->onDump(fd); - } - dprintf(fd, "\t\tstorages (%d):\n", int(mnt.storages.size())); - for (auto&& [storageId, storage] : mnt.storages) { - dprintf(fd, "\t\t\t[%d] -> [%s]\n", storageId, storage.name.c_str()); - } - - dprintf(fd, "\t\tbindPoints (%d):\n", int(mnt.bindPoints.size())); - for (auto&& [target, bind] : mnt.bindPoints) { - dprintf(fd, "\t\t\t[%s]->[%d]:\n", target.c_str(), bind.storage); - dprintf(fd, "\t\t\t\tsavedFilename: %s\n", bind.savedFilename.c_str()); - dprintf(fd, "\t\t\t\tsourceDir: %s\n", bind.sourceDir.c_str()); - dprintf(fd, "\t\t\t\tkind: %s\n", toString(bind.kind)); + const IncFsMount& mnt = *ifs; + dprintf(fd, " [%d]: {\n", id); + if (id != mnt.mountId) { + dprintf(fd, " reference to mountId: %d\n", mnt.mountId); + } else { + dprintf(fd, " mountId: %d\n", mnt.mountId); + dprintf(fd, " root: %s\n", mnt.root.c_str()); + dprintf(fd, " nextStorageDirNo: %d\n", mnt.nextStorageDirNo.load()); + if (mnt.dataLoaderStub) { + mnt.dataLoaderStub->onDump(fd); + } else { + dprintf(fd, " dataLoader: null\n"); + } + dprintf(fd, " storages (%d): {\n", int(mnt.storages.size())); + for (auto&& [storageId, storage] : mnt.storages) { + dprintf(fd, " [%d] -> [%s]\n", storageId, storage.name.c_str()); + } + dprintf(fd, " }\n"); + + dprintf(fd, " bindPoints (%d): {\n", int(mnt.bindPoints.size())); + for (auto&& [target, bind] : mnt.bindPoints) { + dprintf(fd, " [%s]->[%d]:\n", target.c_str(), bind.storage); + dprintf(fd, " savedFilename: %s\n", bind.savedFilename.c_str()); + dprintf(fd, " sourceDir: %s\n", bind.sourceDir.c_str()); + dprintf(fd, " kind: %s\n", toString(bind.kind)); + } + dprintf(fd, " }\n"); } + dprintf(fd, " }\n"); } - - dprintf(fd, "Sorted binds (%d):\n", int(mBindsByPath.size())); + dprintf(fd, "}\n"); + dprintf(fd, "Sorted binds (%d): {\n", int(mBindsByPath.size())); for (auto&& [target, mountPairIt] : mBindsByPath) { const auto& bind = mountPairIt->second; - dprintf(fd, "\t\t[%s]->[%d]:\n", target.c_str(), bind.storage); - dprintf(fd, "\t\t\tsavedFilename: %s\n", bind.savedFilename.c_str()); - dprintf(fd, "\t\t\tsourceDir: %s\n", bind.sourceDir.c_str()); - dprintf(fd, "\t\t\tkind: %s\n", toString(bind.kind)); + dprintf(fd, " [%s]->[%d]:\n", target.c_str(), bind.storage); + dprintf(fd, " savedFilename: %s\n", bind.savedFilename.c_str()); + dprintf(fd, " sourceDir: %s\n", bind.sourceDir.c_str()); + dprintf(fd, " kind: %s\n", toString(bind.kind)); } + dprintf(fd, "}\n"); } void IncrementalService::onSystemReady() { @@ -471,9 +501,9 @@ StorageId IncrementalService::createStorage( metadata::Mount m; m.mutable_storage()->set_id(ifs->mountId); m.mutable_loader()->set_type((int)dataLoaderParams.type); - m.mutable_loader()->set_package_name(dataLoaderParams.packageName); - m.mutable_loader()->set_class_name(dataLoaderParams.className); - m.mutable_loader()->set_arguments(dataLoaderParams.arguments); + m.mutable_loader()->set_allocated_package_name(&dataLoaderParams.packageName); + m.mutable_loader()->set_allocated_class_name(&dataLoaderParams.className); + m.mutable_loader()->set_allocated_arguments(&dataLoaderParams.arguments); const auto metadata = m.SerializeAsString(); m.mutable_loader()->release_arguments(); m.mutable_loader()->release_class_name(); @@ -528,7 +558,7 @@ StorageId IncrementalService::createLinkedStorage(std::string_view mountPoint, } std::unique_lock l(mLock); - const auto& ifs = getIfsLocked(linkedStorage); + auto ifs = getIfsLocked(linkedStorage); if (!ifs) { LOG(ERROR) << "Ifs unavailable"; return kInvalidStorageId; @@ -552,6 +582,8 @@ StorageId IncrementalService::createLinkedStorage(std::string_view mountPoint, bk, l); err < 0) { LOG(ERROR) << "bindMount failed with error: " << err; + (void)mIncFs->unlink(ifs->control, storageIt->second.name); + ifs->storages.erase(storageIt); return kInvalidStorageId; } @@ -561,15 +593,7 @@ StorageId IncrementalService::createLinkedStorage(std::string_view mountPoint, IncrementalService::BindPathMap::const_iterator IncrementalService::findStorageLocked( std::string_view path) const { - auto bindPointIt = mBindsByPath.upper_bound(path); - if (bindPointIt == mBindsByPath.begin()) { - return mBindsByPath.end(); - } - --bindPointIt; - if (!path::startsWith(path, bindPointIt->first)) { - return mBindsByPath.end(); - } - return bindPointIt; + return findParentPath(mBindsByPath, path); } StorageId IncrementalService::findStorageId(std::string_view path) const { @@ -611,13 +635,12 @@ int IncrementalService::setStorageParams(StorageId storageId, bool enableReadLog } binder::Status IncrementalService::applyStorageParams(IncFsMount& ifs, bool enableReadLogs) { - using unique_fd = ::android::base::unique_fd; - ::android::os::incremental::IncrementalFileSystemControlParcel control; - control.cmd.reset(unique_fd(dup(ifs.control.cmd()))); - control.pendingReads.reset(unique_fd(dup(ifs.control.pendingReads()))); + os::incremental::IncrementalFileSystemControlParcel control; + control.cmd.reset(dup(ifs.control.cmd())); + control.pendingReads.reset(dup(ifs.control.pendingReads())); auto logsFd = ifs.control.logs(); if (logsFd >= 0) { - control.log.reset(unique_fd(dup(logsFd))); + control.log.reset(dup(logsFd)); } std::lock_guard l(mMountOperationLock); @@ -664,38 +687,6 @@ StorageId IncrementalService::openStorage(std::string_view pathInMount) { return findStorageId(path::normalize(pathInMount)); } -FileId IncrementalService::nodeFor(StorageId storage, std::string_view subpath) const { - const auto ifs = getIfs(storage); - if (!ifs) { - return kIncFsInvalidFileId; - } - std::unique_lock l(ifs->lock); - auto storageIt = ifs->storages.find(storage); - if (storageIt == ifs->storages.end()) { - return kIncFsInvalidFileId; - } - if (subpath.empty() || subpath == "."sv) { - return kIncFsInvalidFileId; - } - auto path = path::join(ifs->root, constants().mount, storageIt->second.name, subpath); - l.unlock(); - return mIncFs->getFileId(ifs->control, path); -} - -std::pair<FileId, std::string_view> IncrementalService::parentAndNameFor( - StorageId storage, std::string_view subpath) const { - auto name = path::basename(subpath); - if (name.empty()) { - return {kIncFsInvalidFileId, {}}; - } - auto dir = path::dirname(subpath); - if (dir.empty() || dir == "/"sv) { - return {kIncFsInvalidFileId, {}}; - } - auto id = nodeFor(storage, dir); - return {id, name}; -} - IncrementalService::IfsMountPtr IncrementalService::getIfs(StorageId storage) const { std::lock_guard l(mLock); return getIfsLocked(storage); @@ -704,7 +695,7 @@ IncrementalService::IfsMountPtr IncrementalService::getIfs(StorageId storage) co const IncrementalService::IfsMountPtr& IncrementalService::getIfsLocked(StorageId storage) const { auto it = mMounts.find(storage); if (it == mMounts.end()) { - static const android::base::NoDestructor<IfsMountPtr> kEmpty{}; + static const base::NoDestructor<IfsMountPtr> kEmpty{}; return *kEmpty; } return it->second; @@ -713,21 +704,25 @@ const IncrementalService::IfsMountPtr& IncrementalService::getIfsLocked(StorageI int IncrementalService::bind(StorageId storage, std::string_view source, std::string_view target, BindKind kind) { if (!isValidMountTarget(target)) { + LOG(ERROR) << __func__ << ": not a valid bind target " << target; return -EINVAL; } const auto ifs = getIfs(storage); if (!ifs) { + LOG(ERROR) << __func__ << ": no ifs object for storage " << storage; return -EINVAL; } std::unique_lock l(ifs->lock); const auto storageInfo = ifs->storages.find(storage); if (storageInfo == ifs->storages.end()) { + LOG(ERROR) << "no storage"; return -EINVAL; } - std::string normSource = normalizePathToStorageLocked(storageInfo, source); + std::string normSource = normalizePathToStorageLocked(*ifs, storageInfo, source); if (normSource.empty()) { + LOG(ERROR) << "invalid source path"; return -EINVAL; } l.unlock(); @@ -779,33 +774,37 @@ int IncrementalService::unbind(StorageId storage, std::string_view target) { } std::string IncrementalService::normalizePathToStorageLocked( - IncFsMount::StorageMap::iterator storageIt, std::string_view path) { - std::string normPath; - if (path::isAbsolute(path)) { - normPath = path::normalize(path); - if (!path::startsWith(normPath, storageIt->second.name)) { - return {}; - } - } else { - normPath = path::normalize(path::join(storageIt->second.name, path)); + const IncFsMount& incfs, IncFsMount::StorageMap::const_iterator storageIt, + std::string_view path) const { + if (!path::isAbsolute(path)) { + return path::normalize(path::join(storageIt->second.name, path)); } - return normPath; + auto normPath = path::normalize(path); + if (path::startsWith(normPath, storageIt->second.name)) { + return normPath; + } + // not that easy: need to find if any of the bind points match + const auto bindIt = findParentPath(incfs.bindPoints, normPath); + if (bindIt == incfs.bindPoints.end()) { + return {}; + } + return path::join(bindIt->second.sourceDir, path::relativize(bindIt->first, normPath)); } -std::string IncrementalService::normalizePathToStorage(const IncrementalService::IfsMountPtr& ifs, - StorageId storage, std::string_view path) { - std::unique_lock l(ifs->lock); - const auto storageInfo = ifs->storages.find(storage); - if (storageInfo == ifs->storages.end()) { +std::string IncrementalService::normalizePathToStorage(const IncFsMount& ifs, StorageId storage, + std::string_view path) const { + std::unique_lock l(ifs.lock); + const auto storageInfo = ifs.storages.find(storage); + if (storageInfo == ifs.storages.end()) { return {}; } - return normalizePathToStorageLocked(storageInfo, path); + return normalizePathToStorageLocked(ifs, storageInfo, path); } int IncrementalService::makeFile(StorageId storage, std::string_view path, int mode, FileId id, incfs::NewFileParams params) { if (auto ifs = getIfs(storage)) { - std::string normPath = normalizePathToStorage(ifs, storage, path); + std::string normPath = normalizePathToStorage(*ifs, storage, path); if (normPath.empty()) { LOG(ERROR) << "Internal error: storageId " << storage << " failed to normalize: " << path; @@ -823,7 +822,7 @@ int IncrementalService::makeFile(StorageId storage, std::string_view path, int m int IncrementalService::makeDir(StorageId storageId, std::string_view path, int mode) { if (auto ifs = getIfs(storageId)) { - std::string normPath = normalizePathToStorage(ifs, storageId, path); + std::string normPath = normalizePathToStorage(*ifs, storageId, path); if (normPath.empty()) { return -EINVAL; } @@ -837,40 +836,41 @@ int IncrementalService::makeDirs(StorageId storageId, std::string_view path, int if (!ifs) { return -EINVAL; } + return makeDirs(*ifs, storageId, path, mode); +} + +int IncrementalService::makeDirs(const IncFsMount& ifs, StorageId storageId, std::string_view path, + int mode) { std::string normPath = normalizePathToStorage(ifs, storageId, path); if (normPath.empty()) { return -EINVAL; } - auto err = mIncFs->makeDir(ifs->control, normPath, mode); - if (err == -EEXIST) { - return 0; - } else if (err != -ENOENT) { - return err; - } - if (auto err = makeDirs(storageId, path::dirname(normPath), mode)) { - return err; - } - return mIncFs->makeDir(ifs->control, normPath, mode); + return mIncFs->makeDirs(ifs.control, normPath, mode); } int IncrementalService::link(StorageId sourceStorageId, std::string_view oldPath, StorageId destStorageId, std::string_view newPath) { - auto ifsSrc = getIfs(sourceStorageId); - auto ifsDest = sourceStorageId == destStorageId ? ifsSrc : getIfs(destStorageId); - if (ifsSrc && ifsSrc == ifsDest) { - std::string normOldPath = normalizePathToStorage(ifsSrc, sourceStorageId, oldPath); - std::string normNewPath = normalizePathToStorage(ifsDest, destStorageId, newPath); - if (normOldPath.empty() || normNewPath.empty()) { - return -EINVAL; - } - return mIncFs->link(ifsSrc->control, normOldPath, normNewPath); + std::unique_lock l(mLock); + auto ifsSrc = getIfsLocked(sourceStorageId); + if (!ifsSrc) { + return -EINVAL; } - return -EINVAL; + if (sourceStorageId != destStorageId && getIfsLocked(destStorageId) != ifsSrc) { + return -EINVAL; + } + l.unlock(); + std::string normOldPath = normalizePathToStorage(*ifsSrc, sourceStorageId, oldPath); + std::string normNewPath = normalizePathToStorage(*ifsSrc, destStorageId, newPath); + if (normOldPath.empty() || normNewPath.empty()) { + LOG(ERROR) << "Invalid paths in link(): " << normOldPath << " | " << normNewPath; + return -EINVAL; + } + return mIncFs->link(ifsSrc->control, normOldPath, normNewPath); } int IncrementalService::unlink(StorageId storage, std::string_view path) { if (auto ifs = getIfs(storage)) { - std::string normOldPath = normalizePathToStorage(ifs, storage, path); + std::string normOldPath = normalizePathToStorage(*ifs, storage, path); return mIncFs->unlink(ifs->control, normOldPath); } return -EINVAL; @@ -881,10 +881,12 @@ int IncrementalService::addBindMount(IncFsMount& ifs, StorageId storage, std::string&& target, BindKind kind, std::unique_lock<std::mutex>& mainLock) { if (!isValidMountTarget(target)) { + LOG(ERROR) << __func__ << ": invalid mount target " << target; return -EINVAL; } std::string mdFileName; + std::string metadataFullPath; if (kind != BindKind::Temporary) { metadata::BindPoint bp; bp.set_storage_id(storage); @@ -894,17 +896,21 @@ int IncrementalService::addBindMount(IncFsMount& ifs, StorageId storage, bp.release_dest_path(); bp.release_source_subdir(); mdFileName = makeBindMdName(); - auto node = - mIncFs->makeFile(ifs.control, path::join(ifs.root, constants().mount, mdFileName), - 0444, idFromMetadata(metadata), - {.metadata = {metadata.data(), (IncFsSize)metadata.size()}}); + metadataFullPath = path::join(ifs.root, constants().mount, mdFileName); + auto node = mIncFs->makeFile(ifs.control, metadataFullPath, 0444, idFromMetadata(metadata), + {.metadata = {metadata.data(), (IncFsSize)metadata.size()}}); if (node) { + LOG(ERROR) << __func__ << ": couldn't create a mount node " << mdFileName; return int(node); } } - return addBindMountWithMd(ifs, storage, std::move(mdFileName), std::move(source), - std::move(target), kind, mainLock); + const auto res = addBindMountWithMd(ifs, storage, std::move(mdFileName), std::move(source), + std::move(target), kind, mainLock); + if (res) { + mIncFs->unlink(ifs.control, metadataFullPath); + } + return res; } int IncrementalService::addBindMountWithMd(IncrementalService::IncFsMount& ifs, StorageId storage, @@ -929,61 +935,39 @@ int IncrementalService::addBindMountWithMd(IncrementalService::IncFsMount& ifs, mainLock.lock(); } std::lock_guard l(ifs.lock); + addBindMountRecordLocked(ifs, storage, std::move(metadataName), std::move(source), + std::move(target), kind); + return 0; +} + +void IncrementalService::addBindMountRecordLocked(IncFsMount& ifs, StorageId storage, + std::string&& metadataName, std::string&& source, + std::string&& target, BindKind kind) { const auto [it, _] = ifs.bindPoints.insert_or_assign(target, IncFsMount::Bind{storage, std::move(metadataName), std::move(source), kind}); mBindsByPath[std::move(target)] = it; - return 0; } -RawMetadata IncrementalService::getMetadata(StorageId storage, FileId node) const { +RawMetadata IncrementalService::getMetadata(StorageId storage, std::string_view path) const { const auto ifs = getIfs(storage); if (!ifs) { return {}; } - return mIncFs->getMetadata(ifs->control, node); + const auto normPath = normalizePathToStorage(*ifs, storage, path); + if (normPath.empty()) { + return {}; + } + return mIncFs->getMetadata(ifs->control, normPath); } -std::vector<std::string> IncrementalService::listFiles(StorageId storage) const { +RawMetadata IncrementalService::getMetadata(StorageId storage, FileId node) const { const auto ifs = getIfs(storage); if (!ifs) { return {}; } - - std::unique_lock l(ifs->lock); - auto subdirIt = ifs->storages.find(storage); - if (subdirIt == ifs->storages.end()) { - return {}; - } - auto dir = path::join(ifs->root, constants().mount, subdirIt->second.name); - l.unlock(); - - const auto prefixSize = dir.size() + 1; - std::vector<std::string> todoDirs{std::move(dir)}; - std::vector<std::string> result; - do { - auto currDir = std::move(todoDirs.back()); - todoDirs.pop_back(); - - auto d = - std::unique_ptr<DIR, decltype(&::closedir)>(::opendir(currDir.c_str()), ::closedir); - while (auto e = ::readdir(d.get())) { - if (e->d_type == DT_REG) { - result.emplace_back( - path::join(std::string_view(currDir).substr(prefixSize), e->d_name)); - continue; - } - if (e->d_type == DT_DIR) { - if (e->d_name == "."sv || e->d_name == ".."sv) { - continue; - } - todoDirs.emplace_back(path::join(currDir, e->d_name)); - continue; - } - } - } while (!todoDirs.empty()); - return result; + return mIncFs->getMetadata(ifs->control, node); } bool IncrementalService::startLoading(StorageId storage) const { @@ -1003,16 +987,216 @@ bool IncrementalService::startLoading(StorageId storage) const { return true; } -void IncrementalService::mountExistingImages() { - for (const auto& entry : fs::directory_iterator(mIncrementalDir)) { - const auto path = entry.path().u8string(); - const auto name = entry.path().filename().u8string(); - if (!base::StartsWith(name, constants().mountKeyPrefix)) { +std::unordered_set<std::string_view> IncrementalService::adoptMountedInstances() { + std::unordered_set<std::string_view> mountedRootNames; + mIncFs->listExistingMounts([this, &mountedRootNames](auto root, auto backingDir, auto binds) { + LOG(INFO) << "Existing mount: " << backingDir << "->" << root; + for (auto [source, target] : binds) { + LOG(INFO) << " bind: '" << source << "'->'" << target << "'"; + LOG(INFO) << " " << path::join(root, source); + } + + // Ensure it's a kind of a mount that's managed by IncrementalService + if (path::basename(root) != constants().mount || + path::basename(backingDir) != constants().backing) { + return; + } + const auto expectedRoot = path::dirname(root); + if (path::dirname(backingDir) != expectedRoot) { + return; + } + if (path::dirname(expectedRoot) != mIncrementalDir) { + return; + } + if (!path::basename(expectedRoot).starts_with(constants().mountKeyPrefix)) { + return; + } + + LOG(INFO) << "Looks like an IncrementalService-owned: " << expectedRoot; + + // make sure we clean up the mount if it happens to be a bad one. + // Note: unmounting needs to run first, so the cleanup object is created _last_. + auto cleanupFiles = makeCleanup([&]() { + LOG(INFO) << "Failed to adopt existing mount, deleting files: " << expectedRoot; + IncFsMount::cleanupFilesystem(expectedRoot); + }); + auto cleanupMounts = makeCleanup([&]() { + LOG(INFO) << "Failed to adopt existing mount, cleaning up: " << expectedRoot; + for (auto&& [_, target] : binds) { + mVold->unmountIncFs(std::string(target)); + } + mVold->unmountIncFs(std::string(root)); + }); + + auto control = mIncFs->openMount(root); + if (!control) { + LOG(INFO) << "failed to open mount " << root; + return; + } + + auto mountRecord = + parseFromIncfs<metadata::Mount>(mIncFs.get(), control, + path::join(root, constants().infoMdName)); + if (!mountRecord.has_loader() || !mountRecord.has_storage()) { + LOG(ERROR) << "Bad mount metadata in mount at " << expectedRoot; + return; + } + + auto mountId = mountRecord.storage().id(); + mNextId = std::max(mNextId, mountId + 1); + + DataLoaderParamsParcel dataLoaderParams; + { + const auto& loader = mountRecord.loader(); + dataLoaderParams.type = (content::pm::DataLoaderType)loader.type(); + dataLoaderParams.packageName = loader.package_name(); + dataLoaderParams.className = loader.class_name(); + dataLoaderParams.arguments = loader.arguments(); + } + + auto ifs = std::make_shared<IncFsMount>(std::string(expectedRoot), mountId, + std::move(control), *this); + cleanupFiles.release(); // ifs will take care of that now + + std::vector<std::pair<std::string, metadata::BindPoint>> permanentBindPoints; + auto d = openDir(root); + while (auto e = ::readdir(d.get())) { + if (e->d_type == DT_REG) { + auto name = std::string_view(e->d_name); + if (name.starts_with(constants().mountpointMdPrefix)) { + permanentBindPoints + .emplace_back(name, + parseFromIncfs<metadata::BindPoint>(mIncFs.get(), + ifs->control, + path::join(root, + name))); + if (permanentBindPoints.back().second.dest_path().empty() || + permanentBindPoints.back().second.source_subdir().empty()) { + permanentBindPoints.pop_back(); + mIncFs->unlink(ifs->control, path::join(root, name)); + } else { + LOG(INFO) << "Permanent bind record: '" + << permanentBindPoints.back().second.source_subdir() << "'->'" + << permanentBindPoints.back().second.dest_path() << "'"; + } + } + } else if (e->d_type == DT_DIR) { + if (e->d_name == "."sv || e->d_name == ".."sv) { + continue; + } + auto name = std::string_view(e->d_name); + if (name.starts_with(constants().storagePrefix)) { + int storageId; + const auto res = + std::from_chars(name.data() + constants().storagePrefix.size() + 1, + name.data() + name.size(), storageId); + if (res.ec != std::errc{} || *res.ptr != '_') { + LOG(WARNING) << "Ignoring storage with invalid name '" << name + << "' for mount " << expectedRoot; + continue; + } + auto [_, inserted] = mMounts.try_emplace(storageId, ifs); + if (!inserted) { + LOG(WARNING) << "Ignoring storage with duplicate id " << storageId + << " for mount " << expectedRoot; + continue; + } + ifs->storages.insert_or_assign(storageId, + IncFsMount::Storage{path::join(root, name)}); + mNextId = std::max(mNextId, storageId + 1); + } + } + } + + if (ifs->storages.empty()) { + LOG(WARNING) << "No valid storages in mount " << root; + return; + } + + // now match the mounted directories with what we expect to have in the metadata + { + std::unique_lock l(mLock, std::defer_lock); + for (auto&& [metadataFile, bindRecord] : permanentBindPoints) { + auto mountedIt = std::find_if(binds.begin(), binds.end(), + [&, bindRecord = bindRecord](auto&& bind) { + return bind.second == bindRecord.dest_path() && + path::join(root, bind.first) == + bindRecord.source_subdir(); + }); + if (mountedIt != binds.end()) { + LOG(INFO) << "Matched permanent bound " << bindRecord.source_subdir() + << " to mount " << mountedIt->first; + addBindMountRecordLocked(*ifs, bindRecord.storage_id(), std::move(metadataFile), + std::move(*bindRecord.mutable_source_subdir()), + std::move(*bindRecord.mutable_dest_path()), + BindKind::Permanent); + if (mountedIt != binds.end() - 1) { + std::iter_swap(mountedIt, binds.end() - 1); + } + binds = binds.first(binds.size() - 1); + } else { + LOG(INFO) << "Didn't match permanent bound " << bindRecord.source_subdir() + << ", mounting"; + // doesn't exist - try mounting back + if (addBindMountWithMd(*ifs, bindRecord.storage_id(), std::move(metadataFile), + std::move(*bindRecord.mutable_source_subdir()), + std::move(*bindRecord.mutable_dest_path()), + BindKind::Permanent, l)) { + mIncFs->unlink(ifs->control, metadataFile); + } + } + } + } + + // if anything stays in |binds| those are probably temporary binds; system restarted since + // they were mounted - so let's unmount them all. + for (auto&& [source, target] : binds) { + if (source.empty()) { + continue; + } + mVold->unmountIncFs(std::string(target)); + } + cleanupMounts.release(); // ifs now manages everything + + if (ifs->bindPoints.empty()) { + LOG(WARNING) << "No valid bind points for mount " << expectedRoot; + deleteStorage(*ifs); + return; + } + + prepareDataLoaderLocked(*ifs, std::move(dataLoaderParams)); + CHECK(ifs->dataLoaderStub); + + mountedRootNames.insert(path::basename(ifs->root)); + + // not locking here at all: we're still in the constructor, no other calls can happen + mMounts[ifs->mountId] = std::move(ifs); + }); + + return mountedRootNames; +} + +void IncrementalService::mountExistingImages( + const std::unordered_set<std::string_view>& mountedRootNames) { + auto dir = openDir(mIncrementalDir); + if (!dir) { + PLOG(WARNING) << "Couldn't open the root incremental dir " << mIncrementalDir; + return; + } + while (auto entry = ::readdir(dir.get())) { + if (entry->d_type != DT_DIR) { + continue; + } + std::string_view name = entry->d_name; + if (!name.starts_with(constants().mountKeyPrefix)) { + continue; + } + if (mountedRootNames.find(name) != mountedRootNames.end()) { continue; } const auto root = path::join(mIncrementalDir, name); if (!mountExistingImage(root)) { - IncFsMount::cleanupFilesystem(path); + IncFsMount::cleanupFilesystem(root); } } } @@ -1049,7 +1233,7 @@ bool IncrementalService::mountExistingImage(std::string_view root) { DataLoaderParamsParcel dataLoaderParams; { const auto& loader = mount.loader(); - dataLoaderParams.type = (android::content::pm::DataLoaderType)loader.type(); + dataLoaderParams.type = (content::pm::DataLoaderType)loader.type(); dataLoaderParams.packageName = loader.package_name(); dataLoaderParams.className = loader.class_name(); dataLoaderParams.arguments = loader.arguments(); @@ -1059,7 +1243,7 @@ bool IncrementalService::mountExistingImage(std::string_view root) { CHECK(ifs->dataLoaderStub); std::vector<std::pair<std::string, metadata::BindPoint>> bindPoints; - auto d = openDir(path::c_str(mountTarget)); + auto d = openDir(mountTarget); while (auto e = ::readdir(d.get())) { if (e->d_type == DT_REG) { auto name = std::string_view(e->d_name); @@ -1109,12 +1293,14 @@ bool IncrementalService::mountExistingImage(std::string_view root) { } int bindCount = 0; - for (auto&& bp : bindPoints) { + { std::unique_lock l(mLock, std::defer_lock); - bindCount += !addBindMountWithMd(*ifs, bp.second.storage_id(), std::move(bp.first), - std::move(*bp.second.mutable_source_subdir()), - std::move(*bp.second.mutable_dest_path()), - BindKind::Permanent, l); + for (auto&& bp : bindPoints) { + bindCount += !addBindMountWithMd(*ifs, bp.second.storage_id(), std::move(bp.first), + std::move(*bp.second.mutable_source_subdir()), + std::move(*bp.second.mutable_dest_path()), + BindKind::Permanent, l); + } } if (bindCount == 0) { @@ -1123,30 +1309,35 @@ bool IncrementalService::mountExistingImage(std::string_view root) { return false; } + // not locking here at all: we're still in the constructor, no other calls can happen mMounts[ifs->mountId] = std::move(ifs); return true; } IncrementalService::DataLoaderStubPtr IncrementalService::prepareDataLoader( - IncrementalService::IncFsMount& ifs, DataLoaderParamsParcel&& params, + IncFsMount& ifs, DataLoaderParamsParcel&& params, const DataLoaderStatusListener* externalListener) { std::unique_lock l(ifs.lock); + prepareDataLoaderLocked(ifs, std::move(params), externalListener); + return ifs.dataLoaderStub; +} + +void IncrementalService::prepareDataLoaderLocked(IncFsMount& ifs, DataLoaderParamsParcel&& params, + const DataLoaderStatusListener* externalListener) { if (ifs.dataLoaderStub) { LOG(INFO) << "Skipped data loader preparation because it already exists"; - return ifs.dataLoaderStub; + return; } FileSystemControlParcel fsControlParcel; fsControlParcel.incremental = aidl::make_nullable<IncrementalFileSystemControlParcel>(); - fsControlParcel.incremental->cmd.reset(base::unique_fd(::dup(ifs.control.cmd()))); - fsControlParcel.incremental->pendingReads.reset( - base::unique_fd(::dup(ifs.control.pendingReads()))); - fsControlParcel.incremental->log.reset(base::unique_fd(::dup(ifs.control.logs()))); + fsControlParcel.incremental->cmd.reset(dup(ifs.control.cmd())); + fsControlParcel.incremental->pendingReads.reset(dup(ifs.control.pendingReads())); + fsControlParcel.incremental->log.reset(dup(ifs.control.logs())); fsControlParcel.service = new IncrementalServiceConnector(*this, ifs.mountId); ifs.dataLoaderStub = new DataLoaderStub(*this, ifs.mountId, std::move(params), std::move(fsControlParcel), externalListener); - return ifs.dataLoaderStub; } template <class Duration> @@ -1167,7 +1358,7 @@ bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_ } // First prepare target directories if they don't exist yet - if (auto res = makeDirs(storage, libDirRelativePath, 0755)) { + if (auto res = makeDirs(*ifs, storage, libDirRelativePath, 0755)) { LOG(ERROR) << "Failed to prepare target lib directory " << libDirRelativePath << " errno: " << res; return false; @@ -1204,11 +1395,11 @@ bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_ auto startFileTs = Clock::now(); const auto libName = path::basename(fileName); - const auto targetLibPath = path::join(libDirRelativePath, libName); - const auto targetLibPathAbsolute = normalizePathToStorage(ifs, storage, targetLibPath); + auto targetLibPath = path::join(libDirRelativePath, libName); + const auto targetLibPathAbsolute = normalizePathToStorage(*ifs, storage, targetLibPath); // If the extract file already exists, skip if (access(targetLibPathAbsolute.c_str(), F_OK) == 0) { - if (sEnablePerfLogging) { + if (perfLoggingEnabled()) { LOG(INFO) << "incfs: Native lib file already exists: " << targetLibPath << "; skipping extraction, spent " << elapsedMcs(startFileTs, Clock::now()) << "mcs"; @@ -1235,7 +1426,7 @@ bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_ // If it is a zero-byte file, skip data writing if (entry.uncompressed_length == 0) { - if (sEnablePerfLogging) { + if (perfLoggingEnabled()) { LOG(INFO) << "incfs: Extracted " << libName << "(0 bytes): " << elapsedMcs(startFileTs, makeFileTs) << "mcs"; } @@ -1248,7 +1439,7 @@ bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_ extractZipFile(ifs.lock(), zipFile.get(), entry, libFileId, libPath, makeFileTs); }); - if (sEnablePerfLogging) { + if (perfLoggingEnabled()) { auto prepareJobTs = Clock::now(); LOG(INFO) << "incfs: Processed " << libName << ": " << elapsedMcs(startFileTs, prepareJobTs) @@ -1275,7 +1466,7 @@ bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_ mJobCondition.notify_all(); } - if (sEnablePerfLogging) { + if (perfLoggingEnabled()) { auto end = Clock::now(); LOG(INFO) << "incfs: configureNativeBinaries complete in " << elapsedMcs(start, end) << "mcs, make dirs: " << elapsedMcs(start, mkDirsTs) @@ -1340,7 +1531,7 @@ void IncrementalService::extractZipFile(const IfsMountPtr& ifs, ZipArchiveHandle return; } - if (sEnablePerfLogging) { + if (perfLoggingEnabled()) { auto endFileTs = Clock::now(); LOG(INFO) << "incfs: Extracted " << libName << "(" << entry.compressed_length << " -> " << entry.uncompressed_length << " bytes): " << elapsedMcs(startedTs, endFileTs) @@ -1356,7 +1547,7 @@ bool IncrementalService::waitForNativeBinariesExtraction(StorageId storage) { struct WaitPrinter { const Clock::time_point startTs = Clock::now(); ~WaitPrinter() noexcept { - if (sEnablePerfLogging) { + if (perfLoggingEnabled()) { const auto endTs = Clock::now(); LOG(INFO) << "incfs: waitForNativeBinariesExtraction() complete in " << elapsedMcs(startTs, endTs) << "mcs"; @@ -1381,6 +1572,11 @@ bool IncrementalService::waitForNativeBinariesExtraction(StorageId storage) { return mRunning; } +bool IncrementalService::perfLoggingEnabled() { + static const bool enabled = base::GetBoolProperty("incremental.perflogging", false); + return enabled; +} + void IncrementalService::runJobProcessing() { for (;;) { std::unique_lock lock(mJobMutex); @@ -1492,12 +1688,17 @@ bool IncrementalService::DataLoaderStub::requestDestroy() { return setTargetStatus(IDataLoaderStatusListener::DATA_LOADER_DESTROYED); } -bool IncrementalService::DataLoaderStub::setTargetStatus(int status) { +bool IncrementalService::DataLoaderStub::setTargetStatus(int newStatus) { + int oldStatus, curStatus; { std::unique_lock lock(mStatusMutex); - mTargetStatus = status; + oldStatus = mTargetStatus; + mTargetStatus = newStatus; mTargetStatusTs = Clock::now(); + curStatus = mCurrentStatus; } + LOG(DEBUG) << "Target status update for DataLoader " << mId << ": " << oldStatus << " -> " + << newStatus << " (current " << curStatus << ")"; return fsmStep(); } @@ -1596,14 +1797,20 @@ binder::Status IncrementalService::DataLoaderStub::onStatusChanged(MountId mount return binder::Status::fromServiceSpecificError(-EPERM, "Mount ID mismatch."); } + int targetStatus, oldStatus; { std::unique_lock lock(mStatusMutex); if (mCurrentStatus == newStatus) { return binder::Status::ok(); } mCurrentStatus = newStatus; + oldStatus = mCurrentStatus; + targetStatus = mTargetStatus; } + LOG(DEBUG) << "Current status update for DataLoader " << mId << ": " << oldStatus << " -> " + << newStatus << " (target " << targetStatus << ")"; + if (mListener) { mListener->onStatusChanged(mountId, newStatus); } @@ -1616,17 +1823,19 @@ binder::Status IncrementalService::DataLoaderStub::onStatusChanged(MountId mount } void IncrementalService::DataLoaderStub::onDump(int fd) { - dprintf(fd, "\t\tdataLoader:"); - dprintf(fd, "\t\t\tcurrentStatus: %d\n", mCurrentStatus); - dprintf(fd, "\t\t\ttargetStatus: %d\n", mTargetStatus); - dprintf(fd, "\t\t\ttargetStatusTs: %lldmcs\n", + dprintf(fd, " dataLoader: {\n"); + dprintf(fd, " currentStatus: %d\n", mCurrentStatus); + dprintf(fd, " targetStatus: %d\n", mTargetStatus); + dprintf(fd, " targetStatusTs: %lldmcs\n", (long long)(elapsedMcs(mTargetStatusTs, Clock::now()))); const auto& params = mParams; - dprintf(fd, "\t\t\tdataLoaderParams:\n"); - dprintf(fd, "\t\t\t\ttype: %s\n", toString(params.type).c_str()); - dprintf(fd, "\t\t\t\tpackageName: %s\n", params.packageName.c_str()); - dprintf(fd, "\t\t\t\tclassName: %s\n", params.className.c_str()); - dprintf(fd, "\t\t\t\targuments: %s\n", params.arguments.c_str()); + dprintf(fd, " dataLoaderParams: {\n"); + dprintf(fd, " type: %s\n", toString(params.type).c_str()); + dprintf(fd, " packageName: %s\n", params.packageName.c_str()); + dprintf(fd, " className: %s\n", params.className.c_str()); + dprintf(fd, " arguments: %s\n", params.arguments.c_str()); + dprintf(fd, " }\n"); + dprintf(fd, " }\n"); } void IncrementalService::AppOpsListener::opChanged(int32_t, const String16&) { diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h index bd01d7760a01..0a18e21e9d6d 100644 --- a/services/incremental/IncrementalService.h +++ b/services/incremental/IncrementalService.h @@ -16,13 +16,13 @@ #pragma once -#include <android-base/strings.h> -#include <android-base/unique_fd.h> +#include <android/content/pm/BnDataLoaderStatusListener.h> #include <android/content/pm/DataLoaderParamsParcel.h> -#include <binder/IServiceManager.h> +#include <android/content/pm/IDataLoaderStatusListener.h> +#include <android/os/incremental/BnIncrementalServiceConnector.h> +#include <binder/IAppOpsCallback.h> #include <utils/String16.h> #include <utils/StrongPointer.h> -#include <utils/Vector.h> #include <ziparchive/zip_archive.h> #include <atomic> @@ -37,21 +37,14 @@ #include <string_view> #include <thread> #include <unordered_map> +#include <unordered_set> #include <utility> #include <vector> #include "ServiceWrappers.h" -#include "android/content/pm/BnDataLoaderStatusListener.h" -#include "android/os/incremental/BnIncrementalServiceConnector.h" #include "incfs.h" #include "path.h" -using namespace android::os::incremental; - -namespace android::os { -class IVold; -} - namespace android::incremental { using MountId = int; @@ -101,17 +94,14 @@ public: void onSystemReady(); - StorageId createStorage(std::string_view mountPoint, DataLoaderParamsParcel&& dataLoaderParams, + StorageId createStorage(std::string_view mountPoint, + content::pm::DataLoaderParamsParcel&& dataLoaderParams, const DataLoaderStatusListener& dataLoaderStatusListener, CreateOptions options = CreateOptions::Default); StorageId createLinkedStorage(std::string_view mountPoint, StorageId linkedStorage, CreateOptions options = CreateOptions::Default); StorageId openStorage(std::string_view path); - FileId nodeFor(StorageId storage, std::string_view path) const; - std::pair<FileId, std::string_view> parentAndNameFor(StorageId storage, - std::string_view path) const; - int bind(StorageId storage, std::string_view source, std::string_view target, BindKind kind); int unbind(StorageId storage, std::string_view target); void deleteStorage(StorageId storage); @@ -131,9 +121,9 @@ public: return false; } + RawMetadata getMetadata(StorageId storage, std::string_view path) const; RawMetadata getMetadata(StorageId storage, FileId node) const; - std::vector<std::string> listFiles(StorageId storage) const; bool startLoading(StorageId storage) const; bool configureNativeBinaries(StorageId storage, std::string_view apkFullPath, @@ -151,7 +141,7 @@ public: const std::string packageName; }; - class IncrementalServiceConnector : public BnIncrementalServiceConnector { + class IncrementalServiceConnector : public os::incremental::BnIncrementalServiceConnector { public: IncrementalServiceConnector(IncrementalService& incrementalService, int32_t storage) : incrementalService(incrementalService), storage(storage) {} @@ -163,14 +153,13 @@ public: }; private: - static const bool sEnablePerfLogging; - struct IncFsMount; - class DataLoaderStub : public android::content::pm::BnDataLoaderStatusListener { + class DataLoaderStub : public content::pm::BnDataLoaderStatusListener { public: - DataLoaderStub(IncrementalService& service, MountId id, DataLoaderParamsParcel&& params, - FileSystemControlParcel&& control, + DataLoaderStub(IncrementalService& service, MountId id, + content::pm::DataLoaderParamsParcel&& params, + content::pm::FileSystemControlParcel&& control, const DataLoaderStatusListener* externalListener); ~DataLoaderStub(); // Cleans up the internal state and invalidates DataLoaderStub. Any subsequent calls will @@ -184,7 +173,7 @@ private: void onDump(int fd); MountId id() const { return mId; } - const DataLoaderParamsParcel& params() const { return mParams; } + const content::pm::DataLoaderParamsParcel& params() const { return mParams; } private: binder::Status onStatusChanged(MountId mount, int newStatus) final; @@ -202,14 +191,14 @@ private: IncrementalService& mService; MountId mId = kInvalidStorageId; - DataLoaderParamsParcel mParams; - FileSystemControlParcel mControl; + content::pm::DataLoaderParamsParcel mParams; + content::pm::FileSystemControlParcel mControl; DataLoaderStatusListener mListener; std::mutex mStatusMutex; std::condition_variable mStatusCondition; - int mCurrentStatus = IDataLoaderStatusListener::DATA_LOADER_DESTROYED; - int mTargetStatus = IDataLoaderStatusListener::DATA_LOADER_DESTROYED; + int mCurrentStatus = content::pm::IDataLoaderStatusListener::DATA_LOADER_DESTROYED; + int mTargetStatus = content::pm::IDataLoaderStatusListener::DATA_LOADER_DESTROYED; TimePoint mTargetStatusTs = {}; }; using DataLoaderStubPtr = sp<DataLoaderStub>; @@ -228,7 +217,7 @@ private: using Control = incfs::UniqueControl; - using BindMap = std::map<std::string, Bind>; + using BindMap = std::map<std::string, Bind, path::PathLess>; using StorageMap = std::unordered_map<StorageId, Storage>; mutable std::mutex lock; @@ -260,7 +249,10 @@ private: using MountMap = std::unordered_map<MountId, IfsMountPtr>; using BindPathMap = std::map<std::string, IncFsMount::BindMap::iterator, path::PathLess>; - void mountExistingImages(); + static bool perfLoggingEnabled(); + + std::unordered_set<std::string_view> adoptMountedInstances(); + void mountExistingImages(const std::unordered_set<std::string_view>& mountedRootNames); bool mountExistingImage(std::string_view root); IfsMountPtr getIfs(StorageId storage) const; @@ -273,8 +265,14 @@ private: std::string&& source, std::string&& target, BindKind kind, std::unique_lock<std::mutex>& mainLock); - DataLoaderStubPtr prepareDataLoader(IncFsMount& ifs, DataLoaderParamsParcel&& params, + void addBindMountRecordLocked(IncFsMount& ifs, StorageId storage, std::string&& metadataName, + std::string&& source, std::string&& target, BindKind kind); + + DataLoaderStubPtr prepareDataLoader(IncFsMount& ifs, + content::pm::DataLoaderParamsParcel&& params, const DataLoaderStatusListener* externalListener = nullptr); + void prepareDataLoaderLocked(IncFsMount& ifs, content::pm::DataLoaderParamsParcel&& params, + const DataLoaderStatusListener* externalListener = nullptr); BindPathMap::const_iterator findStorageLocked(std::string_view path) const; StorageId findStorageId(std::string_view path) const; @@ -282,11 +280,12 @@ private: void deleteStorage(IncFsMount& ifs); void deleteStorageLocked(IncFsMount& ifs, std::unique_lock<std::mutex>&& ifsLock); MountMap::iterator getStorageSlotLocked(); - std::string normalizePathToStorage(const IfsMountPtr& incfs, StorageId storage, - std::string_view path); - std::string normalizePathToStorageLocked(IncFsMount::StorageMap::iterator storageIt, - std::string_view path); - + std::string normalizePathToStorage(const IncFsMount& incfs, StorageId storage, + std::string_view path) const; + std::string normalizePathToStorageLocked(const IncFsMount& incfs, + IncFsMount::StorageMap::const_iterator storageIt, + std::string_view path) const; + int makeDirs(const IncFsMount& ifs, StorageId storageId, std::string_view path, int mode); binder::Status applyStorageParams(IncFsMount& ifs, bool enableReadLogs); void registerAppOpsCallback(const std::string& packageName); diff --git a/services/incremental/IncrementalServiceValidation.cpp b/services/incremental/IncrementalServiceValidation.cpp new file mode 100644 index 000000000000..abadbbf10742 --- /dev/null +++ b/services/incremental/IncrementalServiceValidation.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "IncrementalServiceValidation.h" + +#include <android-base/stringprintf.h> +#include <binder/IPCThreadState.h> +#include <binder/PermissionCache.h> +#include <binder/PermissionController.h> +#include <errno.h> +#include <utils/String16.h> + +namespace android::incremental { + +binder::Status Ok() { + return binder::Status::ok(); +} + +binder::Status Exception(uint32_t code, const std::string& msg) { + return binder::Status::fromExceptionCode(code, String8(msg.c_str())); +} + +int fromBinderStatus(const binder::Status& status) { + return status.exceptionCode() == binder::Status::EX_SERVICE_SPECIFIC + ? status.serviceSpecificErrorCode() > 0 + ? -status.serviceSpecificErrorCode() + : status.serviceSpecificErrorCode() == 0 ? -EFAULT + : status.serviceSpecificErrorCode() + : -EIO; +} + +binder::Status CheckPermissionForDataDelivery(const char* permission, const char* operation, + const char* package) { + using android::base::StringPrintf; + + int32_t pid; + int32_t uid; + + if (!PermissionCache::checkCallingPermission(String16(permission), &pid, &uid)) { + return Exception(binder::Status::EX_SECURITY, + StringPrintf("UID %d / PID %d lacks permission %s", uid, pid, permission)); + } + + String16 packageName{package}; + + // Caller must also have op granted. + PermissionController pc; + if (auto packageUid = pc.getPackageUid(packageName, 0); packageUid != uid) { + return Exception(binder::Status::EX_SECURITY, + StringPrintf("UID %d / PID %d does not own package %s", uid, pid, + package)); + } + switch (auto result = pc.noteOp(String16(operation), uid, packageName); result) { + case PermissionController::MODE_ALLOWED: + case PermissionController::MODE_DEFAULT: + return binder::Status::ok(); + default: + return Exception(binder::Status::EX_SECURITY, + StringPrintf("UID %d / PID %d / package %s lacks app-op %s, error %d", + uid, pid, package, operation, result)); + } +} + +} // namespace android::incremental diff --git a/services/incremental/IncrementalServiceValidation.h b/services/incremental/IncrementalServiceValidation.h index 48894c6926c8..0e50c4db29f4 100644 --- a/services/incremental/IncrementalServiceValidation.h +++ b/services/incremental/IncrementalServiceValidation.h @@ -16,61 +16,17 @@ #pragma once -#include <android-base/stringprintf.h> -#include <binder/IPCThreadState.h> -#include <binder/PermissionCache.h> -#include <binder/PermissionController.h> #include <binder/Status.h> +#include <stdint.h> -namespace android::incremental { - -inline binder::Status Ok() { - return binder::Status::ok(); -} - -inline binder::Status Exception(uint32_t code, const std::string& msg) { - return binder::Status::fromExceptionCode(code, String8(msg.c_str())); -} - -inline int fromBinderStatus(const binder::Status& status) { - return status.exceptionCode() == binder::Status::EX_SERVICE_SPECIFIC - ? status.serviceSpecificErrorCode() > 0 ? -status.serviceSpecificErrorCode() - : status.serviceSpecificErrorCode() == 0 - ? -EFAULT - : status.serviceSpecificErrorCode() - : -EIO; -} +#include <string> -inline binder::Status CheckPermissionForDataDelivery(const char* permission, const char* operation, - const char* package) { - using android::base::StringPrintf; - - int32_t pid; - int32_t uid; - - if (!PermissionCache::checkCallingPermission(String16(permission), &pid, &uid)) { - return Exception(binder::Status::EX_SECURITY, - StringPrintf("UID %d / PID %d lacks permission %s", uid, pid, permission)); - } - - String16 packageName{package}; +namespace android::incremental { - // Caller must also have op granted. - PermissionController pc; - if (auto packageUid = pc.getPackageUid(packageName, 0); packageUid != uid) { - return Exception(binder::Status::EX_SECURITY, - StringPrintf("UID %d / PID %d does not own package %s", uid, pid, - package)); - } - switch (auto result = pc.noteOp(String16(operation), uid, packageName); result) { - case PermissionController::MODE_ALLOWED: - case PermissionController::MODE_DEFAULT: - return binder::Status::ok(); - default: - return Exception(binder::Status::EX_SECURITY, - StringPrintf("UID %d / PID %d / package %s lacks app-op %s, error %d", - uid, pid, package, operation, result)); - } -} +binder::Status Ok(); +binder::Status Exception(uint32_t code, const std::string& msg); +int fromBinderStatus(const binder::Status& status); +binder::Status CheckPermissionForDataDelivery(const char* permission, const char* operation, + const char* package); } // namespace android::incremental diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp index bf8e696a264c..1e8c3a1f814b 100644 --- a/services/incremental/ServiceWrappers.cpp +++ b/services/incremental/ServiceWrappers.cpp @@ -18,23 +18,29 @@ #include "ServiceWrappers.h" +#include <MountRegistry.h> #include <android-base/logging.h> +#include <android/content/pm/IDataLoaderManager.h> +#include <android/os/IVold.h> +#include <binder/AppOpsManager.h> #include <utils/String16.h> +#include "IncrementalServiceValidation.h" + using namespace std::literals; -namespace android::os::incremental { +namespace android::incremental { static constexpr auto kVoldServiceName = "vold"sv; static constexpr auto kDataLoaderManagerName = "dataloader_manager"sv; class RealVoldService : public VoldServiceWrapper { public: - RealVoldService(const sp<os::IVold> vold) : mInterface(std::move(vold)) {} + RealVoldService(sp<os::IVold> vold) : mInterface(std::move(vold)) {} ~RealVoldService() = default; - binder::Status mountIncFs(const std::string& backingPath, const std::string& targetDir, - int32_t flags, - IncrementalFileSystemControlParcel* _aidl_return) const final { + binder::Status mountIncFs( + const std::string& backingPath, const std::string& targetDir, int32_t flags, + os::incremental::IncrementalFileSystemControlParcel* _aidl_return) const final { return mInterface->mountIncFs(backingPath, targetDir, flags, _aidl_return); } binder::Status unmountIncFs(const std::string& dir) const final { @@ -56,16 +62,18 @@ private: class RealDataLoaderManager : public DataLoaderManagerWrapper { public: - RealDataLoaderManager(const sp<content::pm::IDataLoaderManager> manager) - : mInterface(manager) {} + RealDataLoaderManager(sp<content::pm::IDataLoaderManager> manager) + : mInterface(std::move(manager)) {} ~RealDataLoaderManager() = default; - binder::Status initializeDataLoader(MountId mountId, const DataLoaderParamsParcel& params, - const FileSystemControlParcel& control, - const sp<IDataLoaderStatusListener>& listener, + binder::Status initializeDataLoader(MountId mountId, + const content::pm::DataLoaderParamsParcel& params, + const content::pm::FileSystemControlParcel& control, + const sp<content::pm::IDataLoaderStatusListener>& listener, bool* _aidl_return) const final { return mInterface->initializeDataLoader(mountId, params, control, listener, _aidl_return); } - binder::Status getDataLoader(MountId mountId, sp<IDataLoader>* _aidl_return) const final { + binder::Status getDataLoader(MountId mountId, + sp<content::pm::IDataLoader>* _aidl_return) const final { return mInterface->getDataLoader(mountId, _aidl_return); } binder::Status destroyDataLoader(MountId mountId) const final { @@ -109,21 +117,31 @@ private: class RealIncFs : public IncFsWrapper { public: RealIncFs() = default; - ~RealIncFs() = default; + ~RealIncFs() final = default; + void listExistingMounts(const ExistingMountCallback& cb) const final { + for (auto mount : incfs::defaultMountRegistry().copyMounts()) { + auto binds = mount.binds(); // span() doesn't like rvalue containers, needs to save it. + cb(mount.root(), mount.backingDir(), binds); + } + } + Control openMount(std::string_view path) const final { return incfs::open(path); } Control createControl(IncFsFd cmd, IncFsFd pendingReads, IncFsFd logs) const final { return incfs::createControl(cmd, pendingReads, logs); } ErrorCode makeFile(const Control& control, std::string_view path, int mode, FileId id, - NewFileParams params) const final { + incfs::NewFileParams params) const final { return incfs::makeFile(control, path, mode, id, params); } ErrorCode makeDir(const Control& control, std::string_view path, int mode) const final { return incfs::makeDir(control, path, mode); } - RawMetadata getMetadata(const Control& control, FileId fileid) const final { + ErrorCode makeDirs(const Control& control, std::string_view path, int mode) const final { + return incfs::makeDirs(control, path, mode); + } + incfs::RawMetadata getMetadata(const Control& control, FileId fileid) const final { return incfs::getMetadata(control, fileid); } - RawMetadata getMetadata(const Control& control, std::string_view path) const final { + incfs::RawMetadata getMetadata(const Control& control, std::string_view path) const final { return incfs::getMetadata(control, path); } FileId getFileId(const Control& control, std::string_view path) const final { @@ -138,8 +156,8 @@ public: base::unique_fd openForSpecialOps(const Control& control, FileId id) const final { return base::unique_fd{incfs::openForSpecialOps(control, id).release()}; } - ErrorCode writeBlocks(Span<const DataBlock> blocks) const final { - return incfs::writeBlocks(blocks); + ErrorCode writeBlocks(std::span<const incfs::DataBlock> blocks) const final { + return incfs::writeBlocks({blocks.data(), size_t(blocks.size())}); } }; @@ -165,8 +183,9 @@ std::unique_ptr<VoldServiceWrapper> RealServiceManager::getVoldService() { } std::unique_ptr<DataLoaderManagerWrapper> RealServiceManager::getDataLoaderManager() { - sp<IDataLoaderManager> manager = - RealServiceManager::getRealService<IDataLoaderManager>(kDataLoaderManagerName); + sp<content::pm::IDataLoaderManager> manager = + RealServiceManager::getRealService<content::pm::IDataLoaderManager>( + kDataLoaderManagerName); if (manager) { return std::make_unique<RealDataLoaderManager>(manager); } @@ -239,4 +258,4 @@ JavaVM* RealJniWrapper::getJvm(JNIEnv* env) { return getJavaVm(env); } -} // namespace android::os::incremental +} // namespace android::incremental diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h index 142bf2ef32f3..6b0f59e9c3e5 100644 --- a/services/incremental/ServiceWrappers.h +++ b/services/incremental/ServiceWrappers.h @@ -16,29 +16,23 @@ #pragma once -#include "IncrementalServiceValidation.h" - -#include <android-base/strings.h> #include <android-base/unique_fd.h> #include <android/content/pm/DataLoaderParamsParcel.h> #include <android/content/pm/FileSystemControlParcel.h> #include <android/content/pm/IDataLoader.h> -#include <android/content/pm/IDataLoaderManager.h> #include <android/content/pm/IDataLoaderStatusListener.h> -#include <android/os/IVold.h> -#include <binder/AppOpsManager.h> +#include <binder/IAppOpsCallback.h> #include <binder/IServiceManager.h> +#include <binder/Status.h> #include <incfs.h> #include <jni.h> #include <memory> +#include <span> #include <string> #include <string_view> -using namespace android::incfs; -using namespace android::content::pm; - -namespace android::os::incremental { +namespace android::incremental { // --- Wrapper interfaces --- @@ -47,42 +41,55 @@ using MountId = int32_t; class VoldServiceWrapper { public: virtual ~VoldServiceWrapper() = default; - virtual binder::Status mountIncFs(const std::string& backingPath, const std::string& targetDir, - int32_t flags, - IncrementalFileSystemControlParcel* _aidl_return) const = 0; + virtual binder::Status mountIncFs( + const std::string& backingPath, const std::string& targetDir, int32_t flags, + os::incremental::IncrementalFileSystemControlParcel* result) const = 0; virtual binder::Status unmountIncFs(const std::string& dir) const = 0; virtual binder::Status bindMount(const std::string& sourceDir, const std::string& targetDir) const = 0; - virtual binder::Status setIncFsMountOptions(const ::android::os::incremental::IncrementalFileSystemControlParcel& control, bool enableReadLogs) const = 0; + virtual binder::Status setIncFsMountOptions( + const os::incremental::IncrementalFileSystemControlParcel& control, + bool enableReadLogs) const = 0; }; class DataLoaderManagerWrapper { public: virtual ~DataLoaderManagerWrapper() = default; - virtual binder::Status initializeDataLoader(MountId mountId, - const DataLoaderParamsParcel& params, - const FileSystemControlParcel& control, - const sp<IDataLoaderStatusListener>& listener, - bool* _aidl_return) const = 0; - virtual binder::Status getDataLoader(MountId mountId, sp<IDataLoader>* _aidl_return) const = 0; + virtual binder::Status initializeDataLoader( + MountId mountId, const content::pm::DataLoaderParamsParcel& params, + const content::pm::FileSystemControlParcel& control, + const sp<content::pm::IDataLoaderStatusListener>& listener, bool* result) const = 0; + virtual binder::Status getDataLoader(MountId mountId, + sp<content::pm::IDataLoader>* result) const = 0; virtual binder::Status destroyDataLoader(MountId mountId) const = 0; }; class IncFsWrapper { public: + using Control = incfs::Control; + using FileId = incfs::FileId; + using ErrorCode = incfs::ErrorCode; + + using ExistingMountCallback = + std::function<void(std::string_view root, std::string_view backingDir, + std::span<std::pair<std::string_view, std::string_view>> binds)>; + virtual ~IncFsWrapper() = default; + virtual void listExistingMounts(const ExistingMountCallback& cb) const = 0; + virtual Control openMount(std::string_view path) const = 0; virtual Control createControl(IncFsFd cmd, IncFsFd pendingReads, IncFsFd logs) const = 0; virtual ErrorCode makeFile(const Control& control, std::string_view path, int mode, FileId id, - NewFileParams params) const = 0; + incfs::NewFileParams params) const = 0; virtual ErrorCode makeDir(const Control& control, std::string_view path, int mode) const = 0; - virtual RawMetadata getMetadata(const Control& control, FileId fileid) const = 0; - virtual RawMetadata getMetadata(const Control& control, std::string_view path) const = 0; + virtual ErrorCode makeDirs(const Control& control, std::string_view path, int mode) const = 0; + virtual incfs::RawMetadata getMetadata(const Control& control, FileId fileid) const = 0; + virtual incfs::RawMetadata getMetadata(const Control& control, std::string_view path) const = 0; virtual FileId getFileId(const Control& control, std::string_view path) const = 0; virtual ErrorCode link(const Control& control, std::string_view from, std::string_view to) const = 0; virtual ErrorCode unlink(const Control& control, std::string_view path) const = 0; virtual base::unique_fd openForSpecialOps(const Control& control, FileId id) const = 0; - virtual ErrorCode writeBlocks(Span<const DataBlock> blocks) const = 0; + virtual ErrorCode writeBlocks(std::span<const incfs::DataBlock> blocks) const = 0; }; class AppOpsManagerWrapper { @@ -129,4 +136,4 @@ private: JavaVM* const mJvm; }; -} // namespace android::os::incremental +} // namespace android::incremental diff --git a/services/incremental/path.cpp b/services/incremental/path.cpp index 0d86f2a984d6..338659d40b46 100644 --- a/services/incremental/path.cpp +++ b/services/incremental/path.cpp @@ -44,7 +44,7 @@ bool PathLess::operator()(std::string_view l, std::string_view r) const { PathCharsLess()); } -static void preparePathComponent(std::string_view path, bool trimFront) { +static void preparePathComponent(std::string_view& path, bool trimFront) { if (trimFront) { while (!path.empty() && path.front() == '/') { path.remove_prefix(1); diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp index bfe92f4f2080..7a8560221beb 100644 --- a/services/incremental/test/IncrementalServiceTest.cpp +++ b/services/incremental/test/IncrementalServiceTest.cpp @@ -25,6 +25,7 @@ #include <future> #include "IncrementalService.h" +#include "IncrementalServiceValidation.h" #include "Metadata.pb.h" #include "ServiceWrappers.h" @@ -262,11 +263,15 @@ private: class MockIncFs : public IncFsWrapper { public: + MOCK_CONST_METHOD1(listExistingMounts, void(const ExistingMountCallback& cb)); + MOCK_CONST_METHOD1(openMount, Control(std::string_view path)); MOCK_CONST_METHOD3(createControl, Control(IncFsFd cmd, IncFsFd pendingReads, IncFsFd logs)); MOCK_CONST_METHOD5(makeFile, ErrorCode(const Control& control, std::string_view path, int mode, FileId id, NewFileParams params)); MOCK_CONST_METHOD3(makeDir, ErrorCode(const Control& control, std::string_view path, int mode)); + MOCK_CONST_METHOD3(makeDirs, + ErrorCode(const Control& control, std::string_view path, int mode)); MOCK_CONST_METHOD2(getMetadata, RawMetadata(const Control& control, FileId fileid)); MOCK_CONST_METHOD2(getMetadata, RawMetadata(const Control& control, std::string_view path)); MOCK_CONST_METHOD2(getFileId, FileId(const Control& control, std::string_view path)); @@ -274,10 +279,13 @@ public: ErrorCode(const Control& control, std::string_view from, std::string_view to)); MOCK_CONST_METHOD2(unlink, ErrorCode(const Control& control, std::string_view path)); MOCK_CONST_METHOD2(openForSpecialOps, base::unique_fd(const Control& control, FileId id)); - MOCK_CONST_METHOD1(writeBlocks, ErrorCode(Span<const DataBlock> blocks)); + MOCK_CONST_METHOD1(writeBlocks, ErrorCode(std::span<const DataBlock> blocks)); + + MockIncFs() { ON_CALL(*this, listExistingMounts(_)).WillByDefault(Return()); } void makeFileFails() { ON_CALL(*this, makeFile(_, _, _, _, _)).WillByDefault(Return(-1)); } void makeFileSuccess() { ON_CALL(*this, makeFile(_, _, _, _, _)).WillByDefault(Return(0)); } + RawMetadata getMountInfoMetadata(const Control& control, std::string_view path) { metadata::Mount m; m.mutable_storage()->set_id(100); @@ -692,14 +700,14 @@ TEST_F(IncrementalServiceTest, testMakeDirectory) { IncrementalService::CreateOptions::CreateNew); std::string dir_path("test"); - std::string tempPath(tempDir.path); - std::replace(tempPath.begin(), tempPath.end(), '/', '_'); - std::string mount_dir = std::string(mRootDir.path) + "/MT_" + tempPath.substr(1); - std::string normalized_dir_path = mount_dir + "/mount/st_1_0/" + dir_path; - // Expecting incfs to call makeDir on a path like: - // /data/local/tmp/TemporaryDir-06yixG/data_local_tmp_TemporaryDir-xwdFhT/mount/st_1_0/test - EXPECT_CALL(*mIncFs, makeDir(_, std::string_view(normalized_dir_path), _)); + // <root>/*/mount/<storage>/test + EXPECT_CALL(*mIncFs, + makeDir(_, Truly([&](std::string_view arg) { + return arg.starts_with(mRootDir.path) && + arg.ends_with("/mount/st_1_0/" + dir_path); + }), + _)); auto res = mIncrementalService->makeDir(storageId, dir_path, 0555); ASSERT_EQ(res, 0); } @@ -717,29 +725,15 @@ TEST_F(IncrementalServiceTest, testMakeDirectories) { auto first = "first"sv; auto second = "second"sv; auto third = "third"sv; - - std::string tempPath(tempDir.path); - std::replace(tempPath.begin(), tempPath.end(), '/', '_'); - std::string mount_dir = std::string(mRootDir.path) + "/MT_" + tempPath.substr(1); - - InSequence seq; - auto parent_path = std::string(first) + "/" + std::string(second); - auto dir_path = parent_path + "/" + std::string(third); - - std::string normalized_first_path = mount_dir + "/mount/st_1_0/" + std::string(first); - std::string normalized_parent_path = mount_dir + "/mount/st_1_0/" + parent_path; - std::string normalized_dir_path = mount_dir + "/mount/st_1_0/" + dir_path; - - EXPECT_CALL(*mIncFs, makeDir(_, std::string_view(normalized_dir_path), _)) - .WillOnce(Return(-ENOENT)); - EXPECT_CALL(*mIncFs, makeDir(_, std::string_view(normalized_parent_path), _)) - .WillOnce(Return(-ENOENT)); - EXPECT_CALL(*mIncFs, makeDir(_, std::string_view(normalized_first_path), _)) - .WillOnce(Return(0)); - EXPECT_CALL(*mIncFs, makeDir(_, std::string_view(normalized_parent_path), _)) - .WillOnce(Return(0)); - EXPECT_CALL(*mIncFs, makeDir(_, std::string_view(normalized_dir_path), _)).WillOnce(Return(0)); - auto res = mIncrementalService->makeDirs(storageId, normalized_dir_path, 0555); + auto dir_path = std::string(first) + "/" + std::string(second) + "/" + std::string(third); + + EXPECT_CALL(*mIncFs, + makeDirs(_, Truly([&](std::string_view arg) { + return arg.starts_with(mRootDir.path) && + arg.ends_with("/mount/st_1_0/" + dir_path); + }), + _)); + auto res = mIncrementalService->makeDirs(storageId, dir_path, 0555); ASSERT_EQ(res, 0); } } // namespace android::os::incremental diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 289d79dad367..0fc333f4b38c 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -2174,12 +2174,6 @@ public final class SystemServer { mPackageManagerService.systemReady(); t.traceEnd(); - if (mIncrementalServiceHandle != 0) { - t.traceBegin("MakeIncrementalServiceReady"); - setIncrementalServiceSystemReady(mIncrementalServiceHandle); - t.traceEnd(); - } - t.traceBegin("MakeDisplayManagerServiceReady"); try { // TODO: use boot phase and communicate these flags some other way @@ -2449,6 +2443,12 @@ public final class SystemServer { reportWtf("Notifying incident daemon running", e); } t.traceEnd(); + + if (mIncrementalServiceHandle != 0) { + t.traceBegin("MakeIncrementalServiceReady"); + setIncrementalServiceSystemReady(mIncrementalServiceHandle); + t.traceEnd(); + } }, t); t.traceEnd(); // startOtherServices diff --git a/services/net/Android.bp b/services/net/Android.bp index 9f2979906d42..bb5409b3e032 100644 --- a/services/net/Android.bp +++ b/services/net/Android.bp @@ -41,6 +41,7 @@ java_library { sdk_version: "module_current", libs: [ "unsupportedappusage", + "framework-wifi-util-lib", ], static_libs: [ "dnsresolver_aidl_interface-V2-java", diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java index c87ece29800c..763e19bd14ab 100644 --- a/services/people/java/com/android/server/people/data/DataManager.java +++ b/services/people/java/com/android/server/people/data/DataManager.java @@ -67,6 +67,7 @@ import com.android.internal.os.BackgroundThread; import com.android.internal.telephony.SmsApplication; import com.android.server.LocalServices; import com.android.server.notification.NotificationManagerInternal; +import com.android.server.notification.ShortcutHelper; import java.util.ArrayList; import java.util.Collections; @@ -497,10 +498,6 @@ public class DataManager { EventStore.CATEGORY_SHORTCUT_BASED, shortcutId); } - private boolean isPersonShortcut(@NonNull ShortcutInfo shortcutInfo) { - return shortcutInfo.getPersons() != null && shortcutInfo.getPersons().length != 0; - } - @VisibleForTesting @WorkerThread void addOrUpdateConversationInfo(@NonNull ShortcutInfo shortcutInfo) { @@ -712,7 +709,8 @@ public class DataManager { @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) { mInjector.getBackgroundExecutor().execute(() -> { for (ShortcutInfo shortcut : shortcuts) { - if (isPersonShortcut(shortcut)) { + if (ShortcutHelper.isConversationShortcut( + shortcut, mShortcutServiceInternal, user.getIdentifier())) { addOrUpdateConversationInfo(shortcut); } } diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java index 7a175ca1b7f5..2983d585c45a 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java @@ -541,7 +541,7 @@ public class MockingOomAdjusterTests { doReturn(new ArrayMap<IBinder, ArrayList<ConnectionRecord>>()).when(s).getConnections(); s.startRequested = true; s.lastActivity = SystemClock.uptimeMillis(); - app.services.add(s); + app.startService(s); sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); @@ -585,7 +585,7 @@ public class MockingOomAdjusterTests { doReturn(new ArrayMap<IBinder, ArrayList<ConnectionRecord>>()).when(s).getConnections(); s.startRequested = true; s.lastActivity = SystemClock.uptimeMillis(); - app.services.add(s); + app.startService(s); sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE); @@ -1593,7 +1593,7 @@ public class MockingOomAdjusterTests { s.app = app3; setFieldValue(ServiceRecord.class, s, "connections", new ArrayMap<IBinder, ArrayList<ConnectionRecord>>()); - app3.services.add(s); + app3.startService(s); doCallRealMethod().when(s).getConnections(); s.startRequested = true; s.lastActivity = now; @@ -1698,7 +1698,7 @@ public class MockingOomAdjusterTests { record.app = service; setFieldValue(ServiceRecord.class, record, "connections", new ArrayMap<IBinder, ArrayList<ConnectionRecord>>()); - service.services.add(record); + service.startService(record); doCallRealMethod().when(record).getConnections(); } AppBindRecord binding = new AppBindRecord(record, null, client); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java index 335217719cc9..064e3486823a 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java @@ -16,6 +16,8 @@ package com.android.server.accessibility; +import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_ACCESSIBILITY_ACTIONS; + import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; @@ -308,7 +310,7 @@ public class SystemActionPerformerTest { AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT); verify(mMockScreenshotHelper).takeScreenshot( eq(android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN), anyBoolean(), - anyBoolean(), any(Handler.class), any()); + anyBoolean(), eq(SCREENSHOT_ACCESSIBILITY_ACTIONS), any(Handler.class), any()); } // PendingIntent is a final class and cannot be mocked. So we are using this diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java index 3e5c21c67bb3..cbe49eb12f95 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java @@ -124,6 +124,9 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi // Key is a pair of uri and userId private final Map<Pair<Uri, Integer>, ContentObserver> mContentObservers = new ArrayMap<>(); + // Used as an override when set to nonzero. + private long mCurrentTimeMillis = 0; + public MockInjector(MockSystemServices services, DpmMockContext context) { super(context); this.services = services; @@ -470,5 +473,19 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi @Override public void runCryptoSelfTest() {} + + @Override + public String[] getPersonalAppsForSuspension(int userId) { + return new String[]{}; + } + + public void setSystemCurrentTimeMillis(long value) { + mCurrentTimeMillis = value; + } + + @Override + public long systemCurrentTimeMillis() { + return mCurrentTimeMillis != 0 ? mCurrentTimeMillis : System.currentTimeMillis(); + } } } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 57039e53429e..b042e7794666 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -44,6 +44,7 @@ import static org.mockito.Matchers.eq; import static org.mockito.Matchers.isNull; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.atMost; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; @@ -62,6 +63,7 @@ import android.Manifest.permission; import android.app.Activity; import android.app.AppOpsManager; import android.app.Notification; +import android.app.PendingIntent; import android.app.admin.DeviceAdminReceiver; import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyManagerInternal; @@ -96,6 +98,7 @@ import android.util.Pair; import androidx.test.filters.SmallTest; import com.android.internal.R; +import com.android.internal.messages.nano.SystemMessageProto; import com.android.internal.widget.LockscreenCredential; import com.android.server.LocalServices; import com.android.server.SystemService; @@ -103,6 +106,7 @@ import com.android.server.devicepolicy.DevicePolicyManagerService.RestrictionsLi import org.hamcrest.BaseMatcher; import org.hamcrest.Description; +import org.hamcrest.Matcher; import org.mockito.Mockito; import org.mockito.internal.util.collections.Sets; import org.mockito.stubbing.Answer; @@ -181,6 +185,20 @@ public class DevicePolicyManagerTest extends DpmTestBase { "wQ==\n" + "-----END CERTIFICATE-----\n"; + // Constants for testing setManagedProfileMaximumTimeOff: + // Profile maximum time off value + private static final long PROFILE_OFF_TIMEOUT = TimeUnit.DAYS.toMillis(5); + // Synthetic time at the beginning of test. + private static final long PROFILE_OFF_START = 1; + // Time when warning notification should be posted, + private static final long PROFILE_OFF_WARNING_TIME = + PROFILE_OFF_START + PROFILE_OFF_TIMEOUT - TimeUnit.DAYS.toMillis(1); + // Time when the apps should be suspended + private static final long PROFILE_OFF_DEADLINE = PROFILE_OFF_START + PROFILE_OFF_TIMEOUT; + // Notification titles for setManagedProfileMaximumTimeOff tests: + private static final String PROFILE_OFF_WARNING_TITLE = "suspended_tomorrow"; + private static final String PROFILE_OFF_SUSPENDED_TITLE = "suspended"; + @Override protected void setUp() throws Exception { super.setUp(); @@ -1558,20 +1576,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { dpms.approveCaCert(fourCerts.getList().get(1), userId, true); // a notification should be shown saying that there are two certificates left to approve. verify(getServices().notificationManager, timeout(1000)) - .notifyAsUser(anyString(), anyInt(), argThat( - new BaseMatcher<Notification>() { - @Override - public boolean matches(Object item) { - final Notification noti = (Notification) item; - return TEST_STRING.equals( - noti.extras.getString(Notification.EXTRA_TITLE)); - } - @Override - public void describeTo(Description description) { - description.appendText( - "Notification{title=\"" + TEST_STRING + "\"}"); - } - }), eq(user)); + .notifyAsUser(anyString(), anyInt(), argThat(hasTitle(TEST_STRING)), eq(user)); } /** @@ -6272,7 +6277,214 @@ public class DevicePolicyManagerTest extends DpmTestBase { assertThat(dpm.getAccountTypesWithManagementDisabled()).isEmpty(); } - // admin1 is the outgoing DPC, adminAnotherPakcage is the incoming one. + /** + * Tests the case when the user doesn't turn the profile on in time, verifies that the user is + * warned with a notification and then the apps get suspended. + */ + public void testMaximumProfileTimeOff_profileOffTimeExceeded() throws Exception { + prepareMocksForSetMaximumProfileTimeOff(); + + mContext.binder.callingUid = DpmMockContext.CALLER_UID; + dpm.setManagedProfileMaximumTimeOff(admin1, PROFILE_OFF_TIMEOUT); + + mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; + // The profile is running, neither alarm nor notification should be posted. + verify(getServices().alarmManager, never()) + .set(anyInt(), anyLong(), any(PendingIntent.class)); + verify(getServices().notificationManager, never()) + .notify(anyInt(), any(Notification.class)); + // Apps shouldn't be suspended. + verifyZeroInteractions(getServices().ipackageManager); + clearInvocations(getServices().alarmManager); + + sendUserStoppedBroadcastForProfile(); + + // Verify the alarm was scheduled for time when the warning should be shown. + verify(getServices().alarmManager, times(1)) + .set(anyInt(), eq(PROFILE_OFF_WARNING_TIME), any()); + // But still no notification should be posted at this point. + verify(getServices().notificationManager, never()) + .notify(anyInt(), any(Notification.class)); + // Apps shouldn't be suspended. + verifyZeroInteractions(getServices().ipackageManager); + clearInvocations(getServices().alarmManager); + + // Pretend the alarm went off. + dpms.mMockInjector.setSystemCurrentTimeMillis(PROFILE_OFF_WARNING_TIME + 10); + sendProfileOffDeadlineAlarmBroadcast(); + + // Verify the alarm was scheduled for the actual deadline this time. + verify(getServices().alarmManager, times(1)).set(anyInt(), eq(PROFILE_OFF_DEADLINE), any()); + // Now the user should see a warning notification. + verify(getServices().notificationManager, times(1)) + .notify(anyInt(), argThat(hasTitle(PROFILE_OFF_WARNING_TITLE))); + // Apps shouldn't be suspended yet. + verifyZeroInteractions(getServices().ipackageManager); + clearInvocations(getServices().alarmManager); + clearInvocations(getServices().notificationManager); + + // Pretend the alarm went off. + dpms.mMockInjector.setSystemCurrentTimeMillis(PROFILE_OFF_DEADLINE + 10); + sendProfileOffDeadlineAlarmBroadcast(); + + // Verify the alarm was not set. + verifyZeroInteractions(getServices().alarmManager); + // Now the user should see a notification about suspended apps. + verify(getServices().notificationManager, times(1)) + .notify(anyInt(), argThat(hasTitle(PROFILE_OFF_SUSPENDED_TITLE))); + // Verify that the apps are suspended. + verify(getServices().ipackageManager, times(1)).setPackagesSuspendedAsUser( + any(), eq(true), any(), any(), any(), any(), anyInt()); + } + + /** + * Tests the case when the user turns the profile back on long before the deadline (> 1 day). + */ + public void testMaximumProfileTimeOff_turnOnBeforeWarning() throws Exception { + prepareMocksForSetMaximumProfileTimeOff(); + + mContext.binder.callingUid = DpmMockContext.CALLER_UID; + dpm.setManagedProfileMaximumTimeOff(admin1, PROFILE_OFF_TIMEOUT); + + mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; + sendUserStoppedBroadcastForProfile(); + clearInvocations(getServices().alarmManager); + sendUserUnlockedBroadcastForProfile(); + + // Verify that the alarm got discharged. + verify(getServices().alarmManager, times(1)).cancel((PendingIntent) null); + } + + /** + * Tests the case when the user turns the profile back on after the warning notification. + */ + public void testMaximumProfileTimeOff_turnOnAfterWarning() throws Exception { + prepareMocksForSetMaximumProfileTimeOff(); + + mContext.binder.callingUid = DpmMockContext.CALLER_UID; + dpm.setManagedProfileMaximumTimeOff(admin1, PROFILE_OFF_TIMEOUT); + + mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; + sendUserStoppedBroadcastForProfile(); + + // Pretend the alarm went off. + dpms.mMockInjector.setSystemCurrentTimeMillis(PROFILE_OFF_WARNING_TIME + 10); + sendProfileOffDeadlineAlarmBroadcast(); + + clearInvocations(getServices().alarmManager); + clearInvocations(getServices().notificationManager); + sendUserUnlockedBroadcastForProfile(); + + // Verify that the alarm got discharged. + verify(getServices().alarmManager, times(1)).cancel((PendingIntent) null); + // Verify that the notification is removed. + verify(getServices().notificationManager, times(1)) + .cancel(eq(SystemMessageProto.SystemMessage.NOTE_PERSONAL_APPS_SUSPENDED)); + } + + /** + * Tests the case when the user turns the profile back on when the apps are already suspended. + */ + public void testMaximumProfileTimeOff_turnOnAfterDeadline() throws Exception { + prepareMocksForSetMaximumProfileTimeOff(); + + mContext.binder.callingUid = DpmMockContext.CALLER_UID; + dpm.setManagedProfileMaximumTimeOff(admin1, PROFILE_OFF_TIMEOUT); + + mContext.binder.callingUid = DpmMockContext.SYSTEM_UID; + sendUserStoppedBroadcastForProfile(); + + // Pretend the alarm went off after the deadline. + dpms.mMockInjector.setSystemCurrentTimeMillis(PROFILE_OFF_DEADLINE + 10); + sendProfileOffDeadlineAlarmBroadcast(); + + clearInvocations(getServices().alarmManager); + clearInvocations(getServices().notificationManager); + clearInvocations(getServices().ipackageManager); + + sendUserUnlockedBroadcastForProfile(); + + // Verify that the notification is removed (at this point DPC should show it). + verify(getServices().notificationManager, times(1)) + .cancel(eq(SystemMessageProto.SystemMessage.NOTE_PERSONAL_APPS_SUSPENDED)); + // Verify that the apps are NOT unsuspeded. + verify(getServices().ipackageManager, never()).setPackagesSuspendedAsUser( + any(), eq(false), any(), any(), any(), any(), anyInt()); + } + + private void sendUserUnlockedBroadcastForProfile() throws Exception { + when(getServices().userManager.isUserUnlocked(eq(DpmMockContext.CALLER_USER_HANDLE))) + .thenReturn(true); + final Intent unlockedIntent = new Intent(Intent.ACTION_USER_UNLOCKED) + .putExtra(Intent.EXTRA_USER_HANDLE, DpmMockContext.CALLER_USER_HANDLE); + getServices().injectBroadcast( + mServiceContext, unlockedIntent, DpmMockContext.CALLER_USER_HANDLE); + flushTasks(); + } + + + private void sendProfileOffDeadlineAlarmBroadcast() throws Exception { + final Intent deadlineAlarmIntent = + new Intent(DevicePolicyManagerService.ACTION_PROFILE_OFF_DEADLINE); + getServices().injectBroadcast( + mServiceContext, deadlineAlarmIntent, DpmMockContext.CALLER_USER_HANDLE); + flushTasks(); + } + + private void sendUserStoppedBroadcastForProfile() throws Exception { + when(getServices().userManager.isUserUnlocked(eq(DpmMockContext.CALLER_USER_HANDLE))) + .thenReturn(false); + final Intent stoppedIntent = new Intent(Intent.ACTION_USER_STOPPED) + .putExtra(Intent.EXTRA_USER_HANDLE, DpmMockContext.CALLER_USER_HANDLE); + getServices().injectBroadcast(mServiceContext, stoppedIntent, + DpmMockContext.CALLER_USER_HANDLE); + flushTasks(); + } + + private void prepareMocksForSetMaximumProfileTimeOff() throws Exception { + addManagedProfile(admin1, DpmMockContext.CALLER_UID, admin1); + configureProfileOwnerOfOrgOwnedDevice(admin1, DpmMockContext.CALLER_USER_HANDLE); + + when(getServices().userManager.isUserUnlocked()).thenReturn(true); + + // Pretend our admin handles CHECK_POLICY_COMPLIANCE intent. + final Intent intent = new Intent(DevicePolicyManager.ACTION_CHECK_POLICY_COMPLIANCE); + intent.setPackage(admin1.getPackageName()); + + doReturn(Collections.singletonList(new ResolveInfo())) + .when(getServices().packageManager).queryIntentActivitiesAsUser( + any(Intent.class), anyInt(), eq(DpmMockContext.CALLER_USER_HANDLE)); + + dpms.mMockInjector.setSystemCurrentTimeMillis(PROFILE_OFF_START); + // To allow creation of Notification via Notification.Builder + mContext.applicationInfo = mRealTestContext.getApplicationInfo(); + + // Setup notification titles. + when(mServiceContext.resources + .getString(R.string.personal_apps_suspended_tomorrow_title)) + .thenReturn(PROFILE_OFF_WARNING_TITLE); + when(mServiceContext.resources + .getString(R.string.personal_apps_suspended_title)) + .thenReturn(PROFILE_OFF_SUSPENDED_TITLE); + + clearInvocations(getServices().ipackageManager); + } + + private static Matcher<Notification> hasTitle(String expected) { + return new BaseMatcher<Notification>() { + @Override + public boolean matches(Object item) { + final Notification notification = (Notification) item; + return expected.equals(notification.extras.getString(Notification.EXTRA_TITLE)); + } + @Override + public void describeTo(Description description) { + description.appendText("Notification{title=\"" + expected + "\"}"); + } + }; + } + + // admin1 is the outgoing DPC, adminAnotherPackage is the incoming one. private void assertDeviceOwnershipRevertedWithFakeTransferMetadata() throws Exception { writeFakeTransferMetadataFile(UserHandle.USER_SYSTEM, TransferOwnershipMetadataManager.ADMIN_TYPE_DEVICE_OWNER); @@ -6299,7 +6511,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { mServiceContext.binder.restoreCallingIdentity(ident); } - // admin1 is the outgoing DPC, adminAnotherPakcage is the incoming one. + // admin1 is the outgoing DPC, adminAnotherPackage is the incoming one. private void assertProfileOwnershipRevertedWithFakeTransferMetadata() throws Exception { writeFakeTransferMetadataFile(DpmMockContext.CALLER_USER_HANDLE, TransferOwnershipMetadataManager.ADMIN_TYPE_PROFILE_OWNER); diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java index 7b3417a67857..b69cc47ba738 100644 --- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java @@ -107,9 +107,10 @@ public class AutomaticBrightnessControllerTest { eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class)); SensorEventListener listener = listenerCaptor.getValue(); - // Set up system to return 5 as a brightness value + // Set up system to return 0.02f as a brightness value float lux1 = 100.0f; - float normalizedBrightness1 = 0.0158f; //float equivalent of 5 out of 255 + // Brightness as float (from 0.0f to 1.0f) + float normalizedBrightness1 = 0.02f; when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux1)) .thenReturn(lux1); when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux1)) @@ -120,14 +121,14 @@ public class AutomaticBrightnessControllerTest { // This is the important bit: When the new brightness is set, make sure the new // brightening threshold is beyond the maximum brightness value...so that we can test that // our threshold clamping works. - when(mScreenBrightnessThresholds.getBrighteningThreshold(5)).thenReturn(1.0f); + when(mScreenBrightnessThresholds.getBrighteningThreshold(normalizedBrightness1)) + .thenReturn(1.0f); // Send new sensor value and verify listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, (int) lux1)); - assertEquals(5, controller.getAutomaticScreenBrightness()); + assertEquals(normalizedBrightness1, controller.getAutomaticScreenBrightness(), 0.001f); - - // Set up system to return 255 as a brightness value + // Set up system to return 0.0f (minimum possible brightness) as a brightness value float lux2 = 10.0f; float normalizedBrightness2 = 0.0f; when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux2)) @@ -139,7 +140,7 @@ public class AutomaticBrightnessControllerTest { // Send new sensor value and verify listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, (int) lux2)); - assertEquals(1, controller.getAutomaticScreenBrightness()); + assertEquals(normalizedBrightness2, controller.getAutomaticScreenBrightness(), 0.001f); } @Test @@ -153,9 +154,9 @@ public class AutomaticBrightnessControllerTest { eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class)); SensorEventListener listener = listenerCaptor.getValue(); - // Set up system to return 250 as a brightness value + // Set up system to return 0.98f as a brightness value float lux1 = 100.0f; - float normalizedBrightness1 = 0.981f; //float equivalent of 250 out of 255 + float normalizedBrightness1 = 0.98f; when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux1)) .thenReturn(lux1); when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux1)) @@ -166,14 +167,15 @@ public class AutomaticBrightnessControllerTest { // This is the important bit: When the new brightness is set, make sure the new // brightening threshold is beyond the maximum brightness value...so that we can test that // our threshold clamping works. - when(mScreenBrightnessThresholds.getBrighteningThreshold(250)).thenReturn(260.0f); + when(mScreenBrightnessThresholds.getBrighteningThreshold(normalizedBrightness1)) + .thenReturn(1.1f); // Send new sensor value and verify listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, (int) lux1)); - assertEquals(250, controller.getAutomaticScreenBrightness()); + assertEquals(normalizedBrightness1, controller.getAutomaticScreenBrightness(), 0.001f); - // Set up system to return 255 as a brightness value + // Set up system to return 1.0f as a brightness value (brightness_max) float lux2 = 110.0f; float normalizedBrightness2 = 1.0f; when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux2)) @@ -185,7 +187,7 @@ public class AutomaticBrightnessControllerTest { // Send new sensor value and verify listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, (int) lux2)); - assertEquals(255, controller.getAutomaticScreenBrightness()); + assertEquals(normalizedBrightness2, controller.getAutomaticScreenBrightness(), 0.001f); } @Test @@ -204,10 +206,10 @@ public class AutomaticBrightnessControllerTest { // User sets brightness to 100 controller.configure(true /* enable */, null /* configuration */, - 100 /* brightness */, true /* userChangedBrightness */, 0 /* adjustment */, + 0.5f /* brightness */, true /* userChangedBrightness */, 0 /* adjustment */, false /* userChanged */, DisplayPowerRequest.POLICY_BRIGHT); // There should be a user data point added to the mapper. - verify(mBrightnessMappingStrategy).addUserDataPoint(1000f, 100); + verify(mBrightnessMappingStrategy).addUserDataPoint(1000f, 0.5f); } } diff --git a/services/tests/servicestests/src/com/android/server/emergency/EmergencyAffordanceServiceTest.java b/services/tests/servicestests/src/com/android/server/emergency/EmergencyAffordanceServiceTest.java new file mode 100644 index 000000000000..d438a0eb9411 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/emergency/EmergencyAffordanceServiceTest.java @@ -0,0 +1,486 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.emergency; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.verify; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.os.UserHandle; +import android.provider.Settings; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener; +import android.telephony.TelephonyManager; +import android.test.mock.MockContentResolver; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import androidx.test.InstrumentationRegistry; + +import com.android.internal.util.test.BroadcastInterceptingContext; +import com.android.internal.util.test.FakeSettingsProvider; +import com.android.server.SystemService; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; + +/** + * Unit test for EmergencyAffordanceService (EAS for short) which determines when + * should we enable Emergency Affordance feature (EA for short). + * + * Please refer to https://source.android.com/devices/tech/connect/emergency-affordance + * to see the details of the feature. + */ +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class EmergencyAffordanceServiceTest { + + // Default country ISO that should enable EA. Value comes from resource + // com.android.internal.R.array.config_emergency_iso_country_codes + private static final String EMERGENCY_ISO_CODE = "in"; + // Randomly picked country ISO that should not enable EA. + private static final String NON_EMERGENCY_ISO_CODE = "us"; + + // Valid values for Settings.Global.EMERGENCY_AFFORDANCE_NEEDED + private static final int OFF = 0; // which means feature disabled + private static final int ON = 1; // which means feature enabled + + private static final int ACTIVE_MODEM_COUNT = 2; + + @Mock private Resources mResources; + @Mock private SubscriptionManager mSubscriptionManager; + @Mock private TelephonyManager mTelephonyManager; + + private TestContext mServiceContext; + private MockContentResolver mContentResolver; + private OnSubscriptionsChangedListener mSubscriptionChangedListener; + private EmergencyAffordanceService mService; + + // Testable Context that mocks resources, content resolver and system services + private class TestContext extends BroadcastInterceptingContext { + TestContext(Context base) { + super(base); + } + + @Override + public ContentResolver getContentResolver() { + return mContentResolver; + } + + @Override + public Resources getResources() { + return mResources; + } + + @Override + public Object getSystemService(String name) { + switch (name) { + case Context.TELEPHONY_SUBSCRIPTION_SERVICE: + return mSubscriptionManager; + case Context.TELEPHONY_SERVICE: + return mTelephonyManager; + default: + return super.getSystemService(name); + } + } + } + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + doReturn(new String[] { EMERGENCY_ISO_CODE }).when(mResources) + .getStringArray(com.android.internal.R.array.config_emergency_iso_country_codes); + + final Context context = InstrumentationRegistry.getContext(); + mServiceContext = new TestContext(context); + mContentResolver = new MockContentResolver(mServiceContext); + mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); + + // Initialize feature off, to have constant starting + Settings.Global.putInt(mContentResolver, Settings.Global.EMERGENCY_AFFORDANCE_NEEDED, 0); + mService = new EmergencyAffordanceService(mServiceContext); + } + + /** + * Verify if the device is not voice capable, the feature should be disabled. + */ + @Test + public void testSettings_shouldBeOff_whenVoiceCapableIsFalse() throws Exception { + // Given: the device is not voice capable + // When: setup device and boot service + setUpDevice(false /* withVoiceCapable */, true /* withEmergencyIsoInSim */, + true /* withEmergencyIsoInCell */); + + // Then: EA setting will should be 0 + verifyEmergencyAffordanceNeededSettings(OFF); + } + + /** + * Verify the voice capable device is booted up without EA-enabled cell network, with + * no EA-enabled SIM installed, feature should be disabled. + */ + @Test + public void testSettings_shouldBeOff_whenWithoutEAEanbledNetworkNorSim() throws Exception { + // Given: the device is voice capble, no EA-enable SIM, no EA-enabled Cell + setUpDevice(true /* withVoiceCapable */, false /* withEmergencyIsoInSim */, + false /* withEmergencyIsoInCell */); + + // Then: EA setting will should be 0 + verifyEmergencyAffordanceNeededSettings(OFF); + } + + /** + * Verify the voice capable device is booted up with EA-enabled SIM installed, the + * feature should be enabled. + */ + @Test + public void testSettings_shouldBeOn_whenBootUpWithEAEanbledSim() throws Exception { + // Given: the device is voice capble, with EA-enable SIM, no EA-enabled Cell + setUpDevice(true /* withVoiceCapable */, true /* withEmergencyIsoInSim */, + false /* withEmergencyIsoInCell */); + + // Then: EA setting will immediately update to 1 + verifyEmergencyAffordanceNeededSettings(ON); + } + + /** + * Verify the voice capable device is booted up with EA-enabled Cell network, the + * feature should be enabled. + */ + @Test + public void testSettings_shouldBeOn_whenBootUpWithEAEanbledCell() throws Exception { + // Given: the device is voice capble, with EA-enable SIM, with EA-enabled Cell + setUpDevice(true /* withVoiceCapable */, false /* withEmergencyIsoInSim */, + true /* withEmergencyIsoInCell */); + + // Then: EA setting will immediately update to 1 + verifyEmergencyAffordanceNeededSettings(ON); + } + + /** + * Verify when device boot up with no EA-enabled SIM, but later install one, + * feature should be enabled. + */ + @Test + public void testSettings_shouldBeOn_whenSubscriptionInfoChangedWithEmergencyIso() + throws Exception { + // Given: the device is voice capable, boot up with no EA-enabled SIM, no EA-enabled Cell + setUpDevice(true /* withVoiceCapable */, false/* withEmergencyIsoInSim */, + false /* withEmergencyIsoInCell */); + + // When: Insert EA-enabled SIM and get notified + setUpSim(true /* withEmergencyIsoInSim */); + mSubscriptionChangedListener.onSubscriptionsChanged(); + + // Then: EA Setting will update to 1 + verifyEmergencyAffordanceNeededSettings(ON); + } + + /** + * Verify when feature was on, device re-insert with no EA-enabled SIMs, + * feature should be disabled. + */ + @Test + public void testSettings_shouldBeOff_whenSubscriptionInfoChangedWithoutEmergencyIso() + throws Exception { + // Given: the device is voice capable, no EA-enabled Cell, with EA-enabled SIM + setUpDevice(true /* withVoiceCapable */, true /* withEmergencyIsoInSim */, + false /* withEmergencyIsoInCell */); + + // When: All SIMs are replaced with EA-disabled ones. + setUpSim(false /* withEmergencyIsoInSim */); + mSubscriptionChangedListener.onSubscriptionsChanged(); + + // Then: EA Setting will update to 0 + verifyEmergencyAffordanceNeededSettings(OFF); + } + + /** + * Verify when device boot up with no EA-enabled Cell, but later move into one, + * feature should be enabled. + */ + @Test + public void testSettings_shouldBeOn_whenCountryIsoChangedWithEmergencyIso() + throws Exception { + // Given: the device is voice capable, boot up with no EA-enabled SIM, no EA-enabled Cell + setUpDevice(true /* withVoiceCapable */, false/* withEmergencyIsoInSim */, + false /* withEmergencyIsoInCell */); + + // When: device locale change to EA-enabled Cell and get notified + resetCell(true /* withEmergencyIsoInSim */); + sendBroadcastNetworkCountryChanged(EMERGENCY_COUNTRY_ISO); + + // Then: EA Setting will update to 1 + verifyEmergencyAffordanceNeededSettings(ON); + } + + /** + * Verify when device boot up with EA-enabled Cell, but later move out of it, + * feature should be enabled. + */ + @Test + public void testSettings_shouldBeOff_whenCountryIsoChangedWithoutEmergencyIso() + throws Exception { + // Given: the device is voice capable, boot up with no EA-enabled SIM, with EA-enabled Cell + setUpDevice(true /* withVoiceCapable */, false/* withEmergencyIsoInSim */, + true /* withEmergencyIsoInCell */); + + // When: device locale change to no EA-enabled Cell and get notified + resetCell(false /* withEmergencyIsoInSim */); + sendBroadcastNetworkCountryChanged(NON_EMERGENCY_COUNTRY_ISO); + + // Then: EA Setting will update to 0 + verifyEmergencyAffordanceNeededSettings(OFF); + } + /** + * Verify if device is not in EA-enabled Mobile Network without EA-enable SIM(s) installed, + * when receive SubscriptionInfo change, the feature should not be enabled. + */ + @Test + public void testSettings_shouldBeOff_whenNoEmergencyIsoInCellNorSim() throws Exception { + // Given: the device is voice capable, no EA-enabled Cell, no EA-enabled SIM + setUpDevice(true /* withVoiceCapable */, false /* withEmergencyIsoInSim */, + false /* withEmergencyIsoInCell */); + + // When: Subscription changed event received + mSubscriptionChangedListener.onSubscriptionsChanged(); + + // Then: EA Settings should be 0 + verifyEmergencyAffordanceNeededSettings(OFF); + } + + /** + * Verify while feature was on, when device receive empty country iso change, while APM is + * enabled, feature status should keep on. + */ + @Test + public void testSettings_shouldOn_whenReceiveEmptyISOWithAPMEnabled() throws Exception { + // Given: the device is voice capable, no EA-enabled SIM, with EA-enabled Cell + setUpDevice(true /* withVoiceCapable */, false /* withEmergencyIsoInSim */, + true /* withEmergencyIsoInCell */); + + // Then: EA Settings will update to 1 + verifyEmergencyAffordanceNeededSettings(ON); + + // When: Airplane mode is enabled, and we receive EMPTY ISO in locale change + setAirplaneMode(true); + sendBroadcastNetworkCountryChanged(EMPTY_COUNTRY_ISO); + + // Then: EA Settings will keep to 1 + verifyEmergencyAffordanceNeededSettings(ON); + } + + /** + * Verify while feature was on, when device receive empty country iso change, while APM is + * disabled, feature should be disabled. + */ + @Test + public void testSettings_shouldOff_whenReceiveEmptyISOWithAPMDisabled() throws Exception { + // Given: the device is voice capable, no EA-enabled SIM, with EA-enabled Cell + setUpDevice(true /* withVoiceCapable */, false /* withEmergencyIsoInSim */, + true /* withEmergencyIsoInCell */); + + // Then: EA Settings will update to 1 + verifyEmergencyAffordanceNeededSettings(ON); + + // When: Airplane mode is disabled, and we receive valid empty ISO in locale change + setUpCell(false /* withEmergencyIsoInCell */); + setAirplaneMode(false); + sendBroadcastNetworkCountryChanged(EMPTY_COUNTRY_ISO); + + // Then: EA Settings will keep to 0 + verifyEmergencyAffordanceNeededSettings(OFF); + } + + /** + * Verify when airplane mode is turn on and off in cell network with EA-enabled ISO, + * feature should keep enabled. + */ + @Test + public void testSettings_shouldBeOn_whenAirplaneModeOnOffWithEmergencyIsoInCell() + throws Exception { + // Given: the device is voice capable, no EA-enabled SIM, with EA-enabled Cell + setUpDevice(true /* withVoiceCapable */, false /* withEmergencyIsoInSim */, + true /* withEmergencyIsoInCell */); + + // When: Device receive locale change with EA-enabled iso + sendBroadcastNetworkCountryChanged(EMERGENCY_COUNTRY_ISO); + + // When: Airplane mode is disabled + setAirplaneMode(false); + + // Then: EA Settings will keep with 1 + verifyEmergencyAffordanceNeededSettings(ON); + + // When: Airplane mode is enabled + setAirplaneMode(true); + + // Then: EA Settings is still 1 + verifyEmergencyAffordanceNeededSettings(ON); + } + + /** + * Verify when airplane mode is turn on and off with EA-enabled ISO in SIM, + * feature should keep enabled. + */ + @Test + public void testSettings_shouldBeOn_whenAirplaneModeOnOffWithEmergencyIsoInSim() + throws Exception { + // Given: the device is voice capable, no EA-enabled Cell Network, with EA-enabled SIM + setUpDevice(true /* withVoiceCapable */, true /* withEmergencyIsoInSim */, + false /* withEmergencyIsoInCell */); + + // When: Airplane mode is disabled + setAirplaneMode(false); + + // Then: EA Settings will keep with 1 + verifyEmergencyAffordanceNeededSettings(ON); + + // When: Airplane mode is enabled + setAirplaneMode(true); + + // Then: EA Settings is still 1 + verifyEmergencyAffordanceNeededSettings(ON); + } + + // EAS reads voice capable during boot up and cache it. To test non voice capable device, + // we can not put this in setUp + private void setUpDevice(boolean withVoiceCapable, boolean withEmergencyIsoInSim, + boolean withEmergencyIsoInCell) throws Exception { + setUpVoiceCapable(withVoiceCapable); + + setUpSim(withEmergencyIsoInSim); + + setUpCell(withEmergencyIsoInCell); + + // bypass onStart which is used to publish binder service and need sepolicy policy update + // mService.onStart(); + + mService.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START); + + if (!withVoiceCapable) { + return; + } + + captureSubscriptionChangeListener(); + } + + private void setUpVoiceCapable(boolean voiceCapable) { + doReturn(voiceCapable).when(mTelephonyManager).isVoiceCapable(); + } + + private static final Supplier<String> EMPTY_COUNTRY_ISO = () -> ""; + private static final Supplier<String> EMERGENCY_COUNTRY_ISO = () -> EMERGENCY_ISO_CODE; + private static final Supplier<String> NON_EMERGENCY_COUNTRY_ISO = () -> NON_EMERGENCY_ISO_CODE; + private void sendBroadcastNetworkCountryChanged(Supplier<String> countryIso) { + Intent intent = new Intent(TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED); + intent.putExtra(TelephonyManager.EXTRA_NETWORK_COUNTRY, countryIso.get()); + SubscriptionManager.putPhoneIdAndSubIdExtra(intent, 0); + mServiceContext.sendBroadcastAsUser(intent, UserHandle.ALL); + } + + private void setUpSim(boolean withEmergencyIsoInSim) { + List<SubscriptionInfo> subInfos = getSubscriptionInfoList(withEmergencyIsoInSim); + doReturn(subInfos).when(mSubscriptionManager).getActiveSubscriptionInfoList(); + } + + private void setUpCell(boolean withEmergencyIsoInCell) { + doReturn(ACTIVE_MODEM_COUNT).when(mTelephonyManager).getActiveModemCount(); + doReturn(NON_EMERGENCY_ISO_CODE).when(mTelephonyManager).getNetworkCountryIso(0); + doReturn(withEmergencyIsoInCell ? EMERGENCY_ISO_CODE : NON_EMERGENCY_ISO_CODE) + .when(mTelephonyManager).getNetworkCountryIso(1); + } + + private void resetCell(boolean withEmergencyIsoInCell) { + doReturn(withEmergencyIsoInCell ? EMERGENCY_ISO_CODE : NON_EMERGENCY_ISO_CODE) + .when(mTelephonyManager).getNetworkCountryIso(1); + } + + private void captureSubscriptionChangeListener() { + final ArgumentCaptor<OnSubscriptionsChangedListener> subChangedListenerCaptor = + ArgumentCaptor.forClass(OnSubscriptionsChangedListener.class); + verify(mSubscriptionManager).addOnSubscriptionsChangedListener( + subChangedListenerCaptor.capture()); + mSubscriptionChangedListener = subChangedListenerCaptor.getValue(); + } + + private void setAirplaneMode(boolean enabled) { + // Change the system settings + Settings.Global.putInt(mContentResolver, Settings.Global.AIRPLANE_MODE_ON, + enabled ? 1 : 0); + + // Post the intent + final Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); + intent.putExtra("state", enabled); + mServiceContext.sendBroadcastAsUser(intent, UserHandle.ALL); + } + + private List<SubscriptionInfo> getSubscriptionInfoList(boolean withEmergencyIso) { + List<SubscriptionInfo> subInfos = new ArrayList<>(2); + + // Test with Multiple SIMs. SIM1 is a non-EA SIM + // Only country iso is valuable, all other info are filled with dummy values + SubscriptionInfo subInfo = new SubscriptionInfo(1, "890126042XXXXXXXXXXX", 0, "T-mobile", + "T-mobile", 0, 255, "12345", 0, null, + "310", "226", NON_EMERGENCY_ISO_CODE, false, null, null); + subInfos.add(subInfo); + + // SIM2 can configured to be non-EA or EA SIM according parameter withEmergencyIso + SubscriptionInfo subInfo2 = new SubscriptionInfo(1, "890126042XXXXXXXXXXX", 0, "Airtel", + "Aritel", 0, 255, "12345", 0, null, "310", "226", + withEmergencyIso ? EMERGENCY_ISO_CODE : NON_EMERGENCY_ISO_CODE, + false, null, null); + subInfos.add(subInfo2); + + return subInfos; + } + + // EAS has handler thread to perform heavy work, while FakeSettingProvider does not support + // ContentObserver. To make sure consistent result, we use a simple sleep & retry to wait for + // real work finished before verify result. + private static final int TIME_DELAY_BEFORE_VERIFY_IN_MS = 50; + private static final int RETRIES_BEFORE_VERIFY = 20; + private void verifyEmergencyAffordanceNeededSettings(int expected) throws Exception { + try { + int ct = 0; + int actual = -1; + while (ct++ < RETRIES_BEFORE_VERIFY + && (actual = Settings.Global.getInt(mContentResolver, + Settings.Global.EMERGENCY_AFFORDANCE_NEEDED)) != expected) { + Thread.sleep(TIME_DELAY_BEFORE_VERIFY_IN_MS); + } + assertEquals(expected, actual); + } catch (Settings.SettingNotFoundException e) { + fail("SettingNotFoundException thrown for Settings.Global.EMERGENCY_AFFORDANCE_NEEDED"); + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java index 728e1492c0d5..e16f3145de0b 100644 --- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java @@ -215,6 +215,8 @@ public final class DataManagerTest { mDataManager = new DataManager(mContext, mInjector); mDataManager.initialize(); + when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(), + anyString(), anyInt(), any())).thenReturn(true); verify(mShortcutServiceInternal).addShortcutChangeCallback( mShortcutChangeCallbackCaptor.capture()); mShortcutChangeCallback = mShortcutChangeCallbackCaptor.getValue(); diff --git a/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java b/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java index c4289efe1839..e7e8aca86364 100644 --- a/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java +++ b/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java @@ -158,17 +158,6 @@ public class AttentionDetectorTest extends AndroidTestCase { } @Test - public void testOnUserActivity_disablesSettingIfNotSufficientPermissions() { - when(mPackageManager.checkPermission(any(), any())).thenReturn( - PackageManager.PERMISSION_DENIED); - - registerAttention(); - boolean enabled = Settings.Secure.getIntForUser(getContext().getContentResolver(), - Settings.Secure.ADAPTIVE_SLEEP, 0, UserHandle.USER_CURRENT) == 1; - assertFalse(enabled); - } - - @Test public void testOnUserActivity_doesntCrashIfNoAttentionService() { mAttentionManagerInternal = null; registerAttention(); diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java index 965304f3c433..21af3563b869 100644 --- a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java @@ -26,6 +26,7 @@ import android.media.tv.ITvInputManager; import android.media.tv.TvInputManager; import android.media.tv.TvInputService; import android.media.tv.tuner.frontend.FrontendSettings; +import android.media.tv.tunerresourcemanager.CasSessionRequest; import android.media.tv.tunerresourcemanager.IResourcesReclaimListener; import android.media.tv.tunerresourcemanager.ResourceClientProfile; import android.media.tv.tunerresourcemanager.TunerDemuxRequest; @@ -34,7 +35,6 @@ import android.media.tv.tunerresourcemanager.TunerFrontendInfo; import android.media.tv.tunerresourcemanager.TunerFrontendRequest; import android.media.tv.tunerresourcemanager.TunerLnbRequest; import android.media.tv.tunerresourcemanager.TunerResourceManager; -import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import androidx.test.InstrumentationRegistry; @@ -236,12 +236,8 @@ public class TunerResourceManagerServiceTest { TunerFrontendRequest request = new TunerFrontendRequest(0 /*clientId*/, FrontendSettings.TYPE_DVBT); int[] frontendHandle = new int[1]; - try { - assertThat(mTunerResourceManagerService - .requestFrontendInternal(request, frontendHandle)).isFalse(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + assertThat(mTunerResourceManagerService + .requestFrontendInternal(request, frontendHandle)).isFalse(); assertThat(mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0])) .isEqualTo(TunerResourceManager.INVALID_RESOURCE_HANDLE); } @@ -264,12 +260,8 @@ public class TunerResourceManagerServiceTest { TunerFrontendRequest request = new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT); int[] frontendHandle = new int[1]; - try { - assertThat(mTunerResourceManagerService - .requestFrontendInternal(request, frontendHandle)).isFalse(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + assertThat(mTunerResourceManagerService + .requestFrontendInternal(request, frontendHandle)).isFalse(); assertThat(mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0])) .isEqualTo(TunerResourceManager.INVALID_RESOURCE_HANDLE); } @@ -296,12 +288,8 @@ public class TunerResourceManagerServiceTest { TunerFrontendRequest request = new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT); int[] frontendHandle = new int[1]; - try { - assertThat(mTunerResourceManagerService - .requestFrontendInternal(request, frontendHandle)).isTrue(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + assertThat(mTunerResourceManagerService + .requestFrontendInternal(request, frontendHandle)).isTrue(); assertThat(mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0])) .isEqualTo(0); } @@ -334,23 +322,15 @@ public class TunerResourceManagerServiceTest { int[] frontendHandle = new int[1]; TunerFrontendRequest request = new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT); - try { - assertThat(mTunerResourceManagerService - .requestFrontendInternal(request, frontendHandle)).isTrue(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + assertThat(mTunerResourceManagerService + .requestFrontendInternal(request, frontendHandle)).isTrue(); assertThat(mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0])) .isEqualTo(infos[0].getId()); request = new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT); - try { - assertThat(mTunerResourceManagerService - .requestFrontendInternal(request, frontendHandle)).isTrue(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + assertThat(mTunerResourceManagerService + .requestFrontendInternal(request, frontendHandle)).isTrue(); assertThat(mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0])) .isEqualTo(infos[1].getId()); assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getId()) @@ -394,32 +374,20 @@ public class TunerResourceManagerServiceTest { TunerFrontendRequest request = new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT); int[] frontendHandle = new int[1]; - try { - assertThat(mTunerResourceManagerService - .requestFrontendInternal(request, frontendHandle)).isTrue(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + assertThat(mTunerResourceManagerService + .requestFrontendInternal(request, frontendHandle)).isTrue(); request = new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT); - try { - assertThat(mTunerResourceManagerService - .requestFrontendInternal(request, frontendHandle)).isFalse(); - assertThat(listener.isRelaimed()).isFalse(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + assertThat(mTunerResourceManagerService + .requestFrontendInternal(request, frontendHandle)).isFalse(); + assertThat(listener.isRelaimed()).isFalse(); request = new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS); - try { - assertThat(mTunerResourceManagerService - .requestFrontendInternal(request, frontendHandle)).isFalse(); - assertThat(listener.isRelaimed()).isFalse(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + assertThat(mTunerResourceManagerService + .requestFrontendInternal(request, frontendHandle)).isFalse(); + assertThat(listener.isRelaimed()).isFalse(); } @Test @@ -456,12 +424,8 @@ public class TunerResourceManagerServiceTest { TunerFrontendRequest request = new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT); int[] frontendHandle = new int[1]; - try { - assertThat(mTunerResourceManagerService - .requestFrontendInternal(request, frontendHandle)).isTrue(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + assertThat(mTunerResourceManagerService + .requestFrontendInternal(request, frontendHandle)).isTrue(); assertThat(mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0])) .isEqualTo(infos[0].getId()); assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0]) @@ -470,12 +434,8 @@ public class TunerResourceManagerServiceTest { request = new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS); - try { - assertThat(mTunerResourceManagerService - .requestFrontendInternal(request, frontendHandle)).isTrue(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + assertThat(mTunerResourceManagerService + .requestFrontendInternal(request, frontendHandle)).isTrue(); assertThat(mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0])) .isEqualTo(infos[1].getId()); assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getId()) @@ -511,12 +471,8 @@ public class TunerResourceManagerServiceTest { TunerFrontendRequest request = new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT); int[] frontendHandle = new int[1]; - try { - assertThat(mTunerResourceManagerService - .requestFrontendInternal(request, frontendHandle)).isTrue(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + assertThat(mTunerResourceManagerService + .requestFrontendInternal(request, frontendHandle)).isTrue(); int frontendId = mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0]); assertThat(frontendId).isEqualTo(infos[0].getId()); assertThat(mTunerResourceManagerService @@ -534,6 +490,99 @@ public class TunerResourceManagerServiceTest { } @Test + public void requestCasTest_NoCasAvailable_RequestWithHigherPriority() { + // Register clients + ResourceClientProfile[] profiles = new ResourceClientProfile[2]; + profiles[0] = new ResourceClientProfile("0" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); + profiles[1] = new ResourceClientProfile("1" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); + int[] clientPriorities = {100, 500}; + int[] clientId0 = new int[1]; + int[] clientId1 = new int[1]; + TestResourcesReclaimListener listener = new TestResourcesReclaimListener(); + mTunerResourceManagerService.registerClientProfileInternal( + profiles[0], listener, clientId0); + assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + mTunerResourceManagerService.getClientProfile(clientId0[0]) + .setPriority(clientPriorities[0]); + mTunerResourceManagerService.registerClientProfileInternal( + profiles[1], new TestResourcesReclaimListener(), clientId1); + assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + mTunerResourceManagerService.getClientProfile(clientId1[0]) + .setPriority(clientPriorities[1]); + + // Init cas resources. + mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/); + + CasSessionRequest request = new CasSessionRequest(clientId0[0], 1 /*casSystemId*/); + int[] casSessionHandle = new int[1]; + // Request for 2 cas sessions. + assertThat(mTunerResourceManagerService + .requestCasSessionInternal(request, casSessionHandle)).isTrue(); + assertThat(mTunerResourceManagerService + .requestCasSessionInternal(request, casSessionHandle)).isTrue(); + assertThat(mTunerResourceManagerService.getResourceIdFromHandle(casSessionHandle[0])) + .isEqualTo(1); + assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0]) + .getInUseCasSystemId()).isEqualTo(1); + assertThat(mTunerResourceManagerService.getCasResource(1) + .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId0[0]))); + assertThat(mTunerResourceManagerService.getCasResource(1).isFullyUsed()).isTrue(); + + request = new CasSessionRequest(clientId1[0], 1); + assertThat(mTunerResourceManagerService + .requestCasSessionInternal(request, casSessionHandle)).isTrue(); + assertThat(mTunerResourceManagerService.getResourceIdFromHandle(casSessionHandle[0])) + .isEqualTo(1); + assertThat(mTunerResourceManagerService.getClientProfile(clientId1[0]) + .getInUseCasSystemId()).isEqualTo(1); + assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0]) + .getInUseCasSystemId()).isEqualTo(ClientProfile.INVALID_RESOURCE_ID); + assertThat(mTunerResourceManagerService.getCasResource(1) + .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId1[0]))); + assertThat(mTunerResourceManagerService.getCasResource(1).isFullyUsed()).isFalse(); + assertThat(listener.isRelaimed()).isTrue(); + } + + @Test + public void releaseCasTest() { + // Register clients + ResourceClientProfile[] profiles = new ResourceClientProfile[1]; + profiles[0] = new ResourceClientProfile("0" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); + int[] clientId = new int[1]; + TestResourcesReclaimListener listener = new TestResourcesReclaimListener(); + mTunerResourceManagerService.registerClientProfileInternal(profiles[0], listener, clientId); + assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + + // Init cas resources. + mTunerResourceManagerService.updateCasInfoInternal(1 /*casSystemId*/, 2 /*maxSessionNum*/); + + CasSessionRequest request = new CasSessionRequest(clientId[0], 1 /*casSystemId*/); + int[] casSessionHandle = new int[1]; + // Request for 1 cas sessions. + assertThat(mTunerResourceManagerService + .requestCasSessionInternal(request, casSessionHandle)).isTrue(); + assertThat(mTunerResourceManagerService.getResourceIdFromHandle(casSessionHandle[0])) + .isEqualTo(1); + assertThat(mTunerResourceManagerService.getClientProfile(clientId[0]) + .getInUseCasSystemId()).isEqualTo(1); + assertThat(mTunerResourceManagerService.getCasResource(1) + .getOwnerClientIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(clientId[0]))); + assertThat(mTunerResourceManagerService.getCasResource(1).isFullyUsed()).isFalse(); + + // Release cas + mTunerResourceManagerService.releaseCasSessionInternal(mTunerResourceManagerService + .getCasResource(1), clientId[0]); + assertThat(mTunerResourceManagerService.getClientProfile(clientId[0]) + .getInUseCasSystemId()).isEqualTo(ClientProfile.INVALID_RESOURCE_ID); + assertThat(mTunerResourceManagerService.getCasResource(1).isFullyUsed()).isFalse(); + assertThat(mTunerResourceManagerService.getCasResource(1) + .getOwnerClientIds()).isEmpty(); + } + + @Test public void requestLnbTest_NoLnbAvailable_RequestWithHigherPriority() { // Register clients ResourceClientProfile[] profiles = new ResourceClientProfile[2]; @@ -562,24 +611,16 @@ public class TunerResourceManagerServiceTest { TunerLnbRequest request = new TunerLnbRequest(clientId0[0]); int[] lnbHandle = new int[1]; - try { - assertThat(mTunerResourceManagerService - .requestLnbInternal(request, lnbHandle)).isTrue(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + assertThat(mTunerResourceManagerService + .requestLnbInternal(request, lnbHandle)).isTrue(); assertThat(mTunerResourceManagerService.getResourceIdFromHandle(lnbHandle[0])) .isEqualTo(lnbIds[0]); assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0]) .getInUseLnbIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(lnbIds[0]))); request = new TunerLnbRequest(clientId1[0]); - try { - assertThat(mTunerResourceManagerService - .requestLnbInternal(request, lnbHandle)).isTrue(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + assertThat(mTunerResourceManagerService + .requestLnbInternal(request, lnbHandle)).isTrue(); assertThat(mTunerResourceManagerService.getResourceIdFromHandle(lnbHandle[0])) .isEqualTo(lnbIds[0]); assertThat(mTunerResourceManagerService.getLnbResource(lnbIds[0]) @@ -608,12 +649,8 @@ public class TunerResourceManagerServiceTest { TunerLnbRequest request = new TunerLnbRequest(clientId[0]); int[] lnbHandle = new int[1]; - try { - assertThat(mTunerResourceManagerService - .requestLnbInternal(request, lnbHandle)).isTrue(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + assertThat(mTunerResourceManagerService + .requestLnbInternal(request, lnbHandle)).isTrue(); int lnbId = mTunerResourceManagerService.getResourceIdFromHandle(lnbHandle[0]); assertThat(lnbId).isEqualTo(lnbIds[0]); @@ -647,12 +684,8 @@ public class TunerResourceManagerServiceTest { TunerFrontendRequest request = new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT); int[] frontendHandle = new int[1]; - try { - assertThat(mTunerResourceManagerService - .requestFrontendInternal(request, frontendHandle)).isTrue(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + assertThat(mTunerResourceManagerService + .requestFrontendInternal(request, frontendHandle)).isTrue(); assertThat(mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0])) .isEqualTo(infos[0].getId()); assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getId()) diff --git a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java index 69ef499749a9..df92b6ed95e8 100644 --- a/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/UiModeManagerServiceTest.java @@ -31,7 +31,6 @@ import android.os.PowerManager; import android.os.PowerManagerInternal; import android.os.PowerSaveState; import android.os.RemoteException; -import android.provider.Settings; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import com.android.server.twilight.TwilightManager; @@ -55,7 +54,6 @@ import static junit.framework.TestCase.assertFalse; import static junit.framework.TestCase.assertTrue; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; @@ -146,7 +144,7 @@ public class UiModeManagerServiceTest extends UiServiceTestCase { addLocalService(PowerManagerInternal.class, mLocalPowerManager); addLocalService(TwilightManager.class, mTwilightManager); - mUiManagerService = new UiModeManagerService(mContext, true); + mUiManagerService = new UiModeManagerService(mContext); try { mUiManagerService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY); } catch (SecurityException e) {/* ignore for permission denial */} diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index ecdd9e548e6a..7a5e2266e62f 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -6074,6 +6074,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { // Pretend the shortcut exists List<ShortcutInfo> shortcutInfos = new ArrayList<>(); ShortcutInfo info = mock(ShortcutInfo.class); + when(info.getPackage()).thenReturn(PKG); + when(info.getId()).thenReturn("someshortcutId"); + when(info.getUserId()).thenReturn(USER_SYSTEM); when(info.isLongLived()).thenReturn(true); when(info.isEnabled()).thenReturn(true); shortcutInfos.add(info); @@ -6137,6 +6140,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { // Pretend the shortcut exists List<ShortcutInfo> shortcutInfos = new ArrayList<>(); ShortcutInfo info = mock(ShortcutInfo.class); + when(info.getPackage()).thenReturn(PKG); + when(info.getId()).thenReturn("someshortcutId"); + when(info.getUserId()).thenReturn(USER_SYSTEM); when(info.isLongLived()).thenReturn(true); when(info.isEnabled()).thenReturn(true); shortcutInfos.add(info); @@ -6483,6 +6489,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mPreferencesHelper.getConversations(anyString(), anyInt())).thenReturn(convos); ShortcutInfo si = mock(ShortcutInfo.class); + when(si.getPackage()).thenReturn(PKG_P); + when(si.getId()).thenReturn("convo"); + when(si.getUserId()).thenReturn(USER_SYSTEM); when(si.getShortLabel()).thenReturn("Hello"); when(si.isLongLived()).thenReturn(true); when(si.isEnabled()).thenReturn(true); @@ -6514,6 +6523,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mPreferencesHelper.getConversations(anyString(), anyInt())).thenReturn(convos); ShortcutInfo si = mock(ShortcutInfo.class); + when(si.getPackage()).thenReturn(PKG_P); + when(si.getId()).thenReturn("convo"); + when(si.getUserId()).thenReturn(USER_SYSTEM); when(si.getShortLabel()).thenReturn("Hello"); when(si.isLongLived()).thenReturn(false); when(mLauncherApps.getShortcuts(any(), any())).thenReturn(Arrays.asList(si)); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java index f7304bd0075b..3095c87ba2f6 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java @@ -170,6 +170,9 @@ public class ShortcutHelperTest extends UiServiceTestCase { @Test public void testGetValidShortcutInfo_notLongLived() { ShortcutInfo si = mock(ShortcutInfo.class); + when(si.getPackage()).thenReturn(PKG); + when(si.getId()).thenReturn(SHORTCUT_ID); + when(si.getUserId()).thenReturn(UserHandle.USER_SYSTEM); when(si.isLongLived()).thenReturn(false); when(si.isEnabled()).thenReturn(true); ArrayList<ShortcutInfo> shortcuts = new ArrayList<>(); @@ -184,6 +187,9 @@ public class ShortcutHelperTest extends UiServiceTestCase { @Test public void testGetValidShortcutInfo_notSharingShortcut() { ShortcutInfo si = mock(ShortcutInfo.class); + when(si.getPackage()).thenReturn(PKG); + when(si.getId()).thenReturn(SHORTCUT_ID); + when(si.getUserId()).thenReturn(UserHandle.USER_SYSTEM); when(si.isLongLived()).thenReturn(true); when(si.isEnabled()).thenReturn(true); ArrayList<ShortcutInfo> shortcuts = new ArrayList<>(); @@ -198,6 +204,9 @@ public class ShortcutHelperTest extends UiServiceTestCase { @Test public void testGetValidShortcutInfo_notEnabled() { ShortcutInfo si = mock(ShortcutInfo.class); + when(si.getPackage()).thenReturn(PKG); + when(si.getId()).thenReturn(SHORTCUT_ID); + when(si.getUserId()).thenReturn(UserHandle.USER_SYSTEM); when(si.isLongLived()).thenReturn(true); when(si.isEnabled()).thenReturn(false); ArrayList<ShortcutInfo> shortcuts = new ArrayList<>(); @@ -212,6 +221,9 @@ public class ShortcutHelperTest extends UiServiceTestCase { @Test public void testGetValidShortcutInfo_isValid() { ShortcutInfo si = mock(ShortcutInfo.class); + when(si.getPackage()).thenReturn(PKG); + when(si.getId()).thenReturn(SHORTCUT_ID); + when(si.getUserId()).thenReturn(UserHandle.USER_SYSTEM); when(si.isLongLived()).thenReturn(true); when(si.isEnabled()).thenReturn(true); ArrayList<ShortcutInfo> shortcuts = new ArrayList<>(); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java index 1debd8c9ffb1..e3bb1b6ca9f3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java @@ -16,6 +16,8 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; @@ -270,6 +272,28 @@ public class ActivityDisplayTests extends ActivityTestsBase { anotherAlwaysOnTopStack.setWindowingMode(WINDOWING_MODE_FREEFORM); assertTrue(anotherAlwaysOnTopStack.isAlwaysOnTop()); assertEquals(anotherAlwaysOnTopStack, taskDisplayArea.getStackAt(topPosition - 1)); + + final ActivityStack dreamStack = taskDisplayArea.createStack( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_DREAM, true /* onTop */); + assertEquals(taskDisplayArea, dreamStack.getDisplayArea()); + assertTrue(dreamStack.isAlwaysOnTop()); + topPosition = taskDisplayArea.getStackCount() - 1; + // Ensure dream shows above all activities, including PiP + assertEquals(dreamStack, taskDisplayArea.getTopStack()); + assertEquals(pinnedStack, taskDisplayArea.getStackAt(topPosition - 1)); + + final ActivityStack assistStack = taskDisplayArea.createStack( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */); + assertEquals(taskDisplayArea, assistStack.getDisplayArea()); + assertFalse(assistStack.isAlwaysOnTop()); + topPosition = taskDisplayArea.getStackCount() - 1; + + // Ensure Assistant shows as a non-always-on-top activity when config_assistantOnTopOfDream + // is false and on top of everything when true. + final boolean isAssistantOnTop = mContext.getResources() + .getBoolean(com.android.internal.R.bool.config_assistantOnTopOfDream); + assertEquals(assistStack, taskDisplayArea.getStackAt( + isAssistantOnTop ? topPosition : topPosition - 4)); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java index 93ded1b6b2f3..6c209e496cf6 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java @@ -471,23 +471,39 @@ public class ActivityStackTests extends ActivityTestsBase { assertEquals(STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, splitScreenSecondary2.getVisibility(null /* starting */)); - // Assistant stack shouldn't be visible behind translucent split-screen stack + // Assistant stack shouldn't be visible behind translucent split-screen stack, + // unless it is configured to show on top of everything. doReturn(false).when(assistantStack).isTranslucent(any()); doReturn(true).when(splitScreenPrimary).isTranslucent(any()); doReturn(true).when(splitScreenSecondary2).isTranslucent(any()); splitScreenSecondary2.moveToFront("testShouldBeVisible_SplitScreen"); splitScreenPrimary.moveToFront("testShouldBeVisible_SplitScreen"); - assertFalse(assistantStack.shouldBeVisible(null /* starting */)); - assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */)); - assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */)); - assertEquals(STACK_VISIBILITY_INVISIBLE, - assistantStack.getVisibility(null /* starting */)); - assertEquals(STACK_VISIBILITY_VISIBLE, - splitScreenPrimary.getVisibility(null /* starting */)); - assertEquals(STACK_VISIBILITY_INVISIBLE, - splitScreenSecondary.getVisibility(null /* starting */)); - assertEquals(STACK_VISIBILITY_VISIBLE, - splitScreenSecondary2.getVisibility(null /* starting */)); + + if (isAssistantOnTop()) { + assertTrue(assistantStack.shouldBeVisible(null /* starting */)); + assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */)); + assertFalse(splitScreenSecondary2.shouldBeVisible(null /* starting */)); + assertEquals(STACK_VISIBILITY_VISIBLE, + assistantStack.getVisibility(null /* starting */)); + assertEquals(STACK_VISIBILITY_INVISIBLE, + splitScreenPrimary.getVisibility(null /* starting */)); + assertEquals(STACK_VISIBILITY_INVISIBLE, + splitScreenSecondary.getVisibility(null /* starting */)); + assertEquals(STACK_VISIBILITY_INVISIBLE, + splitScreenSecondary2.getVisibility(null /* starting */)); + } else { + assertFalse(assistantStack.shouldBeVisible(null /* starting */)); + assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */)); + assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */)); + assertEquals(STACK_VISIBILITY_INVISIBLE, + assistantStack.getVisibility(null /* starting */)); + assertEquals(STACK_VISIBILITY_VISIBLE, + splitScreenPrimary.getVisibility(null /* starting */)); + assertEquals(STACK_VISIBILITY_INVISIBLE, + splitScreenSecondary.getVisibility(null /* starting */)); + assertEquals(STACK_VISIBILITY_VISIBLE, + splitScreenSecondary2.getVisibility(null /* starting */)); + } } @Test @@ -927,9 +943,15 @@ public class ActivityStackTests extends ActivityTestsBase { splitScreenSecondary.moveToFront("testSplitScreenMoveToFront"); - assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */)); - assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */)); - assertFalse(assistantStack.shouldBeVisible(null /* starting */)); + if (isAssistantOnTop()) { + assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */)); + assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */)); + assertTrue(assistantStack.shouldBeVisible(null /* starting */)); + } else { + assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */)); + assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */)); + assertFalse(assistantStack.shouldBeVisible(null /* starting */)); + } } private ActivityStack createStandardStackForVisibilityTest(int windowingMode, @@ -1344,6 +1366,11 @@ public class ActivityStackTests extends ActivityTestsBase { anyBoolean()); } + private boolean isAssistantOnTop() { + return mContext.getResources().getBoolean( + com.android.internal.R.bool.config_assistantOnTopOfDream); + } + private void verifyShouldSleepActivities(boolean focusedStack, boolean keyguardGoingAway, boolean displaySleeping, boolean expected) { final DisplayContent display = mock(DisplayContent.class); diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java index 9b7ffd69da15..e8fab2b0243b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java @@ -37,7 +37,6 @@ import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationDefinition; import android.view.RemoteAnimationTarget; -import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import org.junit.Test; @@ -97,7 +96,6 @@ public class AppChangeTransitionTests extends WindowTestsBase { } @Test - @FlakyTest(bugId = 131005232) public void testModeChangeRemoteAnimatorNoSnapshot() { // setup currently defaults to no snapshot. setUpOnDisplay(mDisplayContent); @@ -115,7 +113,6 @@ public class AppChangeTransitionTests extends WindowTestsBase { } @Test - @FlakyTest(bugId = 131005232) public void testCancelPendingChangeOnRemove() { // setup currently defaults to no snapshot. setUpOnDisplay(mDisplayContent); @@ -135,8 +132,7 @@ public class AppChangeTransitionTests extends WindowTestsBase { } @Test - @FlakyTest(bugId = 131005232) - public void testNoChangeWhenMoveDisplay() { + public void testNoChangeOnOldDisplayWhenMoveDisplay() { mDisplayContent.setWindowingMode(WINDOWING_MODE_FULLSCREEN); final DisplayContent dc1 = createNewDisplay(Display.STATE_ON); dc1.setWindowingMode(WINDOWING_MODE_FREEFORM); @@ -151,9 +147,8 @@ public class AppChangeTransitionTests extends WindowTestsBase { assertEquals(WINDOWING_MODE_FULLSCREEN, mTask.getWindowingMode()); - // Make sure we're not waiting for a change animation (no leash) - assertFalse(mTask.isInChangeTransition()); - assertNull(mActivity.mSurfaceFreezer.mSnapshot); + // Make sure the change transition is not the old display + assertFalse(dc1.mChangingContainers.contains(mTask)); waitUntilHandlersIdle(); mActivity.removeImmediately(); diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java index b6eb9010ce90..f83128705a2a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java @@ -50,6 +50,7 @@ import android.view.test.InsetsModeSession; import androidx.test.filters.SmallTest; import org.junit.AfterClass; +import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; @@ -72,6 +73,11 @@ public class InsetsPolicyTest extends WindowTestsBase { sInsetsModeSession.close(); } + @Before + public void setup() { + mWm.mAnimator.ready(); + } + @Test public void testControlsForDispatch_regular() { addWindow(TYPE_STATUS_BAR, "statusBar"); @@ -194,6 +200,7 @@ public class InsetsPolicyTest extends WindowTestsBase { policy.updateBarControlTarget(mAppWindow); policy.showTransient( IntArray.wrap(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR})); + waitUntilWindowAnimatorIdle(); final InsetsSourceControl[] controls = mDisplayContent.getInsetsStateController().getControlsForDispatch(mAppWindow); @@ -221,6 +228,7 @@ public class InsetsPolicyTest extends WindowTestsBase { policy.updateBarControlTarget(mAppWindow); policy.showTransient( IntArray.wrap(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR})); + waitUntilWindowAnimatorIdle(); final InsetsSourceControl[] controls = mDisplayContent.getInsetsStateController().getControlsForDispatch(mAppWindow); @@ -249,6 +257,7 @@ public class InsetsPolicyTest extends WindowTestsBase { policy.updateBarControlTarget(mAppWindow); policy.showTransient( IntArray.wrap(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR})); + waitUntilWindowAnimatorIdle(); InsetsSourceControl[] controls = mDisplayContent.getInsetsStateController().getControlsForDispatch(mAppWindow); @@ -262,6 +271,7 @@ public class InsetsPolicyTest extends WindowTestsBase { state.setSourceVisible(ITYPE_STATUS_BAR, true); state.setSourceVisible(ITYPE_NAVIGATION_BAR, true); policy.onInsetsModified(mAppWindow, state); + waitUntilWindowAnimatorIdle(); controls = mDisplayContent.getInsetsStateController().getControlsForDispatch(mAppWindow); diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java index 15417d73bd02..2251ee047d29 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.clearInvocations; @@ -53,10 +54,102 @@ public class LetterboxTest { mTransaction = spy(StubTransaction.class); } + private static final int TOP_BAR = 0x1; + private static final int BOTTOM_BAR = 0x2; + private static final int LEFT_BAR = 0x4; + private static final int RIGHT_BAR = 0x8; + @Test - public void testOverlappingWith_usesGlobalCoordinates() { - mLetterbox.layout(new Rect(0, 0, 10, 50), new Rect(0, 2, 10, 45), new Point(1000, 2000)); - assertTrue(mLetterbox.isOverlappingWith(new Rect(0, 0, 1, 1))); + public void testNotIntersectsOrFullyContains_usesGlobalCoordinates() { + final Rect outer = new Rect(0, 0, 10, 50); + final Point surfaceOrig = new Point(1000, 2000); + + final Rect topBar = new Rect(0, 0, 10, 2); + final Rect bottomBar = new Rect(0, 45, 10, 50); + final Rect leftBar = new Rect(0, 0, 2, 50); + final Rect rightBar = new Rect(8, 0, 10, 50); + + final LetterboxLayoutVerifier verifier = + new LetterboxLayoutVerifier(outer, surfaceOrig, mLetterbox); + verifier.setBarRect(topBar, bottomBar, leftBar, rightBar); + + // top + verifier.setInner(0, 2, 10, 50).verifyPositions(TOP_BAR | BOTTOM_BAR, BOTTOM_BAR); + // bottom + verifier.setInner(0, 0, 10, 45).verifyPositions(TOP_BAR | BOTTOM_BAR, TOP_BAR); + // left + verifier.setInner(2, 0, 10, 50).verifyPositions(LEFT_BAR | RIGHT_BAR, RIGHT_BAR); + // right + verifier.setInner(0, 0, 8, 50).verifyPositions(LEFT_BAR | RIGHT_BAR, LEFT_BAR); + // top + bottom + verifier.setInner(0, 2, 10, 45).verifyPositions(TOP_BAR | BOTTOM_BAR, 0); + // left + right + verifier.setInner(2, 0, 8, 50).verifyPositions(LEFT_BAR | RIGHT_BAR, 0); + // top + left + verifier.setInner(2, 2, 10, 50).verifyPositions(TOP_BAR | LEFT_BAR, 0); + // top + left + right + verifier.setInner(2, 2, 8, 50).verifyPositions(TOP_BAR | LEFT_BAR | RIGHT_BAR, 0); + // left + right + bottom + verifier.setInner(2, 0, 8, 45).verifyPositions(LEFT_BAR | RIGHT_BAR | BOTTOM_BAR, 0); + // all + verifier.setInner(2, 2, 8, 45) + .verifyPositions(TOP_BAR | BOTTOM_BAR | LEFT_BAR | RIGHT_BAR, 0); + } + + private static class LetterboxLayoutVerifier { + final Rect mOuter; + final Rect mInner = new Rect(); + final Point mSurfaceOrig; + final Letterbox mLetterbox; + final Rect mTempRect = new Rect(); + + final Rect mTop = new Rect(); + final Rect mBottom = new Rect(); + final Rect mLeft = new Rect(); + final Rect mRight = new Rect(); + + LetterboxLayoutVerifier(Rect outer, Point surfaceOrig, Letterbox letterbox) { + mOuter = new Rect(outer); + mSurfaceOrig = new Point(surfaceOrig); + mLetterbox = letterbox; + } + + LetterboxLayoutVerifier setInner(int left, int top, int right, int bottom) { + mInner.set(left, top, right, bottom); + mLetterbox.layout(mOuter, mInner, mSurfaceOrig); + return this; + } + + void setBarRect(Rect top, Rect bottom, Rect left, Rect right) { + mTop.set(top); + mBottom.set(bottom); + mLeft.set(left); + mRight.set(right); + } + + void verifyPositions(int allowedPos, int noOverlapPos) { + assertEquals(mLetterbox.notIntersectsOrFullyContains(mTop), + (allowedPos & TOP_BAR) != 0); + assertEquals(mLetterbox.notIntersectsOrFullyContains(mBottom), + (allowedPos & BOTTOM_BAR) != 0); + assertEquals(mLetterbox.notIntersectsOrFullyContains(mLeft), + (allowedPos & LEFT_BAR) != 0); + assertEquals(mLetterbox.notIntersectsOrFullyContains(mRight), + (allowedPos & RIGHT_BAR) != 0); + + mTempRect.set(mTop.left, mTop.top, mTop.right, mTop.bottom + 1); + assertEquals(mLetterbox.notIntersectsOrFullyContains(mTempRect), + (noOverlapPos & TOP_BAR) != 0); + mTempRect.set(mLeft.left, mLeft.top, mLeft.right + 1, mLeft.bottom); + assertEquals(mLetterbox.notIntersectsOrFullyContains(mTempRect), + (noOverlapPos & LEFT_BAR) != 0); + mTempRect.set(mRight.left - 1, mRight.top, mRight.right, mRight.bottom); + assertEquals(mLetterbox.notIntersectsOrFullyContains(mTempRect), + (noOverlapPos & RIGHT_BAR) != 0); + mTempRect.set(mBottom.left, mBottom.top - 1, mBottom.right, mBottom.bottom); + assertEquals(mLetterbox.notIntersectsOrFullyContains(mTempRect), + (noOverlapPos & BOTTOM_BAR) != 0); + } } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java index f6ed31455f18..d7462f810bb7 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java @@ -40,6 +40,11 @@ class SystemServiceTestsBase { mLockRule.waitForLocked(mSystemServicesTestRule::waitUntilWindowManagerHandlersIdle); } + /** Waits until the choreographer of WindowAnimator has processed all callbacks. */ + void waitUntilWindowAnimatorIdle() { + mLockRule.waitForLocked(mSystemServicesTestRule::waitUntilWindowAnimatorIdle); + } + void cleanupWindowManagerHandlers() { mLockRule.waitForLocked(mSystemServicesTestRule::cleanupWindowManagerHandlers); } diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java index af3ec38631ae..dea9294ff75b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java +++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java @@ -429,6 +429,31 @@ public class SystemServicesTestRule implements TestRule { } } + void waitUntilWindowAnimatorIdle() { + final WindowManagerService wm = getWindowManagerService(); + if (wm == null) { + return; + } + synchronized (mCurrentMessagesProcessed) { + // Add a message to the handler queue and make sure it is fully processed before we move + // on. This makes sure all previous messages in the handler are fully processed vs. just + // popping them from the message queue. + mCurrentMessagesProcessed.set(false); + wm.mAnimator.getChoreographer().postFrameCallback(time -> { + synchronized (mCurrentMessagesProcessed) { + mCurrentMessagesProcessed.set(true); + mCurrentMessagesProcessed.notifyAll(); + } + }); + while (!mCurrentMessagesProcessed.get()) { + try { + mCurrentMessagesProcessed.wait(); + } catch (InterruptedException e) { + } + } + } + } + /** * Throws if caller doesn't hold the given lock. * @param lock the lock diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java index dcc2ff1311a5..60875de5a68b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java @@ -372,7 +372,9 @@ public class TaskRecordTests extends ActivityTestsBase { final int longSide = 1200; final int shortSide = 600; final Rect parentBounds = new Rect(0, 0, 250, 500); + final Rect parentAppBounds = new Rect(0, 0, 250, 480); parentConfig.windowConfiguration.setBounds(parentBounds); + parentConfig.windowConfiguration.setAppBounds(parentAppBounds); parentConfig.densityDpi = 400; parentConfig.screenHeightDp = (parentBounds.bottom * 160) / parentConfig.densityDpi; // 200 parentConfig.screenWidthDp = (parentBounds.right * 160) / parentConfig.densityDpi; // 100 @@ -383,21 +385,25 @@ public class TaskRecordTests extends ActivityTestsBase { assertEquals(parentConfig.screenHeightDp, inOutConfig.screenHeightDp); assertEquals(parentConfig.screenWidthDp, inOutConfig.screenWidthDp); + assertEquals(parentAppBounds, inOutConfig.windowConfiguration.getAppBounds()); assertEquals(Configuration.ORIENTATION_PORTRAIT, inOutConfig.orientation); // If bounds are overridden, config properties should be made to match. Surface hierarchy // will crop for policy. inOutConfig.setToDefaults(); - inOutConfig.windowConfiguration.getBounds().set(0, 0, shortSide, longSide); - // By default, the parent bounds should limit the existing input bounds. + final Rect largerPortraitBounds = new Rect(0, 0, shortSide, longSide); + inOutConfig.windowConfiguration.setBounds(largerPortraitBounds); task.computeConfigResourceOverrides(inOutConfig, parentConfig); - + // The override bounds are beyond the parent, the out appBounds should not be intersected + // by parent appBounds. + assertEquals(largerPortraitBounds, inOutConfig.windowConfiguration.getAppBounds()); assertEquals(longSide, inOutConfig.screenHeightDp * parentConfig.densityDpi / 160); assertEquals(shortSide, inOutConfig.screenWidthDp * parentConfig.densityDpi / 160); inOutConfig.setToDefaults(); // Landscape bounds. - inOutConfig.windowConfiguration.getBounds().set(0, 0, longSide, shortSide); + final Rect largerLandscapeBounds = new Rect(0, 0, longSide, shortSide); + inOutConfig.windowConfiguration.setBounds(largerLandscapeBounds); // Setup the display with a top stable inset. The later assertion will ensure the inset is // excluded from screenHeightDp. @@ -415,6 +421,7 @@ public class TaskRecordTests extends ActivityTestsBase { new ActivityRecord.CompatDisplayInsets(display, task); task.computeConfigResourceOverrides(inOutConfig, parentConfig, compatIntsets); + assertEquals(largerLandscapeBounds, inOutConfig.windowConfiguration.getAppBounds()); assertEquals((shortSide - statusBarHeight) * DENSITY_DEFAULT / parentConfig.densityDpi, inOutConfig.screenHeightDp); assertEquals(longSide * DENSITY_DEFAULT / parentConfig.densityDpi, diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java index a4f1487dde1e..520ac19b89a8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java @@ -16,6 +16,9 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; @@ -251,6 +254,22 @@ public class WallpaperControllerTests extends WindowTestsBase { assertEquals(otherWindowInitialZoom, wallpaperWindow.mWallpaperZoomOut, .01f); } + /** + * Tests that the windowing mode of the wallpaper window must always be fullscreen. + */ + @Test + public void testWallpaperTokenWindowingMode() { + final DisplayContent dc = mWm.mRoot.getDefaultDisplay(); + final WallpaperWindowToken token = new WallpaperWindowToken(mWm, mock(IBinder.class), + true, dc, true /* ownerCanManageAppTokens */); + + // The wallpaper should have requested override fullscreen windowing mode, so the + // configuration (windowing mode) propagation from display won't change it. + dc.setWindowingMode(WINDOWING_MODE_FREEFORM); + assertEquals(WINDOWING_MODE_FULLSCREEN, token.getWindowingMode()); + dc.setWindowingMode(WINDOWING_MODE_UNDEFINED); + assertEquals(WINDOWING_MODE_FULLSCREEN, token.getWindowingMode()); + } private WindowState createWallpaperTargetWindow(DisplayContent dc) { final ActivityRecord homeActivity = new ActivityTestsBase.ActivityBuilder(mWm.mAtmService) diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java index f65328dcbd42..7d2e88014f45 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java @@ -81,7 +81,7 @@ import java.util.List; * Test class for {@link ITaskOrganizer} and {@link android.window.ITaskOrganizerController}. * * Build/Install/Run: - * atest WmTests:TaskOrganizerTests + * atest WmTests:WindowOrganizerTests */ @SmallTest @Presubmit @@ -264,15 +264,22 @@ public class WindowOrganizerTests extends WindowTestsBase { // newly entering the windowing mode. final ITaskOrganizer organizer2 = registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW); stack2.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); - verify(organizer2).onTaskAppeared(any()); + // One each for task and task2 + verify(organizer2, times(2)).onTaskAppeared(any()); + verify(organizer2, times(0)).onTaskVanished(any()); + // One for task + verify(organizer).onTaskVanished(any()); assertTrue(stack2.isOrganized()); // Now we unregister the second one, the first one should automatically be reregistered // so we verify that it's now seeing changes. mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer2); + verify(organizer, times(3)).onTaskAppeared(any()); + verify(organizer2, times(2)).onTaskVanished(any()); stack3.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); - verify(organizer, times(2)).onTaskAppeared(any()); + verify(organizer, times(4)).onTaskAppeared(any()); + verify(organizer2, times(2)).onTaskVanished(any()); assertTrue(stack3.isOrganized()); } @@ -902,12 +909,13 @@ public class WindowOrganizerTests extends WindowTestsBase { task.setHasBeenVisible(true); verify(organizer, times(1)).onTaskAppeared(any()); - task.taskOrganizerUnregistered(); + task.setTaskOrganizer(null); + verify(organizer, times(1)).onTaskVanished(any()); task.setTaskOrganizer(organizer); verify(organizer, times(2)).onTaskAppeared(any()); task.removeImmediately(); - verify(organizer).onTaskVanished(any()); + verify(organizer, times(2)).onTaskVanished(any()); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java index 07a6179c00bd..cdf8eb4faf1d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java @@ -28,9 +28,11 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; +import android.Manifest; import android.app.IApplicationThread; import android.content.ComponentName; import android.content.pm.ApplicationInfo; +import android.content.pm.ServiceInfo; import android.content.res.Configuration; import android.platform.test.annotations.Presubmit; @@ -200,6 +202,57 @@ public class WindowProcessControllerTests extends ActivityTestsBase { assertFalse(wpc.registeredForActivityConfigChanges()); } + @Test + public void testActivityNotOverridingImeProcessConfig() { + ServiceInfo serviceInfo = new ServiceInfo(); + serviceInfo.permission = Manifest.permission.BIND_INPUT_METHOD; + // Notify WPC that this process has started an IME service. + mWpc.onServiceStarted(serviceInfo); + + final ActivityRecord activity = new ActivityBuilder(mService) + .setCreateTask(true) + .setUseProcess(mWpc) + .build(); + + mWpc.addActivityIfNeeded(activity); + // IME processes should not be registered for activity config changes. + assertFalse(mWpc.registeredForActivityConfigChanges()); + } + + @Test + public void testActivityNotOverridingAllyProcessConfig() { + ServiceInfo serviceInfo = new ServiceInfo(); + serviceInfo.permission = Manifest.permission.BIND_ACCESSIBILITY_SERVICE; + // Notify WPC that this process has started an ally service. + mWpc.onServiceStarted(serviceInfo); + + final ActivityRecord activity = new ActivityBuilder(mService) + .setCreateTask(true) + .setUseProcess(mWpc) + .build(); + + mWpc.addActivityIfNeeded(activity); + // Ally processes should not be registered for activity config changes. + assertFalse(mWpc.registeredForActivityConfigChanges()); + } + + @Test + public void testActivityNotOverridingVoiceInteractionProcessConfig() { + ServiceInfo serviceInfo = new ServiceInfo(); + serviceInfo.permission = Manifest.permission.BIND_VOICE_INTERACTION; + // Notify WPC that this process has started an voice interaction service. + mWpc.onServiceStarted(serviceInfo); + + final ActivityRecord activity = new ActivityBuilder(mService) + .setCreateTask(true) + .setUseProcess(mWpc) + .build(); + + mWpc.addActivityIfNeeded(activity); + // Voice interaction service processes should not be registered for activity config changes. + assertFalse(mWpc.registeredForActivityConfigChanges()); + } + private TestDisplayContent createTestDisplayContentInContainer() { return new TestDisplayContent.Builder(mService, 1000, 1500).build(); } diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index 9dfa3ac7a5d8..fa9909547fc4 100755 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -734,6 +734,31 @@ public abstract class Connection extends Conferenceable { "android.telecom.extra.ORIGINAL_CONNECTION_ID"; /** + * Extra key set on a {@link Connection} when it was created via a remote connection service. + * For example, if a connection manager requests a remote connection service to create a call + * using one of the remote connection service's phone account handle, this extra will be set so + * that Telecom knows that the wrapped remote connection originated in a remote connection + * service. We stash this in the extras since connection managers will typically copy the + * extras from a {@link RemoteConnection} to a {@link Connection} (there is ultimately not + * other way to relate a {@link RemoteConnection} to a {@link Connection}. + * @hide + */ + public static final String EXTRA_REMOTE_PHONE_ACCOUNT_HANDLE = + "android.telecom.extra.REMOTE_PHONE_ACCOUNT_HANDLE"; + + /** + * Extra key set from a {@link ConnectionService} when using the remote connection APIs + * (e.g. {@link RemoteConnectionService#createRemoteConnection(PhoneAccountHandle, + * ConnectionRequest, boolean)}) to create a remote connection. Provides the receiving + * {@link ConnectionService} with a means to know the package name of the requesting + * {@link ConnectionService} so that {@link #EXTRA_REMOTE_PHONE_ACCOUNT_HANDLE} can be set for + * better visibility in Telecom of where a connection ultimately originated. + * @hide + */ + public static final String EXTRA_REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME = + "android.telecom.extra.REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME"; + + /** * Boolean connection extra key set on the extras passed to * {@link Connection#sendConnectionEvent} which indicates that audio is present * on the RTT call when the extra value is true. diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java index 1b60e4820ad0..a716b37f7efd 100755 --- a/telecomm/java/android/telecom/ConnectionService.java +++ b/telecomm/java/android/telecom/ConnectionService.java @@ -1859,9 +1859,25 @@ public abstract class ConnectionService extends Service { new DisconnectCause(DisconnectCause.ERROR, "IMPL_RETURNED_NULL_CONFERENCE"), request.getAccountHandle()); } - if (conference.getExtras() != null) { - conference.getExtras().putString(Connection.EXTRA_ORIGINAL_CONNECTION_ID, callId); + + Bundle extras = request.getExtras(); + Bundle newExtras = new Bundle(); + newExtras.putString(Connection.EXTRA_ORIGINAL_CONNECTION_ID, callId); + if (extras != null) { + // If the request originated from a remote connection service, we will add some + // tracking information that Telecom can use to keep informed of which package + // made the remote request, and which remote connection service was used. + if (extras.containsKey(Connection.EXTRA_REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME)) { + newExtras.putString( + Connection.EXTRA_REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME, + extras.getString( + Connection.EXTRA_REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME)); + newExtras.putParcelable(Connection.EXTRA_REMOTE_PHONE_ACCOUNT_HANDLE, + request.getAccountHandle()); + } } + conference.putExtras(newExtras); + mConferenceById.put(callId, conference); mIdByConference.put(conference, callId); conference.addListener(mConferenceListener); @@ -1936,6 +1952,30 @@ public abstract class ConnectionService extends Service { Log.i(this, "createConnection, implementation returned null connection."); connection = Connection.createFailedConnection( new DisconnectCause(DisconnectCause.ERROR, "IMPL_RETURNED_NULL_CONNECTION")); + } else { + try { + Bundle extras = request.getExtras(); + if (extras != null) { + // If the request originated from a remote connection service, we will add some + // tracking information that Telecom can use to keep informed of which package + // made the remote request, and which remote connection service was used. + if (extras.containsKey( + Connection.EXTRA_REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME)) { + Bundle newExtras = new Bundle(); + newExtras.putString( + Connection.EXTRA_REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME, + extras.getString( + Connection.EXTRA_REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME + )); + newExtras.putParcelable(Connection.EXTRA_REMOTE_PHONE_ACCOUNT_HANDLE, + request.getAccountHandle()); + connection.putExtras(newExtras); + } + } + } catch (UnsupportedOperationException ose) { + // Do nothing; if the ConnectionService reported a failure it will be an instance + // of an immutable Connection which we cannot edit, so we're out of luck. + } } boolean isSelfManaged = diff --git a/telecomm/java/android/telecom/Logging/Session.java b/telecomm/java/android/telecom/Logging/Session.java index d82e93fac76d..8d3f4e1df8bc 100644 --- a/telecomm/java/android/telecom/Logging/Session.java +++ b/telecomm/java/android/telecom/Logging/Session.java @@ -427,7 +427,7 @@ public class Session { StringBuilder methodName = new StringBuilder(); methodName.append(getFullMethodPath(false /*truncatePath*/)); if (mOwnerInfo != null && !mOwnerInfo.isEmpty()) { - methodName.append("(InCall package: "); + methodName.append("("); methodName.append(mOwnerInfo); methodName.append(")"); } diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java index cad5b707a146..a0833011715d 100644 --- a/telecomm/java/android/telecom/RemoteConnectionService.java +++ b/telecomm/java/android/telecom/RemoteConnectionService.java @@ -258,6 +258,9 @@ final class RemoteConnectionService { // See comments on Connection.EXTRA_ORIGINAL_CONNECTION_ID for more information. Bundle newExtras = new Bundle(); newExtras.putString(Connection.EXTRA_ORIGINAL_CONNECTION_ID, callId); + // Track the fact this request was relayed through the remote connection service. + newExtras.putParcelable(Connection.EXTRA_REMOTE_PHONE_ACCOUNT_HANDLE, + parcel.getPhoneAccount()); conference.putExtras(newExtras); conference.registerCallback(new RemoteConference.Callback() { @@ -383,6 +386,11 @@ final class RemoteConnectionService { RemoteConnection remoteConnection = new RemoteConnection(callId, mOutgoingConnectionServiceRpc, connection, callingPackage, callingTargetSdkVersion); + // Track that it is via a remote connection. + Bundle newExtras = new Bundle(); + newExtras.putParcelable(Connection.EXTRA_REMOTE_PHONE_ACCOUNT_HANDLE, + connection.getPhoneAccount()); + remoteConnection.putExtras(newExtras); mConnectionById.put(callId, remoteConnection); remoteConnection.registerCallback(new RemoteConnection.Callback() { @Override @@ -535,10 +543,20 @@ final class RemoteConnectionService { ConnectionRequest request, boolean isIncoming) { final String id = UUID.randomUUID().toString(); + Bundle extras = new Bundle(); + if (request.getExtras() != null) { + extras.putAll(request.getExtras()); + } + // We will set the package name for the originator of the remote request; this lets the + // receiving ConnectionService know that the request originated from a remote connection + // service so that it can provide tracking information for Telecom. + extras.putString(Connection.EXTRA_REMOTE_CONNECTION_ORIGINATING_PACKAGE_NAME, + mOurConnectionServiceImpl.getApplicationContext().getOpPackageName()); + final ConnectionRequest newRequest = new ConnectionRequest.Builder() .setAccountHandle(request.getAccountHandle()) .setAddress(request.getAddress()) - .setExtras(request.getExtras()) + .setExtras(extras) .setVideoState(request.getVideoState()) .setRttPipeFromInCall(request.getRttPipeFromInCall()) .setRttPipeToInCall(request.getRttPipeToInCall()) diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java index 151fb59b7550..ede67dd9fd61 100644 --- a/telephony/java/android/telephony/ims/ImsRcsManager.java +++ b/telephony/java/android/telephony/ims/ImsRcsManager.java @@ -49,7 +49,7 @@ import java.util.function.Consumer; * * Use {@link ImsManager#getImsRcsManager(int)} to create an instance of this manager. */ -public class ImsRcsManager implements RegistrationManager { +public class ImsRcsManager { private static final String TAG = "ImsRcsManager"; /** @@ -173,11 +173,11 @@ public class ImsRcsManager implements RegistrationManager { /** * @hide */ - @Override + // @Override add back to RegistrationManager interface once public. @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback( @NonNull @CallbackExecutor Executor executor, - @NonNull RegistrationCallback c) + @NonNull RegistrationManager.RegistrationCallback c) throws ImsException { if (c == null) { throw new IllegalArgumentException("Must include a non-null RegistrationCallback."); @@ -204,7 +204,7 @@ public class ImsRcsManager implements RegistrationManager { /** * @hide */ - @Override + // @Override add back to RegistrationManager interface once public. @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterImsRegistrationCallback( @NonNull RegistrationManager.RegistrationCallback c) { @@ -228,10 +228,10 @@ public class ImsRcsManager implements RegistrationManager { /** * @hide */ - @Override + // @Override add back to RegistrationManager interface once public. @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationState(@NonNull @CallbackExecutor Executor executor, - @NonNull @ImsRegistrationState Consumer<Integer> stateCallback) { + @NonNull @RegistrationManager.ImsRegistrationState Consumer<Integer> stateCallback) { if (stateCallback == null) { throw new IllegalArgumentException("Must include a non-null stateCallback."); } @@ -260,7 +260,6 @@ public class ImsRcsManager implements RegistrationManager { /** * @hide */ - @Override @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationTransportType(@NonNull @CallbackExecutor Executor executor, @NonNull @AccessNetworkConstants.TransportType @@ -347,8 +346,7 @@ public class ImsRcsManager implements RegistrationManager { * inactive subscription, it will result in a no-op. * @param c The RCS {@link AvailabilityCallback} to be removed. * @see #registerRcsAvailabilityCallback(Executor, AvailabilityCallback) - * @throws ImsException if the IMS service is not available when calling this method - * {@link ImsRcsController#unregisterRcsAvailabilityCallback()}. + * @throws ImsException if the IMS service is not available when calling this method. * See {@link ImsException#getCode()} for more information on the error codes. * @hide */ @@ -390,8 +388,7 @@ public class ImsRcsManager implements RegistrationManager { * rather the subscription is capable of this service over IMS. * @see #isAvailable(int) * @see android.telephony.CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL - * @throws ImsException if the IMS service is not available when calling this method - * {@link ImsRcsController#isCapable(int, int)}. + * @throws ImsException if the IMS service is not available when calling this method. * See {@link ImsException#getCode()} for more information on the error codes. * @hide */ @@ -424,9 +421,8 @@ public class ImsRcsManager implements RegistrationManager { * @return true if the RCS capability is currently available for the associated subscription, * false otherwise. If the capability is available, IMS is registered and the service is * currently available over IMS. - * @see #isCapable(int) - * @throws ImsException if the IMS service is not available when calling this method - * {@link ImsRcsController#isAvailable(int, int)}. + * @see #isCapable(int, int) + * @throws ImsException if the IMS service is not available when calling this method. * See {@link ImsException#getCode()} for more information on the error codes. * @hide */ diff --git a/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java b/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java index 35a6c26ec73f..371375c0d932 100644 --- a/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java +++ b/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java @@ -77,7 +77,7 @@ public class DummyBlobData { return mRandomSeed; } - public Builder setFileSize(int fileSize) { + public Builder setFileSize(long fileSize) { mFileSize = fileSize; return this; } diff --git a/tests/BlobStoreTestUtils/src/com/android/utils/blob/Utils.java b/tests/BlobStoreTestUtils/src/com/android/utils/blob/Utils.java index 482b23f129fd..6927e86213d8 100644 --- a/tests/BlobStoreTestUtils/src/com/android/utils/blob/Utils.java +++ b/tests/BlobStoreTestUtils/src/com/android/utils/blob/Utils.java @@ -34,6 +34,9 @@ import java.io.OutputStream; public class Utils { public static final int BUFFER_SIZE_BYTES = 16 * 1024; + public static final long KB_IN_BYTES = 1000; + public static final long MB_IN_BYTES = KB_IN_BYTES * 1000; + public static void copy(InputStream in, OutputStream out, long lengthBytes) throws IOException { final byte[] buffer = new byte[BUFFER_SIZE_BYTES]; diff --git a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java index 737b7c7b9caf..b0213b0ef502 100644 --- a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java +++ b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java @@ -384,7 +384,8 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc * * For example: * To connect to an open network with a SSID prefix of "test" and a BSSID OUI of "10:03:23": - * {@code + * + * <pre>{@code * final NetworkSpecifier specifier = * new Builder() * .setSsidPattern(new PatternMatcher("test", PatterMatcher.PATTERN_PREFIX)) @@ -406,7 +407,7 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc * // etc. * }; * connectivityManager.requestNetwork(request, networkCallback); - * } + * }</pre> * * @return Instance of {@link NetworkSpecifier}. * @throws IllegalStateException on invalid params set. |