Merge "Limit number of window context without any window" into rvc-dev
diff --git a/Android.bp b/Android.bp
index bd30de2..8d3b4af 100644
--- a/Android.bp
+++ b/Android.bp
@@ -708,11 +708,9 @@
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 @@
"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 270c160..8ab9524 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -145,7 +145,6 @@
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: [
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 6af1178..b4a7cd4 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 @@
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 @@
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 80308d2..0d3f420 100644
--- a/apex/statsd/aidl/android/os/IStatsd.aidl
+++ b/apex/statsd/aidl/android/os/IStatsd.aidl
@@ -182,12 +182,6 @@
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 536b71a..4eeae57 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.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 @@
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 @@
* @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 @@
* @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 @@
* @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 @@
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 80e2d00..3f8bee9 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -48759,12 +48759,8 @@
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 755380e..abfd12b 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -126,6 +126,7 @@
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 @@
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 @@
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 acd9ec3..f30ed17c 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -104,7 +104,7 @@
"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 @@
//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 @@
"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.h b/cmds/statsd/src/FieldValue.h
index e251399..ba4cf11 100644
--- a/cmds/statsd/src/FieldValue.h
+++ b/cmds/statsd/src/FieldValue.h
@@ -382,10 +382,6 @@
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 @@
// 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 @@
// 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 d914ab2..cc48d50 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -389,15 +389,24 @@
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 +415,11 @@
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 +436,7 @@
}
- 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 +1055,8 @@
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 +1064,28 @@
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 97512ed..ffd83ba 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -120,6 +120,11 @@
/* 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 ae7a8d0..bd9f7a5 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -118,7 +118,8 @@
}
})),
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 @@
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 @@
packageNames,
installers);
- mBootCompleteLatch.countDown(kUidMapReceivedTag);
+ mBootCompleteTrigger.markComplete(kUidMapReceivedTag);
VLOG("StatsService::informAllUidData UidData proto parsed successfully.");
return Status::ok();
}
@@ -1066,7 +1061,7 @@
ENFORCE_UID(AID_SYSTEM);
VLOG("StatsService::bootCompleted was called");
- mBootCompleteLatch.countDown(kBootCompleteTag);
+ mBootCompleteTrigger.markComplete(kBootCompleteTag);
return Status::ok();
}
@@ -1222,20 +1217,11 @@
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 79324d8..b49fa1d 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 @@
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 @@
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 @@
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 13e7ac1..88824f0 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -46,6 +46,7 @@
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 @@
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 @@
}
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 933f48d..3618bb0 100644
--- a/cmds/statsd/src/external/StatsCallbackPuller.cpp
+++ b/cmds/statsd/src/external/StatsCallbackPuller.cpp
@@ -67,8 +67,14 @@
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 ebe9610..cfd5d14 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -252,9 +252,13 @@
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 ab0ccee..5e18aaa 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -78,11 +78,12 @@
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 @@
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/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index db637b1..46f5dbd 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -54,6 +54,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
"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 @@
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 @@
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 ff31e9e..21e524a 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -461,6 +461,16 @@
*/
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 +509,7 @@
long emptyData = 0;
long registeredCount = 0;
long unregisteredCount = 0;
+ int32_t atomErrorCount = 0;
} PulledAtomStats;
typedef struct {
@@ -546,6 +557,12 @@
// 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 +630,8 @@
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 +650,7 @@
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 61cd017..eb830e1 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -114,14 +114,6 @@
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 @@
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 @@
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 @@
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 41fdcc2..dedcfaf 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -70,7 +70,7 @@
explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
const InstallTrainInfo& installTrainInfo);
- ~LogEvent();
+ ~LogEvent() {}
/**
* Get the timestamp associated with this event.
@@ -184,6 +184,12 @@
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 @@
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 @@
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 @@
// 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 @@
// 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 a4711e8..f9a8842 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -109,10 +109,11 @@
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 cc48f99..6f84076 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -154,12 +154,14 @@
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 aa0cae2..2eb584b 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -73,18 +73,23 @@
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 @@
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 6aba13ca..91c98ea2 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -141,30 +141,25 @@
}
/**
- * 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 @@
// 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 d832ed8..e03de0b 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -189,7 +189,7 @@
StateManager::getInstance().unregisterListener(atomId, it);
}
}
- mPullerManager->UnregisterPullUidProvider(mConfigKey);
+ mPullerManager->UnregisterPullUidProvider(mConfigKey, this);
VLOG("~MetricsManager()");
}
@@ -231,8 +231,8 @@
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::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 @@
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 1fd6572..ef03d20 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -70,6 +70,8 @@
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 e9273dc..c8dc8cc 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -69,8 +69,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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/state/StateTracker.cpp b/cmds/statsd/src/state/StateTracker.cpp
index b7f314a..b63713b 100644
--- a/cmds/statsd/src/state/StateTracker.cpp
+++ b/cmds/statsd/src/state/StateTracker.cpp
@@ -51,7 +51,7 @@
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 f4247ec..868247b 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -425,6 +425,7 @@
message AtomStats {
optional int32 tag = 1;
optional int32 count = 2;
+ optional int32 error_count = 3;
}
repeated AtomStats atom_stats = 7;
@@ -460,6 +461,7 @@
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 5635313..f9fddc8 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -80,6 +80,8 @@
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 +494,7 @@
(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/MultiConditionTrigger.cpp b/cmds/statsd/src/utils/MultiConditionTrigger.cpp
new file mode 100644
index 0000000..43a6933
--- /dev/null
+++ b/cmds/statsd/src/utils/MultiConditionTrigger.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define DEBUG false // STOPSHIP if true
+
+#include "MultiConditionTrigger.h"
+
+#include <thread>
+
+using namespace std;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+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 MultiConditionTrigger::markComplete(const string& conditionName) {
+ bool doTrigger = false;
+ {
+ lock_guard<mutex> lg(mMutex);
+ if (mCompleted) {
+ return;
+ }
+ mRemainingConditionNames.erase(conditionName);
+ mCompleted = mRemainingConditionNames.empty();
+ doTrigger = mCompleted;
+ }
+ if (doTrigger) {
+ std::thread executorThread([this] { mTrigger(); });
+ executorThread.detach();
+ }
+}
+} // 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 0000000..51f6029
--- /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.cpp b/cmds/statsd/src/utils/NamedLatch.cpp
deleted file mode 100644
index 6e77977..0000000
--- a/cmds/statsd/src/utils/NamedLatch.cpp
+++ /dev/null
@@ -1,48 +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.
- */
-#define DEBUG false // STOPSHIP if true
-
-#include "NamedLatch.h"
-
-using namespace std;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-NamedLatch::NamedLatch(const set<string>& eventNames) : mRemainingEventNames(eventNames) {
-}
-
-void NamedLatch::countDown(const string& eventName) {
- bool notify = false;
- {
- lock_guard<mutex> lg(mMutex);
- mRemainingEventNames.erase(eventName);
- notify = mRemainingEventNames.empty();
- }
- if (notify) {
- mConditionVariable.notify_all();
- }
-}
-
-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/NamedLatch.h b/cmds/statsd/src/utils/NamedLatch.h
deleted file mode 100644
index 70238370..0000000
--- 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 bb4578d..e52e2d0 100644
--- a/cmds/statsd/tests/LogEvent_test.cpp
+++ b/cmds/statsd/tests/LogEvent_test.cpp
@@ -352,7 +352,7 @@
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 1075fe4..a44541d 100644
--- a/cmds/statsd/tests/MetricsManager_test.cpp
+++ b/cmds/statsd/tests/MetricsManager_test.cpp
@@ -528,7 +528,7 @@
}));
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 d29394b..b809286 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -301,6 +301,29 @@
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 b173ee0..9117623 100644
--- a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
@@ -89,6 +89,7 @@
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 @@
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 @@
// 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 @@
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 @@
// 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 @@
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 @@
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 @@
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 129fafa..cdde603 100644
--- a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
+++ b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
@@ -486,6 +486,41 @@
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 65f8de6..8131725 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -38,9 +38,9 @@
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 @@
} // 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 30f8159..8ef2519 100644
--- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
@@ -41,10 +41,10 @@
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 @@
} // 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 @@
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 @@
*/
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 @@
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 @@
*/
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 42d0d5d..9d2ec88 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -42,6 +42,8 @@
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 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 @@
}
} // 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 @@
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 @@
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 @@
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 @@
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 @@
.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 @@
.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 @@
.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 3b4d646..f493cc4 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -41,6 +41,8 @@
namespace os {
namespace statsd {
+namespace {
+
const ConfigKey kConfigKey(0, 12345);
const int tagId = 1;
const int64_t metricId = 123;
@@ -58,10 +60,18 @@
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 @@
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 @@
}
};
+// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 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 @@
// 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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 69f7e3f..be410b1 100644
--- a/cmds/statsd/tests/metrics/metrics_test_helper.h
+++ b/cmds/statsd/tests/metrics/metrics_test_helper.h
@@ -44,7 +44,8 @@
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 37b9889..4d68ea2 100644
--- a/cmds/statsd/tests/statsd_test_util.h
+++ b/cmds/statsd/tests/statsd_test_util.h
@@ -42,6 +42,8 @@
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 0000000..db402a0
--- /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 de48a13..0000000
--- 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/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java b/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
index 9a3dad2..623734e 100644
--- a/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
@@ -30,7 +30,6 @@
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
-import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Xml;
@@ -153,7 +152,7 @@
com.android.internal.R.styleable.AccessibilityShortcutTarget_settingsActivity);
asAttributes.recycle();
- if (mDescriptionResId == 0 || mSummaryResId == 0) {
+ if ((mDescriptionResId == 0 && mHtmlDescriptionRes == 0) || mSummaryResId == 0) {
throw new XmlPullParserException("No description or summary in meta-data");
}
} catch (PackageManager.NameNotFoundException e) {
@@ -243,7 +242,10 @@
public String loadHtmlDescription(@NonNull PackageManager packageManager) {
final String htmlDescription = loadResourceString(packageManager, mActivityInfo,
mHtmlDescriptionRes);
- return TextUtils.isEmpty(htmlDescription) ? null : getFilteredHtmlText(htmlDescription);
+ if (htmlDescription != null) {
+ return getFilteredHtmlText(htmlDescription);
+ }
+ return null;
}
/**
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 1b941de..1cc63da 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -23,8 +23,10 @@
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 @@
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 21b56d3..7d6ce41 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -375,7 +375,6 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
}
}
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 @@
* @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 @@
* {@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 @@
* 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 @@
// 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 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 @@
// 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 @@
}
}
+ @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 @@
+ ", 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 8edf03d..af02783 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -2301,11 +2301,11 @@
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 @@
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 @@
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 @@
if (mChannelId != null) {
parcel.writeInt(1);
- parcel.writeString(mChannelId);
+ parcel.writeString8(mChannelId);
} else {
parcel.writeInt(0);
}
@@ -2811,7 +2811,7 @@
if (mShortcutId != null) {
parcel.writeInt(1);
- parcel.writeString(mShortcutId);
+ parcel.writeString8(mShortcutId);
} else {
parcel.writeInt(0);
}
@@ -8873,7 +8873,7 @@
}
mDesiredHeightResId = in.readInt();
if (in.readInt() != 0) {
- mShortcutId = in.readString();
+ mShortcutId = in.readString8();
}
}
@@ -9029,7 +9029,7 @@
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 2feb927..9f8d3c4 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -102,7 +102,7 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
* @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 @@
* @see Notification#getBubbleMetadata()
*/
public boolean canBubble() {
+ return mAllowBubbles == ALLOW_BUBBLE_ON;
+ }
+
+ /**
+ * @hide
+ */
+ public int getAllowBubbles() {
return mAllowBubbles;
}
@@ -872,7 +891,7 @@
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 @@
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 @@
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 0173731..c7a2a1e 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -20,6 +20,7 @@
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 @@
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/content/ClipData.java b/core/java/android/content/ClipData.java
index 9c806fa..1923bf3 100644
--- a/core/java/android/content/ClipData.java
+++ b/core/java/android/content/ClipData.java
@@ -1206,7 +1206,7 @@
}
} else {
dest.writeInt(PARCEL_TYPE_STRING);
- dest.writeString(text);
+ dest.writeString8(text);
}
}
@@ -1215,7 +1215,7 @@
*/
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 494d2aea..1fb426e 100644
--- a/core/java/android/content/ContentProviderOperation.java
+++ b/core/java/android/content/ContentProviderOperation.java
@@ -91,8 +91,8 @@
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 @@
} 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 @@
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 @@
}
if (mSelection != null) {
dest.writeInt(1);
- dest.writeString(mSelection);
+ dest.writeString8(mSelection);
} else {
dest.writeInt(0);
}
@@ -591,7 +591,7 @@
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 @@
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/Intent.java b/core/java/android/content/Intent.java
index b1d6c83..def150a 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -888,8 +888,8 @@
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 void writeToParcel(Parcel dest, int flags) {
- dest.writeString(packageName);
- dest.writeString(resourceName);
+ dest.writeString8(packageName);
+ dest.writeString8(resourceName);
}
@Override
@@ -10807,12 +10807,12 @@
}
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 @@
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 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 @@
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 f25ce76..b1f8869 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1206,17 +1206,17 @@
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 @@
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 @@
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 @@
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 @@
gravity = source.readInt();
minWidth = source.readInt();
minHeight = source.readInt();
- windowLayoutAffinity = source.readString();
+ windowLayoutAffinity = source.readString8();
}
/**
@@ -1476,7 +1476,7 @@
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 b6706011..b37521b 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1704,10 +1704,10 @@
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 @@
} 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 @@
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 @@
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 @@
@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 @@
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 @@
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 @@
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 362098c..628bcd7 100644
--- a/core/java/android/content/pm/ComponentInfo.java
+++ b/core/java/android/content/pm/ComponentInfo.java
@@ -197,8 +197,8 @@
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 @@
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/FeatureInfo.java b/core/java/android/content/pm/FeatureInfo.java
index 9f3ab77..89269e0 100644
--- a/core/java/android/content/pm/FeatureInfo.java
+++ b/core/java/android/content/pm/FeatureInfo.java
@@ -108,7 +108,7 @@
@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 @@
};
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 9819b5d..ffe8b18 100644
--- a/core/java/android/content/pm/IDataLoaderStatusListener.aidl
+++ b/core/java/android/content/pm/IDataLoaderStatusListener.aidl
@@ -21,16 +21,29 @@
* @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 574a1ee..745a6c1 100644
--- a/core/java/android/content/pm/InstrumentationInfo.java
+++ b/core/java/android/content/pm/InstrumentationInfo.java
@@ -157,21 +157,21 @@
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 @@
@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 85c698f..bb56ef7 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -441,14 +441,14 @@
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 @@
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 @@
@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 @@
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 7fd5531..d41ace5 100644
--- a/core/java/android/content/pm/PackageItemInfo.java
+++ b/core/java/android/content/pm/PackageItemInfo.java
@@ -422,8 +422,8 @@
}
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 @@
}
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 85bafd9..8a57f82 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -6873,9 +6873,9 @@
/** @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 44def33..8212224 100644
--- a/core/java/android/content/pm/PackageParserCacheHelper.java
+++ b/core/java/android/content/pm/PackageParserCacheHelper.java
@@ -78,10 +78,19 @@
/**
* 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 @@
* 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 @@
}
}
+ @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 07d42dc..3984ade 100644
--- a/core/java/android/content/pm/ProviderInfo.java
+++ b/core/java/android/content/pm/ProviderInfo.java
@@ -145,9 +145,9 @@
@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 @@
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 5f90b6c..d3f9e24 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -244,7 +244,7 @@
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 @@
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 f7c96a3..2f67f6d 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -228,19 +228,26 @@
} catch (SQLiteCantOpenDatabaseException e) {
String message = String.format("Cannot open database '%s'", file);
- final Path path = FileSystems.getDefault().getPath(file);
- final Path dir = path.getParent();
+ try {
+ // Try to diagnose for common reasons. If something fails in here, that's fine;
+ // just swallow the exception.
- 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";
+ 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 462110f..ad9bf07 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -65,21 +65,23 @@
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 525bbfd..1cb4fe8 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -500,7 +500,7 @@
}
static Uri readFrom(Parcel parcel) {
- return new StringUri(parcel.readString());
+ return new StringUri(parcel.readString8());
}
public int describeContents() {
@@ -509,7 +509,7 @@
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 @@
static Uri readFrom(Parcel parcel) {
return new OpaqueUri(
- parcel.readString(),
+ parcel.readString8(),
Part.readFrom(parcel),
Part.readFrom(parcel)
);
@@ -887,7 +887,7 @@
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 @@
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 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 @@
+ mCanonicalRepresentation + ")");
}
parcel.writeInt(mCanonicalRepresentation);
- parcel.writeString(canonicalValue);
+ parcel.writeString8(canonicalValue);
}
}
@@ -2060,7 +2060,7 @@
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 @@
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 f0b7b5f..93f6607 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -307,7 +307,9 @@
@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 @@
@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 @@
* 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 @@
* 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 @@
* 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 @@
* @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 @@
*/
@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 @@
*
* @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 251995a..321dc9e 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.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 @@
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 @@
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");
+ 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.openStorage(incrementalPath);
- } else {
- mDefaultDir = getTempDir();
- if (mDefaultDir == null) {
- throw new IOException("Failed to create storage: tempDir is empty");
- }
- 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 @@
* 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 35518db..916edfa 100644
--- a/core/java/android/os/incremental/IncrementalManager.java
+++ b/core/java/android/os/incremental/IncrementalManager.java
@@ -32,8 +32,12 @@
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 @@
}
/**
- * 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 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);
}
- linkFiles(apkStorage, beforeCodeFile, "", linkedApkStorage, afterCodePathName);
- apkStorage.unBind(beforeCodePath);
+ try {
+ final String afterCodePathName = afterCodeFile.getName();
+ linkFiles(apkStorage, beforeCodeAbsolute, "", linkedApkStorage, afterCodePathName);
+ apkStorage.unBind(beforeCodeAbsolute.toString());
+ } catch (Exception e) {
+ linkedApkStorage.unBind(targetStorageDir);
+ throw e;
+ }
}
/**
@@ -252,22 +235,27 @@
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()) {
- sourceStorage.makeLink(
- sourceEntryRelativePath, targetStorage, targetEntryRelativePath);
- } else if (entry.isDirectory()) {
- linkFiles(sourceStorage, entry, sourceEntryRelativePath, targetStorage,
- targetEntryRelativePath);
+ 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(
+ file.toAbsolutePath().toString(), targetStorage,
+ targetRelative.resolve(relativeFile).toString());
+ return FileVisitResult.CONTINUE;
+ }
+ });
}
/**
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index aee32ed..a1a11ed 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -2447,7 +2447,12 @@
final String filePath = path.getCanonicalPath();
final StorageVolume volume = getStorageVolume(path);
if (volume == null) {
- throw new IllegalStateException("Failed to update quota type for " + filePath);
+ Log.w(TAG, "Failed to update quota type for " + filePath);
+ return;
+ }
+ if (!volume.isEmulated()) {
+ // We only support quota tracking on emulated filesystems
+ return;
}
final int userId = volume.getOwner().getIdentifier();
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index df4ead1..28639b3 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -748,7 +748,7 @@
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 @@
} 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 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 f37e423..d6d99f8 100644
--- a/core/java/android/text/style/DynamicDrawableSpan.java
+++ b/core/java/android/text/style/DynamicDrawableSpan.java
@@ -166,7 +166,7 @@
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 bdc8ba8..df89130 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -153,7 +153,7 @@
@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 a723839..2dcfd89 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -69,6 +69,13 @@
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 @@
} 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 @@
* 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 cc380f3..e4dbd637 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -514,33 +514,24 @@
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}
+ * Enum listing the possible sources from which a screenshot was originated. Used for logging.
+ *
* @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.
- * @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/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 4980b33..6646c31 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -3437,6 +3437,7 @@
* @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 9f03d95..16e87f8 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -5863,6 +5863,7 @@
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 8d9ae58..00526d9 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -821,6 +821,7 @@
// 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
old mode 100644
new mode 100755
index 8595fec..6425cf1
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -1005,6 +1005,7 @@
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 e9e0c14..baaf2a7 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -1033,6 +1033,7 @@
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 7f6c0d2..7016c5c 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -1213,7 +1213,7 @@
BitmapReflectionAction(Parcel in) {
viewId = in.readInt();
- methodName = in.readString();
+ methodName = in.readString8();
bitmapId = in.readInt();
bitmap = mBitmapCache.getBitmapForId(bitmapId);
}
@@ -1221,7 +1221,7 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(viewId);
- dest.writeString(methodName);
+ dest.writeString8(methodName);
dest.writeInt(bitmapId);
}
@@ -1282,7 +1282,7 @@
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 @@
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 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 @@
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
old mode 100644
new mode 100755
index 15959c2..6ef570c
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -1076,7 +1076,8 @@
// 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 217693e..61c77bc 100644
--- a/core/java/android/widget/SimpleMonthView.java
+++ b/core/java/android/widget/SimpleMonthView.java
@@ -420,6 +420,7 @@
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 e933f18a..ec07574 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -8359,6 +8359,7 @@
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 @@
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 b1e356d..bcb32fb 100644
--- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
@@ -437,6 +437,9 @@
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 @@
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 970bab9..b671fa7 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -2721,11 +2721,12 @@
}
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 @@
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 @@
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 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 @@
}
}
+ /**
+ * 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 @@
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 @@
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 3eb0923..2c48925 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.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 @@
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 @@
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 @@
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 @@
}
@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 @@
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 1bc982c..00faa3b 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -146,6 +146,7 @@
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 @@
}
@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 @@
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 @@
}
setupViewVisibilities();
maybeLogProfileChange();
+ onProfileTabSelected();
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.RESOLVER_SWITCH_TABS)
.setInt(viewPager.getCurrentItem())
@@ -1628,6 +1641,12 @@
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 @@
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 ad31d8b..885d1bb 100644
--- a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
@@ -213,6 +213,12 @@
/* 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 43bd4a6..6432475 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -6093,7 +6093,8 @@
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 ebfea450..56a6db9 100644
--- a/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java
+++ b/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java
@@ -46,6 +46,9 @@
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 @@
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 7cff90b..adadc5e 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.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 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 @@
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 @@
* 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 @@
}
/**
- * 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 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 @@
* 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 @@
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 @@
}
};
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 31527e8..fb2ecf3 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -1084,6 +1084,7 @@
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 @@
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 @@
static class SavedState extends BaseSavedState {
boolean open;
+ private int mCollapsibleHeightReserved;
SavedState(Parcelable superState) {
super(superState);
@@ -1150,12 +1153,14 @@
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 b51d4f5..4cb2e97 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -301,6 +301,8 @@
}
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 @@
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 483b455..e7a2fb4 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -273,7 +273,28 @@
}
}
-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 @@
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 @@
// @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 @@
// @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 924dc4b..21985f0 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -119,6 +119,7 @@
static pid_t gSystemServerPid = 0;
+static constexpr const char* kVoldAppDataIsolation = "persist.sys.vold_app_data_isolation_enabled";
static constexpr const char* kPropFuse = "persist.sys.fuse";
static const char kZygoteClassName[] = "com/android/internal/os/Zygote";
static jclass gZygoteClass;
@@ -831,6 +832,7 @@
multiuser_get_uid(user_id, AID_EVERYBODY), fail_fn);
bool isFuse = GetBoolProperty(kPropFuse, false);
+ bool isAppDataIsolationEnabled = GetBoolProperty(kVoldAppDataIsolation, false);
if (isFuse) {
if (mount_mode == MOUNT_EXTERNAL_PASS_THROUGH) {
@@ -840,6 +842,9 @@
} else if (mount_mode == MOUNT_EXTERNAL_INSTALLER) {
const std::string installer_source = StringPrintf("/mnt/installer/%d", user_id);
BindMount(installer_source, "/storage", fail_fn);
+ } else if (isAppDataIsolationEnabled && mount_mode == MOUNT_EXTERNAL_ANDROID_WRITABLE) {
+ const std::string writable_source = StringPrintf("/mnt/androidwritable/%d", user_id);
+ BindMount(writable_source, "/storage", fail_fn);
} else {
BindMount(user_source, "/storage", fail_fn);
}
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 3e007e4..997829e 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2523,16 +2523,10 @@
// 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/stats/accessibility/accessibility_enums.proto b/core/proto/android/stats/accessibility/accessibility_enums.proto
new file mode 100644
index 0000000..5118ad5
--- /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/res/res/layout/resolver_empty_states.xml b/core/res/res/layout/resolver_empty_states.xml
index 5890bed..fe11769 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"
@@ -43,6 +44,7 @@
android:fontFamily="@string/config_headlineFontFamilyMedium"
android:textColor="@color/resolver_empty_state_text"
android:textSize="14sp"
+ android:gravity="center_horizontal"
android:layout_centerHorizontal="true" />
<TextView
android:id="@+id/resolver_empty_state_subtitle"
diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml
index b754e0c..76ecefc 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" />
+ <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 1a98553..6fac46e 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 05c00ce..4efd7ff 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 0000000..daa90c2
--- /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 46873b9..dcb3e2f 100644
--- a/core/tests/coretests/src/android/os/ParcelTest.java
+++ b/core/tests/coretests/src/android/os/ParcelTest.java
@@ -87,4 +87,27 @@
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 5424b6f..43590ba 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 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 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 @@
}
@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 @@
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 cd6b3af..fe33cd8 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.Rect;
import android.os.Handler;
import android.os.Looper;
+import android.view.WindowManager;
import androidx.test.runner.AndroidJUnit4;
@@ -91,7 +92,8 @@
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 1db2f63..b351b3d 100644
--- a/identity/java/android/security/identity/IdentityCredential.java
+++ b/identity/java/android/security/identity/IdentityCredential.java
@@ -95,9 +95,7 @@
/**
* 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 @@
* 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 @@
* 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 @@
* ? "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 @@
* 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 @@
* 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 @@
* @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 @@
* 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 @@
* <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 a1dfc77..4f834d2 100644
--- a/identity/java/android/security/identity/IdentityCredentialStore.java
+++ b/identity/java/android/security/identity/IdentityCredentialStore.java
@@ -78,17 +78,21 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 13552d6..37de2c4 100644
--- a/identity/java/android/security/identity/ResultData.java
+++ b/identity/java/android/security/identity/ResultData.java
@@ -34,23 +34,23 @@
/** 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 @@
*
* 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 @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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 @@
/**
* 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 e2a389b..c7aa328 100644
--- a/identity/java/android/security/identity/WritableIdentityCredential.java
+++ b/identity/java/android/security/identity/WritableIdentityCredential.java
@@ -41,15 +41,16 @@
* <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 3e1f72d..8ea6883 100755
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -2713,6 +2713,32 @@
}
/**
+ * @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 14249cb..8e8dfaf 100644
--- a/media/java/android/media/AudioPortEventHandler.java
+++ b/media/java/android/media/AudioPortEventHandler.java
@@ -78,7 +78,8 @@
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 c11762b..373f6e1 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -214,6 +214,175 @@
}
}
+ /**
+ * @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/IMediaRouter2.aidl b/media/java/android/media/IMediaRouter2.aidl
index dc06153..a8b82ba 100644
--- a/media/java/android/media/IMediaRouter2.aidl
+++ b/media/java/android/media/IMediaRouter2.aidl
@@ -24,11 +24,15 @@
* @hide
*/
oneway interface IMediaRouter2 {
- void notifyRestoreRoute();
void notifyRoutesAdded(in List<MediaRoute2Info> routes);
void notifyRoutesRemoved(in List<MediaRoute2Info> routes);
void notifyRoutesChanged(in List<MediaRoute2Info> routes);
void notifySessionCreated(int requestId, in @nullable RoutingSessionInfo sessionInfo);
void notifySessionInfoChanged(in RoutingSessionInfo sessionInfo);
void notifySessionReleased(in RoutingSessionInfo sessionInfo);
+ /**
+ * Gets hints of the new session for the given route.
+ * Call MediaRouterService#notifySessionHintsForCreatingSession to pass the result.
+ */
+ void getSessionHintsForCreatingSession(long uniqueRequestId, in MediaRoute2Info route);
}
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index 0d87736..52bac67 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -59,6 +59,8 @@
void requestCreateSessionWithRouter2(IMediaRouter2 router, int requestId,
in MediaRoute2Info route, in @nullable Bundle sessionHints);
+ void notifySessionHintsForCreatingSession(IMediaRouter2 router, long uniqueRequestId,
+ in MediaRoute2Info route, in @nullable Bundle sessionHints);
void selectRouteWithRouter2(IMediaRouter2 router, String sessionId, in MediaRoute2Info route);
void deselectRouteWithRouter2(IMediaRouter2 router, String sessionId, in MediaRoute2Info route);
void transferToRouteWithRouter2(IMediaRouter2 router, String sessionId,
diff --git a/media/java/android/media/MediaCas.java b/media/java/android/media/MediaCas.java
index 97d32d8..c652628 100644
--- a/media/java/android/media/MediaCas.java
+++ b/media/java/android/media/MediaCas.java
@@ -388,7 +388,7 @@
@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 @@
}
}
- private int getSessionResourceId() throws MediaCasException {
+ private int getSessionResourceHandle() throws MediaCasException {
validateInternalStates();
int[] sessionResourceHandle = new int[1];
@@ -881,14 +881,14 @@
"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 @@
* @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 @@
@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 @@
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 540955f..f6f482d 100644
--- a/media/java/android/media/MediaMetrics.java
+++ b/media/java/android/media/MediaMetrics.java
@@ -38,6 +38,117 @@
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 cfe6db9..0ea9624 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -690,6 +690,31 @@
matchingController.releaseInternal(/* shouldReleaseSession= */ false);
}
+ void onGetControllerHintsForCreatingSessionOnHandler(long uniqueRequestId,
+ MediaRoute2Info route) {
+ OnGetControllerHintsListener listener = mOnGetControllerHintsListener;
+ Bundle controllerHints = null;
+ if (listener != null) {
+ controllerHints = listener.onGetControllerHints(route);
+ if (controllerHints != null) {
+ controllerHints = new Bundle(controllerHints);
+ }
+ }
+
+ MediaRouter2Stub stub;
+ synchronized (sRouterLock) {
+ stub = mStub;
+ }
+ if (stub != null) {
+ try {
+ mMediaRouterService.notifySessionHintsForCreatingSession(
+ stub, uniqueRequestId, route, controllerHints);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "getSessionHintsOnHandler: Unable to request.", ex);
+ }
+ }
+ }
+
private List<MediaRoute2Info> filterRoutes(List<MediaRoute2Info> routes,
RouteDiscoveryPreference discoveryRequest) {
return routes.stream()
@@ -820,13 +845,14 @@
*/
public interface OnGetControllerHintsListener {
/**
- * Called when the {@link MediaRouter2} is about to request
- * the media route provider service to create a controller with the given route.
+ * Called when the {@link MediaRouter2} or the system is about to request
+ * a media route provider service to create a controller with the given route.
* The {@link Bundle} returned here will be sent to media route provider service as a hint.
* <p>
- * To send hints when creating the controller, set the listener before calling
- * {@link #transferTo(MediaRoute2Info)}. The method will be called
- * on the same thread which calls {@link #transferTo(MediaRoute2Info)}.
+ * Since controller creation can be requested by the {@link MediaRouter2} and the system,
+ * set the listener as soon as possible after acquiring {@link MediaRouter2} instance.
+ * The method will be called on the same thread that calls
+ * {@link #transferTo(MediaRoute2Info)} or the main thread if it is requested by the system.
*
* @param route The route to create controller with
* @return An optional bundle of app-specific arguments to send to the provider,
@@ -1378,9 +1404,6 @@
class MediaRouter2Stub extends IMediaRouter2.Stub {
@Override
- public void notifyRestoreRoute() throws RemoteException {}
-
- @Override
public void notifyRoutesAdded(List<MediaRoute2Info> routes) {
mHandler.sendMessage(obtainMessage(MediaRouter2::addRoutesOnHandler,
MediaRouter2.this, routes));
@@ -1415,5 +1438,13 @@
mHandler.sendMessage(obtainMessage(MediaRouter2::releaseControllerOnHandler,
MediaRouter2.this, sessionInfo));
}
+
+ @Override
+ public void getSessionHintsForCreatingSession(long uniqueRequestId,
+ @NonNull MediaRoute2Info route) {
+ mHandler.sendMessage(obtainMessage(
+ MediaRouter2::onGetControllerHintsForCreatingSessionOnHandler,
+ MediaRouter2.this, uniqueRequestId, route));
+ }
}
}
diff --git a/media/java/android/media/audiopolicy/AudioVolumeGroupChangeHandler.java b/media/java/android/media/audiopolicy/AudioVolumeGroupChangeHandler.java
index adf4d3d..022cfee 100644
--- a/media/java/android/media/audiopolicy/AudioVolumeGroupChangeHandler.java
+++ b/media/java/android/media/audiopolicy/AudioVolumeGroupChangeHandler.java
@@ -80,7 +80,8 @@
(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 0000000..e5d0370
--- /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 d331126..50af60a 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -353,13 +353,16 @@
@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 @@
}
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 @@
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 @@
@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 @@
+ "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 @@
* @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 @@
* @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 @@
*/
@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 @@
*/
@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 @@
*/
@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 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 @@
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 @@
}
mLnb = newLnb;
mLnb.setCallback(executor, cb, this);
+ setLnb(mLnb);
}
return mLnb;
}
@@ -944,7 +976,9 @@
*/
@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 @@
@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 @@
@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 @@
@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 7077cd1..487b444 100644
--- a/media/java/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
+++ b/media/java/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
@@ -218,11 +218,11 @@
* <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 @@
*
* <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 @@
*
* <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 b4dcc5d..be102d8 100644
--- a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
+++ b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
@@ -362,17 +362,16 @@
* 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 @@
* <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 e6962a1..aba74e5 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -46,6 +46,7 @@
import android.view.WindowManager;
import com.android.internal.annotations.VisibleForNative;
+import com.android.internal.annotations.VisibleForTesting;
import dalvik.system.CloseGuard;
@@ -408,7 +409,8 @@
}
@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 @@
}
@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 @@
}
@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 @@
}
@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 ba75263..88c32a3 100644
--- a/media/java/android/mtp/MtpStorage.java
+++ b/media/java/android/mtp/MtpStorage.java
@@ -36,7 +36,7 @@
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 614fe73..ab311c0e 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -1130,7 +1130,7 @@
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 @@
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 @@
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 @@
{ "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 750b146..3da78ac 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -191,6 +191,8 @@
jobject getFrontendStatus(jintArray types);
Result openDemux();
jint close();
+ jint closeFrontend();
+ jint closeDemux();
protected:
virtual ~JTuner();
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
index 207534b..6a1e965 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
@@ -48,6 +48,7 @@
import android.media.MediaRouter2Utils;
import android.media.RouteDiscoveryPreference;
import android.media.RoutingSessionInfo;
+import android.os.Bundle;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -74,6 +75,8 @@
private static final String TAG = "MediaRouter2ManagerTest";
private static final int WAIT_TIME_MS = 2000;
private static final int TIMEOUT_MS = 5000;
+ private static final String TEST_KEY = "test_key";
+ private static final String TEST_VALUE = "test_value";
private Context mContext;
private MediaRouter2Manager mManager;
@@ -513,6 +516,56 @@
assertEquals(VOLUME_MAX, variableVolumeRoute.getVolumeMax());
}
+ @Test
+ public void testRouter2SetOnGetControllerHintsListener() throws Exception {
+ Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
+ addRouterCallback(new RouteCallback() {});
+
+ MediaRoute2Info route = routes.get(ROUTE_ID1);
+ assertNotNull(route);
+
+ final Bundle controllerHints = new Bundle();
+ controllerHints.putString(TEST_KEY, TEST_VALUE);
+ final CountDownLatch hintLatch = new CountDownLatch(1);
+ final MediaRouter2.OnGetControllerHintsListener listener =
+ route1 -> {
+ hintLatch.countDown();
+ return controllerHints;
+ };
+
+ final CountDownLatch successLatch = new CountDownLatch(1);
+ final CountDownLatch failureLatch = new CountDownLatch(1);
+
+ addManagerCallback(new MediaRouter2Manager.Callback() {
+ @Override
+ public void onTransferred(RoutingSessionInfo oldSession,
+ RoutingSessionInfo newSession) {
+ assertTrue(newSession.getSelectedRoutes().contains(route.getId()));
+ // The StubMediaRoute2ProviderService is supposed to set control hints
+ // with the given controllerHints.
+ Bundle controlHints = newSession.getControlHints();
+ assertNotNull(controlHints);
+ assertTrue(controlHints.containsKey(TEST_KEY));
+ assertEquals(TEST_VALUE, controlHints.getString(TEST_KEY));
+
+ successLatch.countDown();
+ }
+
+ @Override
+ public void onTransferFailed(RoutingSessionInfo session,
+ MediaRoute2Info requestedRoute) {
+ failureLatch.countDown();
+ }
+ });
+
+ mRouter2.setOnGetControllerHintsListener(listener);
+ mManager.selectRoute(mPackageName, route);
+ assertTrue(hintLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertTrue(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+ assertFalse(failureLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ }
+
Map<String, MediaRoute2Info> waitAndGetRoutesWithManager(List<String> routeFeatures)
throws Exception {
CountDownLatch addedLatch = new CountDownLatch(1);
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 0000000..e69de29
--- /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 0000000..e2e8ff4
--- /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 7004fb6..8b235e6 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_top_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
index ce0d31c..a7347f2 100644
--- a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
@@ -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"
@@ -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"
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 a71567c..aa0a8c5 100644
--- a/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml
+++ b/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml
@@ -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"
@@ -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"
diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml
index e6fb501..52d13c3 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.car.voicerecognition.ConnectedDeviceVoiceRecognitionNotifier</item>
<item>com.android.systemui.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 59fa9d0..91e222c 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java
@@ -19,6 +19,8 @@
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.bubbles.dagger.BubbleModule;
import com.android.systemui.car.notification.CarNotificationModule;
+import com.android.systemui.car.voicerecognition.ConnectedDeviceVoiceRecognitionNotifier;
+import com.android.systemui.car.volume.VolumeUI;
import com.android.systemui.globalactions.GlobalActionsComponent;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.dagger.KeyguardModule;
@@ -38,8 +40,6 @@
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;
@@ -65,7 +65,7 @@
@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 4ea48ba..13a5556 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
@@ -25,6 +25,7 @@
import com.android.systemui.car.CarDeviceProvisionedController;
import com.android.systemui.car.CarDeviceProvisionedControllerImpl;
import com.android.systemui.car.keyguard.CarKeyguardViewController;
+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;
@@ -55,7 +56,6 @@
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;
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/AnimatedTemperatureView.java b/packages/CarSystemUI/src/com/android/systemui/car/hvac/AnimatedTemperatureView.java
similarity index 97%
rename from packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/AnimatedTemperatureView.java
rename to packages/CarSystemUI/src/com/android/systemui/car/hvac/AnimatedTemperatureView.java
index 908aaad..a729431 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.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
similarity index 98%
rename from packages/CarSystemUI/src/com/android/systemui/navigationbar/car/hvac/HvacController.java
rename to packages/CarSystemUI/src/com/android/systemui/car/hvac/HvacController.java
index fd9c488..af8ddb6 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
similarity index 96%
rename from packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/TemperatureBackgroundAnimator.java
rename to packages/CarSystemUI/src/com/android/systemui/car/hvac/TemperatureBackgroundAnimator.java
index 3c6d623..a4c4573 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
similarity index 97%
rename from packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/TemperatureColorStore.java
rename to packages/CarSystemUI/src/com/android/systemui/car/hvac/TemperatureColorStore.java
index a40ffaf..9a7b0b9 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
similarity index 95%
rename from packages/CarSystemUI/src/com/android/systemui/statusbar/hvac/TemperatureTextAnimator.java
rename to packages/CarSystemUI/src/com/android/systemui/car/hvac/TemperatureTextAnimator.java
index 8ee5ef6..74d9704 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
similarity index 95%
rename from packages/CarSystemUI/src/com/android/systemui/navigationbar/car/hvac/TemperatureTextView.java
rename to packages/CarSystemUI/src/com/android/systemui/car/hvac/TemperatureTextView.java
index ad4fcd9..521a665 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
similarity index 93%
rename from packages/CarSystemUI/src/com/android/systemui/navigationbar/car/hvac/TemperatureView.java
rename to packages/CarSystemUI/src/com/android/systemui/car/hvac/TemperatureView.java
index 963f318..6b903fa 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 60ee19e..4fde309 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
@@ -205,6 +205,9 @@
@Override
public void onCancelClicked() {
+ getOverlayViewGlobalStateController().setWindowFocusable(/* focusable= */ false);
+ getOverlayViewGlobalStateController().setWindowNeedsInput(/* needsInput= */ false);
+
mBouncer.hide(/* destroyView= */ true);
mKeyguardCancelClickedListener.onCancelClicked();
}
@@ -226,7 +229,8 @@
@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/notification/CarHeadsUpNotificationSystemContainer.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainer.java
index 53e5d9f..20fcca0 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.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 @@
@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 @@
@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 @@
private void animateShow() {
if ((mEnableHeadsUpNotificationWhenNotificationShadeOpen
- || !mCarStatusBarLazy.get().isPanelExpanded()) && isCurrentUserSetup()) {
+ || !mNotificationPanelViewControllerLazy.get().isPanelExpanded())
+ && mCarDeviceProvisionedController.isCurrentUserFullySetup()) {
mWindow.setVisibility(View.VISIBLE);
}
}
@@ -114,9 +113,4 @@
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/sideloaded/car/CarSideLoadedAppDetector.java b/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/CarSideLoadedAppDetector.java
similarity index 98%
rename from packages/CarSystemUI/src/com/android/systemui/sideloaded/car/CarSideLoadedAppDetector.java
rename to packages/CarSystemUI/src/com/android/systemui/car/sideloaded/CarSideLoadedAppDetector.java
index c0dbb58..f145b14 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/voicerecognition/car/ConnectedDeviceVoiceRecognitionNotifier.java b/packages/CarSystemUI/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifier.java
similarity index 98%
rename from packages/CarSystemUI/src/com/android/systemui/voicerecognition/car/ConnectedDeviceVoiceRecognitionNotifier.java
rename to packages/CarSystemUI/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifier.java
index 2f79f96..c054d20 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
similarity index 83%
rename from packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogComponent.java
rename to packages/CarSystemUI/src/com/android/systemui/car/volume/CarVolumeDialogComponent.java
index 5a34436..a22d1ab 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 @@
@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
similarity index 93%
rename from packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
rename to packages/CarSystemUI/src/com/android/systemui/car/volume/CarVolumeDialogImpl.java
index 873d7d7..1281884 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 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 @@
*/
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 @@
// 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 @@
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 @@
private void showH(int reason) {
- if (D.BUG) {
+ if (DEBUG) {
Log.d(TAG, "showH r=" + Events.DISMISS_REASONS[reason]);
}
@@ -323,7 +341,7 @@
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 @@
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 @@
}
private void dismissH(int reason) {
- if (D.BUG) {
+ if (DEBUG) {
Log.d(TAG, "dismissH r=" + Events.DISMISS_REASONS[reason]);
}
@@ -365,7 +383,7 @@
.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 @@
/* 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 @@
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 @@
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 @@
/* showSupplementalIconDivider= */ false);
}
- volumeItem.carVolumeItem = carVolumeItem;
- volumeItem.progress = seekbarProgressValue;
+ volumeItem.mCarVolumeItem = carVolumeItem;
+ volumeItem.mProgress = seekbarProgressValue;
return carVolumeItem;
}
@@ -490,13 +508,12 @@
* 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 @@
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
similarity index 96%
rename from packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeItem.java
rename to packages/CarSystemUI/src/com/android/systemui/car/volume/CarVolumeItem.java
index b83740f..1e7e534 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 @@
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
similarity index 94%
rename from packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeItemAdapter.java
rename to packages/CarSystemUI/src/com/android/systemui/car/volume/CarVolumeItemAdapter.java
index 5c1f817..7f336b5 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 0000000..2bdb85f
--- /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/navigationbar/car/CarNavigationBar.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
index 9fdfc0f..4e315c6 100644
--- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
@@ -51,8 +51,6 @@
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 @@
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 @@
ButtonSelectionStateListener buttonSelectionStateListener,
@Main Handler mainHandler,
Lazy<KeyguardStateController> keyguardStateControllerLazy,
- Lazy<NavigationBarController> navigationBarControllerLazy,
- SuperStatusBarViewFactory superStatusBarViewFactory,
ButtonSelectionStateController buttonSelectionStateController,
PhoneStatusBarPolicy iconPolicy,
StatusBarIconController iconController
@@ -142,8 +136,6 @@
mButtonSelectionStateListener = buttonSelectionStateListener;
mMainHandler = mainHandler;
mKeyguardStateControllerLazy = keyguardStateControllerLazy;
- mNavigationBarControllerLazy = navigationBarControllerLazy;
- mSuperStatusBarViewFactory = superStatusBarViewFactory;
mButtonSelectionStateController = buttonSelectionStateController;
mIconPolicy = iconPolicy;
mIconController = iconController;
@@ -319,11 +311,6 @@
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/navigationbar/car/CarNavigationBarController.java
index 37a8225..8f3ae1a 100644
--- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarController.java
@@ -24,7 +24,7 @@
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/window/OverlayViewGlobalStateController.java b/packages/CarSystemUI/src/com/android/systemui/window/OverlayViewGlobalStateController.java
index 402d742..5fe03f1 100644
--- a/packages/CarSystemUI/src/com/android/systemui/window/OverlayViewGlobalStateController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/window/OverlayViewGlobalStateController.java
@@ -123,6 +123,12 @@
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/SystemUIOverlayWindowController.java b/packages/CarSystemUI/src/com/android/systemui/window/SystemUIOverlayWindowController.java
index 04d69ea..5df5d6e 100644
--- a/packages/CarSystemUI/src/com/android/systemui/window/SystemUIOverlayWindowController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/window/SystemUIOverlayWindowController.java
@@ -132,6 +132,16 @@
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/tests/src/com/android/systemui/navigationbar/car/hvac/HvacControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/HvacControllerTest.java
similarity index 97%
rename from packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/hvac/HvacControllerTest.java
rename to packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/HvacControllerTest.java
index a71d1db..7996170 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/notification/CarHeadsUpNotificationSystemContainerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/notification/CarHeadsUpNotificationSystemContainerTest.java
index 05b8e6a..6ac72a6 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 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 @@
@Mock
private CarDeviceProvisionedController mCarDeviceProvisionedController;
@Mock
- private CarStatusBar mCarStatusBar;
+ private NotificationPanelViewController mNotificationPanelViewController;
@Mock
private WindowManager mWindowManager;
@@ -61,7 +60,7 @@
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 @@
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 @@
@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
similarity index 99%
rename from packages/CarSystemUI/tests/src/com/android/systemui/sideloaded/car/CarSideLoadedAppDetectorTest.java
rename to packages/CarSystemUI/tests/src/com/android/systemui/car/sideloaded/CarSideLoadedAppDetectorTest.java
index aebb0e0..80f3d1ee 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
similarity index 93%
rename from packages/CarSystemUI/tests/src/com/android/systemui/voicerecognition/car/ConnectedDeviceVoiceRecognitionNotifierTest.java
rename to packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java
index 38b47d0..eca51e3 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/navigationbar/car/CarNavigationBarControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarControllerTest.java
index bbcd0d4..28c69c7 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarControllerTest.java
@@ -31,7 +31,7 @@
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/navigationbar/car/CarNavigationBarTest.java
index 3ecb29f..f789d38 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarTest.java
@@ -37,8 +37,6 @@
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 @@
@Mock
private KeyguardStateController mKeyguardStateController;
@Mock
- private NavigationBarController mNavigationBarController;
- @Mock
- private SuperStatusBarViewFactory mSuperStatusBarViewFactory;
- @Mock
private ButtonSelectionStateController mButtonSelectionStateController;
@Mock
private PhoneStatusBarPolicy mIconPolicy;
@@ -91,9 +85,8 @@
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/CtsShim/build/Android.bp b/packages/CtsShim/build/Android.bp
index 54986a4..be79010 100644
--- a/packages/CtsShim/build/Android.bp
+++ b/packages/CtsShim/build/Android.bp
@@ -70,6 +70,7 @@
// 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 @@
},
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 @@
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 1007d83..7baaf49 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 653c8ad..a210e90 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.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 @@
}
});
- // 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/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index c713d78..d7e76a1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -133,6 +133,35 @@
}
}
+ /**
+ * Fetches initial state as if a WifiManager.NETWORK_STATE_CHANGED_ACTION have been received.
+ * This replaces the dependency on the initial sticky broadcast.
+ */
+ public void fetchInitialState() {
+ if (mWifiManager == null) {
+ return;
+ }
+ updateWifiState();
+ final NetworkInfo networkInfo =
+ mConnectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
+ connected = networkInfo != null && networkInfo.isConnected();
+ mWifiInfo = null;
+ ssid = null;
+ if (connected) {
+ mWifiInfo = mWifiManager.getConnectionInfo();
+ if (mWifiInfo != null) {
+ if (mWifiInfo.isPasspointAp() || mWifiInfo.isOsuAp()) {
+ ssid = mWifiInfo.getPasspointProviderFriendlyName();
+ } else {
+ ssid = getValidSsid(mWifiInfo);
+ }
+ updateRssi(mWifiInfo.getRssi());
+ maybeRequestNetworkScore();
+ }
+ }
+ updateStatusLabel();
+ }
+
public void handleBroadcast(Intent intent) {
if (mWifiManager == null) {
return;
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 342d127a..4b779ac 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 @@
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 @@
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 @@
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 0000000..a793680
--- /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 be3c598..f0dfd21 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 0000000..129282d
--- /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 ae57563..835e54e 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">
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textSize="@dimen/controls_title_size"
+ android:textAlignment="center" />
- <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" />
-
- </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 1c4ec64..66f57fd 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 d506e7e..db109fe 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 93aa270..2d51011 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 4482cda..8b6b5f6 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>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 599ed16..39aa771 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 cb20e7a..d639ed0 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 7e24f5d..4ed819e 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/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 367058f..a96ef91 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1301,6 +1301,7 @@
private FingerprintManager mFpm;
private FaceManager mFaceManager;
private boolean mFingerprintLockedOut;
+ private TelephonyManager mTelephonyManager;
/**
* When we receive a
@@ -1728,10 +1729,22 @@
}
updateAirplaneModeState();
- TelephonyManager telephony =
+ mTelephonyManager =
(TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
- if (telephony != null) {
- telephony.listen(mPhoneStateListener, LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE);
+ if (mTelephonyManager != null) {
+ mTelephonyManager.listen(mPhoneStateListener,
+ LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE);
+ // Set initial sim states values.
+ for (int slot = 0; slot < mTelephonyManager.getActiveModemCount(); slot++) {
+ int state = mTelephonyManager.getSimState(slot);
+ int[] subIds = mSubscriptionManager.getSubscriptionIds(slot);
+ if (subIds != null) {
+ for (int subId : subIds) {
+ mHandler.obtainMessage(MSG_SIM_STATE_CHANGE, subId, slot, state)
+ .sendToTarget();
+ }
+ }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
index 409ae3f..c92174a 100644
--- a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
+++ b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
@@ -164,7 +164,9 @@
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 7262f8c..1f27ae2 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 @@
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 71f2bc0..38bfffb 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 @@
}
private FlyoutMessage mFlyoutMessage;
+ private Drawable mBadgedAppIcon;
private Bitmap mBadgedImage;
private int mDotColor;
private Path mDotPath;
@@ -133,6 +134,10 @@
return mBadgedImage;
}
+ public Drawable getBadgedAppIcon() {
+ return mBadgedAppIcon;
+ }
+
@Override
public int getDotColor() {
return mDotColor;
@@ -239,6 +244,7 @@
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 c6883c8..e488cf2 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 @@
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 3524696..bb23655 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.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.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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
/**
- * 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 @@
* 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 @@
}
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 0aabdff..c906931 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.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.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.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.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.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.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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
mExpandedViewContainer.setClipChildren(false);
addView(mExpandedViewContainer);
+ setUpManageMenu();
+
setUpFlyout();
mFlyoutTransitionSpring.setSpring(new SpringForce()
.setStiffness(SpringForce.STIFFNESS_LOW)
@@ -838,7 +861,9 @@
// 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 @@
});
}
+ 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 @@
setUpFlyout();
setUpOverflow();
setUpUserEducation();
+ setUpManageMenu();
}
/** Respond to the phone being rotated by repositioning the stack and hiding any flyouts. */
@@ -960,6 +1046,9 @@
Math.max(0f, Math.min(1f, mVerticalPosPercentBeforeRotation));
addOnLayoutChangeListener(mOrientationChangedListener);
hideFlyoutImmediate();
+
+ mManageMenu.setVisibility(View.INVISIBLE);
+ mShowingManage = false;
}
@Override
@@ -1100,6 +1189,12 @@
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 @@
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 @@
}
private void animateCollapse() {
+ // Hide the menu if it's visible.
+ showManageMenu(false);
+
mIsExpanded = false;
final BubbleViewProvider previouslySelected = mExpandedBubble;
beforeExpandedViewAnimation();
@@ -1570,9 +1667,9 @@
*/
@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 @@
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 @@
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 @@
&& 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 @@
}
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 c96f9a4..8a57a73 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 @@
ShortcutInfo shortcutInfo;
String appName;
Bitmap badgedBubbleImage;
+ Drawable badgedAppIcon;
int dotColor;
Path dotPath;
Bubble.FlyoutMessage flyoutMessage;
@@ -176,6 +177,7 @@
}
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 ef84c73..ca3e2e2 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.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 0002e86..35406c7 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
@@ -128,15 +128,29 @@
*/
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, 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, @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) {
- mAnimatingCollapse = false;
- mAnimatingExpand = true;
- mAfterExpand = after;
-
- startOrUpdatePathAnimation(true /* expanding */);
+ expandFromStack(after, null /* leadBubbleEndAction */);
}
/** Animate collapsing the bubbles back to their stacked position. */
@@ -237,11 +251,17 @@
? (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 b1bbafc..a7d1be1 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 @@
* 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 7e8fec7..e84f439 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.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 @@
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 @@
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 @@
}
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 79a5b0a..bc97c10 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 @@
* 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 5626a5d..8e88756 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 @@
private var userChanging: Boolean = true
- private var loadCanceller: Runnable? = null
-
private var seedingInProgress = false
private val seedingCallbacks = mutableListOf<Consumer<Boolean>>()
@@ -276,28 +274,29 @@
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 @@
}
override fun error(message: String) {
- loadCanceller = null
executor.execute {
val controls = Favorites.getStructuresForComponent(componentName)
.flatMap { st ->
@@ -348,6 +346,7 @@
}
}
}
+ )
)
}
@@ -432,12 +431,6 @@
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 0000000..4ca47d1
--- /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 ee1ce7a..640c90d 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.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 @@
*/
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 @@
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 @@
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 @@
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 6f34dee..9a2ccb5 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.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.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 @@
@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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
}
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 @@
StructureInfo(component!!, it.structureName, favoritesForStorage)
)
}
- finishAffinity()
+
+ ControlsAnimations.exitAnimation(
+ rootView,
+ object : Runnable {
+ override fun run() {
+ finish()
+ }
+ }
+ ).start()
+ globalActionsComponent.handleShowGlobalActionsMenu()
}
}
}
@@ -302,15 +312,39 @@
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 3be5900..0044854 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 @@
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 @@
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 @@
* @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 @@
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 61a1a98..aed7cd3 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 @@
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 2adfb1b..cfd8df0 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 @@
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 @@
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 @@
}
}
- 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 @@
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 @@
}
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 88f96a8..8c572fe 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 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.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 @@
@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 2e9ce12..90cd13f 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.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 @@
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 3226605..a24fede 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 @@
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.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.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 @@
*/
@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 @@
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 @@
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 @@
private ControlsUiController mControlsUiController;
private ViewGroup mControlsView;
+ private ViewGroup mContainer;
ActionsDialog(Context context, MyAdapter adapter, MyOverflowAdapter overflowAdapter,
GlobalActionsPanelPlugin.PanelViewController plugin,
@@ -2046,6 +2051,11 @@
});
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 @@
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 @@
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();
- }
-
- void dismissImmediately() {
- mShowing = false;
- if (mControlsUiController != null) mControlsUiController.hide();
- dismissPanel();
- dismissOverflow();
- resetOrientation();
- completeDismiss();
+ animation.run();
}
private void completeDismiss() {
+ mShowing = false;
+ resetOrientation();
+ dismissPanel();
+ dismissOverflow();
+ if (mControlsUiController != null) mControlsUiController.hide();
mNotificationShadeWindowController.setForceHasTopUi(mHadTopUi);
mDepthController.updateGlobalDialogVisibility(0, null /* view */);
super.dismiss();
@@ -2304,7 +2319,7 @@
initializeLayout();
mGlobalActionsLayout.updateList();
if (mControlsUiController != null) {
- mControlsUiController.show(mControlsView);
+ mControlsUiController.show(mControlsView, this::dismissForControlsActivity);
}
}
@@ -2343,10 +2358,9 @@
&& 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 14e3e93..123cf78 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 @@
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 0000000..20fc6ff
--- /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 f25de6a..8dcf528 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.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.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 @@
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 @@
* 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 @@
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 @@
* @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 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 dba4343..f322489 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 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 @@
}
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 0125153..9eae3ca 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_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 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 @@
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 @@
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 @@
mSurfaceTransactionHelper = surfaceTransactionHelper;
mPipAnimationController = new PipAnimationController(context, surfaceTransactionHelper);
mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
+ mSplitDivider = divider;
}
public Handler getUpdateHandler() {
@@ -226,20 +231,21 @@
/**
* 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 @@
*/
@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 @@
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 @@
}
/**
+ * 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 ba9a30f..7897573 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 @@
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.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 @@
DeviceConfigProxy deviceConfig,
PipBoundsHandler pipBoundsHandler,
PipSnapAlgorithm pipSnapAlgorithm,
- PipSurfaceTransactionHelper surfaceTransactionHelper) {
+ PipSurfaceTransactionHelper surfaceTransactionHelper,
+ Divider divider) {
mContext = context;
mActivityManager = ActivityManager.getService();
@@ -214,7 +216,7 @@
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 @@
@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 3a2d786..6c5312d 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.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 @@
@Inject
public PipManager(Context context, BroadcastDispatcher broadcastDispatcher,
PipBoundsHandler pipBoundsHandler,
- PipSurfaceTransactionHelper surfaceTransactionHelper) {
+ PipSurfaceTransactionHelper surfaceTransactionHelper,
+ Divider divider) {
if (mInitialized) {
return;
}
@@ -249,7 +251,7 @@
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 0f06566..9e574a1 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.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 @@
}
// 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 @@
* 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 @@
* @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 1eb5778..1252008 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.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 @@
* 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 @@
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 7ba7c5f..89b36da 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 @@
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 @@
* 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 @@
* @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 @@
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 9cee7e7..1a6a104 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 @@
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 @@
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 66bc177..fecb7b6 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.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 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 e1cc0b0..70454d4 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.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.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 @@
*/
static class SaveImageInBackgroundData {
public Bitmap image;
- public Uri imageUri;
public Consumer<Uri> finisher;
public GlobalScreenshot.ActionsReadyListener mActionsReadyListener;
public int errorMsgResId;
@@ -112,13 +113,33 @@
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 @@
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 @@
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 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 @@
@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 @@
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 @@
mScreenBitmap.setHasAlpha(false);
mScreenBitmap.prepareToDraw();
+ updateDarkTheme();
+
mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
mScreenshotLayout.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
@@ -407,7 +440,7 @@
* 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 @@
}
/**
+ * 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 @@
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 @@
@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 @@
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 @@
} 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 f3614ff..095c32f 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.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 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 @@
// 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 c828c4c..170174d 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 @@
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 @@
SaveImageInBackgroundTask(Context context, GlobalScreenshot.SaveImageInBackgroundData data) {
mContext = context;
+ mImageData = new GlobalScreenshot.SavedImageData();
// Prepare all the output metadata
mParams = data;
@@ -145,6 +147,7 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
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 0000000..20fa991
--- /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 811a8d9..fbcd6ba 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.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.SystemUI;
import com.android.systemui.util.NotificationChannels;
-import java.util.List;
-
import javax.inject.Inject;
/**
@@ -185,23 +182,20 @@
/**
* 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 8b8b6f8..f68cb74 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.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 @@
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 @@
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 @@
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 @@
@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 @@
@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 4de978c..825a7f3 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 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 0000000..2c5c3ce
--- /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 a4b1310..f5d6cb6 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.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 @@
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 @@
* 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 5aa7946..3027bd2 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 @@
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 @@
// 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/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
index 1b752452..8c24c54 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.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 @@
? 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 afb5002..02c41e5 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.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 @@
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 @@
BubbleController bubbleController,
DynamicPrivacyController privacyController,
ForegroundServiceSectionController fgsSectionController,
- DynamicChildBindController dynamicChildBindController) {
+ DynamicChildBindController dynamicChildBindController,
+ LowPriorityInflationHelper lowPriorityInflationHelper) {
mContext = context;
mHandler = mainHandler;
mLockscreenUserManager = notificationLockscreenUserManager;
@@ -124,6 +127,7 @@
mBubbleController = bubbleController;
mDynamicPrivacyController = privacyController;
mDynamicChildBindController = dynamicChildBindController;
+ mLowPriorityInflationHelper = lowPriorityInflationHelper;
}
public void setUpWithPresenter(NotificationPresenter presenter,
@@ -177,6 +181,7 @@
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 e64b423..de7e36d9 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.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 @@
BubbleController bubbleController,
DynamicPrivacyController privacyController,
ForegroundServiceSectionController fgsSectionController,
- DynamicChildBindController dynamicChildBindController) {
+ DynamicChildBindController dynamicChildBindController,
+ LowPriorityInflationHelper lowPriorityInflationHelper) {
return new NotificationViewHierarchyManager(
context,
mainHandler,
@@ -156,7 +158,8 @@
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 4e6df0a..d364689 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 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 @@
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 @@
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 @@
mBubbleController.collapseStack();
}
- mNotificationActivityStarter.onNotificationClicked(sbn, row);
+ mNotificationActivityStarter.onNotificationClicked(entry.getSbn(), row);
}
private boolean isMenuVisible(ExpandableNotificationRow row) {
@@ -107,4 +115,30 @@
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 0000000..fbf033b
--- /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 12d190b..f1cb783 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 @@
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 0000000..73c0fdc
--- /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 e6a4cff..673aa39 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 @@
private final ExpandableNotificationRowComponent.Builder
mExpandableNotificationRowComponentBuilder;
private final IconManager mIconManager;
+ private final LowPriorityInflationHelper mLowPriorityInflationHelper;
private NotificationPresenter mPresenter;
private NotificationListContainer mListContainer;
@@ -81,7 +82,8 @@
NotificationInterruptStateProvider notificationInterruptionStateProvider,
Provider<RowInflaterTask> rowInflaterTaskProvider,
ExpandableNotificationRowComponent.Builder expandableNotificationRowComponentBuilder,
- IconManager iconManager) {
+ IconManager iconManager,
+ LowPriorityInflationHelper lowPriorityInflationHelper) {
mContext = context;
mNotifBindPipeline = notifBindPipeline;
mRowContentBindStage = rowContentBindStage;
@@ -92,6 +94,7 @@
mRowInflaterTaskProvider = rowInflaterTaskProvider;
mExpandableNotificationRowComponentBuilder = expandableNotificationRowComponentBuilder;
mIconManager = iconManager;
+ mLowPriorityInflationHelper = lowPriorityInflationHelper;
}
/**
@@ -225,11 +228,15 @@
@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 7f2f898..c975404 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 @@
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 @@
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 c613e64..ba72e28 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 @@
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 @@
}
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 9d54437..582e3e5 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 @@
mConversationProcessor,
row,
bindParams.isLowPriority,
- bindParams.isChildInGroup,
bindParams.usesIncreasedHeight,
bindParams.usesIncreasedHeadsUpHeight,
callback,
@@ -156,7 +155,6 @@
InflationProgress result = createRemoteViews(reInflateFlags,
builder,
bindParams.isLowPriority,
- bindParams.isChildInGroup,
bindParams.usesIncreasedHeight,
bindParams.usesIncreasedHeadsUpHeight,
packageContext);
@@ -285,11 +283,9 @@
}
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 @@
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 @@
ConversationNotificationProcessor conversationProcessor,
ExpandableNotificationRow row,
boolean isLowPriority,
- boolean isChildInGroup,
boolean usesIncreasedHeight,
boolean usesIncreasedHeadsUpHeight,
InflationCallback callback,
@@ -743,7 +737,6 @@
mRemoteViewCache = cache;
mContext = mRow.getContext();
mIsLowPriority = isLowPriority;
- mIsChildInGroup = isChildInGroup;
mUsesIncreasedHeight = usesIncreasedHeight;
mUsesIncreasedHeadsUpHeight = usesIncreasedHeadsUpHeight;
mRemoteViewClickHandler = remoteViewClickHandler;
@@ -781,7 +774,7 @@
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 9bd8d47..a9f83c8 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 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 d3fec69..f26ecc3 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 @@
*/
public final class RowContentBindParams {
private boolean mUseLowPriority;
- private boolean mUseChildInGroup;
private boolean mUseIncreasedHeight;
private boolean mUseIncreasedHeadsUpHeight;
private boolean mViewsNeedReinflation;
@@ -56,20 +55,6 @@
}
/**
- * 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 @@
@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 c632f3e..c6f0a13 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 @@
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 796f22c..b96cff8 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 @@
Drawable iconDrawable = notif.getSmallIcon().loadDrawable(mContext);
panel.getMediaPlayer().setMediaSession(token,
iconDrawable,
+ notif.getLargeIcon(),
tintColor,
mBackgroundColor,
mActions,
@@ -201,6 +202,7 @@
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 7d422e3..f9119c7 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 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 @@
/**
* 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 @@
private boolean mIsGesturalModeEnabled;
private boolean mIsEnabled;
private boolean mIsNavBarShownTransiently;
+ private boolean mIsBackGestureAllowed;
private InputMonitor mInputMonitor;
private InputEventReceiver mInputEventReceiver;
@@ -200,7 +203,7 @@
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 @@
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 @@
Settings.Global.getUriFor(FIXED_ROTATION_TRANSFORM_SETTING_NAME),
false /* notifyForDescendants */, mFixedRotationObserver, UserHandle.USER_ALL);
updateIsEnabled();
+ startTracking();
}
/**
@@ -255,6 +269,7 @@
}
mContext.getContentResolver().unregisterContentObserver(mFixedRotationObserver);
updateIsEnabled();
+ stopTracking();
}
private void setRotationCallbacks(boolean enable) {
@@ -269,10 +284,13 @@
}
}
- 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 @@
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 @@
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 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/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 90bc075b..ae7867d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -85,6 +85,8 @@
import com.android.systemui.tuner.LockscreenFragment.LockButtonFactory;
import com.android.systemui.tuner.TunerService;
+import java.util.concurrent.Executor;
+
/**
* Implementation for the bottom area of the Keyguard, including camera/phone affordance and status
* text.
@@ -553,7 +555,7 @@
}
};
if (!mKeyguardStateController.canDismissLockScreen()) {
- AsyncTask.execute(runnable);
+ Dependency.get(Executor.class).execute(runnable);
} else {
boolean dismissShade = !TextUtils.isEmpty(mRightButtonStr)
&& Dependency.get(TunerService.class).getValue(LOCKSCREEN_RIGHT_UNLOCK, 1) != 0;
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 d35e1e1..3e5eb5f 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.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 @@
private AppearanceRegion[] mAppearanceRegions = new AppearanceRegion[0];
private int mStatusBarMode;
private int mNavigationBarMode;
+ private int mNavigationMode;
private final Color mDarkModeColor;
/**
@@ -84,11 +86,14 @@
@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 @@
}
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 84aecd4..2978772 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
}
- 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 @@
@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 @@
@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 d24ccf3..6061b1e 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.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 @@
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 @@
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 @@
}
}
- 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 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 @@
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 1a6b415..bf52a7a 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 @@
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 53fa263..fbe3e9b 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.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 @@
*/
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 Lazy<AssistManager> mAssistManagerLazy;
- private final NotificationGroupManager mGroupManager;
- private final StatusBarRemoteInputCallback mStatusBarRemoteInputCallback;
- private final NotificationRemoteInputManager mRemoteInputManager;
- private final NotificationLockscreenUserManager mLockscreenUserManager;
- private final ShadeController mShadeController;
- private final StatusBar mStatusBar;
- private final KeyguardStateController mKeyguardStateController;
- private final ActivityStarter mActivityStarter;
+ 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 FeatureFlags mFeatureFlags;
- private final StatusBarStateController mStatusBarStateController;
- private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
- private final MetricsLogger mMetricsLogger;
- private final Context mContext;
- private final NotificationPanelViewController mNotificationPanel;
- private final NotificationPresenter mPresenter;
- private final LockPatternUtils mLockPatternUtils;
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 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 final Lazy<AssistManager> mAssistManagerLazy;
+ private final NotificationRemoteInputManager mRemoteInputManager;
+ private final NotificationGroupManager mGroupManager;
+ private final NotificationLockscreenUserManager mLockscreenUserManager;
+ private final ShadeController mShadeController;
+ private final KeyguardStateController mKeyguardStateController;
+ 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 StatusBarNotificationActivityStarterLogger mLogger;
+
+ private final StatusBar mStatusBar;
+ private final NotificationPresenter mPresenter;
+ private final NotificationPanelViewController mNotificationPanel;
+ private final ActivityLaunchAnimator mActivityLaunchAnimator;
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;
- mHeadsUpManager = headsUpManager;
- mActivityLaunchAnimator = activityLaunchAnimator;
- mBarService = statusBarService;
mCommandQueue = commandQueue;
+ mMainThreadHandler = mainThreadHandler;
+ mBackgroundHandler = backgroundHandler;
+ mUiBgExecutor = uiBgExecutor;
+ mEntryManager = entryManager;
+ mNotifPipeline = notifPipeline;
+ mNotifCollection = notifCollection;
+ mHeadsUpManager = headsUpManager;
+ mActivityStarter = activityStarter;
+ mBarService = statusBarService;
+ mStatusBarStateController = statusBarStateController;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mKeyguardManager = keyguardManager;
mDreamManager = dreamManager;
+ mBubbleController = bubbleController;
+ mAssistManagerLazy = assistManagerLazy;
mRemoteInputManager = remoteInputManager;
+ mGroupManager = groupManager;
mLockscreenUserManager = lockscreenUserManager;
mShadeController = shadeController;
+ mKeyguardStateController = keyguardStateController;
+ mNotificationInterruptStateProvider = notificationInterruptStateProvider;
+ mLockPatternUtils = lockPatternUtils;
+ mStatusBarRemoteInputCallback = remoteInputCallback;
+ mActivityIntentHelper = activityIntentHelper;
+
+ mFeatureFlags = featureFlags;
+ mMetricsLogger = metricsLogger;
+ mLogger = logger;
+
// 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;
- mFeatureFlags = featureFlags;
- mNotifPipeline = notifPipeline;
- mNotifCollection = notifCollection;
+ mPresenter = presenter;
+ mNotificationPanel = panel;
+ mActivityLaunchAnimator = activityLaunchAnimator;
+
if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
@Override
@@ -192,11 +218,6 @@
}
});
}
-
- mStatusBarRemoteInputCallback = remoteInputCallback;
- mMainThreadHandler = mainThreadHandler;
- mActivityIntentHelper = activityIntentHelper;
- mBubbleController = bubbleController;
}
/**
@@ -207,6 +228,8 @@
*/
@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 @@
// 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 @@
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 @@
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 @@
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 @@
}
}
- 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 @@
} 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 @@
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 @@
});
// 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 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 @@
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 KeyguardStateController mKeyguardStateController;
- private final MetricsLogger mMetricsLogger;
- private final LockPatternUtils mLockPatternUtils;
- private final Handler mMainThreadHandler;
- private final Handler mBackgroundHandler;
- private final Executor mUiBgExecutor;
- private final ActivityIntentHelper mActivityIntentHelper;
- private final BubbleController mBubbleController;
- private NotificationPanelViewController mNotificationPanelViewController;
- private NotificationInterruptStateProvider mNotificationInterruptStateProvider;
private final ShadeController mShadeController;
- private NotificationPresenter mNotificationPresenter;
- private ActivityLaunchAnimator mActivityLaunchAnimator;
+ private final KeyguardStateController mKeyguardStateController;
+ private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
+ private final LockPatternUtils mLockPatternUtils;
+ private final StatusBarRemoteInputCallback mRemoteInputCallback;
+ private final ActivityIntentHelper mActivityIntentHelper;
+
+ private final FeatureFlags mFeatureFlags;
+ private final MetricsLogger mMetricsLogger;
+ private final StatusBarNotificationActivityStarterLogger mLogger;
+
private StatusBar mStatusBar;
+ private NotificationPresenter mNotificationPresenter;
+ private NotificationPanelViewController mNotificationPanelViewController;
+ private ActivityLaunchAnimator mActivityLaunchAnimator;
@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 @@
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 @@
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 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 0000000..d118747
--- /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/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 051bd29..a284335 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -357,7 +357,18 @@
mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mReceiverHandler);
mListening = true;
+ // Initial setup of connectivity. Handled as if we had received a sticky broadcast of
+ // ConnectivityManager.CONNECTIVITY_ACTION or ConnectivityManager.INET_CONDITION_ACTION.
+ mReceiverHandler.post(this::updateConnectivity);
+
+ // Initial setup of WifiSignalController. Handled as if we had received a sticky broadcast
+ // of WifiManager.WIFI_STATE_CHANGED_ACTION or WifiManager.NETWORK_STATE_CHANGED_ACTION
+ mReceiverHandler.post(mWifiSignalController::fetchInitialState);
updateMobileControllers();
+
+ // Initial setup of emergency information. Handled as if we had received a sticky broadcast
+ // of TelephonyManager.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED.
+ mReceiverHandler.post(this::recalculateEmergency);
}
private void unregisterListeners() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index b258fd4..5257ce4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -102,6 +102,20 @@
}
/**
+ * Fetches wifi initial state replacing the initial sticky broadcast.
+ */
+ public void fetchInitialState() {
+ mWifiTracker.fetchInitialState();
+ mCurrentState.enabled = mWifiTracker.enabled;
+ mCurrentState.connected = mWifiTracker.connected;
+ mCurrentState.ssid = mWifiTracker.ssid;
+ mCurrentState.rssi = mWifiTracker.rssi;
+ mCurrentState.level = mWifiTracker.level;
+ mCurrentState.statusLabel = mWifiTracker.statusLabel;
+ notifyListenersIfNecessary();
+ }
+
+ /**
* Extract wifi state directly from broadcasts about changes in wifi state.
*/
public void handleBroadcast(Intent intent) {
diff --git a/packages/SystemUI/src/com/android/systemui/util/RelativeTouchListener.kt b/packages/SystemUI/src/com/android/systemui/util/RelativeTouchListener.kt
index d65b285..8880df9 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 @@
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 8625d63..db08d64 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 @@
/**
* 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 @@
/** 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 @@
}
/**
+ * 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 @@
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 @@
) {
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 @@
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 cc6d607..8acfbf2 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 @@
}
/**
+ * 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 0000000..aefdc99
--- /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 0000000..c03e10e
--- /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/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 7403a11..6c00eca 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -61,6 +61,7 @@
import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableContext;
@@ -133,20 +134,23 @@
@Mock
private BroadcastDispatcher mBroadcastDispatcher;
@Mock
- private Executor mBackgroundExecutor;
- @Mock
private RingerModeTracker mRingerModeTracker;
@Mock
private LiveData<Integer> mRingerModeLiveData;
+ @Mock
+ private TelephonyManager mTelephonyManager;
+ // Direct executor
+ private Executor mBackgroundExecutor = Runnable::run;
private TestableLooper mTestableLooper;
private TestableKeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private TestableContext mSpiedContext;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- TestableContext context = spy(mContext);
+ mSpiedContext = spy(mContext);
when(mPackageManager.hasSystemFeature(anyString())).thenReturn(true);
- when(context.getPackageManager()).thenReturn(mPackageManager);
+ when(mSpiedContext.getPackageManager()).thenReturn(mPackageManager);
doAnswer(invocation -> {
IBiometricEnabledOnKeyguardCallback callback = invocation.getArgument(0);
callback.onChanged(BiometricSourceType.FACE, true /* enabled */,
@@ -161,19 +165,20 @@
when(mStrongAuthTracker
.isUnlockingWithBiometricAllowed(anyBoolean() /* isStrongBiometric */))
.thenReturn(true);
- context.addMockSystemService(TrustManager.class, mTrustManager);
- context.addMockSystemService(FingerprintManager.class, mFingerprintManager);
- context.addMockSystemService(BiometricManager.class, mBiometricManager);
- context.addMockSystemService(FaceManager.class, mFaceManager);
- context.addMockSystemService(UserManager.class, mUserManager);
- context.addMockSystemService(DevicePolicyManager.class, mDevicePolicyManager);
- context.addMockSystemService(SubscriptionManager.class, mSubscriptionManager);
+ mSpiedContext.addMockSystemService(TrustManager.class, mTrustManager);
+ mSpiedContext.addMockSystemService(FingerprintManager.class, mFingerprintManager);
+ mSpiedContext.addMockSystemService(BiometricManager.class, mBiometricManager);
+ mSpiedContext.addMockSystemService(FaceManager.class, mFaceManager);
+ mSpiedContext.addMockSystemService(UserManager.class, mUserManager);
+ mSpiedContext.addMockSystemService(DevicePolicyManager.class, mDevicePolicyManager);
+ mSpiedContext.addMockSystemService(SubscriptionManager.class, mSubscriptionManager);
+ mSpiedContext.addMockSystemService(TelephonyManager.class, mTelephonyManager);
when(mRingerModeTracker.getRingerMode()).thenReturn(mRingerModeLiveData);
mTestableLooper = TestableLooper.get(this);
allowTestableLooperAsMainThread();
- mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(context);
+ mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mSpiedContext);
}
@After
@@ -192,6 +197,22 @@
}
@Test
+ public void testSimStateInitialized() {
+ final int subId = 3;
+ final int state = TelephonyManager.SIM_STATE_ABSENT;
+
+ when(mTelephonyManager.getActiveModemCount()).thenReturn(1);
+ when(mTelephonyManager.getSimState(anyInt())).thenReturn(state);
+ when(mSubscriptionManager.getSubscriptionIds(anyInt())).thenReturn(new int[] { subId });
+
+ KeyguardUpdateMonitor testKUM = new TestableKeyguardUpdateMonitor(mSpiedContext);
+
+ mTestableLooper.processAllMessages();
+
+ assertThat(testKUM.getSimState(subId)).isEqualTo(state);
+ }
+
+ @Test
public void testIgnoresSimStateCallback_rebroadcast() {
Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 9e18e61..3ef693a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -85,6 +85,7 @@
import com.android.systemui.statusbar.phone.LockscreenLockIconController;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -183,6 +184,8 @@
private DumpManager mDumpManager;
@Mock
private LockscreenLockIconController mLockIconController;
+ @Mock
+ private NotificationShadeWindowView mNotificationShadeWindowView;
private SuperStatusBarViewFactory mSuperStatusBarViewFactory;
private BubbleData mBubbleData;
@@ -219,8 +222,7 @@
mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
mConfigurationController, mKeyguardBypassController, mColorExtractor,
mDumpManager);
- mNotificationShadeWindowController.setNotificationShadeView(
- mSuperStatusBarViewFactory.getNotificationShadeWindowView());
+ mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
mNotificationShadeWindowController.attach();
// Need notifications for bubbles
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
index 49236e0..8e6fc8a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
@@ -79,6 +79,7 @@
import com.android.systemui.statusbar.phone.LockscreenLockIconController;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -134,6 +135,8 @@
private KeyguardBypassController mKeyguardBypassController;
@Mock
private FloatingContentCoordinator mFloatingContentCoordinator;
+ @Mock
+ private NotificationShadeWindowView mNotificationShadeWindowView;
private SysUiState mSysUiState = new SysUiState();
@@ -206,8 +209,7 @@
mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
mConfigurationController, mKeyguardBypassController, mColorExtractor,
mDumpManager);
- mNotificationShadeWindowController.setNotificationShadeView(
- mSuperStatusBarViewFactory.getNotificationShadeWindowView());
+ mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
mNotificationShadeWindowController.attach();
// Need notifications for bubbles
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 f6ee46b..e3f25c6 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 @@
@Captor
private lateinit var controlLoadCallbackCaptor:
ArgumentCaptor<ControlsBindingController.LoadCallback>
+
@Captor
private lateinit var broadcastReceiverCaptor: ArgumentCaptor<BroadcastReceiver>
@Captor
@@ -97,6 +98,7 @@
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 @@
}
}
+ canceller = DidRunRunnable()
+ `when`(bindingController.bindAndLoad(any(), any())).thenReturn(canceller)
+
controller = ControlsControllerImpl(
wrapper,
delayableExecutor,
@@ -266,7 +271,7 @@
assertTrue(favorites.isEmpty())
assertFalse(data.errorOnLoad)
- })
+ }, Consumer {})
verify(bindingController).bindAndLoad(eq(TEST_COMPONENT),
capture(controlLoadCallbackCaptor))
@@ -301,7 +306,7 @@
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 @@
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 @@
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 @@
@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 @@
}
@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 @@
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 @@
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 e8e98b4..4ac5912d 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 @@
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 11649ca..ffe3cd5 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 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 @@
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 @@
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 @@
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 @@
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 @@
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));
+ 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());
+ }
- 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());
- }
+ // 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/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index e55ea41..d41b6cf 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.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 @@
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 855f524..2894abb8 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.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 @@
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 96a58e2..ad3bd71 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 @@
}
@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/KeyguardBottomAreaTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt
index 2b091f2..210744e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt
@@ -6,11 +6,18 @@
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.assist.AssistManager
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.policy.AccessibilityController
+import com.android.systemui.statusbar.policy.FlashlightController
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.tuner.TunerService
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.MockitoAnnotations
+import java.util.concurrent.Executor
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -24,6 +31,15 @@
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
+ // Mocked dependencies
+ mDependency.injectMockDependency(AccessibilityController::class.java)
+ mDependency.injectMockDependency(ActivityStarter::class.java)
+ mDependency.injectMockDependency(AssistManager::class.java)
+ mDependency.injectTestDependency(Executor::class.java, Executor { it.run() })
+ mDependency.injectMockDependency(FlashlightController::class.java)
+ mDependency.injectMockDependency(KeyguardStateController::class.java)
+ mDependency.injectMockDependency(TunerService::class.java)
+
mKeyguardBottomArea = LayoutInflater.from(mContext).inflate(
R.layout.keyguard_bottom_area, null, false) as KeyguardBottomAreaView
mKeyguardBottomArea.setStatusBar(mStatusBar)
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 8ab660c..3e46907 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 @@
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 27a5002..14c6e9f9 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 @@
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 @@
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 dd28687..1afe132 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 @@
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/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index 962d773..aef454f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -32,6 +32,7 @@
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Instrumentation;
@@ -222,7 +223,7 @@
ArgumentCaptor<ConnectivityManager.NetworkCallback> callbackArg =
ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
- Mockito.verify(mMockCm, atLeastOnce())
+ verify(mMockCm, atLeastOnce())
.registerDefaultNetworkCallback(callbackArg.capture(), isA(Handler.class));
mNetworkCallback = callbackArg.getValue();
assertNotNull(mNetworkCallback);
@@ -384,7 +385,7 @@
}
protected void verifyHasNoSims(boolean hasNoSimsVisible) {
- Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setNoSims(
+ verify(mCallbackHandler, Mockito.atLeastOnce()).setNoSims(
eq(hasNoSimsVisible), eq(false));
}
@@ -395,7 +396,7 @@
ArgumentCaptor<Boolean> dataInArg = ArgumentCaptor.forClass(Boolean.class);
ArgumentCaptor<Boolean> dataOutArg = ArgumentCaptor.forClass(Boolean.class);
- Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setMobileDataIndicators(
+ verify(mCallbackHandler, Mockito.atLeastOnce()).setMobileDataIndicators(
any(),
iconArg.capture(),
anyInt(),
@@ -429,7 +430,7 @@
ArgumentCaptor<Integer> typeIconArg = ArgumentCaptor.forClass(Integer.class);
// TODO: Verify all fields.
- Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setMobileDataIndicators(
+ verify(mCallbackHandler, Mockito.atLeastOnce()).setMobileDataIndicators(
iconArg.capture(),
any(),
typeIconArg.capture(),
@@ -475,7 +476,7 @@
ArgumentCaptor<CharSequence> typeContentDescriptionHtmlArg =
ArgumentCaptor.forClass(CharSequence.class);
- Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setMobileDataIndicators(
+ verify(mCallbackHandler, Mockito.atLeastOnce()).setMobileDataIndicators(
iconArg.capture(),
qsIconArg.capture(),
typeIconArg.capture(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
index 9c250c5..988e022 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
@@ -2,12 +2,14 @@
import static junit.framework.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.content.Intent;
+import android.net.ConnectivityManager;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.wifi.WifiInfo;
@@ -171,6 +173,32 @@
verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[1][testLevel]);
}
+ @Test
+ public void testFetchInitialData() {
+ mNetworkController.mWifiSignalController.fetchInitialState();
+ Mockito.verify(mMockWm).getWifiState();
+ Mockito.verify(mMockCm).getNetworkInfo(ConnectivityManager.TYPE_WIFI);
+ }
+
+ @Test
+ public void testFetchInitialData_correctValues() {
+ String testSsid = "TEST";
+
+ when(mMockWm.getWifiState()).thenReturn(WifiManager.WIFI_STATE_ENABLED);
+ NetworkInfo networkInfo = mock(NetworkInfo.class);
+ when(networkInfo.isConnected()).thenReturn(true);
+ when(mMockCm.getNetworkInfo(ConnectivityManager.TYPE_WIFI)).thenReturn(networkInfo);
+ WifiInfo wifiInfo = mock(WifiInfo.class);
+ when(wifiInfo.getSSID()).thenReturn(testSsid);
+ when(mMockWm.getConnectionInfo()).thenReturn(wifiInfo);
+
+ mNetworkController.mWifiSignalController.fetchInitialState();
+
+ assertTrue(mNetworkController.mWifiSignalController.mCurrentState.enabled);
+ assertTrue(mNetworkController.mWifiSignalController.mCurrentState.connected);
+ assertEquals(testSsid, mNetworkController.mWifiSignalController.mCurrentState.ssid);
+ }
+
protected void setWifiActivity(int activity) {
// TODO: Not this, because this variable probably isn't sticking around.
mNetworkController.mWifiSignalController.setActivity(activity);
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 0000000..00f37ae
--- /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 1258805..056168b 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 9c36192..ac468dd 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 9f84ce4..7d5bad3 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 8855822..0913504 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 eba50eb..dce70da 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 5b0e488..b0774ec 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 5966c71..a8acebe 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 ed58d73..94fb2d8 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 8d9880a..aea02b9 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 2361b9d..de23272 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 6752b51..88b795c 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 5fdd53a..8c1b83b 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 2775dfa..f413e70 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 9046cd5..f057d78 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 3b9f537..b3c986b 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 56b88a5..769e0120 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 56b88a5..769e0120 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 56b88a5..769e0120 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 56b88a5..769e0120 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 7f47fc8..f1674be 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 e4618b8..63689f4 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 8dc1575..9a34ed5 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 872c8a7..0970341 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 6c4605e..632019e 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 bc2ee23..2e21c85 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 ff0fca6..413db3f 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 1f5df0e..eb2e4ba 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 daf7c9d..22259c5 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 0d16a1d..ded82fc 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 9d6b02f..7cbbc2d 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 9c29d9a..08af81b 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 d0d25bb..827c135 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 3129659..eb68d6b 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 8ba6435..912941e 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 1e093ab..a4e175a 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 f5769d5..e9f6670 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 e0b3724..ffb9196 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 c002c44..7adcb47 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 314bde0..f68a730 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 7bbd81d..7c22e82 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 7fd87a1..0857d06 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 2f85224..536e3d1 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 f11a83ea..32f5492 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 57f24f5..156b247 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 7985485..18ee5fd 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 78f1585..b127670 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 ebff8ac..8427baf 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 54d0048..aa2d699 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 0000000..19d659c
--- /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 0000000..8995430
--- /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 0000000..54f3b53
--- /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 0000000..e215141c
--- /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 0000000..1fd8e4c
--- /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 0000000..1abe4f3
--- /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 0000000..38dbd1e
--- /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 0000000..04b44db
--- /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 0000000..579d1be
--- /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 0000000..9ce3efe
--- /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 0000000..46d4c35
--- /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 0000000..cc13860
--- /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 0000000..92c3ae1
--- /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 0000000..967eb4d
--- /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 0000000..5fb4974
--- /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 0000000..45647f9
--- /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 0000000..45647f9
--- /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 0000000..45647f9
--- /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 0000000..45647f9
--- /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 0000000..7877074
--- /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 0000000..08edd81
--- /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 0000000..79f51d0
--- /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 0000000..2da5f8a
--- /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 0000000..2073f28
--- /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 0000000..e21b2a0
--- /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 0000000..88b0b13
--- /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 0000000..3b781bc
--- /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 0000000..51d7203
--- /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 0000000..008ccb4
--- /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 0000000..f2e3b4d
--- /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 0000000..b11839d
--- /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 0000000..0a5aca2
--- /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 0000000..21c689a4
--- /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 0000000..689d9287
--- /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 0000000..a5f4d19
--- /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 0000000..fc7e8aa
--- /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 0000000..6456dd1
--- /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 0000000..46b24bd
--- /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 0000000..e6eb277
--- /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 0000000..aeddd71
--- /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 0000000..255f0a2
--- /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 0000000..2bceb1c
--- /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 0000000..ed76930
--- /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 0000000..6e50494
--- /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 0000000..d68128b
--- /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 0000000..03e134a
--- /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 0000000..652cedc
--- /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 0000000..2219722
--- /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 0000000..227f9e3
--- /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 0000000..ec43885
--- /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 0000000..e263573
--- /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 0000000..adf845d
--- /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 0000000..f65c451
--- /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 0000000..4118e77
--- /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 0000000..3685358
--- /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 0000000..2a73300
--- /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 0000000..1d88894
--- /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 0000000..8038815
--- /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 0000000..819833e
--- /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 0000000..65e4380
--- /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 0000000..d886617
--- /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 0000000..bfd45ca
--- /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 0000000..d886617
--- /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 0000000..8d87a9e5
--- /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 0000000..dbdb9eb
--- /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 0000000..d8301e4
--- /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 0000000..bef7136
--- /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 0000000..3202c62
--- /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 0000000..37f6ad2
--- /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 0000000..5566d03
--- /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 0000000..9765acd
--- /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 0000000..cf850c9
--- /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 0000000..ea04821
--- /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 0000000..937d34d
--- /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 0000000..f781fae
--- /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 0000000..8d5d4653
--- /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 0000000..80cab33
--- /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 0000000..c05932a
--- /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 0000000..d820eee
--- /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 0000000..726148a
--- /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 0000000..b7cb045
--- /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 0000000..af91aff
--- /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 0000000..28e6b80
--- /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 0000000..05b90692
--- /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 0000000..11eb666
--- /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 9dadd49..ce9ff60 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 618df90..0000000
--- 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 0000000..9bfa531
--- /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 0000000..5949dfa
--- /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 0000000..8467f9b
--- /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 0000000..9776bd8
--- /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 0000000..e6d3eaf
--- /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 0000000..4c8a1df
--- /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 0000000..edfa41e
--- /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 0000000..f563981
--- /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 0000000..d8ecd2e
--- /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 0000000..b85fd5e
--- /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 0000000..a357215
--- /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 0000000..91196be
--- /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 0000000..1968900
--- /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 0000000..eb3f8c5
--- /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 0000000..56c3d81
--- /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 0000000..dd1a197
--- /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 0000000..dd1a197
--- /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 0000000..dd1a197
--- /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 0000000..dd1a197
--- /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 0000000..d3347aa
--- /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 0000000..2f0504f
--- /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 0000000..2d8f882
--- /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 0000000..8493c470
--- /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 0000000..33bccab
--- /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 0000000..cf8a0cc
--- /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 0000000..6a3ab80
--- /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 0000000..ffb9bf60
--- /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 0000000..768bce3
--- /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 0000000..0c4195a
--- /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 0000000..e9d33a7
--- /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 0000000..aa418ac
--- /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 0000000..51c524a
--- /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 0000000..164e45e
--- /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 0000000..e76c0a4
--- /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 0000000..2b817f8
--- /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 0000000..a338d9c
--- /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 0000000..77769c2
--- /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 0000000..5267b51
--- /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 0000000..66a9a6d
--- /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 0000000..d8ad880
--- /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 0000000..1ddd6b4
--- /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 0000000..cf5a137
--- /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 0000000..68ae68b
--- /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 0000000..17185ba
--- /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 0000000..6a9fb98
--- /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 0000000..bcc4b57
--- /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 0000000..011c2c1
--- /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 0000000..5cb2f3b
--- /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 0000000..4cbfd88
--- /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 0000000..9cf4eaf
--- /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 0000000..47c82c1
--- /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 0000000..ad9e809
--- /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 0000000..e708cb8
--- /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 0000000..ba54622
--- /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 0000000..57db484a2
--- /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 0000000..617c50d
--- /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 0000000..b08133f
--- /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 0000000..1ad4ca3
--- /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 0000000..88def56
--- /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 0000000..f9890ab
--- /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 0000000..ce3b884
--- /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 0000000..7e883ea
--- /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 0000000..ce3b884
--- /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 0000000..1009417
--- /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 0000000..88683be
--- /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 0000000..176bcdb
--- /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 0000000..b9e2127
--- /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 0000000..e8140e6
--- /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 0000000..61e698d
--- /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 0000000..b4c411c
--- /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 0000000..4f543e4
--- /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 0000000..ac347ab
--- /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 0000000..0e43759
--- /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 0000000..9360297
--- /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 0000000..9c4d7e0
--- /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 0000000..a7c78a5
--- /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 0000000..93da2c3
--- /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 0000000..ee0dcd2
--- /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 0000000..41cd28e
--- /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 0000000..c847bc9
--- /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 0000000..a74326f
--- /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 0000000..d737003
--- /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 0000000..f378a9d
--- /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 0000000..ea01b94
--- /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 0000000..32f6df5
--- /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 9dadd49..ce9ff60 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 618df90..0000000
--- 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-mcc312-mnc530/strings.xml b/packages/Tethering/res/values-mcc312-mnc530/strings.xml
deleted file mode 100644
index 618df90..0000000
--- a/packages/Tethering/res/values-mcc312-mnc530/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 0fab8aa..9ad9b9a 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 fd7e556..9db79ce 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 4596577..42d1edb 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 85c9ade..13995b6 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 ec6bdbd..d6a67f3 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 83978b6..49f6b88 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 9abf32d..9594e0a 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 c886929..72ae3a8 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 0ec4bff..18b2bbf 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 4576857..a15a6db 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 deddf2e..a8235e4 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 48d8468..ccb017d 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 32c22b8..a0a4745 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 641e22f..e3f03fc 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 32c22b8..a0a4745 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 f861f73..5706a4a 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 027cb41..7cb6f7d 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 7d8599f..ec34c22 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 a8fe297..43e787c 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 b5e5e38..5943362 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 fdd4906..21e1155 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 9fab34589..e2e4dc6 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 10eeb0f..72702c2 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 3353963..65e4aa8 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 b1e5cc2..4aba62d 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 aae40de..1f91791 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 1b80056..44171c0 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 12863f9..7347dd3 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 bfcf1ac..32030f1 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 8e159c0..1ca89b3 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 89195d4..d72c7d4 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 0ac4d4a..af3b2eb 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 85a4db8..21a0735 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 ff1fe03..98e3b4b 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 0de39fa..9cafd42 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 9a117bb..9d738a7 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 8fe10d8..f210f87 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 66fbefca..83c99d2 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 bbba3f3..16ae8ad 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 4fa60d4..d63c7c5 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 85a23fb..55344fc 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 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/TetheringNotificationUpdater.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java
index f490cc4..7fd6b61 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.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 @@
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 @@
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
- ENABLE_NOTIFICATION_ID,
RESTRICTED_NOTIFICATION_ID,
NO_UPSTREAM_NOTIFICATION_ID,
ROAMING_NOTIFICATION_ID
@@ -223,7 +214,6 @@
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 @@
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;
@@ -310,64 +291,6 @@
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;
- }
-
private boolean setupRoamingNotification() {
final Resources res = getResourcesForSubId(mContext, mActiveDataSubId);
final boolean upstreamRoamingNotification =
@@ -403,29 +326,6 @@
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) {
diff --git a/packages/Tethering/tests/integration/Android.bp b/packages/Tethering/tests/integration/Android.bp
index 6b751af..3305ed0 100644
--- a/packages/Tethering/tests/integration/Android.bp
+++ b/packages/Tethering/tests/integration/Android.bp
@@ -69,6 +69,7 @@
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 26517ce..08cfb30 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 @@
],
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
similarity index 100%
rename from packages/Tethering/tests/unit/src/android/net/TetheredClientTest.kt
rename to packages/Tethering/tests/unit/common/android/net/TetheredClientTest.kt
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 04f31a7..745468f 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.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.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.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 @@
@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 @@
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 @@
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 @@
.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 @@
}
@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 @@
}
@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 @@
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 @@
}
@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 @@
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/overlays/IconPackCircularLauncherOverlay/res/drawable/ic_corp.xml b/packages/overlays/IconPackCircularLauncherOverlay/res/drawable/ic_corp.xml
index b000146..a05a389 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 0000000..a810251
--- /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 0000000..e8608a5
--- /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 0000000..4e265fd
--- /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 0000000..726d1aa
--- /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 7139313..0dfaf81 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 0000000..b3f353a
--- /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 0000000..1d291c9
--- /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 0000000..df4525d
--- /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 0000000..89ee527
--- /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 38f515f..be31fb9 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 0000000..8d298f7
--- /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/overlays/IconPackRoundedLauncherOverlay/res/drawable/ic_screenshot.xml b/packages/overlays/IconPackRoundedLauncherOverlay/res/drawable/ic_screenshot.xml
new file mode 100644
index 0000000..ed90b85
--- /dev/null
+++ b/packages/overlays/IconPackRoundedLauncherOverlay/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="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 0000000..b699a44
--- /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 0000000..36dd3ba
--- /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/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 132b692..da9bdf3 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 @@
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 b9e3050..a1fc3fa 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 @@
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 b27c5d5..35089d6 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.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 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 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 @@
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 @@
}
@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/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 63b56e0..81de29c 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -983,4 +983,9 @@
* Returns if a package name is a valid system package.
*/
public abstract boolean isSystemPackage(@NonNull String packageName);
+
+ /**
+ * Unblocks uninstall for all packages for the user.
+ */
+ public abstract void clearBlockUninstallForUser(@UserIdInt int userId);
}
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index ce65110..4009caf 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -603,8 +603,7 @@
}
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 2bbf278..97a5cfe 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1146,7 +1146,7 @@
&& 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 @@
// 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 58c388e..b09d741 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -122,7 +122,6 @@
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 @@
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 @@
}
};
- 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 @@
}
@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 @@
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 @@
@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 @@
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 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 @@
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 21760cd..419389f 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.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 @@
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 @@
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 @@
}
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 @@
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 @@
// Cleanup.
if (newService) {
- app.services.remove(r);
+ app.stopService(r);
r.setProcess(null);
}
@@ -3362,7 +3361,7 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
}
if (!allowRestart) {
- app.services.clear();
+ app.stopAllServices();
app.clearBoundClientUids();
// Make sure there are no more restarting services for this process.
@@ -4920,8 +4917,8 @@
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 3fdf541..5d7590d 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 @@
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 @@
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 b1fc029..50d2cab 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 @@
}
// 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 dbad562..b647818 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 @@
@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 2d6ef81..ad85853 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 @@
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 @@
}
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/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 61ebc36..a1ec07c 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 @@
// 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 @@
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 @@
}
}
+ /**
+ * 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 @@
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 @@
}
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/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index c2c79d3..032ad63 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.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 @@
}
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 c17ed3e..3e97a1e 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.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 @@
// 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 @@
+ " 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 @@
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 @@
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 @@
}
@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 @@
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 @@
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 @@
+ address + " codec=" + a2dpCodec).printLog(TAG));
}
}
+ mmi.record();
}
/*package*/ void onMakeA2dpDeviceUnavailableNow(String address, int a2dpCodec) {
@@ -399,6 +504,9 @@
/*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 @@
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 @@
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 @@
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 @@
AudioSystem.DEVICE_STATE_AVAILABLE, "", "",
"android"); // reconnect
}
+ mmi.record();
}
/*package*/ void onSaveSetPreferredDevice(int strategy, @NonNull AudioDeviceAttributes device) {
@@ -535,6 +658,12 @@
+ 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 @@
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 @@
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 @@
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 @@
toRemove.add(deviceInfo.mDeviceAddress);
}
});
+ new MediaMetrics.Item(mMetricsId + "disconnectA2dpSink")
+ .record();
toRemove.stream().forEach(deviceAddress -> makeA2dpSrcUnavailable(deviceAddress));
}
}
@@ -615,6 +755,8 @@
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 @@
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 @@
@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 @@
// 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 @@
mApmConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
// Remove A2DP routes as well
setCurrentAudioRouteNameIfPossible(null);
+ mmi.record();
}
@GuardedBy("mDevicesLock")
@@ -862,6 +1015,14 @@
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 @@
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 @@
@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 @@
// 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 f840f2d..7cac376 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.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 @@
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 @@
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 @@
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 @@
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 @@
}
// 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 @@
}
// 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 @@
/** @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 @@
/** @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 @@
!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 @@
&& 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 @@
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 @@
+ 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 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 @@
}
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 @@
}
}
+ /**
+ * 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 add620e..5913567 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 @@
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 @@
mCaller = caller;
mGroupName = null;
mAudioAttributes = null;
+ logMetricEvent();
}
/** used for VOL_SET_HEARING_AID_VOL*/
@@ -132,6 +134,7 @@
mCaller = null;
mGroupName = null;
mAudioAttributes = null;
+ logMetricEvent();
}
/** used for VOL_SET_AVRCP_VOL */
@@ -144,6 +147,7 @@
mCaller = null;
mGroupName = null;
mAudioAttributes = null;
+ logMetricEvent();
}
/** used for VOL_VOICE_ACTIVITY_HEARING_AID */
@@ -156,6 +160,7 @@
mCaller = null;
mGroupName = null;
mAudioAttributes = null;
+ logMetricEvent();
}
/** used for VOL_MODE_CHANGE_HEARING_AID */
@@ -168,6 +173,7 @@
mCaller = null;
mGroupName = null;
mAudioAttributes = null;
+ logMetricEvent();
}
/** used for VOL_SET_GROUP_VOL */
@@ -179,6 +185,102 @@
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 93d1bed..accb90c 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 @@
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 @@
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 bfab991..26281b7 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.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 @@
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 @@
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 @@
* */
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 f4d7f9a..36d69c9 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 @@
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 @@
// 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 @@
// 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 @@
// 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 @@
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 @@
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 @@
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 @@
} 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 @@
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 @@
}
}
- // 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 @@
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 b4f7cdb..02d499f 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.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 @@
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 @@
}
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 @@
}
@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 48e30bf..bafeb77 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 @@
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 f0a505d..2b56569 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 @@
* 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 @@
* 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 1cf27ff..cc7915c 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.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 static final int NETWORK_COUNTRY_CHANGED = 2;
+ private static final int SUBSCRIPTION_CHANGED = 3;
+ private static final int UPDATE_AIRPLANE_MODE_STATUS = 4;
+
+ // 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 final Context mContext;
- private final ArrayList<Integer> mEmergencyCallMccNumbers;
-
- private final Object mLock = new Object();
-
- private TelephonyManager mTelephonyManager;
+ // 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();
- }
- }
-
- @Override
- public void onCellLocationChanged(CellLocation location) {
- if (!isEmergencyAffordanceNeeded()) {
- requestCellScan();
- }
- }
- };
- private BroadcastReceiver mAirplaneModeReceiver = 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();
- }
- }
- };
- private boolean mSimNeedsEmergencyAffordance;
- private boolean mNetworkNeedsEmergencyAffordance;
+ private boolean mAnySimNeedsEmergencyAffordance;
+ private boolean mAnyNetworkNeedsEmergencyAffordance;
+ private boolean mEmergencyAffordanceNeeded;
+ private boolean mAirplaneModeEnabled;
private boolean mVoiceCapable;
- private void requestCellScan() {
- mHandler.obtainMessage(CELL_INFO_STATE_CHANGED).sendToTarget();
- }
+ private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ 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 SubscriptionManager.OnSubscriptionsChangedListener mSubscriptionChangedListener
= new SubscriptionManager.OnSubscriptionsChangedListener() {
@@ -116,207 +106,200 @@
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()) {
- return;
- }
+ if (DBG) Slog.d(TAG, "handleInitializeState");
+ handleUpdateAirplaneModeStatus();
+ handleUpdateSimSubscriptionInfo();
+ updateNetworkCountry();
updateEmergencyAffordanceNeeded();
}
- private boolean handleUpdateSimSubscriptionInfo() {
- boolean neededBefore = simNeededAffordanceBefore();
- boolean neededNow = neededBefore;
+ private void handleThirdPartyBootPhase() {
+ if (DBG) Slog.d(TAG, "handleThirdPartyBootPhase");
+ mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
+ mVoiceCapable = mTelephonyManager.isVoiceCapable();
+ if (!mVoiceCapable) {
+ updateEmergencyAffordanceNeeded();
+ return;
+ }
+
+ 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 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) {
+ private void handleNetworkCountryChanged(String countryIso, int slotId) {
+ if (DBG) {
+ Slog.d(TAG, "handleNetworkCountryChanged: countryIso=" + countryIso
+ + ", slotId=" + slotId);
+ }
+
+ if (TextUtils.isEmpty(countryIso) && mAirplaneModeEnabled) {
+ Slog.w(TAG, "Ignore empty countryIso report when APM is on.");
+ return;
+ }
+
+ updateNetworkCountry();
+
+ updateEmergencyAffordanceNeeded();
+ }
+
+ 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;
+ }
+ }
+
+ mAnyNetworkNeedsEmergencyAffordance = needsAffordance;
+
+ updateEmergencyAffordanceNeeded();
+ }
+
+ private boolean isoRequiresEmergencyAffordance(String iso) {
+ return mEmergencyCallCountryIsos.contains(iso);
+ }
+
+ 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(),
- EMERGENCY_SIM_INSERTED_SETTING,
- simNeedsEmergencyAffordance ? 1 : 0);
- }
- if (simNeedsEmergencyAffordance != mSimNeedsEmergencyAffordance) {
- mSimNeedsEmergencyAffordance = simNeedsEmergencyAffordance;
- updateEmergencyAffordanceNeeded();
+ Settings.Global.EMERGENCY_AFFORDANCE_NEEDED,
+ mEmergencyAffordanceNeeded ? 1 : 0);
}
}
- private boolean simNeededAffordanceBefore() {
- return Settings.Global.getInt(mContext.getContentResolver(),
- EMERGENCY_SIM_INSERTED_SETTING, 0) != 0;
+ 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 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();
+ private final class BinderService extends Binder {
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) {
+ return;
}
- 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;
- }
- }
- if (stopScanningAfterScan) {
- stopScanning();
- } else {
- onCellScanFinishedUnsuccessful();
- }
- setNetworkNeedsEmergencyAffordance(false);
- return false;
- }
- private void setNetworkNeedsEmergencyAffordance(boolean needsAffordance) {
- synchronized (mLock) {
- mNetworkNeedsEmergencyAffordance = needsAffordance;
- updateEmergencyAffordanceNeeded();
+ dumpInternal(new IndentingPrintWriter(pw, " "));
}
}
-
- private void onCellScanFinishedUnsuccessful() {
- synchronized (mLock) {
- mScansCompleted++;
- if (mScansCompleted >= NUM_SCANS_UNTIL_ABORT) {
- stopScanning();
- }
- }
- }
-
- private boolean mccRequiresEmergencyAffordance(int mcc) {
- return mEmergencyCallMccNumbers.contains(mcc);
- }
}
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 0d89997..d8bf9ed 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -16,6 +16,8 @@
package com.android.server.media;
+import static android.media.MediaRoute2ProviderService.REASON_ROUTE_NOT_AVAILABLE;
+import static android.media.MediaRoute2ProviderService.REASON_UNKNOWN_ERROR;
import static android.media.MediaRoute2ProviderService.REQUEST_ID_NONE;
import static android.media.MediaRouter2Utils.getOriginalId;
import static android.media.MediaRouter2Utils.getProviderId;
@@ -247,6 +249,22 @@
}
}
+ public void notifySessionHintsForCreatingSession(IMediaRouter2 router,
+ long uniqueRequestId, MediaRoute2Info route, Bundle sessionHints) {
+ Objects.requireNonNull(router, "router must not be null");
+ Objects.requireNonNull(route, "route must not be null");
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ notifySessionHintsForCreatingSessionLocked(uniqueRequestId,
+ router, route, sessionHints);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
public void selectRouteWithRouter2(IMediaRouter2 router, String uniqueSessionId,
MediaRoute2Info route) {
Objects.requireNonNull(router, "router must not be null");
@@ -265,7 +283,6 @@
}
}
-
public void deselectRouteWithRouter2(IMediaRouter2 router, String uniqueSessionId,
MediaRoute2Info route) {
Objects.requireNonNull(router, "router must not be null");
@@ -634,12 +651,30 @@
long uniqueRequestId = toUniqueRequestId(routerRecord.mRouterId, requestId);
routerRecord.mUserRecord.mHandler.sendMessage(
- obtainMessage(UserHandler::requestCreateSessionOnHandler,
+ obtainMessage(UserHandler::requestCreateSessionWithRouter2OnHandler,
routerRecord.mUserRecord.mHandler,
- uniqueRequestId, routerRecord, /* managerRecord= */ null, route,
+ uniqueRequestId, routerRecord, route,
sessionHints));
}
+ private void notifySessionHintsForCreatingSessionLocked(long uniqueRequestId,
+ @NonNull IMediaRouter2 router,
+ @NonNull MediaRoute2Info route, @Nullable Bundle sessionHints) {
+ final IBinder binder = router.asBinder();
+ final RouterRecord routerRecord = mAllRouterRecords.get(binder);
+
+ if (routerRecord == null) {
+ Slog.w(TAG, "requestCreateSessionWithRouter2ByManagerRequestLocked: "
+ + "Ignoring unknown router.");
+ return;
+ }
+
+ routerRecord.mUserRecord.mHandler.sendMessage(
+ obtainMessage(UserHandler::requestCreateSessionWithManagerOnHandler,
+ routerRecord.mUserRecord.mHandler,
+ uniqueRequestId, routerRecord, route, sessionHints));
+ }
+
private void selectRouteWithRouter2Locked(@NonNull IMediaRouter2 router,
@NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) {
final IBinder binder = router.asBinder();
@@ -826,12 +861,13 @@
}
long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId);
- //TODO(b/152851868): Use MediaRouter2's OnCreateSessionListener to send session hints.
+
+ // Before requesting to the provider, get session hints from the media router.
+ // As a return, media router will request to create a session.
routerRecord.mUserRecord.mHandler.sendMessage(
- obtainMessage(UserHandler::requestCreateSessionOnHandler,
+ obtainMessage(UserHandler::getSessionHintsForCreatingSessionOnHandler,
routerRecord.mUserRecord.mHandler,
- uniqueRequestId, routerRecord, managerRecord, route,
- /* sessionHints= */ null));
+ uniqueRequestId, routerRecord, managerRecord, route));
}
private void selectRouteWithManagerLocked(int requestId, @NonNull IMediaRouter2Manager manager,
@@ -1149,7 +1185,6 @@
this, provider, uniqueRequestId, sessionInfo));
}
-
@Override
public void onSessionUpdated(@NonNull MediaRoute2Provider provider,
@NonNull RoutingSessionInfo sessionInfo) {
@@ -1267,8 +1302,26 @@
return -1;
}
- private void requestCreateSessionOnHandler(long uniqueRequestId,
- @NonNull RouterRecord routerRecord, @Nullable ManagerRecord managerRecord,
+ private void getSessionHintsForCreatingSessionOnHandler(long uniqueRequestId,
+ @NonNull RouterRecord routerRecord, @NonNull ManagerRecord managerRecord,
+ @NonNull MediaRoute2Info route) {
+ SessionCreationRequest request =
+ new SessionCreationRequest(routerRecord, uniqueRequestId, route, managerRecord);
+ mSessionCreationRequests.add(request);
+
+ try {
+ routerRecord.mRouter.getSessionHintsForCreatingSession(uniqueRequestId, route);
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "requestGetSessionHintsOnHandler: "
+ + "Failed to request. Router probably died.");
+ mSessionCreationRequests.remove(request);
+ notifyRequestFailedToManager(managerRecord.mManager,
+ toOriginalRequestId(uniqueRequestId), REASON_UNKNOWN_ERROR);
+ }
+ }
+
+ private void requestCreateSessionWithRouter2OnHandler(long uniqueRequestId,
+ @NonNull RouterRecord routerRecord,
@NonNull MediaRoute2Info route, @Nullable Bundle sessionHints) {
final MediaRoute2Provider provider = findProvider(route.getProviderId());
@@ -1281,13 +1334,50 @@
}
SessionCreationRequest request =
- new SessionCreationRequest(routerRecord, uniqueRequestId, route, managerRecord);
+ new SessionCreationRequest(routerRecord, uniqueRequestId, route, null);
mSessionCreationRequests.add(request);
provider.requestCreateSession(uniqueRequestId, routerRecord.mPackageName,
route.getOriginalId(), sessionHints);
}
+ private void requestCreateSessionWithManagerOnHandler(long uniqueRequestId,
+ @NonNull RouterRecord routerRecord,
+ @NonNull MediaRoute2Info route, @Nullable Bundle sessionHints) {
+ SessionCreationRequest matchingRequest = null;
+ for (SessionCreationRequest request : mSessionCreationRequests) {
+ if (request.mUniqueRequestId == uniqueRequestId) {
+ matchingRequest = request;
+ break;
+ }
+ }
+ if (matchingRequest == null) {
+ Slog.w(TAG, "requestCreateSessionWithKnownRequestOnHandler: "
+ + "Ignoring an unknown request.");
+ return;
+ }
+
+ if (!TextUtils.equals(matchingRequest.mRoute.getId(), route.getId())) {
+ Slog.w(TAG, "requestCreateSessionWithKnownRequestOnHandler: "
+ + "The given route is different from the requested route.");
+ return;
+ }
+
+ final MediaRoute2Provider provider = findProvider(route.getProviderId());
+ if (provider == null) {
+ Slog.w(TAG, "Ignoring session creation request since no provider found for"
+ + " given route=" + route);
+
+ mSessionCreationRequests.remove(matchingRequest);
+ notifyRequestFailedToManager(matchingRequest.mRequestedManagerRecord.mManager,
+ toOriginalRequestId(uniqueRequestId), REASON_ROUTE_NOT_AVAILABLE);
+ return;
+ }
+
+ provider.requestCreateSession(uniqueRequestId, routerRecord.mPackageName,
+ route.getOriginalId(), sessionHints);
+ }
+
// routerRecord can be null if the session is system's or RCN.
private void selectRouteOnHandler(long uniqueRequestId, @Nullable RouterRecord routerRecord,
@NonNull String uniqueSessionId, @NonNull MediaRoute2Info route) {
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index d6bf9fb..bf2cc5e 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -487,6 +487,14 @@
// Binder call
@Override
+ public void notifySessionHintsForCreatingSession(IMediaRouter2 router,
+ long uniqueRequestId, MediaRoute2Info route, Bundle sessionHints) {
+ mService2.notifySessionHintsForCreatingSession(router,
+ uniqueRequestId, route, sessionHints);
+ }
+
+ // Binder call
+ @Override
public void selectRouteWithRouter2(IMediaRouter2 router, String sessionId,
MediaRoute2Info route) {
mService2.selectRouteWithRouter2(router, sessionId, route);
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index d432fc8..3e6d6f5 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 @@
}
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 @@
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 1d48438..96da649 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_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 @@
/**
* 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 @@
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 @@
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 @@
}
/**
- * 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 @@
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 7c47cf0..d36d038 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 @@
}
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 ae9c384..062e9e1 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 com.android.internal.annotations.GuardedBy;
import com.android.server.SystemService;
+import libcore.io.IoUtils;
+
import java.util.List;
/**
@@ -64,31 +66,52 @@
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 @@
}
}
- class DataLoaderServiceConnection implements ServiceConnection {
+ class DataLoaderServiceConnection implements ServiceConnection, AutoCloseable {
final int mId;
final DataLoaderParamsParcel mParams;
final FileSystemControlParcel mControl;
@@ -204,15 +227,34 @@
@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 @@
} catch (RemoteException ignored) {
}
mContext.unbindService(this);
+ close();
+ remove();
}
private void remove() {
@@ -230,5 +274,14 @@
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 4cfd1ab7..3367cd5 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 @@
@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 5447bcb..28987ed 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 @@
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 @@
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,
@@ -24721,6 +24732,14 @@
return packageName.equals(
PackageManagerService.this.ensureSystemPackageName(packageName));
}
+
+ @Override
+ public void clearBlockUninstallForUser(@UserIdInt int userId) {
+ synchronized (mLock) {
+ mSettings.clearBlockUninstallLPw(userId);
+ mSettings.writePackageRestrictionsLPr(userId);
+ }
+ }
}
@GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 44a61d8..ddeab29 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -1833,6 +1833,10 @@
}
}
+ void clearBlockUninstallLPw(int userId) {
+ mBlockUninstallPackages.remove(userId);
+ }
+
boolean getBlockUninstallLPr(int userId, String packageName) {
ArraySet<String> packages = mBlockUninstallPackages.get(userId);
if (packages == null) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index fc70af4..4561d2e 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.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;
@@ -3153,13 +3154,17 @@
/**
* Removes the app restrictions file for a specific package and user id, if it exists.
+ *
+ * @return whether there were any restrictions.
*/
- private static void cleanAppRestrictionsForPackageLAr(String pkg, @UserIdInt int userId) {
- File dir = Environment.getUserSystemDirectory(userId);
- File resFile = new File(dir, packageToRestrictionsFileName(pkg));
+ private static boolean cleanAppRestrictionsForPackageLAr(String pkg, @UserIdInt int userId) {
+ final File dir = Environment.getUserSystemDirectory(userId);
+ final File resFile = new File(dir, packageToRestrictionsFileName(pkg));
if (resFile.exists()) {
resFile.delete();
+ return true;
}
+ return false;
}
/**
@@ -4003,17 +4008,24 @@
if (restrictions != null) {
restrictions.setDefusable(true);
}
+ final boolean changed;
synchronized (mAppRestrictionsLock) {
if (restrictions == null || restrictions.isEmpty()) {
- cleanAppRestrictionsForPackageLAr(packageName, userId);
+ changed = cleanAppRestrictionsForPackageLAr(packageName, userId);
} else {
// Write the restrictions to XML
writeApplicationRestrictionsLAr(packageName, restrictions, userId);
+ // TODO(b/154323615): avoid unnecessary broadcast when there is no change.
+ changed = true;
}
}
+ if (!changed) {
+ return;
+ }
+
// Notify package of changes via an intent - only sent to explicitly registered receivers.
- Intent changeIntent = new Intent(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED);
+ final Intent changeIntent = new Intent(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED);
changeIntent.setPackage(packageName);
changeIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
mContext.sendBroadcastAsUser(changeIntent, UserHandle.of(userId));
@@ -4505,6 +4517,8 @@
switch(cmd) {
case "list":
return runList(pw, shell);
+ case "list-missing-system-packages":
+ return runListMissingSystemPackages(pw, shell);
default:
return shell.handleDefaultCommands(cmd);
}
@@ -4571,6 +4585,30 @@
}
}
+ 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;
@@ -5143,6 +5181,9 @@
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 85c2306..cd1087f5 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.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 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 @@
if (!isLogMode(mode) && !isEnforceMode(mode)) {
return;
}
- Slog.v(TAG, "Checking that all system packages are whitelisted.");
- final Set<String> allWhitelistedPackages = getWhitelistedSystemPackages();
- PackageManagerInternal pmInt = LocalServices.getService(PackageManagerInternal.class);
-
- // Check whether all whitelisted packages are indeed on the system.
- for (String pkgName : allWhitelistedPackages) {
- AndroidPackage pkg = pmInt.getPackage(pkgName);
- if (pkg == null) {
- Slog.w(TAG, pkgName + " is whitelisted but not present.");
- } else if (!pkg.isSystem()) {
- Slog.w(TAG, pkgName + " is whitelisted and present but not a system package.");
- }
- }
-
- // Check whether all system packages are indeed whitelisted.
- if (isImplicitWhitelistMode(mode) && !isLogMode(mode)) {
+ 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;
}
- final boolean doWtf = 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 (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();
+ 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) {
+ final AndroidPackage pkg = pmInt.getPackage(pkgName);
+ if (pkg == null) {
+ warnings.add(new Pair<>(false, String.format(notPresentFmt, pkgName)));
+ } else if (!pkg.isSystem()) {
+ warnings.add(new Pair<>(false, String.format(notSystemFmt, pkgName)));
+ }
+ }
+
+ // Check whether all system packages are indeed whitelisted.
+ 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()) 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 @@
}
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 @@
}
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 780b234..4a1a6a7 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.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 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 4624e9e..2f84a99 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_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 @@
mScreenshotChordVolumeDownKeyConsumed = true;
cancelPendingPowerKeyAction();
mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN);
+ mScreenshotRunnable.setScreenshotSource(SCREENSHOT_KEY_CHORD);
mHandler.postDelayed(mScreenshotRunnable, getScreenshotChordLongPressDelay());
}
}
@@ -1411,14 +1414,19 @@
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 @@
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 @@
} 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/power/AttentionDetector.java b/services/core/java/com/android/server/power/AttentionDetector.java
index 1ab6ade..b69c450 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 @@
}
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/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index 801d75b..f6d46e2 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -25,6 +25,7 @@
import android.content.IntentFilter;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.VersionedPackage;
import android.content.rollback.PackageRollbackInfo;
import android.content.rollback.RollbackInfo;
@@ -41,10 +42,13 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.LocalServices;
import com.android.server.PackageWatchdog;
import com.android.server.PackageWatchdog.FailureReasons;
import com.android.server.PackageWatchdog.PackageHealthObserver;
import com.android.server.PackageWatchdog.PackageHealthObserverImpact;
+import com.android.server.pm.ApexManager;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import java.io.BufferedReader;
import java.io.File;
@@ -71,6 +75,7 @@
private final Context mContext;
private final Handler mHandler;
+ private final ApexManager mApexManager;
private final File mLastStagedRollbackIdsFile;
// Staged rollback ids that have been committed but their session is not yet ready
@GuardedBy("mPendingStagedRollbackIds")
@@ -85,6 +90,7 @@
dataDir.mkdirs();
mLastStagedRollbackIdsFile = new File(dataDir, "last-staged-rollback-ids");
PackageWatchdog.getInstance(mContext).registerHealthObserver(this);
+ mApexManager = ApexManager.getInstance();
}
@Override
@@ -302,6 +308,18 @@
* Returns true if the package name is the name of a module.
*/
private boolean isModule(String packageName) {
+ // Check if the package is an APK inside an APEX. If it is, use the parent APEX package when
+ // querying PackageManager.
+ PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
+ AndroidPackage apkPackage = pmi.getPackage(packageName);
+ if (apkPackage != null) {
+ String apexPackageName = mApexManager.getActiveApexPackageNameContainingPackage(
+ apkPackage);
+ if (apexPackageName != null) {
+ packageName = apexPackageName;
+ }
+ }
+
PackageManager pm = mContext.getPackageManager();
try {
return pm.getModuleInfo(packageName, 0) != null;
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 0000000..e5d0370
--- /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 0000000..54ad1d2
--- /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 4cdc172..2b0fe8a 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 @@
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 @@
* also lose their resources.
*/
private int mGroupId = INVALID_GROUP_ID;
-
/**
* Optional nice value for TRM to reduce client’s priority.
*/
@@ -73,6 +73,11 @@
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 @@
}
/**
+ * 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 7231813..2f70840 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 @@
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 @@
@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 @@
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 @@
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");
}
-
- return true;
+ synchronized (mLock) {
+ if (!checkClientExists(request.getClientId())) {
+ throw new RemoteException("Request cas from unregistered client:"
+ + request.getClientId());
+ }
+ return requestCasSessionInternal(request, casSessionHandle);
+ }
}
@Override
@@ -248,11 +261,11 @@
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 @@
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 @@
}
@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 @@
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 @@
}
@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 @@
} 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 @@
}
@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 @@
} 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 @@
}
@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 @@
}
@VisibleForTesting
- void releaseLnbInternal(LnbResource lnb) {
+ protected void releaseLnbInternal(LnbResource lnb) {
if (DEBUG) {
Slog.d(TAG, "releaseLnb(lnbId=" + lnb.getId() + ")");
}
@@ -604,7 +709,15 @@
}
@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 @@
}
@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 @@
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 @@
private void removeFrontendResource(int removingId) {
FrontendResource fe = getFrontendResource(removingId);
+ if (fe == null) {
+ return;
+ }
if (fe.isInUse()) {
releaseFrontendInternal(fe);
}
@@ -811,6 +942,9 @@
private void removeLnbResource(int removingId) {
LnbResource lnb = getLnbResource(removingId);
+ if (lnb == null) {
+ return;
+ }
if (lnb.isInUse()) {
releaseLnbInternal(lnb);
}
@@ -819,6 +953,39 @@
@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 @@
}
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 @@
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 abccf99..785ca90 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.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.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 @@
*/
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 @@
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 @@
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 @@
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 @@
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 @@
}, 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 @@
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 @@
private final long mRecurringAccessibilityEventsIntervalMillis;
+ private final IntArray mEmbeddedDisplayIdList = new IntArray(0);
+
public WindowsForAccessibilityObserver(WindowManagerService windowManagerService,
int displayId,
WindowsForAccessibilityCallback callback) {
@@ -1176,6 +1232,21 @@
}
}
+ 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 2648c86..c058317 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 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 @@
task.computeConfigResourceOverrides(newConfig, task.getParent().getConfiguration());
}
schedulePictureInPictureModeChanged(newConfig);
- scheduleMultiWindowModeChanged(newConfig);
+ if (shouldScheduleMultiWindowModeChange) {
+ scheduleMultiWindowModeChanged(newConfig);
+ }
}
}
@@ -3137,7 +3142,6 @@
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 +6134,8 @@
@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 0754a34..8edc84f 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 @@
// 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 @@
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 736bc9f..02f6a69 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 @@
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 @@
}
return;
}
- process.mIsImeProcess = true;
process.registerDisplayConfigurationListener(displayContent);
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index e9d3d56..8aace21 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 @@
@Px
private int mRightGestureInset;
+ private boolean mNavButtonForcedVisible;
+
StatusBarManagerInternal getStatusBarManagerInternal() {
synchronized (mServiceAcquireLock) {
if (mStatusBarManagerInternal == null) {
@@ -1046,12 +1048,14 @@
// 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 @@
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 @@
* @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 61a199a..5a9bf80 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 @@
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 1ca82ce..351743f 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 @@
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 @@
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 @@
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 @@
return true;
}
+ void onSurfaceTransactionApplied() {
+ mIsLeashReadyForDispatching = true;
+ }
+
private void setClientVisible(boolean clientVisible) {
if (mClientVisible == clientVisible) {
return;
@@ -334,6 +341,13 @@
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 4ac319d..765f980 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 @@
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/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index 9d44cad..86e0818 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 @@
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 df5cfee..5bf49a6 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 @@
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 @@
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
@@ -2897,22 +2901,12 @@
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 +4429,20 @@
// 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 +4479,6 @@
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/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index adc50bf..306c100 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 @@
}
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 @@
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 @@
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 @@
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 203ca25..5f3c633 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 @@
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 f3e2992..591bc54 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 @@
*/
@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 @@
* @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 @@
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 4efbe09..8eb4b26 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2454,17 +2454,15 @@
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;
}
}
}
@@ -6141,15 +6139,6 @@
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=");
@@ -6165,6 +6154,9 @@
mInputManagerCallback.dump(pw, " ");
mTaskSnapshotController.dump(pw, " ");
+ if (mAccessibilityController != null) {
+ mAccessibilityController.dump(pw, " ");
+ }
if (dumpAll) {
final WindowState imeWindow = mRoot.getCurrentInputMethodWindow();
@@ -7686,15 +7678,8 @@
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 194ed3e..41bd707 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.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.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 @@
// 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 @@
// 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 @@
/** 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 @@
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 @@
* 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 @@
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 @@
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 @@
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 @@
}
}
+ /**
+ * 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/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 9957707..bfe3b28 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 @@
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 @@
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 6e2e2c5..99deab4 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 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 2c0d4c0..5d5e424 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.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.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 @@
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 @@
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 @@
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 @@
}
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 @@
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 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();
+ }
}
/**
@@ -2703,8 +2728,9 @@
final ComponentName doAdminReceiver = doAdmin.info.getComponent();
clearDeviceOwnerLocked(doAdmin, doUserId);
Slog.i(LOG_TAG, "Removing admin artifacts...");
- // TODO(b/149075700): Clean up application restrictions in UserManager.
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
@@ -2716,6 +2742,47 @@
.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) {
@@ -3999,10 +4066,6 @@
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.
@@ -4010,6 +4073,16 @@
}
}
+ 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();
@@ -8766,6 +8839,8 @@
saveSettingsLocked(UserHandle.USER_SYSTEM);
clearUserPoliciesLocked(userId);
clearOverrideApnUnchecked();
+ clearApplicationRestrictions(userId);
+ mInjector.getPackageManagerInternal().clearBlockUninstallForUser(userId);
mOwners.clearDeviceOwner();
mOwners.writeDeviceOwner();
@@ -8779,6 +8854,19 @@
toggleBackupServiceActive(UserHandle.USER_SYSTEM, true);
}
+ private void clearApplicationRestrictions(int userId) {
+ // Changing app restrictions involves disk IO, offload it to the background thread.
+ mBackgroundHandler.post(() -> {
+ final List<PackageInfo> installedPackageInfos = mInjector.getPackageManager(userId)
+ .getInstalledPackages(MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE);
+ final UserHandle userHandle = UserHandle.of(userId);
+ for (final PackageInfo packageInfo : installedPackageInfos) {
+ mInjector.getUserManager().setApplicationRestrictions(
+ packageInfo.packageName, null /* restrictions */, userHandle);
+ }
+ });
+ }
+
@Override
public boolean setProfileOwner(ComponentName who, String ownerName, int userHandle) {
if (!mHasFeature) {
@@ -8898,6 +8986,7 @@
policyData.mOwnerInstalledCaCerts.clear();
saveSettingsLocked(userId);
clearUserPoliciesLocked(userId);
+ clearApplicationRestrictions(userId);
mOwners.removeProfileOwner(userId);
mOwners.writeProfileOwner(userId);
deleteTransferOwnershipBundleLocked(userId);
@@ -15831,11 +15920,8 @@
}
}
- 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)
@@ -15845,44 +15931,54 @@
}
/**
- * 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.
@@ -15898,52 +15994,51 @@
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 {
+
+ if (alarmTime == 0) {
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);
+ 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)) {
@@ -15962,37 +16057,38 @@
}
}
- 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();
@@ -16024,7 +16120,7 @@
}
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 d9db17e..da716ea 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 @@
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 @@
*/
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 @@
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 @@
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 @@
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 @@
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 b13d330..b8bd3b4 100644
--- a/services/incremental/Android.bp
+++ b/services/incremental/Android.bp
@@ -62,6 +62,7 @@
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 fc8c6fe..8476674 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::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::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 f423119..dc05cb6 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 @@
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 @@
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 @@
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 @@
}
} // 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 @@
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 @@
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 @@
runJobProcessing();
});
- mountExistingImages();
+ const auto mountedRootNames = adoptMountedInstances();
+ mountExistingImages(mountedRootNames);
}
IncrementalService::~IncrementalService() {
@@ -268,15 +296,7 @@
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 @@
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());
- }
+ 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, "\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));
+ 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 @@
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 @@
}
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
}
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;
-}
-
-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()) {
+ 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 normalizePathToStorageLocked(storageInfo, path);
+ return path::join(bindIt->second.sourceDir, path::relativize(bindIt->first, normPath));
+}
+
+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(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::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 @@
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 @@
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 @@
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,12 +935,31 @@
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, std::string_view path) const {
+ const auto ifs = getIfs(storage);
+ if (!ifs) {
+ return {};
+ }
+ const auto normPath = normalizePathToStorage(*ifs, storage, path);
+ if (normPath.empty()) {
+ return {};
+ }
+ return mIncFs->getMetadata(ifs->control, normPath);
}
RawMetadata IncrementalService::getMetadata(StorageId storage, FileId node) const {
@@ -945,47 +970,6 @@
return mIncFs->getMetadata(ifs->control, node);
}
-std::vector<std::string> IncrementalService::listFiles(StorageId storage) 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;
-}
-
bool IncrementalService::startLoading(StorageId storage) const {
DataLoaderStubPtr dataLoaderStub;
{
@@ -1003,16 +987,216 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
}
// 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;
@@ -1205,10 +1396,10 @@
const auto libName = path::basename(fileName);
const auto targetLibPath = path::join(libDirRelativePath, libName);
- const auto targetLibPathAbsolute = normalizePathToStorage(ifs, storage, targetLibPath);
+ 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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 bd01d77..0a18e21 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 @@
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 @@
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 @@
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 @@
};
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 0000000..abadbbf
--- /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 48894c6..0e50c4d 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>
+
+#include <string>
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;
-}
-
-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};
-
- // 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 bf8e696..32aa849 100644
--- a/services/incremental/ServiceWrappers.cpp
+++ b/services/incremental/ServiceWrappers.cpp
@@ -18,12 +18,18 @@
#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;
@@ -32,9 +38,9 @@
public:
RealVoldService(const 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 @@
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 @@
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 @@
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<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 @@
return getJavaVm(env);
}
-} // namespace android::os::incremental
+} // namespace android::incremental
diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h
index 142bf2e..6b0f59e 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 @@
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 @@
JavaVM* const mJvm;
};
-} // namespace android::os::incremental
+} // namespace android::incremental
diff --git a/services/incremental/path.cpp b/services/incremental/path.cpp
index 0d86f2a..338659d 100644
--- a/services/incremental/path.cpp
+++ b/services/incremental/path.cpp
@@ -44,7 +44,7 @@
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 bfe92f4..7a85602 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 @@
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 @@
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 @@
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 @@
auto first = "first"sv;
auto second = "second"sv;
auto third = "third"sv;
+ auto dir_path = std::string(first) + "/" + std::string(second) + "/" + std::string(third);
- 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);
+ 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 289d79d..0fc333f 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -2174,12 +2174,6 @@
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 @@
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 9f29799..bb5409b 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -41,6 +41,7 @@
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 c87ece2..763e19b 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.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 @@
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 @@
@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 7a175ca1..2983d58 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 @@
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 @@
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 @@
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 @@
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 3352177..064e348 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 @@
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/DevicePolicyManagerServiceMigrationTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
index 74e7f8c..a0b9d9d 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
@@ -62,6 +62,10 @@
mContext = getContext();
+ // Make createContextAsUser to work.
+ mContext.packageName = "com.android.frameworks.servicestests";
+ getServices().addPackageContext(UserHandle.of(0), mContext);
+
when(getServices().packageManager.hasSystemFeature(eq(PackageManager.FEATURE_DEVICE_ADMIN)))
.thenReturn(true);
}
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 3e5c21c..cbe49eb 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 @@
// 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 @@
@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 09d1d3a..b042e77 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.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.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 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 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 @@
"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();
@@ -196,6 +214,11 @@
anyInt(),
any(UserHandle.class));
+ // Make createContextAsUser to work.
+ mContext.packageName = "com.android.frameworks.servicestests";
+ getServices().addPackageContext(UserHandle.of(0), mContext);
+ getServices().addPackageContext(UserHandle.of(DpmMockContext.CALLER_USER_HANDLE), mContext);
+
// By default, pretend all users are running and unlocked.
when(getServices().userManager.isUserUnlocked(anyInt())).thenReturn(true);
@@ -1553,20 +1576,7 @@
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));
}
/**
@@ -6267,7 +6277,214 @@
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);
@@ -6294,7 +6511,7 @@
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/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index 8625a1e..20716ab 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -460,6 +460,15 @@
}
@Override
+ public Context createContextAsUser(UserHandle user, int flags) {
+ try {
+ return mMockSystemServices.createPackageContextAsUser(packageName, flags, user);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ @Override
public ContentResolver getContentResolver() {
return mMockSystemServices.contentResolver;
}
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 7b3417a..b69cc47 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 @@
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 @@
// 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 @@
// 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 @@
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 @@
// 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 @@
// 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 @@
// 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 0000000..d438a0e
--- /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 728e149..e16f314 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 @@
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 c4289ef..e7e8aca 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 @@
}
@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 965304f..21af356 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.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.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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
@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 @@
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 @@
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 @@
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 69ef499..df92b6e 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.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.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 @@
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 ecdd9e5..7a5e226 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 @@
// 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 @@
// 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 @@
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 @@
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 f7304bd..3095c87 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 @@
@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 @@
@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 @@
@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 @@
@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/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
index 9b7ffd6..e8fab2b 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.RemoteAnimationDefinition;
import android.view.RemoteAnimationTarget;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import org.junit.Test;
@@ -97,7 +96,6 @@
}
@Test
- @FlakyTest(bugId = 131005232)
public void testModeChangeRemoteAnimatorNoSnapshot() {
// setup currently defaults to no snapshot.
setUpOnDisplay(mDisplayContent);
@@ -115,7 +113,6 @@
}
@Test
- @FlakyTest(bugId = 131005232)
public void testCancelPendingChangeOnRemove() {
// setup currently defaults to no snapshot.
setUpOnDisplay(mDisplayContent);
@@ -135,8 +132,7 @@
}
@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 @@
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 b6eb901..f831287 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 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 @@
sInsetsModeSession.close();
}
+ @Before
+ public void setup() {
+ mWm.mAnimator.ready();
+ }
+
@Test
public void testControlsForDispatch_regular() {
addWindow(TYPE_STATUS_BAR, "statusBar");
@@ -194,6 +200,7 @@
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 @@
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 @@
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 @@
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/SystemServiceTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/SystemServiceTestsBase.java
index f6ed314..d7462f8 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 @@
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 af3ec38..dea9294 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 @@
}
}
+ 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/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
index a4f1487..520ac19 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 @@
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 f65328d..7d2e880 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 @@
* 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 @@
// 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 @@
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 07a6179..cdf8eb4 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.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 @@
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 9dfa3ac..fa99095 100755
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -734,6 +734,31 @@
"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 1b60e48..a716b37 100755
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -1859,9 +1859,25 @@
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 @@
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 d82e93f..8d3f4e1 100644
--- a/telecomm/java/android/telecom/Logging/Session.java
+++ b/telecomm/java/android/telecom/Logging/Session.java
@@ -427,7 +427,7 @@
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 cad5b70..a083301 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -258,6 +258,9 @@
// 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 @@
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 @@
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 151fb59..ede67dd 100644
--- a/telephony/java/android/telephony/ims/ImsRcsManager.java
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -49,7 +49,7 @@
*
* 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 @@
/**
* @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 @@
/**
* @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 @@
/**
* @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 @@
/**
* @hide
*/
- @Override
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public void getRegistrationTransportType(@NonNull @CallbackExecutor Executor executor,
@NonNull @AccessNetworkConstants.TransportType
@@ -347,8 +346,7 @@
* 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 @@
* 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 @@
* @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 35a6c26..371375c 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 @@
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 482b23f..6927e86 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 @@
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 737b7c7..b0213b0 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
@@ -384,7 +384,8 @@
*
* 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 @@
* // etc.
* };
* connectivityManager.requestNetwork(request, networkCallback);
- * }
+ * }</pre>
*
* @return Instance of {@link NetworkSpecifier}.
* @throws IllegalStateException on invalid params set.