Merge "Reference libcore, conscrypt and icu sources directly" into rvc-dev
diff --git a/Android.bp b/Android.bp
index 8d3b4af..935b2bb 100644
--- a/Android.bp
+++ b/Android.bp
@@ -722,7 +722,6 @@
"core/java/com/android/internal/util/IndentingPrintWriter.java",
"core/java/com/android/internal/util/IState.java",
"core/java/com/android/internal/util/MessageUtils.java",
- "core/java/com/android/internal/util/Preconditions.java",
"core/java/com/android/internal/util/State.java",
"core/java/com/android/internal/util/StateMachine.java",
"core/java/com/android/internal/util/TrafficStatsConstants.java",
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index 3c1fafb..24728dd 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -110,6 +110,7 @@
import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.usage.AppIdleHistory.AppUsageHistory;
import java.io.File;
@@ -1862,10 +1863,15 @@
public List<UserHandle> getValidCrossProfileTargets(String pkg, int userId) {
final int uid = mPackageManagerInternal.getPackageUidInternal(pkg, 0, userId);
+ final AndroidPackage aPkg = mPackageManagerInternal.getPackage(uid);
if (uid < 0
- || !mPackageManagerInternal.getPackage(uid).isCrossProfile()
+ || aPkg == null
+ || !aPkg.isCrossProfile()
|| !mCrossProfileAppsInternal
.verifyUidHasInteractAcrossProfilePermission(pkg, uid)) {
+ if (uid >= 0 && aPkg == null) {
+ Slog.wtf(TAG, "Null package retrieved for UID " + uid);
+ }
return Collections.emptyList();
}
return mCrossProfileAppsInternal.getTargetUserProfiles(pkg, userId);
diff --git a/api/test-current.txt b/api/test-current.txt
index 6cdcc32..5d91adf 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -2889,8 +2889,10 @@
public final class PermissionControllerManager {
method @RequiresPermission(anyOf={"android.permission.GRANT_RUNTIME_PERMISSIONS", "android.permission.RESTORE_RUNTIME_PERMISSIONS"}) public void applyStagedRuntimePermissionBackup(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+ method @RequiresPermission("android.permission.GET_RUNTIME_PERMISSIONS") public void countPermissionApps(@NonNull java.util.List<java.lang.String>, int, @NonNull android.permission.PermissionControllerManager.OnCountPermissionAppsResultCallback, @Nullable android.os.Handler);
method @RequiresPermission("android.permission.GET_RUNTIME_PERMISSIONS") public void getAppPermissions(@NonNull String, @NonNull android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, @Nullable android.os.Handler);
method @RequiresPermission("android.permission.GET_RUNTIME_PERMISSIONS") public void getRuntimePermissionBackup(@NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<byte[]>);
+ method @RequiresPermission("android.permission.REVOKE_RUNTIME_PERMISSIONS") public void revokeRuntimePermission(@NonNull String, @NonNull String);
method @RequiresPermission("android.permission.REVOKE_RUNTIME_PERMISSIONS") public void revokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, @NonNull java.util.concurrent.Executor, @NonNull android.permission.PermissionControllerManager.OnRevokeRuntimePermissionsCallback);
method @RequiresPermission(anyOf={"android.permission.GRANT_RUNTIME_PERMISSIONS", "android.permission.RESTORE_RUNTIME_PERMISSIONS"}) public void stageAndApplyRuntimePermissionsBackup(@NonNull byte[], @NonNull android.os.UserHandle);
field public static final int COUNT_ONLY_WHEN_GRANTED = 1; // 0x1
@@ -2899,6 +2901,10 @@
field public static final int REASON_MALWARE = 1; // 0x1
}
+ public static interface PermissionControllerManager.OnCountPermissionAppsResultCallback {
+ method public void onCountPermissionApps(int);
+ }
+
public static interface PermissionControllerManager.OnGetAppPermissionResultCallback {
method public void onGetAppPermissions(@NonNull java.util.List<android.permission.RuntimePermissionPresentationInfo>);
}
diff --git a/cmds/statsd/tools/localtools/Android.bp b/cmds/statsd/tools/localtools/Android.bp
index 75a57a3..69a43a8 100644
--- a/cmds/statsd/tools/localtools/Android.bp
+++ b/cmds/statsd/tools/localtools/Android.bp
@@ -11,9 +11,8 @@
],
}
-java_binary_host {
- name: "statsd_testdrive",
- manifest: "testdrive_manifest.txt",
+java_library_host {
+ name: "statsd_testdrive_lib",
srcs: [
"src/com/android/statsd/shelltools/testdrive/*.java",
"src/com/android/statsd/shelltools/Utils.java",
@@ -22,4 +21,26 @@
"platformprotos",
"guava",
],
-}
\ No newline at end of file
+}
+
+
+java_binary_host {
+ name: "statsd_testdrive",
+ manifest: "testdrive_manifest.txt",
+ static_libs: [
+ "statsd_testdrive_lib",
+ ],
+}
+
+java_test_host {
+ name: "statsd_testdrive_test",
+ test_suites: ["general-tests"],
+ srcs: ["test/com/android/statsd/shelltools/testdrive/*.java"],
+ static_libs: [
+ "statsd_testdrive_lib",
+ "junit",
+ "platformprotos",
+ "guava",
+ ],
+}
+
diff --git a/cmds/statsd/tools/localtools/TEST_MAPPING b/cmds/statsd/tools/localtools/TEST_MAPPING
new file mode 100644
index 0000000..7c8a3db
--- /dev/null
+++ b/cmds/statsd/tools/localtools/TEST_MAPPING
@@ -0,0 +1,8 @@
+{
+ "presubmit": [
+ {
+ "name": "statsd_testdrive_test",
+ "host": true
+ }
+ ]
+}
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java
index 2909048..6a74480 100644
--- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java
@@ -229,4 +229,56 @@
}
return null;
}
+
+ /**
+ * Returns ANDROID_SERIAL environment variable, or null if that is undefined or unavailable.
+ * @param logger Destination of error messages.
+ * @return String value of ANDROID_SERIAL environment variable, or null.
+ */
+ public static String getDefaultDevice(Logger logger) {
+ try {
+ return System.getenv("ANDROID_SERIAL");
+ } catch (Exception ex) {
+ logger.log(Level.SEVERE, "Failed to check ANDROID_SERIAL environment variable.",
+ ex);
+ }
+ return null;
+ }
+
+ /**
+ * Returns the device to use if one can be deduced, or null.
+ * @param device Command-line specified device, or null.
+ * @param connectedDevices List of all connected devices.
+ * @param defaultDevice Environment-variable specified device, or null.
+ * @param logger Destination of error messages.
+ * @return Device to use, or null.
+ */
+ public static String chooseDevice(String device, List<String> connectedDevices,
+ String defaultDevice, Logger logger) {
+ if (connectedDevices == null || connectedDevices.isEmpty()) {
+ logger.severe("No connected device.");
+ return null;
+ }
+ if (device != null) {
+ if (connectedDevices.contains(device)) {
+ return device;
+ }
+ logger.severe("Device not connected: " + device);
+ return null;
+ }
+ if (connectedDevices.size() == 1) {
+ return connectedDevices.get(0);
+ }
+ if (defaultDevice != null) {
+ if (connectedDevices.contains(defaultDevice)) {
+ return defaultDevice;
+ } else {
+ logger.severe("ANDROID_SERIAL device is not connected: " + defaultDevice);
+ return null;
+ }
+ }
+ logger.severe("More than one device is connected. Choose one"
+ + " with -s DEVICE_SERIAL or environment variable ANDROID_SERIAL.");
+ return null;
+ }
}
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java
index 7db5141..ec3c7df 100644
--- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java
@@ -27,7 +27,6 @@
import java.io.FileReader;
import java.io.IOException;
import java.util.List;
-import java.util.logging.Level;
import java.util.logging.Logger;
/**
@@ -112,17 +111,9 @@
}
List<String> connectedDevices = Utils.getDeviceSerials(sLogger);
- if (connectedDevices == null || connectedDevices.size() == 0) {
- sLogger.log(Level.SEVERE, "No device connected.");
- return;
- }
- if (connectedDevices.size() == 1 && deviceSerial == null) {
- deviceSerial = connectedDevices.get(0);
- }
-
+ deviceSerial = Utils.chooseDevice(deviceSerial, connectedDevices,
+ Utils.getDefaultDevice(sLogger), sLogger);
if (deviceSerial == null) {
- sLogger.log(Level.SEVERE, "More than one devices connected. Please specify"
- + " with -s DEVICE_SERIAL");
return;
}
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
index 54a744b..8ac251e 100644
--- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
@@ -15,6 +15,7 @@
*/
package com.android.statsd.shelltools.testdrive;
+import com.android.internal.os.StatsdConfigProto;
import com.android.internal.os.StatsdConfigProto.AtomMatcher;
import com.android.internal.os.StatsdConfigProto.EventMetric;
import com.android.internal.os.StatsdConfigProto.FieldFilter;
@@ -29,6 +30,7 @@
import com.android.os.StatsLog.StatsLogReport;
import com.android.statsd.shelltools.Utils;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.io.Files;
import java.io.File;
@@ -39,6 +41,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -73,179 +76,269 @@
};
private static final Logger LOGGER = Logger.getLogger(TestDrive.class.getName());
- private String mAdditionalAllowedPackage;
- private String mDeviceSerial;
- private final Set<Long> mTrackedMetrics = new HashSet<>();
+ @VisibleForTesting
+ String mDeviceSerial = null;
public static void main(String[] args) {
+ final Configuration configuration = new Configuration();
+
TestDrive testDrive = new TestDrive();
- Set<Integer> trackedAtoms = new HashSet<>();
Utils.setUpLogger(LOGGER, false);
- String remoteConfigPath = null;
+ if (!testDrive.processArgs(configuration, args,
+ Utils.getDeviceSerials(LOGGER), Utils.getDefaultDevice(LOGGER))) {
+ return;
+ }
+
+ final ConfigMetricsReportList reports = testDrive.testDriveAndGetReports(
+ configuration.createConfig(), configuration.hasPulledAtoms(),
+ configuration.hasPushedAtoms());
+ if (reports != null) {
+ configuration.dumpMetrics(reports);
+ }
+ }
+
+ boolean processArgs(Configuration configuration, String[] args, List<String> connectedDevices,
+ String defaultDevice) {
if (args.length < 1) {
- LOGGER.log(Level.SEVERE, "Usage: ./test_drive [-p additional_allowed_package] "
- + "[-s DEVICE_SERIAL_NUMBER]"
+ LOGGER.severe("Usage: ./test_drive [-one] "
+ + "[-p additional_allowed_package] "
+ + "[-s DEVICE_SERIAL_NUMBER] "
+ "<atomId1> <atomId2> ... <atomIdN>");
- return;
+ return false;
}
- List<String> connectedDevices = Utils.getDeviceSerials(LOGGER);
- if (connectedDevices == null || connectedDevices.size() == 0) {
- LOGGER.log(Level.SEVERE, "No device connected.");
- return;
- }
-
- int arg_index = 0;
- while (arg_index < args.length) {
- String arg = args[arg_index];
- if (arg.equals("-p")) {
- testDrive.mAdditionalAllowedPackage = args[++arg_index];
- } else if (arg.equals("-s")) {
- testDrive.mDeviceSerial = args[++arg_index];
+ int first_arg = 0;
+ // Consume all flags, which must precede all atoms
+ for (; first_arg < args.length; ++first_arg) {
+ String arg = args[first_arg];
+ int remaining_args = args.length - first_arg;
+ if (remaining_args >= 2 && arg.equals("-one")) {
+ LOGGER.info("Creating one event metric to catch all pushed atoms.");
+ configuration.mOnePushedAtomEvent = true;
+ } else if (remaining_args >= 3 && arg.equals("-p")) {
+ configuration.mAdditionalAllowedPackage = args[++first_arg];
+ } else if (remaining_args >= 3 && arg.equals("-s")) {
+ mDeviceSerial = args[++first_arg];
} else {
- break;
+ break; // Found the atom list
}
- arg_index++;
}
- if (connectedDevices.size() == 1 && testDrive.mDeviceSerial == null) {
- testDrive.mDeviceSerial = connectedDevices.get(0);
+ mDeviceSerial = Utils.chooseDevice(mDeviceSerial, connectedDevices, defaultDevice, LOGGER);
+ if (mDeviceSerial == null) {
+ return false;
}
- if (testDrive.mDeviceSerial == null) {
- LOGGER.log(Level.SEVERE, "More than one devices connected. Please specify"
- + " with -s DEVICE_SERIAL");
- return;
- }
-
- for (int i = arg_index; i < args.length; i++) {
+ for ( ; first_arg < args.length; ++first_arg) {
+ String atom = args[first_arg];
try {
- int atomId = Integer.valueOf(args[i]);
- if (Atom.getDescriptor().findFieldByNumber(atomId) == null) {
- LOGGER.log(Level.SEVERE, "No such atom found: " + args[i]);
- continue;
- }
- trackedAtoms.add(atomId);
+ configuration.addAtom(Integer.valueOf(atom));
} catch (NumberFormatException e) {
- LOGGER.log(Level.SEVERE, "Bad atom id provided: " + args[i]);
- continue;
+ LOGGER.severe("Bad atom id provided: " + atom);
}
}
+ return configuration.hasPulledAtoms() || configuration.hasPushedAtoms();
+ }
+
+ private ConfigMetricsReportList testDriveAndGetReports(StatsdConfig config,
+ boolean hasPulledAtoms, boolean hasPushedAtoms) {
+ if (config == null) {
+ LOGGER.severe("Failed to create valid config.");
+ return null;
+ }
+
+ String remoteConfigPath = null;
try {
- StatsdConfig config = testDrive.createConfig(trackedAtoms);
- if (config == null) {
- LOGGER.log(Level.SEVERE, "Failed to create valid config.");
- return;
- }
- remoteConfigPath = testDrive.pushConfig(config, testDrive.mDeviceSerial);
- LOGGER.info("Pushed the following config to statsd:");
+ remoteConfigPath = pushConfig(config, mDeviceSerial);
+ LOGGER.info("Pushed the following config to statsd on device '" + mDeviceSerial
+ + "':");
LOGGER.info(config.toString());
- if (!hasPulledAtom(trackedAtoms)) {
+ if (hasPushedAtoms) {
+ LOGGER.info("Now please play with the device to trigger the event.");
+ }
+ if (!hasPulledAtoms) {
LOGGER.info(
- "Now please play with the device to trigger the event. All events should "
- + "be dumped after 1 min ...");
+ "All events should be dumped after 1 min ...");
Thread.sleep(60_000);
} else {
- LOGGER.info("Now wait for 1.5 minutes ...");
+ LOGGER.info("All events should be dumped after 1.5 minutes ...");
Thread.sleep(15_000);
- Utils.logAppBreadcrumb(0, 0, LOGGER, testDrive.mDeviceSerial);
+ Utils.logAppBreadcrumb(0, 0, LOGGER, mDeviceSerial);
Thread.sleep(75_000);
}
- testDrive.dumpMetrics();
+ return Utils.getReportList(CONFIG_ID, true, false, LOGGER,
+ mDeviceSerial);
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Failed to test drive: " + e.getMessage(), e);
} finally {
- testDrive.removeConfig(testDrive.mDeviceSerial);
+ removeConfig(mDeviceSerial);
if (remoteConfigPath != null) {
try {
Utils.runCommand(null, LOGGER,
- "adb", "-s", testDrive.mDeviceSerial, "shell", "rm", remoteConfigPath);
+ "adb", "-s", mDeviceSerial, "shell", "rm",
+ remoteConfigPath);
} catch (Exception e) {
LOGGER.log(Level.WARNING,
"Unable to remove remote config file: " + remoteConfigPath, e);
}
}
}
+ return null;
}
- private void dumpMetrics() throws Exception {
- ConfigMetricsReportList reportList = Utils.getReportList(CONFIG_ID, true, false, LOGGER,
- mDeviceSerial);
- // We may get multiple reports. Take the last one.
- ConfigMetricsReport report = reportList.getReports(reportList.getReportsCount() - 1);
- for (StatsLogReport statsLog : report.getMetricsList()) {
- if (mTrackedMetrics.contains(statsLog.getMetricId())) {
- LOGGER.info(statsLog.toString());
+ static class Configuration {
+ boolean mOnePushedAtomEvent = false;
+ @VisibleForTesting
+ Set<Integer> mPushedAtoms = new TreeSet<>();
+ @VisibleForTesting
+ Set<Integer> mPulledAtoms = new TreeSet<>();
+ @VisibleForTesting
+ String mAdditionalAllowedPackage = null;
+ private final Set<Long> mTrackedMetrics = new HashSet<>();
+
+ private void dumpMetrics(ConfigMetricsReportList reportList) {
+ // We may get multiple reports. Take the last one.
+ ConfigMetricsReport report = reportList.getReports(reportList.getReportsCount() - 1);
+ for (StatsLogReport statsLog : report.getMetricsList()) {
+ if (isTrackedMetric(statsLog.getMetricId())) {
+ LOGGER.info(statsLog.toString());
+ }
}
}
- }
- private StatsdConfig createConfig(Set<Integer> atomIds) {
- long metricId = METRIC_ID_BASE;
- long atomMatcherId = ATOM_MATCHER_ID_BASE;
-
- ArrayList<String> allowedSources = new ArrayList<>();
- Collections.addAll(allowedSources, ALLOWED_LOG_SOURCES);
- if (mAdditionalAllowedPackage != null) {
- allowedSources.add(mAdditionalAllowedPackage);
+ boolean isTrackedMetric(long metricId) {
+ return mTrackedMetrics.contains(metricId);
}
- StatsdConfig.Builder builder = StatsdConfig.newBuilder();
- builder
- .addAllAllowedLogSource(allowedSources)
- .addAllDefaultPullPackages(Arrays.asList(DEFAULT_PULL_SOURCES))
- .addPullAtomPackages(PullAtomPackages.newBuilder()
- .setAtomId(Atom.GPU_STATS_GLOBAL_INFO_FIELD_NUMBER)
- .addPackages("AID_GPU_SERVICE"))
- .addPullAtomPackages(PullAtomPackages.newBuilder()
- .setAtomId(Atom.GPU_STATS_APP_INFO_FIELD_NUMBER)
- .addPackages("AID_GPU_SERVICE"))
- .addPullAtomPackages(PullAtomPackages.newBuilder()
- .setAtomId(Atom.TRAIN_INFO_FIELD_NUMBER)
- .addPackages("AID_STATSD"))
- .setHashStringsInMetricReport(false);
-
- if (hasPulledAtom(atomIds)) {
- builder.addAtomMatcher(
- createAtomMatcher(
- Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER, APP_BREADCRUMB_MATCHER_ID));
+ static boolean isPulledAtom(int atomId) {
+ return atomId >= PULL_ATOM_START && atomId <= MAX_PLATFORM_ATOM_TAG
+ || atomId >= VENDOR_PULLED_ATOM_START_TAG;
}
- for (int atomId : atomIds) {
- if (isPulledAtom(atomId)) {
+ void addAtom(Integer atom) {
+ if (Atom.getDescriptor().findFieldByNumber(atom) == null) {
+ LOGGER.severe("No such atom found: " + atom);
+ return;
+ }
+ if (isPulledAtom(atom)) {
+ mPulledAtoms.add(atom);
+ } else {
+ mPushedAtoms.add(atom);
+ }
+ }
+
+ private boolean hasPulledAtoms() {
+ return !mPulledAtoms.isEmpty();
+ }
+
+ private boolean hasPushedAtoms() {
+ return !mPushedAtoms.isEmpty();
+ }
+
+ StatsdConfig createConfig() {
+ long metricId = METRIC_ID_BASE;
+ long atomMatcherId = ATOM_MATCHER_ID_BASE;
+
+ StatsdConfig.Builder builder = baseBuilder();
+
+ if (hasPulledAtoms()) {
+ builder.addAtomMatcher(
+ createAtomMatcher(
+ Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER,
+ APP_BREADCRUMB_MATCHER_ID));
+ }
+
+ for (int atomId : mPulledAtoms) {
builder.addAtomMatcher(createAtomMatcher(atomId, atomMatcherId));
GaugeMetric.Builder gaugeMetricBuilder = GaugeMetric.newBuilder();
gaugeMetricBuilder
- .setId(metricId)
- .setWhat(atomMatcherId)
- .setTriggerEvent(APP_BREADCRUMB_MATCHER_ID)
- .setGaugeFieldsFilter(FieldFilter.newBuilder().setIncludeAll(true).build())
- .setBucket(TimeUnit.ONE_MINUTE)
- .setSamplingType(GaugeMetric.SamplingType.FIRST_N_SAMPLES)
- .setMaxNumGaugeAtomsPerBucket(100);
+ .setId(metricId)
+ .setWhat(atomMatcherId)
+ .setTriggerEvent(APP_BREADCRUMB_MATCHER_ID)
+ .setGaugeFieldsFilter(FieldFilter.newBuilder().setIncludeAll(true).build())
+ .setBucket(TimeUnit.ONE_MINUTE)
+ .setSamplingType(GaugeMetric.SamplingType.FIRST_N_SAMPLES)
+ .setMaxNumGaugeAtomsPerBucket(100);
builder.addGaugeMetric(gaugeMetricBuilder.build());
- } else {
- EventMetric.Builder eventMetricBuilder = EventMetric.newBuilder();
- eventMetricBuilder
- .setId(metricId)
- .setWhat(atomMatcherId);
- builder.addEventMetric(eventMetricBuilder.build());
- builder.addAtomMatcher(createAtomMatcher(atomId, atomMatcherId));
+ atomMatcherId++;
+ mTrackedMetrics.add(metricId++);
}
- atomMatcherId++;
- mTrackedMetrics.add(metricId++);
- }
- return builder.build();
- }
- private static AtomMatcher createAtomMatcher(int atomId, long matcherId) {
- AtomMatcher.Builder atomMatcherBuilder = AtomMatcher.newBuilder();
- atomMatcherBuilder
- .setId(matcherId)
- .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder().setAtomId(atomId));
- return atomMatcherBuilder.build();
+ // A simple atom matcher for each pushed atom.
+ List<AtomMatcher> simpleAtomMatchers = new ArrayList<>();
+ for (int atomId : mPushedAtoms) {
+ final AtomMatcher atomMatcher = createAtomMatcher(atomId, atomMatcherId++);
+ simpleAtomMatchers.add(atomMatcher);
+ builder.addAtomMatcher(atomMatcher);
+ }
+
+ if (mOnePushedAtomEvent) {
+ // Create a union event metric, using an matcher that matches all pulled atoms.
+ AtomMatcher unionAtomMatcher = createUnionMatcher(simpleAtomMatchers,
+ atomMatcherId);
+ builder.addAtomMatcher(unionAtomMatcher);
+ EventMetric.Builder eventMetricBuilder = EventMetric.newBuilder();
+ eventMetricBuilder.setId(metricId).setWhat(unionAtomMatcher.getId());
+ builder.addEventMetric(eventMetricBuilder.build());
+ mTrackedMetrics.add(metricId++);
+ } else {
+ // Create multiple event metrics, one per pulled atom.
+ for (AtomMatcher atomMatcher : simpleAtomMatchers) {
+ EventMetric.Builder eventMetricBuilder = EventMetric.newBuilder();
+ eventMetricBuilder
+ .setId(metricId)
+ .setWhat(atomMatcher.getId());
+ builder.addEventMetric(eventMetricBuilder.build());
+ mTrackedMetrics.add(metricId++);
+ }
+ }
+
+ return builder.build();
+ }
+
+ private static AtomMatcher createAtomMatcher(int atomId, long matcherId) {
+ AtomMatcher.Builder atomMatcherBuilder = AtomMatcher.newBuilder();
+ atomMatcherBuilder
+ .setId(matcherId)
+ .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder().setAtomId(atomId));
+ return atomMatcherBuilder.build();
+ }
+
+ private AtomMatcher createUnionMatcher(List<AtomMatcher> simpleAtomMatchers,
+ long atomMatcherId) {
+ AtomMatcher.Combination.Builder combinationBuilder =
+ AtomMatcher.Combination.newBuilder();
+ combinationBuilder.setOperation(StatsdConfigProto.LogicalOperation.OR);
+ for (AtomMatcher matcher : simpleAtomMatchers) {
+ combinationBuilder.addMatcher(matcher.getId());
+ }
+ AtomMatcher.Builder atomMatcherBuilder = AtomMatcher.newBuilder();
+ atomMatcherBuilder.setId(atomMatcherId).setCombination(combinationBuilder.build());
+ return atomMatcherBuilder.build();
+ }
+
+ private StatsdConfig.Builder baseBuilder() {
+ ArrayList<String> allowedSources = new ArrayList<>();
+ Collections.addAll(allowedSources, ALLOWED_LOG_SOURCES);
+ if (mAdditionalAllowedPackage != null) {
+ allowedSources.add(mAdditionalAllowedPackage);
+ }
+ return StatsdConfig.newBuilder()
+ .addAllAllowedLogSource(allowedSources)
+ .addAllDefaultPullPackages(Arrays.asList(DEFAULT_PULL_SOURCES))
+ .addPullAtomPackages(PullAtomPackages.newBuilder()
+ .setAtomId(Atom.GPU_STATS_GLOBAL_INFO_FIELD_NUMBER)
+ .addPackages("AID_GPU_SERVICE"))
+ .addPullAtomPackages(PullAtomPackages.newBuilder()
+ .setAtomId(Atom.GPU_STATS_APP_INFO_FIELD_NUMBER)
+ .addPackages("AID_GPU_SERVICE"))
+ .addPullAtomPackages(PullAtomPackages.newBuilder()
+ .setAtomId(Atom.TRAIN_INFO_FIELD_NUMBER)
+ .addPackages("AID_STATSD"))
+ .setHashStringsInMetricReport(false);
+ }
}
private static String pushConfig(StatsdConfig config, String deviceSerial)
@@ -267,21 +360,7 @@
Utils.runCommand(null, LOGGER, "adb", "-s", deviceSerial,
"shell", Utils.CMD_REMOVE_CONFIG, String.valueOf(CONFIG_ID));
} catch (Exception e) {
- LOGGER.log(Level.SEVERE, "Failed to remove config: " + e.getMessage());
+ LOGGER.severe("Failed to remove config: " + e.getMessage());
}
}
-
- private static boolean isPulledAtom(int atomId) {
- return atomId >= PULL_ATOM_START && atomId <= MAX_PLATFORM_ATOM_TAG
- || atomId >= VENDOR_PULLED_ATOM_START_TAG;
- }
-
- private static boolean hasPulledAtom(Set<Integer> atoms) {
- for (Integer i : atoms) {
- if (isPulledAtom(i)) {
- return true;
- }
- }
- return false;
- }
}
diff --git a/cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/ConfigurationTest.java b/cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/ConfigurationTest.java
new file mode 100644
index 0000000..b1cc60f
--- /dev/null
+++ b/cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/ConfigurationTest.java
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.statsd.shelltools.testdrive;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import com.android.internal.os.StatsdConfigProto;
+import com.android.internal.os.StatsdConfigProto.StatsdConfig;
+import com.android.os.AtomsProto;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Tests for {@link TestDrive}
+ */
+public class ConfigurationTest {
+
+ private StatsdConfigProto.AtomMatcher findAndRemoveAtomMatcherById(
+ List<StatsdConfigProto.AtomMatcher> atomMatchers, long id) {
+ int numMatches = 0;
+ StatsdConfigProto.AtomMatcher match = null;
+ for (StatsdConfigProto.AtomMatcher atomMatcher : atomMatchers) {
+ if (id == atomMatcher.getId()) {
+ ++numMatches;
+ match = atomMatcher;
+ }
+ }
+ if (numMatches == 1) {
+ atomMatchers.remove(match);
+ return match;
+ }
+ return null; // Too many, or not found
+ }
+
+ private final TestDrive.Configuration mConfiguration = new TestDrive.Configuration();
+
+ @Test
+ public void testOnePushed() {
+ final int atom = 90;
+ assertFalse(TestDrive.Configuration.isPulledAtom(atom));
+ mConfiguration.addAtom(atom);
+ StatsdConfig config = mConfiguration.createConfig();
+
+ //event_metric {
+ // id: 1111
+ // what: 1234567
+ //}
+ //atom_matcher {
+ // id: 1234567
+ // simple_atom_matcher {
+ // atom_id: 90
+ // }
+ //}
+
+ assertEquals(1, config.getEventMetricCount());
+ assertEquals(0, config.getGaugeMetricCount());
+
+ assertTrue(mConfiguration.isTrackedMetric(config.getEventMetric(0).getId()));
+
+ final List<StatsdConfigProto.AtomMatcher> atomMatchers =
+ new ArrayList<>(config.getAtomMatcherList());
+ assertEquals(atom,
+ findAndRemoveAtomMatcherById(atomMatchers, config.getEventMetric(0).getWhat())
+ .getSimpleAtomMatcher().getAtomId());
+ assertEquals(0, atomMatchers.size());
+ }
+
+ @Test
+ public void testOnePulled() {
+ final int atom = 10022;
+ assertTrue(TestDrive.Configuration.isPulledAtom(atom));
+ mConfiguration.addAtom(atom);
+ StatsdConfig config = mConfiguration.createConfig();
+
+ //gauge_metric {
+ // id: 1111
+ // what: 1234567
+ // gauge_fields_filter {
+ // include_all: true
+ // }
+ // bucket: ONE_MINUTE
+ // sampling_type: FIRST_N_SAMPLES
+ // max_num_gauge_atoms_per_bucket: 100
+ // trigger_event: 1111111
+ //}
+ //atom_matcher {
+ // id: 1111111
+ // simple_atom_matcher {
+ // atom_id: 47
+ // }
+ //}
+ //atom_matcher {
+ // id: 1234567
+ // simple_atom_matcher {
+ // atom_id: 10022
+ // }
+ //}
+
+ assertEquals(0, config.getEventMetricCount());
+ assertEquals(1, config.getGaugeMetricCount());
+
+ assertTrue(mConfiguration.isTrackedMetric(config.getGaugeMetric(0).getId()));
+
+ final StatsdConfigProto.GaugeMetric gaugeMetric = config.getGaugeMetric(0);
+ assertTrue(gaugeMetric.getGaugeFieldsFilter().getIncludeAll());
+
+ final List<StatsdConfigProto.AtomMatcher> atomMatchers =
+ new ArrayList<>(config.getAtomMatcherList());
+ assertEquals(atom,
+ findAndRemoveAtomMatcherById(atomMatchers, gaugeMetric.getWhat())
+ .getSimpleAtomMatcher().getAtomId());
+ assertEquals(AtomsProto.Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER,
+ findAndRemoveAtomMatcherById(atomMatchers, gaugeMetric.getTriggerEvent())
+ .getSimpleAtomMatcher().getAtomId());
+ assertEquals(0, atomMatchers.size());
+ }
+
+ @Test
+ public void testOnePulledTwoPushed() {
+ final int pulledAtom = 10022;
+ assertTrue(TestDrive.Configuration.isPulledAtom(pulledAtom));
+ mConfiguration.addAtom(pulledAtom);
+
+ Integer[] pushedAtoms = new Integer[]{244, 245};
+ for (int atom : pushedAtoms) {
+ assertFalse(TestDrive.Configuration.isPulledAtom(atom));
+ mConfiguration.addAtom(atom);
+ }
+ StatsdConfig config = mConfiguration.createConfig();
+
+ // event_metric {
+ // id: 1111
+ // what: 1234567
+ // }
+ // event_metric {
+ // id: 1112
+ // what: 1234568
+ // }
+ // gauge_metric {
+ // id: 1114
+ // what: 1234570
+ // gauge_fields_filter {
+ // include_all: true
+ // }
+ // bucket: ONE_MINUTE
+ // sampling_type: FIRST_N_SAMPLES
+ // max_num_gauge_atoms_per_bucket: 100
+ // trigger_event: 1111111
+ // }
+ // atom_matcher {
+ // id: 1111111
+ // simple_atom_matcher {
+ // atom_id: 47
+ // }
+ // }
+ // atom_matcher {
+ // id: 1234567
+ // simple_atom_matcher {
+ // atom_id: 244
+ // }
+ // }
+ // atom_matcher {
+ // id: 1234568
+ // simple_atom_matcher {
+ // atom_id: 245
+ // }
+ // }
+ // atom_matcher {
+ // id: 1234570
+ // simple_atom_matcher {
+ // atom_id: 10022
+ // }
+ // }
+
+ assertEquals(2, config.getEventMetricCount());
+ assertEquals(1, config.getGaugeMetricCount());
+
+ final StatsdConfigProto.GaugeMetric gaugeMetric = config.getGaugeMetric(0);
+ assertTrue(mConfiguration.isTrackedMetric(gaugeMetric.getId()));
+ assertTrue(gaugeMetric.getGaugeFieldsFilter().getIncludeAll());
+ for (StatsdConfigProto.EventMetric eventMetric : config.getEventMetricList()) {
+ assertTrue(mConfiguration.isTrackedMetric(eventMetric.getId()));
+ }
+
+ final List<StatsdConfigProto.AtomMatcher> atomMatchers =
+ new ArrayList<>(config.getAtomMatcherList());
+
+ assertEquals(pulledAtom, findAndRemoveAtomMatcherById(atomMatchers, gaugeMetric.getWhat())
+ .getSimpleAtomMatcher().getAtomId());
+ assertEquals(AtomsProto.Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER,
+ findAndRemoveAtomMatcherById(atomMatchers, gaugeMetric.getTriggerEvent())
+ .getSimpleAtomMatcher().getAtomId());
+
+ Integer[] actualAtoms = new Integer[]{
+ findAndRemoveAtomMatcherById(atomMatchers, config.getEventMetric(0).getWhat())
+ .getSimpleAtomMatcher().getAtomId(),
+ findAndRemoveAtomMatcherById(atomMatchers, config.getEventMetric(1).getWhat())
+ .getSimpleAtomMatcher().getAtomId()};
+ Arrays.sort(actualAtoms);
+ assertArrayEquals(pushedAtoms, actualAtoms);
+
+ assertEquals(0, atomMatchers.size());
+ }
+
+ @Test
+ public void testOnePulledTwoPushedTogether() {
+ mConfiguration.mOnePushedAtomEvent = true; // Use one event grabbing all pushed atoms
+
+ final int pulledAtom = 10022;
+ assertTrue(TestDrive.Configuration.isPulledAtom(pulledAtom));
+ mConfiguration.addAtom(pulledAtom);
+
+ Integer[] pushedAtoms = new Integer[]{244, 245};
+ for (int atom : pushedAtoms) {
+ assertFalse(TestDrive.Configuration.isPulledAtom(atom));
+ mConfiguration.addAtom(atom);
+ }
+ StatsdConfig config = mConfiguration.createConfig();
+
+ // event_metric {
+ // id: 1112
+ // what: 1234570
+ // }
+ // gauge_metric {
+ // id: 1111
+ // what: 1234567
+ // gauge_fields_filter {
+ // include_all: true
+ // }
+ // bucket: ONE_MINUTE
+ // sampling_type: FIRST_N_SAMPLES
+ // max_num_gauge_atoms_per_bucket: 100
+ // trigger_event: 1111111
+ // }
+ // atom_matcher {
+ // id: 1111111
+ // simple_atom_matcher {
+ // atom_id: 47
+ // }
+ // }
+ // atom_matcher {
+ // id: 1234567
+ // simple_atom_matcher {
+ // atom_id: 10022
+ // }
+ // }
+ // atom_matcher {
+ // id: 1234568
+ // simple_atom_matcher {
+ // atom_id: 244
+ // }
+ // }
+ // atom_matcher {
+ // id: 1234569
+ // simple_atom_matcher {
+ // atom_id: 245
+ // }
+ // }
+ // atom_matcher {
+ // id: 1234570
+ // combination {
+ // operation: OR
+ // matcher: 1234568
+ // matcher: 1234569
+ // }
+ // }
+
+ assertEquals(1, config.getEventMetricCount());
+ assertEquals(1, config.getGaugeMetricCount());
+
+ final StatsdConfigProto.GaugeMetric gaugeMetric = config.getGaugeMetric(0);
+ assertTrue(mConfiguration.isTrackedMetric(gaugeMetric.getId()));
+ assertTrue(gaugeMetric.getGaugeFieldsFilter().getIncludeAll());
+
+ StatsdConfigProto.EventMetric eventMetric = config.getEventMetric(0);
+ assertTrue(mConfiguration.isTrackedMetric(eventMetric.getId()));
+
+ final List<StatsdConfigProto.AtomMatcher> atomMatchers =
+ new ArrayList<>(config.getAtomMatcherList());
+
+ assertEquals(pulledAtom, findAndRemoveAtomMatcherById(atomMatchers, gaugeMetric.getWhat())
+ .getSimpleAtomMatcher().getAtomId());
+ assertEquals(AtomsProto.Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER,
+ findAndRemoveAtomMatcherById(atomMatchers, gaugeMetric.getTriggerEvent())
+ .getSimpleAtomMatcher().getAtomId());
+
+ StatsdConfigProto.AtomMatcher unionMatcher = findAndRemoveAtomMatcherById(atomMatchers,
+ eventMetric.getWhat());
+ assertNotNull(unionMatcher.getCombination());
+ assertEquals(2, unionMatcher.getCombination().getMatcherCount());
+
+ Integer[] actualAtoms = new Integer[]{
+ findAndRemoveAtomMatcherById(atomMatchers,
+ unionMatcher.getCombination().getMatcher(0))
+ .getSimpleAtomMatcher().getAtomId(),
+ findAndRemoveAtomMatcherById(atomMatchers,
+ unionMatcher.getCombination().getMatcher(1))
+ .getSimpleAtomMatcher().getAtomId()};
+ Arrays.sort(actualAtoms);
+ assertArrayEquals(pushedAtoms, actualAtoms);
+
+ assertEquals(0, atomMatchers.size());
+ }
+}
diff --git a/cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/TestDriveTest.java b/cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/TestDriveTest.java
new file mode 100644
index 0000000..9d986e7
--- /dev/null
+++ b/cmds/statsd/tools/localtools/test/com/android/statsd/shelltools/testdrive/TestDriveTest.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.statsd.shelltools.testdrive;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Tests for {@link TestDrive}
+ */
+@RunWith(Parameterized.class)
+public class TestDriveTest {
+ /**
+ * Expected results of a single iteration of the paramerized test.
+ */
+ static class Expect {
+ public boolean success;
+ public Integer[] atoms;
+ public boolean onePushedAtomEvent;
+ public String extraPackage;
+ public String target;
+
+ static Expect success(Integer... atoms) {
+ return new Expect(true, atoms, false, null,
+ TARGET);
+ }
+ Expect(boolean success, Integer[] atoms, boolean onePushedAtomEvent, String extraPackage,
+ String target) {
+ this.success = success;
+ this.atoms = atoms;
+ this.onePushedAtomEvent = onePushedAtomEvent;
+ this.extraPackage = extraPackage;
+ this.target = target;
+ }
+ static final Expect FAILURE = new Expect(false, null,
+ false, null, null);
+ Expect onePushedAtomEvent() {
+ this.onePushedAtomEvent = true;
+ return this;
+ }
+ Expect extraPackage() {
+ this.extraPackage = TestDriveTest.PACKAGE;
+ return this;
+ }
+ }
+
+ @Parameterized.Parameter(0)
+ public String[] mArgs;
+
+ @Parameterized.Parameter(1)
+ public List<String> mConnectedDevices;
+
+ @Parameterized.Parameter(2)
+ public String mDefaultDevice;
+
+ @Parameterized.Parameter(3)
+ public Expect mExpect;
+
+ private static final String TARGET = "target";
+ private static final List<String> TARGET_AND_OTHER = Arrays.asList("otherDevice",
+ TARGET);
+ private static final List<String> TWO_OTHER_DEVICES = Arrays.asList(
+ "other1", "other2");
+ private static final List<String> TARGET_ONLY = Collections.singletonList(TARGET);
+ private static final List<String> NOT_TARGET = Collections.singletonList("other");
+ private static final List<String> NO_DEVICES = Collections.emptyList();
+ private static final String PACKAGE = "extraPackage";
+
+ @Parameterized.Parameters
+ public static Collection<Object[]> data() {
+ return Arrays.asList(
+ new Object[]{new String[]{}, null, null,
+ Expect.FAILURE}, // Usage explanation
+ new Object[]{new String[]{"244", "245"}, null, null,
+ Expect.FAILURE}, // Failure looking up connected devices
+ new Object[]{new String[]{"244", "245"}, NO_DEVICES, null,
+ Expect.FAILURE}, // No connected devices
+ new Object[]{new String[]{"-s", TARGET, "244", "245"}, NOT_TARGET, null,
+ Expect.FAILURE}, // Wrong device connected
+ new Object[]{new String[]{"244", "245"}, TWO_OTHER_DEVICES, null,
+ Expect.FAILURE}, // Wrong devices connected
+ new Object[]{new String[]{"244", "245"}, TARGET_ONLY, null,
+ Expect.success(244, 245)}, // If only one device connected, guess that one
+ new Object[]{new String[]{"244", "not_an_atom"}, TARGET_ONLY, null,
+ Expect.success(244)}, // Ignore non-atoms
+ new Object[]{new String[]{"not_an_atom"}, TARGET_ONLY, null,
+ Expect.FAILURE}, // Require at least one atom
+ new Object[]{new String[]{"244", "245"}, TWO_OTHER_DEVICES, TARGET,
+ Expect.FAILURE}, // ANDROID_SERIAL specifies non-connected target
+ new Object[]{new String[]{"244", "245"}, TARGET_AND_OTHER, TARGET,
+ Expect.success(244, 245)}, // ANDROID_SERIAL specifies a valid target
+ new Object[]{new String[]{"244", "245"}, TARGET_AND_OTHER, null,
+ Expect.FAILURE}, // Two connected devices, no indication of which to use
+ new Object[]{new String[]{"-one", "244", "245"}, TARGET_ONLY, null,
+ Expect.success(244, 245).onePushedAtomEvent()},
+ new Object[]{new String[]{"-p", PACKAGE, "244", "245"}, TARGET_ONLY, null,
+ Expect.success(244, 245).extraPackage()},
+ new Object[]{new String[]{"-p", PACKAGE, "-one", "244", "245"}, TARGET_ONLY, null,
+ Expect.success(244, 245).extraPackage().onePushedAtomEvent()},
+ new Object[]{new String[]{"-one", "-p", PACKAGE, "244", "245"}, TARGET_ONLY, null,
+ Expect.success(244, 245).extraPackage().onePushedAtomEvent()},
+ new Object[]{new String[]{"-s", TARGET, "-one", "-p", PACKAGE, "244", "245"},
+ TARGET_AND_OTHER, null,
+ Expect.success(244, 245).extraPackage().onePushedAtomEvent()},
+ new Object[]{new String[]{"-one", "-s", TARGET, "-p", PACKAGE, "244", "245"},
+ TARGET_AND_OTHER, null,
+ Expect.success(244, 245).extraPackage().onePushedAtomEvent()},
+ new Object[]{new String[]{"-one", "-p", PACKAGE, "-s", TARGET, "244", "245"},
+ TARGET_AND_OTHER, null,
+ Expect.success(244, 245).extraPackage().onePushedAtomEvent()}
+ );
+ }
+
+ private final TestDrive.Configuration mConfiguration = new TestDrive.Configuration();
+ private final TestDrive mTestDrive = new TestDrive();
+
+ private static Integer[] collectAtoms(TestDrive.Configuration configuration) {
+ Integer[] result = new Integer[configuration.mPulledAtoms.size()
+ + configuration.mPushedAtoms.size()];
+ int result_index = 0;
+ for (Integer atom : configuration.mPushedAtoms) {
+ result[result_index++] = atom;
+ }
+ for (Integer atom : configuration.mPulledAtoms) {
+ result[result_index++] = atom;
+ }
+ Arrays.sort(result);
+ return result;
+ }
+
+ @Test
+ public void testProcessArgs() {
+ boolean result = mTestDrive.processArgs(mConfiguration, mArgs, mConnectedDevices,
+ mDefaultDevice);
+ if (mExpect.success) {
+ assertTrue(result);
+ assertArrayEquals(mExpect.atoms, collectAtoms(mConfiguration));
+ assertEquals(mExpect.onePushedAtomEvent, mConfiguration.mOnePushedAtomEvent);
+ assertEquals(mExpect.target, mTestDrive.mDeviceSerial);
+ } else {
+ assertFalse(result);
+ }
+ }
+}
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 18e5c3d..77b3c81 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -501,6 +501,18 @@
*/
public static final int GLOBAL_ACTION_KEYCODE_HEADSETHOOK = 10;
+ /**
+ * Action to trigger the Accessibility Button
+ * @hide
+ */
+ public static final int GLOBAL_ACTION_ACCESSIBILITY_BUTTON = 11;
+
+ /**
+ * Action to bring up the Accessibility Button's chooser menu
+ * @hide
+ */
+ public static final int GLOBAL_ACTION_ACCESSIBILITY_BUTTON_CHOOSER = 12;
+
private static final String LOG_TAG = "AccessibilityService";
/**
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 6480a6a..e0ae750 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -6549,8 +6549,12 @@
* {@link #RESULT_CANCELED} if the activity explicitly returned that,
* didn't return any result, or crashed during its operation.
*
- * <p>You will receive this call immediately before onResume() when your
- * activity is re-starting.
+ * <p>An activity can never receive a result in the resumed state. You can count on
+ * {@link #onResume} being called after this method, though not necessarily immediately after.
+ * If the activity was resumed, it will be paused and the result will be delivered, followed
+ * by {@link #onResume}. If the activity wasn't in the resumed state, then the result will
+ * be delivered, with {@link #onResume} called sometime later when the activity becomes active
+ * again.
*
* <p>This method is never invoked if your activity sets
* {@link android.R.styleable#AndroidManifestActivity_noHistory noHistory} to
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index bb64c34..a4256a9 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -250,6 +250,10 @@
private final Object mSync = new Object();
+ /**
+ * Whether this is created from {@link #createSystemContext(ActivityThread)} or
+ * {@link #createSystemUiContext(ContextImpl, int)} or any {@link Context} that system UI uses.
+ */
private boolean mIsSystemOrSystemUiContext;
private boolean mIsUiContext;
private boolean mIsAssociatedWithDisplay;
@@ -1922,16 +1926,18 @@
/** @hide */
@Override
public boolean isUiContext() {
- return mIsSystemOrSystemUiContext || mIsUiContext || isSystemOrSystemUI();
+ return mIsSystemOrSystemUiContext || mIsUiContext;
}
/**
* Temporary workaround to permit incorrect usages of Context by SystemUI.
- * TODO(b/149790106): Fix usages and remove.
+ * TODO(b/147647877): Fix usages and remove.
*/
- private boolean isSystemOrSystemUI() {
- return ActivityThread.isSystem() || checkPermission("android.permission.STATUS_BAR_SERVICE",
- Binder.getCallingPid(), Binder.getCallingUid()) == PERMISSION_GRANTED;
+ private static boolean isSystemOrSystemUI(Context context) {
+ return ActivityThread.isSystem() || context.checkPermission(
+ "android.permission.STATUS_BAR_SERVICE",
+ Binder.getCallingPid(),
+ Binder.getCallingUid()) == PERMISSION_GRANTED;
}
private static boolean isUiComponent(String name) {
@@ -2467,7 +2473,7 @@
@Override
public Display getDisplay() {
- if (!mIsSystemOrSystemUiContext && !mIsAssociatedWithDisplay && !isSystemOrSystemUI()) {
+ if (!mIsSystemOrSystemUiContext && !mIsAssociatedWithDisplay) {
throw new UnsupportedOperationException("Tried to obtain display from a Context not "
+ "associated with one. Only visual Contexts (such as Activity or one created "
+ "with Context#createWindowContext) or ones created with "
@@ -2643,6 +2649,7 @@
ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, null,
0, null, opPackageName);
context.setResources(packageInfo.getResources());
+ context.mIsSystemOrSystemUiContext = isSystemOrSystemUI(context);
return context;
}
@@ -2672,6 +2679,7 @@
activityInfo.splitName, activityToken, null, 0, classLoader, null);
context.mIsUiContext = true;
context.mIsAssociatedWithDisplay = true;
+ context.mIsSystemOrSystemUiContext = isSystemOrSystemUI(context);
// Clamp display ID to DEFAULT_DISPLAY if it is INVALID_DISPLAY.
displayId = (displayId != Display.INVALID_DISPLAY) ? displayId : Display.DEFAULT_DISPLAY;
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index 145d513..1a619bd 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -43,9 +43,10 @@
* @param homeVisible whether or not the home task is visible
* @param clearedTask whether or not the launch activity also cleared the task as a part of
* starting
+ * @param wasVisible whether the activity was visible before the restart attempt
*/
void onActivityRestartAttempt(in ActivityManager.RunningTaskInfo task, boolean homeTaskVisible,
- boolean clearedTask);
+ boolean clearedTask, boolean wasVisible);
/**
* Called when we launched an activity that we forced to be resizable.
diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
index 93772de..bfa91aa 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -55,7 +55,7 @@
@Override
@UnsupportedAppUsage
public void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible,
- boolean clearedTask) throws RemoteException {
+ boolean clearedTask, boolean wasVisible) throws RemoteException {
}
@Override
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 4b05045..8073982 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -10251,6 +10251,9 @@
* <p>
* The confirm credentials screen can be created using
* {@link android.app.KeyguardManager#createConfirmDeviceCredentialIntent}.
+ * <p>
+ * Starting from Android R, the organization color will no longer be used as the background
+ * color of the confirm credentials screen.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param color The 24bit (0xRRGGBB) representation of the color to be used.
diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java
index 1923bf3..2342165 100644
--- a/core/java/android/content/ClipData.java
+++ b/core/java/android/content/ClipData.java
@@ -27,7 +27,6 @@
import android.net.Uri;
import android.os.Build;
import android.os.Parcel;
-import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
import android.os.StrictMode;
import android.text.Html;
@@ -171,11 +170,6 @@
static final String[] MIMETYPES_TEXT_INTENT = new String[] {
ClipDescription.MIMETYPE_TEXT_INTENT };
- // Constants used in {@link #writeHtmlTextToParcel}.
- static final int PARCEL_MAX_SIZE_BYTES = 800 * 1024;
- static final int PARCEL_TYPE_STRING = 0;
- static final int PARCEL_TYPE_PFD = 1;
-
final ClipDescription mClipDescription;
final Bitmap mIcon;
@@ -231,8 +225,7 @@
* with an alternative HTML formatted representation. You <em>must</em>
* supply a plain text representation in addition to HTML text; coercion
* will not be done from HTML formatted text into plain text.
- * <p class="note"><strong>Note:</strong> It is strongly recommended to
- * use content: URI for sharing large clip data. Starting on API 30,
+ * <p><strong>Warning:</strong> Use content: URI for sharing large clip data.
* ClipData.Item doesn't accept an HTML text if it's larger than 800KB.
* </p>
*/
@@ -1135,7 +1128,7 @@
for (int i=0; i<N; i++) {
Item item = mItems.get(i);
TextUtils.writeToParcel(item.mText, dest, flags);
- writeHtmlTextToParcel(item.mHtmlText, dest, flags);
+ dest.writeString8(item.mHtmlText);
if (item.mIntent != null) {
dest.writeInt(1);
item.mIntent.writeToParcel(dest, flags);
@@ -1162,7 +1155,7 @@
final int N = in.readInt();
for (int i=0; i<N; i++) {
CharSequence text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
- String htmlText = readHtmlTextFromParcel(in);
+ String htmlText = in.readString8();
Intent intent = in.readInt() != 0 ? Intent.CREATOR.createFromParcel(in) : null;
Uri uri = in.readInt() != 0 ? Uri.CREATOR.createFromParcel(in) : null;
mItems.add(new Item(text, htmlText, intent, uri));
@@ -1182,61 +1175,4 @@
return new ClipData[size];
}
};
-
- /**
- * Helper function for writing an HTML text into a parcel.
- * If the text size is larger than 400KB, it writes the text to a file descriptor to prevent the
- * parcel from exceeding 800KB binder size limit. {@link android.os.Binder#checkParcel()}
- * Otherwise, it directly writes the text into the parcel.
- * Note: This function is a workaround for existing applications that still use HTML for sharing
- * large clip data. We will ask application developers to use content: URI instead and remove
- * this function in API 30.
- */
- private static void writeHtmlTextToParcel(String text, Parcel dest, int flags) {
- byte[] textData = (text != null) ? text.getBytes() : new byte[0];
- if (textData.length > PARCEL_MAX_SIZE_BYTES / 2
- && Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {
- try {
- ParcelFileDescriptor pfd = ParcelFileDescriptor.fromData(textData, null);
- dest.writeInt(PARCEL_TYPE_PFD);
- dest.writeParcelable(pfd, flags);
- } catch (IOException e) {
- throw new IllegalStateException(
- "Error creating the shared memory area: " + e.toString());
- }
- } else {
- dest.writeInt(PARCEL_TYPE_STRING);
- dest.writeString8(text);
- }
- }
-
- /**
- * Reads a text written by writeHtmlTextToParcel.
- */
- private static String readHtmlTextFromParcel(Parcel in) {
- if (in.readInt() == PARCEL_TYPE_STRING) {
- return in.readString8();
- }
- ParcelFileDescriptor pfd =
- in.readParcelable(ParcelFileDescriptor.class.getClassLoader());
- if (pfd == null) {
- throw new IllegalStateException("Error reading ParcelFileDescriptor from Parcel");
- }
- FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
- InputStreamReader reader = new InputStreamReader(fis);
- StringBuilder builder = new StringBuilder();
- char[] buffer = new char[4096];
- int numRead;
- try {
- while ((numRead = reader.read(buffer)) != -1) {
- builder.append(buffer, 0, numRead);
- }
- return builder.toString();
- } catch (IOException e) {
- throw new IllegalStateException(
- "Error reading data from ParcelFileDescriptor: " + e.toString());
- } finally {
- IoUtils.closeQuietly(fis);
- }
- }
}
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 87dc0a1..bbcac56 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -507,7 +507,8 @@
/**
* Indicates that one or more shortcuts, that match the {@link ShortcutQuery} used to
* register this callback, have been added or updated.
- * @see LauncherApps#registerShortcutChangeCallback(ShortcutChangeCallback, ShortcutQuery)
+ * @see LauncherApps#registerShortcutChangeCallback(ShortcutChangeCallback, ShortcutQuery,
+ * Executor)
*
* <p>Only the applications that are allowed to access the shortcut information,
* as defined in {@link #hasShortcutHostPermission()}, will receive it.
@@ -525,7 +526,8 @@
/**
* Indicates that one or more shortcuts, that match the {@link ShortcutQuery} used to
* register this callback, have been removed.
- * @see LauncherApps#registerShortcutChangeCallback(ShortcutChangeCallback, ShortcutQuery)
+ * @see LauncherApps#registerShortcutChangeCallback(ShortcutChangeCallback, ShortcutQuery,
+ * Executor)
*
* <p>Only the applications that are allowed to access the shortcut information,
* as defined in {@link #hasShortcutHostPermission()}, will receive it.
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index 8d8776f..41f9a6e 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -1759,6 +1759,12 @@
return isDeclaredInManifest() && isVisibleToPublisher();
}
+ /** @hide */
+ public boolean isNonManifestVisible() {
+ return !isDeclaredInManifest() && isVisibleToPublisher()
+ && (isPinned() || isCached() || isDynamic());
+ }
+
/**
* Return if a shortcut is immutable, in which case it cannot be modified with any of
* {@link ShortcutManager} APIs.
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index 8108cf0..e7bba69 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -775,17 +775,10 @@
* @see android.content.pm.ApplicationInfo#uid
*/
public static long getUidTxBytes(int uid) {
- // This isn't actually enforcing any security; it just returns the
- // unsupported value. The real filtering is done at the kernel level.
- final int callingUid = android.os.Process.myUid();
- if (callingUid == android.os.Process.SYSTEM_UID || callingUid == uid) {
- try {
- return getStatsService().getUidStats(uid, TYPE_TX_BYTES);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- } else {
- return UNSUPPORTED;
+ try {
+ return getStatsService().getUidStats(uid, TYPE_TX_BYTES);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
@@ -808,17 +801,10 @@
* @see android.content.pm.ApplicationInfo#uid
*/
public static long getUidRxBytes(int uid) {
- // This isn't actually enforcing any security; it just returns the
- // unsupported value. The real filtering is done at the kernel level.
- final int callingUid = android.os.Process.myUid();
- if (callingUid == android.os.Process.SYSTEM_UID || callingUid == uid) {
- try {
- return getStatsService().getUidStats(uid, TYPE_RX_BYTES);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- } else {
- return UNSUPPORTED;
+ try {
+ return getStatsService().getUidStats(uid, TYPE_RX_BYTES);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
@@ -841,17 +827,10 @@
* @see android.content.pm.ApplicationInfo#uid
*/
public static long getUidTxPackets(int uid) {
- // This isn't actually enforcing any security; it just returns the
- // unsupported value. The real filtering is done at the kernel level.
- final int callingUid = android.os.Process.myUid();
- if (callingUid == android.os.Process.SYSTEM_UID || callingUid == uid) {
- try {
- return getStatsService().getUidStats(uid, TYPE_TX_PACKETS);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- } else {
- return UNSUPPORTED;
+ try {
+ return getStatsService().getUidStats(uid, TYPE_TX_PACKETS);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
@@ -874,17 +853,10 @@
* @see android.content.pm.ApplicationInfo#uid
*/
public static long getUidRxPackets(int uid) {
- // This isn't actually enforcing any security; it just returns the
- // unsupported value. The real filtering is done at the kernel level.
- final int callingUid = android.os.Process.myUid();
- if (callingUid == android.os.Process.SYSTEM_UID || callingUid == uid) {
- try {
- return getStatsService().getUidStats(uid, TYPE_RX_PACKETS);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- } else {
- return UNSUPPORTED;
+ try {
+ return getStatsService().getUidStats(uid, TYPE_RX_PACKETS);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java
index f08e3d25..ed429dd 100644
--- a/core/java/android/permission/PermissionControllerManager.java
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -161,6 +161,7 @@
*
* @hide
*/
+ @TestApi
public interface OnCountPermissionAppsResultCallback {
/**
* The result for {@link #countPermissionApps(List, int,
@@ -514,6 +515,7 @@
*
* @hide
*/
+ @TestApi
@RequiresPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS)
public void revokeRuntimePermission(@NonNull String packageName,
@NonNull String permissionName) {
@@ -534,6 +536,7 @@
*
* @hide
*/
+ @TestApi
@RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS)
public void countPermissionApps(@NonNull List<String> permissionNames,
@CountPermissionAppsFlag int flags,
diff --git a/core/java/android/permission/Permissions.md b/core/java/android/permission/Permissions.md
index e116dc6..2bf08e2 100644
--- a/core/java/android/permission/Permissions.md
+++ b/core/java/android/permission/Permissions.md
@@ -728,7 +728,7 @@
Almost always the protection level is app-op | something else, like
[signature](#signature-permissions) (in the case above) or [privileged](#privileged-permissions).
-#### Checking a app-op permission
+#### Checking an app-op permission
The `PermissionChecker` utility can check app-op permissions with the [same syntax as runtime
permissions](#checking-a-runtime-permission).
@@ -764,7 +764,7 @@
}
```
-#### Granting a app-op permission
+#### Granting an app-op permission
The permission's grant state is only considered if the app-op's mode is `MODE_DEFAULT`. This
allows to have default grants while still being overridden by the app-op.
diff --git a/core/java/android/permission/TEST_MAPPING b/core/java/android/permission/TEST_MAPPING
index ba9f36a..69113ef 100644
--- a/core/java/android/permission/TEST_MAPPING
+++ b/core/java/android/permission/TEST_MAPPING
@@ -5,6 +5,9 @@
"options": [
{
"include-filter": "android.permission.cts.PermissionControllerTest"
+ },
+ {
+ "include-filter": "android.permission.cts.RuntimePermissionPresentationInfoTest"
}
]
}
diff --git a/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl b/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl
index bed4302..172cfef 100644
--- a/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl
+++ b/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl
@@ -28,7 +28,7 @@
oneway interface IInlineSuggestionUiCallback {
void onClick();
void onLongClick();
- void onContent(in SurfaceControlViewHost.SurfacePackage surface);
+ void onContent(in SurfaceControlViewHost.SurfacePackage surface, int width, int height);
void onError();
void onTransferTouchFocusToImeWindow(in IBinder sourceInputToken, int displayId);
void onStartIntentSender(in IntentSender intentSender);
diff --git a/core/java/android/service/autofill/InlineSuggestionRenderService.java b/core/java/android/service/autofill/InlineSuggestionRenderService.java
index d3ad1c6..19961e5 100644
--- a/core/java/android/service/autofill/InlineSuggestionRenderService.java
+++ b/core/java/android/service/autofill/InlineSuggestionRenderService.java
@@ -22,7 +22,6 @@
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.Service;
-import android.app.slice.Slice;
import android.content.Intent;
import android.content.IntentSender;
import android.graphics.PixelFormat;
@@ -34,14 +33,15 @@
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.util.Log;
+import android.util.Size;
import android.view.Display;
import android.view.SurfaceControlViewHost;
import android.view.View;
+import android.view.ViewGroup;
import android.view.WindowManager;
/**
- * A service that renders an inline presentation given the {@link InlinePresentation} containing
- * a {@link Slice} built using the {@link androidx.autofill.AutofillSliceBuilder}.
+ * A service that renders an inline presentation view given the {@link InlinePresentation}.
*
* {@hide}
*/
@@ -55,8 +55,8 @@
* The {@link Intent} that must be declared as handled by the service.
*
* <p>To be supported, the service must also require the
- * {@link android.Manifest.permission#BIND_INLINE_SUGGESTION_RENDER_SERVICE} permission so
- * that other applications can not abuse it.
+ * {@link android.Manifest.permission#BIND_INLINE_SUGGESTION_RENDER_SERVICE} permission so that
+ * other applications can not abuse it.
*/
public static final String SERVICE_INTERFACE =
"android.service.autofill.InlineSuggestionRenderService";
@@ -65,6 +65,45 @@
private IInlineSuggestionUiCallback mCallback;
+ /**
+ * If the specified {@code width}/{@code height} is an exact value, then it will be returned as
+ * is, otherwise the method tries to measure a size that is just large enough to fit the view
+ * content, within constraints posed by {@code minSize} and {@code maxSize}.
+ *
+ * @param view the view for which we measure the size
+ * @param width the expected width of the view, either an exact value or {@link
+ * ViewGroup.LayoutParams#WRAP_CONTENT}
+ * @param height the expected width of the view, either an exact value or {@link
+ * ViewGroup.LayoutParams#WRAP_CONTENT}
+ * @param minSize the lower bound of the size to be returned
+ * @param maxSize the upper bound of the size to be returned
+ * @return the measured size of the view based on the given size constraints.
+ */
+ private Size measuredSize(@NonNull View view, int width, int height, @NonNull Size minSize,
+ @NonNull Size maxSize) {
+ if (width != ViewGroup.LayoutParams.WRAP_CONTENT
+ && height != ViewGroup.LayoutParams.WRAP_CONTENT) {
+ return new Size(width, height);
+ }
+ int widthMeasureSpec;
+ if (width == ViewGroup.LayoutParams.WRAP_CONTENT) {
+ widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(maxSize.getWidth(),
+ View.MeasureSpec.AT_MOST);
+ } else {
+ widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
+ }
+ int heightMeasureSpec;
+ if (height == ViewGroup.LayoutParams.WRAP_CONTENT) {
+ heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(maxSize.getHeight(),
+ View.MeasureSpec.AT_MOST);
+ } else {
+ heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);
+ }
+ view.measure(widthMeasureSpec, heightMeasureSpec);
+ return new Size(Math.max(view.getMeasuredWidth(), minSize.getWidth()),
+ Math.max(view.getMeasuredHeight(), minSize.getHeight()));
+ }
+
private void handleRenderSuggestion(IInlineSuggestionUiCallback callback,
InlinePresentation presentation, int width, int height, IBinder hostInputToken,
int displayId) {
@@ -82,6 +121,7 @@
try {
final View suggestionView = onRenderSuggestion(presentation, width, height);
if (suggestionView == null) {
+ Log.w(TAG, "ExtServices failed to render the inline suggestion view.");
try {
callback.onError();
} catch (RemoteException e) {
@@ -90,13 +130,16 @@
return;
}
mCallback = callback;
+ final Size measuredSize = measuredSize(suggestionView, width, height,
+ presentation.getInlinePresentationSpec().getMinSize(),
+ presentation.getInlinePresentationSpec().getMaxSize());
+ Log.v(TAG, "width=" + width + ", height=" + height + ", measuredSize=" + measuredSize);
final InlineSuggestionRoot suggestionRoot = new InlineSuggestionRoot(this, callback);
suggestionRoot.addView(suggestionView);
- WindowManager.LayoutParams lp =
- new WindowManager.LayoutParams(width, height,
- WindowManager.LayoutParams.TYPE_APPLICATION, 0,
- PixelFormat.TRANSPARENT);
+ WindowManager.LayoutParams lp = new WindowManager.LayoutParams(measuredSize.getWidth(),
+ measuredSize.getHeight(), WindowManager.LayoutParams.TYPE_APPLICATION, 0,
+ PixelFormat.TRANSPARENT);
final SurfaceControlViewHost host = new SurfaceControlViewHost(this, getDisplay(),
hostInputToken);
@@ -124,7 +167,8 @@
return true;
});
- sendResult(callback, host.getSurfacePackage());
+ sendResult(callback, host.getSurfacePackage(), measuredSize.getWidth(),
+ measuredSize.getHeight());
} finally {
updateDisplay(Display.DEFAULT_DISPLAY);
}
@@ -136,9 +180,9 @@
}
private void sendResult(@NonNull IInlineSuggestionUiCallback callback,
- @Nullable SurfaceControlViewHost.SurfacePackage surface) {
+ @Nullable SurfaceControlViewHost.SurfacePackage surface, int width, int height) {
try {
- callback.onContent(surface);
+ callback.onContent(surface, width, height);
} catch (RemoteException e) {
Log.w(TAG, "RemoteException calling onContent(" + surface + ")");
}
@@ -154,10 +198,10 @@
public void renderSuggestion(@NonNull IInlineSuggestionUiCallback callback,
@NonNull InlinePresentation presentation, int width, int height,
@Nullable IBinder hostInputToken, int displayId) {
- mHandler.sendMessage(obtainMessage(
- InlineSuggestionRenderService::handleRenderSuggestion,
- InlineSuggestionRenderService.this, callback, presentation,
- width, height, hostInputToken, displayId));
+ mHandler.sendMessage(
+ obtainMessage(InlineSuggestionRenderService::handleRenderSuggestion,
+ InlineSuggestionRenderService.this, callback, presentation,
+ width, height, hostInputToken, displayId));
}
@Override
@@ -176,7 +220,8 @@
/**
* Starts the {@link IntentSender} from the client app.
*
- * @param intentSender the {@link IntentSender} to start the attribution UI from the client app.
+ * @param intentSender the {@link IntentSender} to start the attribution UI from the client
+ * app.
*/
public final void startIntentSender(@NonNull IntentSender intentSender) {
if (mCallback == null) return;
@@ -188,8 +233,8 @@
}
/**
- * Returns the metadata about the renderer. Returns {@code Bundle.Empty} if no metadata is
- * provided.
+ * Returns the metadata about the renderer. Returns {@code Bundle.Empty} if no metadata is
+ * provided.
*/
@NonNull
public Bundle onGetInlineSuggestionsRendererInfo() {
@@ -200,8 +245,8 @@
* Renders the slice into a view.
*/
@Nullable
- public View onRenderSuggestion(@NonNull InlinePresentation presentation,
- int width, int height) {
+ public View onRenderSuggestion(@NonNull InlinePresentation presentation, int width,
+ int height) {
Log.e(TAG, "service implementation (" + getClass() + " does not implement "
+ "onRenderSuggestion()");
return null;
diff --git a/core/java/android/view/DisplayAdjustments.java b/core/java/android/view/DisplayAdjustments.java
index 834dd7b..27c2d5c 100644
--- a/core/java/android/view/DisplayAdjustments.java
+++ b/core/java/android/view/DisplayAdjustments.java
@@ -16,6 +16,8 @@
package android.view;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
@@ -27,25 +29,25 @@
public static final DisplayAdjustments DEFAULT_DISPLAY_ADJUSTMENTS = new DisplayAdjustments();
private volatile CompatibilityInfo mCompatInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
- private Configuration mConfiguration;
+ private final Configuration mConfiguration = new Configuration(Configuration.EMPTY);
@UnsupportedAppUsage
public DisplayAdjustments() {
}
- public DisplayAdjustments(Configuration configuration) {
- mConfiguration = new Configuration(configuration != null
- ? configuration : Configuration.EMPTY);
+ public DisplayAdjustments(@Nullable Configuration configuration) {
+ if (configuration != null) {
+ mConfiguration.setTo(configuration);
+ }
}
- public DisplayAdjustments(DisplayAdjustments daj) {
+ public DisplayAdjustments(@NonNull DisplayAdjustments daj) {
setCompatibilityInfo(daj.mCompatInfo);
- mConfiguration = new Configuration(daj.mConfiguration != null
- ? daj.mConfiguration : Configuration.EMPTY);
+ mConfiguration.setTo(daj.getConfiguration());
}
@UnsupportedAppUsage
- public void setCompatibilityInfo(CompatibilityInfo compatInfo) {
+ public void setCompatibilityInfo(@Nullable CompatibilityInfo compatInfo) {
if (this == DEFAULT_DISPLAY_ADJUSTMENTS) {
throw new IllegalArgumentException(
"setCompatbilityInfo: Cannot modify DEFAULT_DISPLAY_ADJUSTMENTS");
@@ -62,7 +64,13 @@
return mCompatInfo;
}
- public void setConfiguration(Configuration configuration) {
+ /**
+ * Updates the configuration for the DisplayAdjustments with new configuration.
+ * Default to EMPTY configuration if new configuration is {@code null}
+ * @param configuration new configuration
+ * @throws IllegalArgumentException if trying to modify DEFAULT_DISPLAY_ADJUSTMENTS
+ */
+ public void setConfiguration(@Nullable Configuration configuration) {
if (this == DEFAULT_DISPLAY_ADJUSTMENTS) {
throw new IllegalArgumentException(
"setConfiguration: Cannot modify DEFAULT_DISPLAY_ADJUSTMENTS");
@@ -71,6 +79,7 @@
}
@UnsupportedAppUsage
+ @NonNull
public Configuration getConfiguration() {
return mConfiguration;
}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index b3b53f0..58597cf 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -161,6 +161,14 @@
SurfaceControl addShellRoot(int displayId, IWindow client, int windowType);
/**
+ * Sets the window token sent to accessibility for a particular shell root. The
+ * displayId and windowType identify which shell-root to update.
+ *
+ * @param target The IWindow that accessibility service interfaces with.
+ */
+ void setShellRootAccessibilityWindow(int displayId, int windowType, IWindow target);
+
+ /**
* Like overridePendingAppTransitionMultiThumb, but uses a future to supply the specs. This is
* used for recents, where generating the thumbnails of the specs takes a non-trivial amount of
* time, so we want to move that off the critical path for starting the new activity.
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 43c7bed..f135328 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -1002,7 +1002,8 @@
}
}
- private void applyAnimation(@InsetsType final int types, boolean show, boolean fromIme) {
+ @VisibleForTesting
+ public void applyAnimation(@InsetsType final int types, boolean show, boolean fromIme) {
if (types == 0) {
// nothing to animate.
return;
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index cfceb03..3d6da6f 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -39,7 +39,7 @@
* {@link SurfaceView#setChildSurfacePackage}.
*/
public class SurfaceControlViewHost {
- private ViewRootImpl mViewRoot;
+ private final ViewRootImpl mViewRoot;
private WindowlessWindowManager mWm;
private SurfaceControl mSurfaceControl;
@@ -226,6 +226,14 @@
}
/**
+ * @return the ViewRootImpl wrapped by this host.
+ * @hide
+ */
+ public IWindow getWindowToken() {
+ return mViewRoot.mWindow;
+ }
+
+ /**
* @hide
*/
@TestApi
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 9d275cd..8b1e6cb 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -327,6 +327,8 @@
private boolean mForceNextConfigUpdate;
private boolean mUseBLASTAdapter;
+ private boolean mForceDisableBLAST;
+ private boolean mEnableTripleBuffering;
/**
* Signals that compatibility booleans have been initialized according to
@@ -784,7 +786,6 @@
loadSystemProperties();
mImeFocusController = new ImeFocusController(this);
- mUseBLASTAdapter = WindowManagerGlobal.useBLAST();
}
public static void addFirstDrawHandler(Runnable callback) {
@@ -925,10 +926,9 @@
if (mWindowAttributes.packageName == null) {
mWindowAttributes.packageName = mBasePackageName;
}
- if (mUseBLASTAdapter) {
- mWindowAttributes.privateFlags |=
+ mWindowAttributes.privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST;
- }
+
attrs = mWindowAttributes;
setTag();
@@ -1102,6 +1102,13 @@
"Unable to add window -- unknown error code " + res);
}
+ if ((res & WindowManagerGlobal.ADD_FLAG_USE_BLAST) != 0) {
+ mUseBLASTAdapter = true;
+ }
+ if ((res & WindowManagerGlobal.ADD_FLAG_USE_TRIPLE_BUFFERING) != 0) {
+ mEnableTripleBuffering = true;
+ }
+
if (view instanceof RootViewSurfaceTaker) {
mInputQueueCallback =
((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
@@ -1414,10 +1421,8 @@
}
mWindowAttributes.privateFlags |= compatibleWindowFlag;
- if (mUseBLASTAdapter) {
- mWindowAttributes.privateFlags |=
+ mWindowAttributes.privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST;
- }
if (mWindowAttributes.preservePreviousSurfaceInsets) {
// Restore old surface insets.
@@ -1784,7 +1789,7 @@
Surface ret = null;
if (mBlastBufferQueue == null) {
mBlastBufferQueue = new BLASTBufferQueue(
- mBlastSurfaceControl, width, height);
+ mBlastSurfaceControl, width, height, mEnableTripleBuffering);
// We only return the Surface the first time, as otherwise
// it hasn't changed and there is no need to update.
ret = mBlastBufferQueue.getSurface();
@@ -7393,7 +7398,7 @@
mPendingDisplayCutout, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
mTempControls, mSurfaceSize, mBlastSurfaceControl);
if (mSurfaceControl.isValid()) {
- if (!mUseBLASTAdapter) {
+ if (!useBLAST()) {
mSurface.copyFrom(mSurfaceControl);
} else {
final Surface blastSurface = getOrCreateBLASTSurface(mSurfaceSize.x,
@@ -9760,7 +9765,7 @@
* @hide
*/
public SurfaceControl getRenderSurfaceControl() {
- if (mUseBLASTAdapter) {
+ if (useBLAST()) {
return mBlastSurfaceControl;
} else {
return mSurfaceControl;
@@ -9777,11 +9782,11 @@
* flag. Needs to be called before addView.
*/
void forceDisableBLAST() {
- mUseBLASTAdapter = false;
+ mForceDisableBLAST = true;
}
boolean useBLAST() {
- return mUseBLASTAdapter;
+ return mUseBLASTAdapter && !mForceDisableBLAST;
}
/**
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 94591ea..8490f2a 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -124,8 +124,10 @@
*/
public static final int RELAYOUT_DEFER_SURFACE_DESTROY = 0x2;
+ public static final int ADD_FLAG_IN_TOUCH_MODE = 0x1;
public static final int ADD_FLAG_APP_VISIBLE = 0x2;
- public static final int ADD_FLAG_IN_TOUCH_MODE = RELAYOUT_RES_IN_TOUCH_MODE;
+ public static final int ADD_FLAG_USE_TRIPLE_BUFFERING = 0x4;
+ public static final int ADD_FLAG_USE_BLAST = 0x8;
/**
* Like {@link #RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS}, but as a "hint" when adding the
diff --git a/core/java/android/view/inputmethod/InlineSuggestion.java b/core/java/android/view/inputmethod/InlineSuggestion.java
index f50f0dea..6b1a480 100644
--- a/core/java/android/view/inputmethod/InlineSuggestion.java
+++ b/core/java/android/view/inputmethod/InlineSuggestion.java
@@ -29,6 +29,7 @@
import android.util.Size;
import android.util.Slog;
import android.view.SurfaceControlViewHost;
+import android.view.ViewGroup;
import android.widget.inline.InlineContentView;
import com.android.internal.util.DataClass;
@@ -94,19 +95,26 @@
/**
* Inflates a view with the content of this suggestion at a specific size.
- * The size must be between the
+ *
+ * <p> The size must be either 1) between the
* {@link android.widget.inline.InlinePresentationSpec#getMinSize() min size} and the
* {@link android.widget.inline.InlinePresentationSpec#getMaxSize() max size} of the
- * presentation spec returned by {@link InlineSuggestionInfo#getInlinePresentationSpec()}.
+ * presentation spec returned by {@link InlineSuggestionInfo#getInlinePresentationSpec()},
+ * or 2) {@link ViewGroup.LayoutParams#WRAP_CONTENT}. If the size is set to
+ * {@link ViewGroup.LayoutParams#WRAP_CONTENT}, then the size of the inflated view will be just
+ * large enough to fit the content, while still conforming to the min / max size specified by
+ * the {@link android.widget.inline.InlinePresentationSpec}.
*
* <p> The caller can attach an {@link android.view.View.OnClickListener} and/or an
* {@link android.view.View.OnLongClickListener} to the view in the
- * {@code callback} to receive click and
- * long click events on the view.
+ * {@code callback} to receive click and long click events on the view.
*
* @param context Context in which to inflate the view.
- * @param size The size at which to inflate the suggestion.
- * @param callback Callback for receiving the inflated view.
+ * @param size The size at which to inflate the suggestion. For each dimension, it maybe
+ * an exact value or {@link ViewGroup.LayoutParams#WRAP_CONTENT}.
+ * @param callback Callback for receiving the inflated view, where the
+ * {@link ViewGroup.LayoutParams} of the view is set as the actual size of
+ * the underlying remote view.
* @throws IllegalArgumentException If an invalid argument is passed.
* @throws IllegalStateException If this method is already called.
*/
@@ -115,10 +123,11 @@
@NonNull Consumer<InlineContentView> callback) {
final Size minSize = mInfo.getInlinePresentationSpec().getMinSize();
final Size maxSize = mInfo.getInlinePresentationSpec().getMaxSize();
- if (size.getHeight() < minSize.getHeight() || size.getHeight() > maxSize.getHeight()
- || size.getWidth() < minSize.getWidth() || size.getWidth() > maxSize.getWidth()) {
- throw new IllegalArgumentException("size not between min:"
- + minSize + " and max:" + maxSize);
+ if (!isValid(size.getWidth(), minSize.getWidth(), maxSize.getWidth())
+ || !isValid(size.getHeight(), minSize.getHeight(), maxSize.getHeight())) {
+ throw new IllegalArgumentException(
+ "size is neither between min:" + minSize + " and max:" + maxSize
+ + ", nor wrap_content");
}
mInlineContentCallback = getInlineContentCallback(context, callbackExecutor, callback);
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
@@ -136,6 +145,17 @@
});
}
+ /**
+ * Returns true if the {@code actual} length is within [min, max] or is {@link
+ * ViewGroup.LayoutParams#WRAP_CONTENT}.
+ */
+ private static boolean isValid(int actual, int min, int max) {
+ if (actual == ViewGroup.LayoutParams.WRAP_CONTENT) {
+ return true;
+ }
+ return actual >= min && actual <= max;
+ }
+
private synchronized InlineContentCallbackImpl getInlineContentCallback(Context context,
Executor callbackExecutor, Consumer<InlineContentView> callback) {
if (mInlineContentCallback != null) {
@@ -154,10 +174,11 @@
@Override
@BinderThread
- public void onContent(SurfaceControlViewHost.SurfacePackage content) {
+ public void onContent(SurfaceControlViewHost.SurfacePackage content, int width,
+ int height) {
final InlineContentCallbackImpl callbackImpl = mCallbackImpl.get();
if (callbackImpl != null) {
- callbackImpl.onContent(content);
+ callbackImpl.onContent(content, width, height);
}
}
@@ -196,11 +217,13 @@
}
@BinderThread
- public void onContent(SurfaceControlViewHost.SurfacePackage content) {
+ public void onContent(SurfaceControlViewHost.SurfacePackage content, int width,
+ int height) {
if (content == null) {
mCallbackExecutor.execute(() -> mCallback.accept(/* view */null));
} else {
mView = new InlineContentView(mContext);
+ mView.setLayoutParams(new ViewGroup.LayoutParams(width, height));
mView.setChildSurfacePackage(content);
mCallbackExecutor.execute(() -> mCallback.accept(mView));
}
@@ -398,10 +421,10 @@
};
@DataClass.Generated(
- time = 1585180783541L,
+ time = 1587771173367L,
codegenVersion = "1.0.15",
sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestion.java",
- inputSignatures = "private static final java.lang.String TAG\nprivate final @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo mInfo\nprivate final @android.annotation.Nullable com.android.internal.view.inline.IInlineContentProvider mContentProvider\nprivate @com.android.internal.util.DataClass.ParcelWith(android.view.inputmethod.InlineSuggestion.InlineContentCallbackImplParceling.class) @android.annotation.Nullable android.view.inputmethod.InlineSuggestion.InlineContentCallbackImpl mInlineContentCallback\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestion newInlineSuggestion(android.view.inputmethod.InlineSuggestionInfo)\npublic void inflate(android.content.Context,android.util.Size,java.util.concurrent.Executor,java.util.function.Consumer<android.widget.inline.InlineContentView>)\nprivate synchronized android.view.inputmethod.InlineSuggestion.InlineContentCallbackImpl getInlineContentCallback(android.content.Context,java.util.concurrent.Executor,java.util.function.Consumer<android.widget.inline.InlineContentView>)\nclass InlineSuggestion extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
+ inputSignatures = "private static final java.lang.String TAG\nprivate final @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo mInfo\nprivate final @android.annotation.Nullable com.android.internal.view.inline.IInlineContentProvider mContentProvider\nprivate @com.android.internal.util.DataClass.ParcelWith(android.view.inputmethod.InlineSuggestion.InlineContentCallbackImplParceling.class) @android.annotation.Nullable android.view.inputmethod.InlineSuggestion.InlineContentCallbackImpl mInlineContentCallback\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestion newInlineSuggestion(android.view.inputmethod.InlineSuggestionInfo)\npublic void inflate(android.content.Context,android.util.Size,java.util.concurrent.Executor,java.util.function.Consumer<android.widget.inline.InlineContentView>)\nprivate static boolean isValid(int,int,int)\nprivate synchronized android.view.inputmethod.InlineSuggestion.InlineContentCallbackImpl getInlineContentCallback(android.content.Context,java.util.concurrent.Executor,java.util.function.Consumer<android.widget.inline.InlineContentView>)\nclass InlineSuggestion extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index 6425cf1..4311ffb 100755
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -746,6 +746,15 @@
}
/**
+ * Remove existing exit transition from PopupWindow and force immediate dismissal.
+ * @hide
+ */
+ public void dismissImmediate() {
+ mPopup.setExitTransition(null);
+ dismiss();
+ }
+
+ /**
* Set a listener to receive a callback when the popup is dismissed.
*
* @param listener Listener that will be notified when the popup is dismissed.
diff --git a/core/java/android/window/IWindowOrganizerController.aidl b/core/java/android/window/IWindowOrganizerController.aidl
index 7f4b26d..7e9c783 100644
--- a/core/java/android/window/IWindowOrganizerController.aidl
+++ b/core/java/android/window/IWindowOrganizerController.aidl
@@ -16,9 +16,12 @@
package android.window;
+import android.view.SurfaceControl;
+
import android.window.IDisplayAreaOrganizerController;
import android.window.ITaskOrganizerController;
import android.window.IWindowContainerTransactionCallback;
+import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
/** @hide */
@@ -47,4 +50,15 @@
/** @return An interface enabling the management of display area organizers. */
IDisplayAreaOrganizerController getDisplayAreaOrganizerController();
+
+ /**
+ * Take a screenshot of the requested Window token and place the content of the screenshot into
+ * outSurfaceControl. The SurfaceControl will be a child of the token's parent, so it will be
+ * a sibling of the token's window
+ * @param token The token for the WindowContainer that should get a screenshot taken.
+ * @param outSurfaceControl The SurfaceControl where the screenshot will be attached.
+ *
+ * @return true if the screenshot was successful, false otherwise.
+ */
+ boolean takeScreenshot(in WindowContainerToken token, out SurfaceControl outSurfaceControl);
}
diff --git a/core/java/android/window/WindowOrganizer.java b/core/java/android/window/WindowOrganizer.java
index 4578271..ff40dda 100644
--- a/core/java/android/window/WindowOrganizer.java
+++ b/core/java/android/window/WindowOrganizer.java
@@ -17,11 +17,13 @@
package android.window;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.TestApi;
import android.app.ActivityTaskManager;
import android.os.RemoteException;
import android.util.Singleton;
+import android.view.SurfaceControl;
/**
* Base class for organizing specific types of windows like Tasks and DisplayAreas
@@ -63,6 +65,28 @@
}
}
+ /**
+ * Take a screenshot for a specified Window
+ * @param token The token for the WindowContainer that should get a screenshot taken.
+ * @return A SurfaceControl where the screenshot will be attached, or null if failed.
+ *
+ * @hide
+ */
+ @Nullable
+ @RequiresPermission(android.Manifest.permission.READ_FRAME_BUFFER)
+ public static SurfaceControl takeScreenshot(@NonNull WindowContainerToken token) {
+ try {
+ SurfaceControl surfaceControl = new SurfaceControl();
+ if (getWindowOrganizerController().takeScreenshot(token, surfaceControl)) {
+ return surfaceControl;
+ } else {
+ return null;
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
static IWindowOrganizerController getWindowOrganizerController() {
return IWindowOrganizerControllerSingleton.get();
diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
index b58dbdd..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()));
@@ -555,12 +564,6 @@
}
}
- /**
- * Callback called when the button layout has been hidden.
- * <p>This method is meant to be overridden by subclasses.
- */
- protected void onButtonLayoutHidden() { }
-
public interface OnProfileSelectedListener {
/**
* Callback for when the user changes the active tab from personal to work or vice versa.
diff --git a/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java b/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java
index def1137..986614c 100644
--- a/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java
+++ b/core/java/com/android/internal/app/AppPredictionServiceResolverComparator.java
@@ -53,6 +53,7 @@
private final AppPredictor mAppPredictor;
private final Context mContext;
private final Map<ComponentName, Integer> mTargetRanks = new HashMap<>();
+ private final Map<ComponentName, Integer> mTargetScores = new HashMap<>();
private final UserHandle mUser;
private final Intent mIntent;
private final String mReferrerPackage;
@@ -138,6 +139,11 @@
// Null value is okay if we have defaulted to the ResolverRankerService.
if (msg.what == RANKER_SERVICE_RESULT && msg.obj != null) {
final List<AppTarget> sortedAppTargets = (List<AppTarget>) msg.obj;
+ if (checkAppTargetRankValid(sortedAppTargets)) {
+ sortedAppTargets.forEach(target -> mTargetScores.put(
+ new ComponentName(target.getPackageName(), target.getClassName()),
+ target.getRank()));
+ }
for (int i = 0; i < sortedAppTargets.size(); i++) {
mTargetRanks.put(new ComponentName(sortedAppTargets.get(i).getPackageName(),
sortedAppTargets.get(i).getClassName()), i);
@@ -147,11 +153,23 @@
}
}
+ private boolean checkAppTargetRankValid(List<AppTarget> sortedAppTargets) {
+ for (AppTarget target : sortedAppTargets) {
+ if (target.getRank() != 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
@Override
float getScore(ComponentName name) {
if (mResolverRankerService != null) {
return mResolverRankerService.getScore(name);
}
+ if (!mTargetScores.isEmpty()) {
+ return mTargetScores.get(name);
+ }
Integer rank = mTargetRanks.get(name);
if (rank == null) {
Log.w(TAG, "Score requested for unknown component.");
diff --git a/core/java/com/android/internal/app/ChooserListAdapter.java b/core/java/com/android/internal/app/ChooserListAdapter.java
index bdfcda5..69a4927 100644
--- a/core/java/com/android/internal/app/ChooserListAdapter.java
+++ b/core/java/com/android/internal/app/ChooserListAdapter.java
@@ -207,9 +207,6 @@
if (mListViewDataChanged) {
if (mAppendDirectShareEnabled) {
appendServiceTargetsWithQuota();
- if (mPendingChooserTargetService.isEmpty()) {
- fillAllServiceTargets();
- }
}
super.notifyDataSetChanged();
}
@@ -493,6 +490,8 @@
pendingChooserTargetServiceConnections) {
ComponentName origComponentName = origTarget != null ? origTarget.getResolvedComponentName()
: !targets.isEmpty() ? targets.get(0).getComponentName() : null;
+ Log.i(TAG,
+ "parkTargetIntoMemory " + origComponentName + ", " + targets.size() + " targets");
mPendingChooserTargetService = pendingChooserTargetServiceConnections.stream()
.map(ChooserActivity.ChooserTargetServiceConnection::getComponentName)
.filter(componentName -> !componentName.equals(origComponentName))
@@ -532,32 +531,56 @@
private void appendServiceTargetsWithQuota() {
int maxRankedTargets = mChooserListCommunicator.getMaxRankedTargets();
List<ComponentName> topComponentNames = getTopComponentNames(maxRankedTargets);
- int appRank = 0;
+ float totalScore = 0f;
for (ComponentName component : topComponentNames) {
if (!mPendingChooserTargetService.contains(component)
&& !mParkingDirectShareTargets.containsKey(component)) {
continue;
}
- appRank++;
+ totalScore += super.getScore(component);
+ }
+ boolean shouldWaitPendingService = false;
+ for (ComponentName component : topComponentNames) {
+ if (!mPendingChooserTargetService.contains(component)
+ && !mParkingDirectShareTargets.containsKey(component)) {
+ continue;
+ }
+ float score = super.getScore(component);
+ int quota = Math.round(maxRankedTargets * score / totalScore);
+ if (mPendingChooserTargetService.contains(component) && quota >= 1) {
+ shouldWaitPendingService = true;
+ }
+ if (!mParkingDirectShareTargets.containsKey(component)) {
+ continue;
+ }
+ // Append targets into direct share row as per quota.
Pair<List<ChooserTargetInfo>, Integer> parkingTargetsItem =
mParkingDirectShareTargets.get(component);
- if (parkingTargetsItem != null && parkingTargetsItem.second == 0) {
- List<ChooserTargetInfo> parkingTargets = parkingTargetsItem.first;
- int initTargetsQuota = appRank <= maxRankedTargets / 2 ? 2 : 1;
- int insertedNum = 0;
- while (insertedNum < initTargetsQuota && !parkingTargets.isEmpty()) {
- if (!checkDuplicateTarget(parkingTargets.get(0))) {
- mServiceTargets.add(mValidServiceTargetsNum, parkingTargets.get(0));
- mValidServiceTargetsNum++;
- insertedNum++;
- }
- parkingTargets.remove(0);
+ List<ChooserTargetInfo> parkingTargets = parkingTargetsItem.first;
+ int insertedNum = parkingTargetsItem.second;
+ while (insertedNum < quota && !parkingTargets.isEmpty()) {
+ if (!checkDuplicateTarget(parkingTargets.get(0))) {
+ mServiceTargets.add(mValidServiceTargetsNum, parkingTargets.get(0));
+ mValidServiceTargetsNum++;
+ insertedNum++;
}
- mParkingDirectShareTargets.put(component, new Pair<>(parkingTargets, insertedNum));
- if (mShortcutComponents.contains(component)) {
- mNumShortcutResults += insertedNum;
- }
+ parkingTargets.remove(0);
}
+ Log.i(TAG, " appendServiceTargetsWithQuota component=" + component
+ + " appendNum=" + (insertedNum - parkingTargetsItem.second));
+ if (DEBUG) {
+ Log.d(TAG, " appendServiceTargetsWithQuota component=" + component
+ + " score=" + score
+ + " totalScore=" + totalScore
+ + " quota=" + quota);
+ }
+ if (mShortcutComponents.contains(component)) {
+ mNumShortcutResults += insertedNum - parkingTargetsItem.second;
+ }
+ mParkingDirectShareTargets.put(component, new Pair<>(parkingTargets, insertedNum));
+ }
+ if (!shouldWaitPendingService) {
+ fillAllServiceTargets();
}
}
@@ -568,6 +591,7 @@
if (mParkingDirectShareTargets.isEmpty()) {
return;
}
+ Log.i(TAG, " fillAllServiceTargets");
int maxRankedTargets = mChooserListCommunicator.getMaxRankedTargets();
List<ComponentName> topComponentNames = getTopComponentNames(maxRankedTargets);
// Append all remaining targets of top recommended components into direct share row.
@@ -581,7 +605,8 @@
if (mShortcutComponents.contains(component)) {
mNumShortcutResults++;
}
- mServiceTargets.add(target);
+ mServiceTargets.add(mValidServiceTargetsNum, target);
+ mValidServiceTargetsNum++;
});
mParkingDirectShareTargets.remove(component);
}
@@ -593,7 +618,8 @@
.forEach(targets -> {
for (ChooserTargetInfo target : targets) {
if (!checkDuplicateTarget(target)) {
- mServiceTargets.add(target);
+ mServiceTargets.add(mValidServiceTargetsNum, target);
+ mValidServiceTargetsNum++;
mNumShortcutResults++;
}
}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 84c833e..4ba6ce4 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1030,6 +1030,10 @@
if (isAutolaunching()) {
return;
}
+ if (isIntentPicker()) {
+ ((ResolverMultiProfilePagerAdapter) mMultiProfilePagerAdapter)
+ .setUseLayoutWithDefault(useLayoutWithDefault());
+ }
if (mMultiProfilePagerAdapter.shouldShowEmptyStateScreen(listAdapter)) {
mMultiProfilePagerAdapter.showEmptyResolverListEmptyState(listAdapter);
} else {
@@ -1782,13 +1786,14 @@
if (buttonBarDivider != null) {
buttonBarDivider.setVisibility(View.INVISIBLE);
}
- mMultiProfilePagerAdapter.onButtonLayoutHidden();
+ setButtonBarIgnoreOffset(/* ignoreOffset */ false);
return;
}
if (buttonBarDivider != null) {
buttonBarDivider.setVisibility(View.VISIBLE);
}
buttonLayout.setVisibility(View.VISIBLE);
+ setButtonBarIgnoreOffset(/* ignoreOffset */ true);
if (!useLayoutWithDefault()) {
int inset = mSystemWindowInsets != null ? mSystemWindowInsets.bottom : 0;
@@ -1802,6 +1807,21 @@
resetAlwaysOrOnceButtonBar();
}
+ /**
+ * Updates the button bar container {@code ignoreOffset} layout param.
+ * <p>Setting this to {@code true} means that the button bar will be glued to the bottom of
+ * the screen.
+ */
+ private void setButtonBarIgnoreOffset(boolean ignoreOffset) {
+ View buttonBarContainer = findViewById(R.id.button_bar_container);
+ if (buttonBarContainer != null) {
+ ResolverDrawerLayout.LayoutParams layoutParams =
+ (ResolverDrawerLayout.LayoutParams) buttonBarContainer.getLayoutParams();
+ layoutParams.ignoreOffset = ignoreOffset;
+ buttonBarContainer.setLayoutParams(layoutParams);
+ }
+ }
+
private void resetAlwaysOrOnceButtonBar() {
// Disable both buttons initially
setAlwaysButtonEnabled(false, ListView.INVALID_POSITION, false);
diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java
index cee8a92..62bddb1 100644
--- a/core/java/com/android/internal/app/ResolverListAdapter.java
+++ b/core/java/com/android/internal/app/ResolverListAdapter.java
@@ -154,6 +154,13 @@
}
/**
+ * Returns the app share score of the given {@code componentName}.
+ */
+ public float getScore(ComponentName componentName) {
+ return mResolverListController.getScore(componentName);
+ }
+
+ /**
* Returns the list of top K component names which have highest
* {@link #getScore(DisplayResolveInfo)}
*/
diff --git a/core/java/com/android/internal/app/ResolverListController.java b/core/java/com/android/internal/app/ResolverListController.java
index 033ac72..2b59907 100644
--- a/core/java/com/android/internal/app/ResolverListController.java
+++ b/core/java/com/android/internal/app/ResolverListController.java
@@ -378,6 +378,13 @@
}
/**
+ * Returns the app share score of the given {@code componentName}.
+ */
+ public float getScore(ComponentName componentName) {
+ return mResolverComparator.getScore(componentName);
+ }
+
+ /**
* Returns the list of top K component names which have highest
* {@link #getScore(DisplayResolveInfo)}
*/
diff --git a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
index 5e2470e..b4f9f08 100644
--- a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
@@ -36,6 +36,7 @@
private final ResolverProfileDescriptor[] mItems;
private final boolean mShouldShowNoCrossProfileIntentsEmptyState;
+ private boolean mUseLayoutWithDefault;
ResolverMultiProfilePagerAdapter(Context context,
ResolverListAdapter adapter,
@@ -213,13 +214,15 @@
/* subtitleRes */ 0);
}
+ void setUseLayoutWithDefault(boolean useLayoutWithDefault) {
+ mUseLayoutWithDefault = useLayoutWithDefault;
+ }
+
@Override
- protected void onButtonLayoutHidden() {
- View emptyStateContainer = getItem(getCurrentPage()).getEmptyStateView()
- .findViewById(R.id.resolver_empty_state_container);
- emptyStateContainer.setPadding(emptyStateContainer.getPaddingLeft(),
- emptyStateContainer.getPaddingTop(), emptyStateContainer.getPaddingRight(),
- /* bottom */ 0);
+ protected void setupContainerPadding(View container) {
+ int bottom = mUseLayoutWithDefault ? container.getPaddingBottom() : 0;
+ container.setPadding(container.getPaddingLeft(), container.getPaddingTop(),
+ container.getPaddingRight(), bottom);
}
class ResolverProfileDescriptor extends ProfileDescriptor {
diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java
index adadc5e..49c9302 100644
--- a/core/java/com/android/internal/util/ScreenshotHelper.java
+++ b/core/java/com/android/internal/util/ScreenshotHelper.java
@@ -27,6 +27,9 @@
public class ScreenshotHelper {
+ public static final int SCREENSHOT_MSG_URI = 1;
+ public static final int SCREENSHOT_MSG_PROCESS_COMPLETE = 2;
+
/**
* Describes a screenshot request (to make it easier to pass data through to the handler).
*/
@@ -135,6 +138,7 @@
private final int SCREENSHOT_TIMEOUT_MS = 10000;
private final Object mScreenshotLock = new Object();
+ private IBinder mScreenshotService = null;
private ServiceConnection mScreenshotConnection = null;
private final Context mContext;
@@ -251,85 +255,104 @@
private void takeScreenshot(final int screenshotType, long timeoutMs, @NonNull Handler handler,
ScreenshotRequest screenshotRequest, @Nullable Consumer<Uri> completionConsumer) {
synchronized (mScreenshotLock) {
- if (mScreenshotConnection != null) {
- return;
- }
- final ComponentName serviceComponent = ComponentName.unflattenFromString(
- mContext.getResources().getString(
- com.android.internal.R.string.config_screenshotServiceComponent));
- final Intent serviceIntent = new Intent();
- final Runnable mScreenshotTimeout = new Runnable() {
+ final Runnable mScreenshotTimeout = () -> {
+ synchronized (mScreenshotLock) {
+ if (mScreenshotConnection != null) {
+ mContext.unbindService(mScreenshotConnection);
+ mScreenshotConnection = null;
+ mScreenshotService = null;
+ notifyScreenshotError();
+ }
+ }
+ if (completionConsumer != null) {
+ completionConsumer.accept(null);
+ }
+ };
+
+ Message msg = Message.obtain(null, screenshotType, screenshotRequest);
+ final ServiceConnection myConn = mScreenshotConnection;
+ Handler h = new Handler(handler.getLooper()) {
@Override
- public void run() {
- synchronized (mScreenshotLock) {
- if (mScreenshotConnection != null) {
- mContext.unbindService(mScreenshotConnection);
- mScreenshotConnection = null;
- notifyScreenshotError();
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case SCREENSHOT_MSG_URI:
+ if (completionConsumer != null) {
+ completionConsumer.accept((Uri) msg.obj);
+ }
+ handler.removeCallbacks(mScreenshotTimeout);
+ break;
+ case SCREENSHOT_MSG_PROCESS_COMPLETE:
+ synchronized (mScreenshotLock) {
+ if (mScreenshotConnection == myConn) {
+ mContext.unbindService(mScreenshotConnection);
+ mScreenshotConnection = null;
+ mScreenshotService = null;
+ }
+ }
+ break;
+ }
+ }
+ };
+ msg.replyTo = new Messenger(h);
+
+ if (mScreenshotConnection == null) {
+ final ComponentName serviceComponent = ComponentName.unflattenFromString(
+ mContext.getResources().getString(
+ com.android.internal.R.string.config_screenshotServiceComponent));
+ final Intent serviceIntent = new Intent();
+
+ serviceIntent.setComponent(serviceComponent);
+ ServiceConnection conn = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ synchronized (mScreenshotLock) {
+ if (mScreenshotConnection != this) {
+ return;
+ }
+ mScreenshotService = service;
+ Messenger messenger = new Messenger(mScreenshotService);
+
+ try {
+ messenger.send(msg);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Couldn't take screenshot: " + e);
+ if (completionConsumer != null) {
+ completionConsumer.accept(null);
+ }
+ }
}
}
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ synchronized (mScreenshotLock) {
+ if (mScreenshotConnection != null) {
+ mContext.unbindService(mScreenshotConnection);
+ mScreenshotConnection = null;
+ mScreenshotService = null;
+ handler.removeCallbacks(mScreenshotTimeout);
+ notifyScreenshotError();
+ }
+ }
+ }
+ };
+ if (mContext.bindServiceAsUser(serviceIntent, conn,
+ Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
+ UserHandle.CURRENT)) {
+ mScreenshotConnection = conn;
+ handler.postDelayed(mScreenshotTimeout, timeoutMs);
+ }
+ } else {
+ Messenger messenger = new Messenger(mScreenshotService);
+ try {
+ messenger.send(msg);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Couldn't take screenshot: " + e);
if (completionConsumer != null) {
completionConsumer.accept(null);
}
}
- };
-
- serviceIntent.setComponent(serviceComponent);
- ServiceConnection conn = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- synchronized (mScreenshotLock) {
- if (mScreenshotConnection != this) {
- return;
- }
- Messenger messenger = new Messenger(service);
- Message msg = Message.obtain(null, screenshotType, screenshotRequest);
- final ServiceConnection myConn = this;
- Handler h = new Handler(handler.getLooper()) {
- @Override
- public void handleMessage(Message msg) {
- synchronized (mScreenshotLock) {
- if (mScreenshotConnection == myConn) {
- mContext.unbindService(mScreenshotConnection);
- mScreenshotConnection = null;
- handler.removeCallbacks(mScreenshotTimeout);
- }
- }
- if (completionConsumer != null) {
- completionConsumer.accept((Uri) msg.obj);
- }
- }
- };
- msg.replyTo = new Messenger(h);
-
- try {
- messenger.send(msg);
- } catch (RemoteException e) {
- Log.e(TAG, "Couldn't take screenshot: " + e);
- if (completionConsumer != null) {
- completionConsumer.accept(null);
- }
- }
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- synchronized (mScreenshotLock) {
- if (mScreenshotConnection != null) {
- mContext.unbindService(mScreenshotConnection);
- mScreenshotConnection = null;
- handler.removeCallbacks(mScreenshotTimeout);
- notifyScreenshotError();
- }
- }
- }
- };
- if (mContext.bindServiceAsUser(serviceIntent, conn,
- Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
- UserHandle.CURRENT)) {
- mScreenshotConnection = conn;
handler.postDelayed(mScreenshotTimeout, timeoutMs);
}
}
diff --git a/core/java/com/android/internal/view/inline/IInlineContentCallback.aidl b/core/java/com/android/internal/view/inline/IInlineContentCallback.aidl
index feb3f02..64f7fa7 100644
--- a/core/java/com/android/internal/view/inline/IInlineContentCallback.aidl
+++ b/core/java/com/android/internal/view/inline/IInlineContentCallback.aidl
@@ -23,7 +23,7 @@
* {@hide}
*/
oneway interface IInlineContentCallback {
- void onContent(in SurfaceControlViewHost.SurfacePackage content);
+ void onContent(in SurfaceControlViewHost.SurfacePackage content, int width, int height);
void onClick();
void onLongClick();
}
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index 2668420..297ebb0 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -35,10 +35,14 @@
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
+import android.graphics.Typeface;
import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.os.Parcelable;
+import android.text.Spannable;
+import android.text.SpannableString;
import android.text.TextUtils;
+import android.text.style.StyleSpan;
import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
@@ -109,7 +113,7 @@
private CharSequence mNameReplacement;
private boolean mIsCollapsed;
private ImageResolver mImageResolver;
- private CachingIconView mConversationIcon;
+ private CachingIconView mConversationIconView;
private View mConversationIconContainer;
private int mConversationIconTopPadding;
private int mConversationIconTopPaddingExpandedGroup;
@@ -157,6 +161,7 @@
private ViewGroup mAppOps;
private Rect mAppOpsTouchRect = new Rect();
private float mMinTouchSize;
+ private Icon mConversationIcon;
public ConversationLayout(@NonNull Context context) {
super(context);
@@ -192,7 +197,7 @@
mAvatarSize = getResources().getDimensionPixelSize(R.dimen.messaging_avatar_size);
mTextPaint.setTextAlign(Paint.Align.CENTER);
mTextPaint.setAntiAlias(true);
- mConversationIcon = findViewById(R.id.conversation_icon);
+ mConversationIconView = findViewById(R.id.conversation_icon);
mConversationIconContainer = findViewById(R.id.conversation_icon_container);
mIcon = findViewById(R.id.icon);
mAppOps = findViewById(com.android.internal.R.id.app_ops);
@@ -232,7 +237,7 @@
});
// When the conversation icon is gone, hide the whole badge
- mConversationIcon.setOnForceHiddenChangedListener((forceHidden) -> {
+ mConversationIconView.setOnForceHiddenChangedListener((forceHidden) -> {
animateViewForceHidden(mConversationIconBadgeBg, forceHidden);
animateViewForceHidden(mImportanceRingView, forceHidden);
animateViewForceHidden(mIcon, forceHidden);
@@ -463,7 +468,7 @@
CharSequence conversationText = mConversationTitle;
if (mIsOneToOne) {
// Let's resolve the icon / text from the last sender
- mConversationIcon.setVisibility(VISIBLE);
+ mConversationIconView.setVisibility(VISIBLE);
mConversationFacePile.setVisibility(GONE);
CharSequence userKey = getKey(mUser);
for (int i = mGroups.size() - 1; i >= 0; i--) {
@@ -480,17 +485,20 @@
if (avatarIcon == null) {
avatarIcon = createAvatarSymbol(conversationText, "", mLayoutColor);
}
- mConversationIcon.setImageIcon(avatarIcon);
+ mConversationIcon = avatarIcon;
+ mConversationIconView.setImageIcon(mConversationIcon);
break;
}
}
} else {
if (mLargeIcon != null) {
- mConversationIcon.setVisibility(VISIBLE);
+ mConversationIcon = mLargeIcon;
+ mConversationIconView.setVisibility(VISIBLE);
mConversationFacePile.setVisibility(GONE);
- mConversationIcon.setImageIcon(mLargeIcon);
+ mConversationIconView.setImageIcon(mLargeIcon);
} else {
- mConversationIcon.setVisibility(GONE);
+ mConversationIcon = null;
+ mConversationIconView.setVisibility(GONE);
// This will also inflate it!
mConversationFacePile.setVisibility(VISIBLE);
// rebind the value to the inflated view instead of the stub
@@ -561,15 +569,8 @@
mImageMessageContainer.setVisibility(newMessage != null ? VISIBLE : GONE);
}
- private void bindFacePile() {
- // Let's bind the face pile
- ImageView bottomBackground = mConversationFacePile.findViewById(
- R.id.conversation_face_pile_bottom_background);
+ public void bindFacePile(ImageView bottomBackground, ImageView bottomView, ImageView topView) {
applyNotificationBackgroundColor(bottomBackground);
- ImageView bottomView = mConversationFacePile.findViewById(
- R.id.conversation_face_pile_bottom);
- ImageView topView = mConversationFacePile.findViewById(
- R.id.conversation_face_pile_top);
// Let's find the two last conversations:
Icon secondLastIcon = null;
CharSequence lastKey = null;
@@ -601,6 +602,17 @@
secondLastIcon = createAvatarSymbol("", "", mLayoutColor);
}
topView.setImageIcon(secondLastIcon);
+ }
+
+ private void bindFacePile() {
+ ImageView bottomBackground = mConversationFacePile.findViewById(
+ R.id.conversation_face_pile_bottom_background);
+ ImageView bottomView = mConversationFacePile.findViewById(
+ R.id.conversation_face_pile_bottom);
+ ImageView topView = mConversationFacePile.findViewById(
+ R.id.conversation_face_pile_top);
+
+ bindFacePile(bottomBackground, bottomView, topView);
int conversationAvatarSize;
int facepileAvatarSize;
@@ -614,7 +626,7 @@
facepileAvatarSize = mFacePileAvatarSizeExpandedGroup;
facePileBackgroundSize = facepileAvatarSize + 2 * mFacePileProtectionWidthExpanded;
}
- LayoutParams layoutParams = (LayoutParams) mConversationIcon.getLayoutParams();
+ LayoutParams layoutParams = (LayoutParams) mConversationIconView.getLayoutParams();
layoutParams.width = conversationAvatarSize;
layoutParams.height = conversationAvatarSize;
mConversationFacePile.setLayoutParams(layoutParams);
@@ -664,11 +676,11 @@
layoutParams.setMarginStart(sidemargin);
mConversationIconBadge.setLayoutParams(layoutParams);
- if (mConversationIcon.getVisibility() == VISIBLE) {
- layoutParams = (LayoutParams) mConversationIcon.getLayoutParams();
+ if (mConversationIconView.getVisibility() == VISIBLE) {
+ layoutParams = (LayoutParams) mConversationIconView.getLayoutParams();
layoutParams.width = conversationAvatarSize;
layoutParams.height = conversationAvatarSize;
- mConversationIcon.setLayoutParams(layoutParams);
+ mConversationIconView.setLayoutParams(layoutParams);
}
}
@@ -719,6 +731,10 @@
mConversationTitle = conversationTitle;
}
+ public CharSequence getConversationTitle() {
+ return mConversationText.getText();
+ }
+
private void removeGroups(ArrayList<MessagingGroup> oldGroups) {
int size = oldGroups.size();
for (int i = 0; i < size; i++) {
@@ -1218,4 +1234,43 @@
public void setMessagingClippingDisabled(boolean clippingDisabled) {
mMessagingLinearLayout.setClipBounds(clippingDisabled ? null : mMessagingClipRect);
}
+
+ @Nullable
+ public CharSequence getConversationSenderName() {
+ if (mGroups.isEmpty()) {
+ return null;
+ }
+ final CharSequence name = mGroups.get(mGroups.size() - 1).getSenderName();
+ return getResources().getString(R.string.conversation_single_line_name_display, name);
+ }
+
+ public boolean isOneToOne() {
+ return mIsOneToOne;
+ }
+
+ @Nullable
+ public CharSequence getConversationText() {
+ if (mMessages.isEmpty()) {
+ return null;
+ }
+ final MessagingMessage messagingMessage = mMessages.get(mMessages.size() - 1);
+ final CharSequence text = messagingMessage.getMessage().getText();
+ if (text == null && messagingMessage instanceof MessagingImageMessage) {
+ final String unformatted =
+ getResources().getString(R.string.conversation_single_line_image_placeholder);
+ SpannableString spannableString = new SpannableString(unformatted);
+ spannableString.setSpan(
+ new StyleSpan(Typeface.ITALIC),
+ 0,
+ spannableString.length(),
+ Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
+ return spannableString;
+ }
+ return text;
+ }
+
+ @Nullable
+ public Icon getConversationIcon() {
+ return mConversationIcon;
+ }
}
diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp
index 185e581..23f4325 100644
--- a/core/jni/android_graphics_BLASTBufferQueue.cpp
+++ b/core/jni/android_graphics_BLASTBufferQueue.cpp
@@ -29,9 +29,10 @@
namespace android {
-static jlong nativeCreate(JNIEnv* env, jclass clazz, jlong surfaceControl, jlong width, jlong height) {
+static jlong nativeCreate(JNIEnv* env, jclass clazz, jlong surfaceControl, jlong width, jlong height,
+ jboolean enableTripleBuffering) {
sp<BLASTBufferQueue> queue = new BLASTBufferQueue(
- reinterpret_cast<SurfaceControl*>(surfaceControl), width, height);
+ reinterpret_cast<SurfaceControl*>(surfaceControl), width, height, enableTripleBuffering);
queue->incStrong((void*)nativeCreate);
return reinterpret_cast<jlong>(queue.get());
}
@@ -59,7 +60,7 @@
static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
- { "nativeCreate", "(JJJ)J",
+ { "nativeCreate", "(JJJZ)J",
(void*)nativeCreate },
{ "nativeGetSurface", "(J)Landroid/view/Surface;",
(void*)nativeGetSurface },
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index 6cbc587..8d193bf 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -307,6 +307,9 @@
status);
goto native_init_failure;
}
+ // Set caller name so it can be logged in destructor.
+ // MediaMetricsConstants.h: AMEDIAMETRICS_PROP_CALLERNAME_VALUE_JAVA
+ lpRecorder->setCallerName("java");
} else { // end if nativeRecordInJavaObj == 0)
lpRecorder = (AudioRecord*)nativeRecordInJavaObj;
// TODO: We need to find out which members of the Java AudioRecord might need to be
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 1351818..5c045b6 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -425,6 +425,9 @@
ALOGE("Error %d initializing AudioTrack", status);
goto native_init_failure;
}
+ // Set caller name so it can be logged in destructor.
+ // MediaMetricsConstants.h: AMEDIAMETRICS_PROP_CALLERNAME_VALUE_JAVA
+ lpTrack->setCallerName("java");
} else { // end if (nativeAudioTrack == 0)
lpTrack = (AudioTrack*)nativeAudioTrack;
// TODO: We need to find out which members of the Java AudioTrack might
diff --git a/core/res/res/layout/conversation_face_pile_layout.xml b/core/res/res/layout/conversation_face_pile_layout.xml
index 5285625..95c14d7 100644
--- a/core/res/res/layout/conversation_face_pile_layout.xml
+++ b/core/res/res/layout/conversation_face_pile_layout.xml
@@ -12,7 +12,7 @@
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT 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 underthe License
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index ece59e2..23b8bd3 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -24,12 +24,20 @@
android:layout_height="@dimen/notification_header_height"
android:clipChildren="false"
style="?attr/notificationHeaderStyle">
- <com.android.internal.widget.CachingIconView
- android:id="@+id/icon"
- android:layout_width="?attr/notificationHeaderIconSize"
- android:layout_height="?attr/notificationHeaderIconSize"
- android:layout_marginEnd="@dimen/notification_header_icon_margin_end"
+ <!-- Wrapper used to expand the width of the "space" containing the icon programmatically -->
+ <FrameLayout
+ android:id="@+id/header_icon_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <com.android.internal.widget.CachingIconView
+ android:id="@+id/icon"
+ android:layout_width="?attr/notificationHeaderIconSize"
+ android:layout_height="?attr/notificationHeaderIconSize"
+ android:layout_marginEnd="@dimen/notification_header_icon_margin_end"
+ android:layout_gravity="center"
/>
+ </FrameLayout>
<TextView
android:id="@+id/app_name_text"
android:layout_width="wrap_content"
diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml
index 76ecefc..4d0837f 100644
--- a/core/res/res/layout/resolver_list.xml
+++ b/core/res/res/layout/resolver_list.xml
@@ -113,11 +113,13 @@
</LinearLayout>
</TabHost>
<LinearLayout
+ android:id="@+id/button_bar_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alwaysShow="true"
android:orientation="vertical"
- android:background="?attr/colorBackgroundFloating">
+ android:background="?attr/colorBackgroundFloating"
+ android:layout_ignoreOffset="true">
<View
android:id="@+id/resolver_button_bar_divider"
android:layout_width="match_parent"
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 9c93c3e..c2b8c2a 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4842,10 +4842,10 @@
<string name="confirm_battery_saver">OK</string>
<!-- [CHAR_LIMIT=NONE] Battery saver: Feature description, with a "learn more" link. -->
- <string name="battery_saver_description_with_learn_more">To extend battery life, Battery Saver:\n·Turns on Dark theme\n·Turns off or restricts background activity, some visual effects, and other features like \u201cHey Google\u201d\n\n<annotation id="url">Learn more</annotation></string>
+ <string name="battery_saver_description_with_learn_more">To extend battery life, Battery Saver:\n\n\u2022Turns on Dark theme\n\u2022Turns off or restricts background activity, some visual effects, and other features like \u201cHey Google\u201d\n\n<annotation id="url">Learn more</annotation></string>
<!-- [CHAR_LIMIT=NONE] Battery saver: Feature description, without a "learn more" link. -->
- <string name="battery_saver_description">To extend battery life, Battery Saver:\n·Turns on Dark theme\n·Turns off or restricts background activity, some visual effects, and other features like \u201cHey Google\u201d</string>
+ <string name="battery_saver_description">To extend battery life, Battery Saver:\n\n\u2022Turns on Dark theme\n\u2022Turns off or restricts background activity, some visual effects, and other features like \u201cHey Google\u201d</string>
<!-- [CHAR_LIMIT=NONE] Data saver: Feature description -->
<string name="data_saver_description">To help reduce data usage, Data Saver prevents some apps from sending or receiving data in the background. An app you’re currently using can access data, but may do so less frequently. This may mean, for example, that images don’t display until you tap them.</string>
@@ -5437,14 +5437,14 @@
<string name="accessibility_system_action_quick_settings_label">Quick Settings</string>
<!-- Label for opening power dialog action [CHAR LIMIT=NONE] -->
<string name="accessibility_system_action_power_dialog_label">Power Dialog</string>
- <!-- Label for toggle split screen action [CHAR LIMIT=NONE] -->
- <string name="accessibility_system_action_toggle_split_screen_label">Toggle Split Screen</string>
<!-- Label for lock screen action [CHAR LIMIT=NONE] -->
<string name="accessibility_system_action_lock_screen_label">Lock Screen</string>
<!-- Label for taking screenshot action [CHAR LIMIT=NONE] -->
<string name="accessibility_system_action_screenshot_label">Screenshot</string>
- <!-- Label for showing accessibility menu action [CHAR LIMIT=NONE] -->
- <string name="accessibility_system_action_accessibility_menu_label">Accessibility Menu</string>
+ <!-- Label for showing accessibility shortcut action [CHAR LIMIT=NONE] -->
+ <string name="accessibility_system_action_accessibility_button_label">On-screen Accessibility Shortcut</string>
+ <!-- Label for showing accessibility shortcut menu action [CHAR LIMIT=NONE] -->
+ <string name="accessibility_system_action_accessibility_button_chooser_label">On-screen Accessibility Shortcut Chooser</string>
<!-- Accessibility description of caption view -->
<string name="accessibility_freeform_caption">Caption bar of <xliff:g id="app_name">%1$s</xliff:g>.</string>
@@ -5455,6 +5455,9 @@
<!-- The way a conversation name is displayed when single line. The text will be displayed to the end of this text with some spacing -->
<string name="conversation_single_line_name_display"><xliff:g id="sender_name" example="Sara">%1$s</xliff:g>:</string>
+ <!-- Text used when a conversation is displayed in a single-line when the latest message is an image. [CHAR_LIMIT=NONE] -->
+ <string name="conversation_single_line_image_placeholder">sent an image</string>
+
<!-- Conversation Title fallback if the there is no name provided in a 1:1 conversation [CHAR LIMIT=40]-->
<string name="conversation_title_fallback_one_to_one">Conversation</string>
diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml
index 64768cf..e9ac679 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -296,6 +296,10 @@
<style name="TextAppearance.DeviceDefault.Notification.Info" parent="TextAppearance.Material.Notification.Info">
<item name="fontFamily">@string/config_bodyFontFamily</item>
</style>
+ <style name="TextAppearance.DeviceDefault.Notification.Conversation.AppName"
+ parent="@*android:style/TextAppearance.DeviceDefault.Notification.Title">
+ <item name="android:textSize">16sp</item>
+ </style>
<style name="TextAppearance.DeviceDefault.Widget" parent="TextAppearance.Material.Widget">
<item name="fontFamily">@string/config_bodyFontFamily</item>
</style>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 4315497..da64e77 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1598,6 +1598,8 @@
<java-symbol type="style" name="Theme.DeviceDefault.VoiceInteractionSession" />
<java-symbol type="style" name="Pointer" />
<java-symbol type="style" name="LargePointer" />
+ <java-symbol type="style" name="TextAppearance.DeviceDefault.Notification.Title" />
+ <java-symbol type="style" name="TextAppearance.DeviceDefault.Notification.Info" />
<java-symbol type="attr" name="mediaRouteButtonStyle" />
<java-symbol type="attr" name="externalRouteEnabledDrawable" />
@@ -3839,7 +3841,8 @@
<java-symbol type="string" name="accessibility_system_action_recents_label" />
<java-symbol type="string" name="accessibility_system_action_screenshot_label" />
<java-symbol type="string" name="accessibility_system_action_toggle_split_screen_label" />
- <java-symbol type="string" name="accessibility_system_action_accessibility_menu_label" />
+ <java-symbol type="string" name="accessibility_system_action_accessibility_button_label" />
+ <java-symbol type="string" name="accessibility_system_action_accessibility_button_chooser_label" />
<java-symbol type="string" name="accessibility_freeform_caption" />
@@ -3874,7 +3877,10 @@
<java-symbol type="array" name="config_defaultImperceptibleKillingExemptionPkgs" />
<java-symbol type="array" name="config_defaultImperceptibleKillingExemptionProcStates" />
+ <java-symbol type="id" name="header_icon_container" />
+ <java-symbol type="attr" name="notificationHeaderTextAppearance" />
<java-symbol type="string" name="conversation_single_line_name_display" />
+ <java-symbol type="string" name="conversation_single_line_image_placeholder" />
<java-symbol type="string" name="conversation_title_fallback_one_to_one" />
<java-symbol type="string" name="conversation_title_fallback_group_chat" />
<java-symbol type="id" name="conversation_icon" />
@@ -3918,6 +3924,7 @@
<java-symbol type="layout" name="conversation_face_pile_layout" />
<java-symbol type="id" name="conversation_unread_count" />
<java-symbol type="string" name="unread_convo_overflow" />
+ <java-symbol type="style" name="TextAppearance.DeviceDefault.Notification.Conversation.AppName" />
<!-- Intent resolver and share sheet -->
<java-symbol type="string" name="resolver_personal_tab" />
@@ -3934,6 +3941,7 @@
<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="id" name="button_bar_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/coretests/src/android/content/ContextTest.java b/core/tests/coretests/src/android/content/ContextTest.java
index f0997a6..2057a81 100644
--- a/core/tests/coretests/src/android/content/ContextTest.java
+++ b/core/tests/coretests/src/android/content/ContextTest.java
@@ -18,19 +18,26 @@
import static android.view.Display.DEFAULT_DISPLAY;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import android.app.ActivityThread;
import android.hardware.display.DisplayManager;
import android.os.UserHandle;
-import androidx.test.InstrumentationRegistry;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.Test;
import org.junit.runner.RunWith;
+/**
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:ContextTest
+ */
@SmallTest
@RunWith(AndroidJUnit4.class)
public class ContextTest {
@@ -43,6 +50,14 @@
}
@Test
+ public void testDisplayIdForSystemUiContext() {
+ final Context systemUiContext =
+ ActivityThread.currentActivityThread().getSystemUiContext();
+
+ assertEquals(systemUiContext.getDisplay().getDisplayId(), systemUiContext.getDisplayId());
+ }
+
+ @Test
public void testDisplayIdForTestContext() {
final Context testContext =
InstrumentationRegistry.getInstrumentation().getTargetContext();
@@ -89,4 +104,27 @@
InstrumentationRegistry.getInstrumentation().getTargetContext();
testContext.startActivityAsUser(new Intent(), new UserHandle(UserHandle.USER_ALL));
}
+
+ @Test
+ public void testIsUiContext_appContext_returnsFalse() {
+ final Context appContext = ApplicationProvider.getApplicationContext();
+
+ assertThat(appContext.isUiContext()).isFalse();
+ }
+
+ @Test
+ public void testIsUiContext_systemContext_returnsTrue() {
+ final Context systemContext =
+ ActivityThread.currentActivityThread().getSystemContext();
+
+ assertThat(systemContext.isUiContext()).isTrue();
+ }
+
+ @Test
+ public void testIsUiContext_systemUiContext_returnsTrue() {
+ final Context systemUiContext =
+ ActivityThread.currentActivityThread().getSystemUiContext();
+
+ assertThat(systemUiContext.isUiContext()).isTrue();
+ }
}
diff --git a/core/tests/coretests/src/android/view/DisplayAdjustmentsTests.java b/core/tests/coretests/src/android/view/DisplayAdjustmentsTests.java
new file mode 100644
index 0000000..afbf8db
--- /dev/null
+++ b/core/tests/coretests/src/android/view/DisplayAdjustmentsTests.java
@@ -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 android.view;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.res.Configuration;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link DisplayAdjustmentsTests}.
+ *
+ * <p>Build/Install/Run:
+ * atest FrameworksCoreTests:DisplayAdjustmentsTests
+ */
+@RunWith(AndroidJUnit4.class)
+public class DisplayAdjustmentsTests {
+
+ @Test
+ public void testDefaultConstructor_hasEmptyConfiguration() {
+ DisplayAdjustments emptyAdjustments = new DisplayAdjustments();
+
+ assertEquals(Configuration.EMPTY, emptyAdjustments.getConfiguration());
+ }
+
+ @Test
+ public void testConfigurationConstructor_nullConfigurationBecomesEmpty() {
+ DisplayAdjustments emptyAdjustments = new DisplayAdjustments((Configuration) null);
+
+ assertEquals(Configuration.EMPTY, emptyAdjustments.getConfiguration());
+ }
+
+ @Test
+ public void testConfigurationConstructor_copiesConfiguration() {
+ Configuration configuration = new Configuration();
+ configuration.colorMode = 1000;
+ DisplayAdjustments adjustments = new DisplayAdjustments(configuration);
+
+ assertEquals(configuration, adjustments.getConfiguration());
+ }
+
+ @Test
+ public void testDisplayAdjustmentsConstructor_copiesConfiguration() {
+ Configuration configuration = new Configuration();
+ configuration.colorMode = 1000;
+ DisplayAdjustments oldAdjustments = new DisplayAdjustments(configuration);
+
+ DisplayAdjustments newAdjustments = new DisplayAdjustments(oldAdjustments);
+
+ assertEquals(configuration, newAdjustments.getConfiguration());
+ }
+}
diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
index 03aba25..5c9e339 100644
--- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
@@ -23,36 +23,47 @@
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
import android.content.Context;
import android.graphics.Insets;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Bundle;
import android.platform.test.annotations.Presubmit;
-import android.view.SurfaceControl.Transaction;
import android.view.WindowManager.BadTokenException;
import android.view.WindowManager.LayoutParams;
import android.view.inputmethod.EditorInfo;
import android.widget.TextView;
-import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.FlakyTest;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+import org.mockito.Spy;
import java.util.ArrayList;
+/**
+ * Test {@link InsetsSourceConsumer} with IME type.
+ *
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:ImeInsetsSourceConsumerTest
+ */
@Presubmit
@FlakyTest(detail = "Promote once confirmed non-flaky")
@RunWith(AndroidJUnit4.class)
public class ImeInsetsSourceConsumerTest {
- Context mContext = InstrumentationRegistry.getTargetContext();
+ Context mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
ImeInsetsSourceConsumer mImeConsumer;
- InsetsController mController;
+ @Spy InsetsController mController;
SurfaceControl mLeash;
@Before
@@ -67,7 +78,7 @@
} catch (BadTokenException e) {
// activity isn't running, we will ignore BadTokenException.
}
- mController = new InsetsController(viewRootImpl);
+ mController = Mockito.spy(new InsetsController(viewRootImpl));
final Rect rect = new Rect(5, 5, 5, 5);
mController.calculateInsets(
false,
@@ -75,8 +86,7 @@
new DisplayCutout(
Insets.of(10, 10, 10, 10), rect, rect, rect, rect),
SOFT_INPUT_ADJUST_RESIZE, 0);
- mImeConsumer = new ImeInsetsSourceConsumer(
- new InsetsState(), Transaction::new, mController);
+ mImeConsumer = (ImeInsetsSourceConsumer) mController.getSourceConsumer(ITYPE_IME);
});
}
@@ -100,6 +110,27 @@
}
@Test
+ public void testImeRequestedVisibleAwaitingControl() {
+ // Set null control and then request show.
+ mController.onControlsChanged(new InsetsSourceControl[] { null });
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ // Request IME visible before control is available.
+ mImeConsumer.onWindowFocusGained();
+ mImeConsumer.applyImeVisibility(true /* setVisible */);
+
+ // set control and verify visibility is applied.
+ InsetsSourceControl control = new InsetsSourceControl(ITYPE_IME, mLeash, new Point());
+ mController.onControlsChanged(new InsetsSourceControl[] { control });
+ // IME show animation should be triggered when control becomes available.
+ verify(mController).applyAnimation(
+ eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(true) /* fromIme */);
+ verify(mController, never()).applyAnimation(
+ eq(WindowInsets.Type.ime()), eq(false) /* show */, eq(true) /* fromIme */);
+ });
+ }
+
+ @Test
public void testAreEditorsSimilar() {
EditorInfo info1 = new EditorInfo();
info1.privateImeOptions = "dummy";
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index d432dda..ade31d8 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -60,8 +60,8 @@
import android.view.test.InsetsModeSession;
import android.widget.TextView;
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
import com.android.server.testutils.OffsettableClock;
import com.android.server.testutils.TestHandler;
@@ -113,7 +113,7 @@
.setName("testSurface")
.build();
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
- Context context = InstrumentationRegistry.getTargetContext();
+ Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
// cannot mock ViewRootImpl since it's final.
mViewRoot = new ViewRootImpl(context, context.getDisplayNoVerify());
try {
diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
index 754c679..cc93f9a 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
@@ -17,8 +17,8 @@
package android.view;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
-
import static android.view.WindowInsets.Type.statusBars;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertTrue;
@@ -37,8 +37,8 @@
import android.view.WindowManager.LayoutParams;
import android.widget.TextView;
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.Before;
import org.junit.Test;
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index 0d497e1..ecc3b4f 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -40,9 +40,9 @@
import android.view.WindowInsets.Side;
import android.view.WindowInsets.Type;
-import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.Before;
import org.junit.Test;
@@ -51,6 +51,12 @@
import java.lang.reflect.Field;
import java.lang.reflect.Method;
+/**
+ * Tests for {@link ViewRootImpl}
+ *
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:ViewRootImplTest
+ */
@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -61,7 +67,7 @@
@Before
public void setUp() throws Exception {
- mContext = InstrumentationRegistry.getContext();
+ mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
mViewRootImpl = new ViewRootImplAccessor(
diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java
index 8c6a9371..4c7e960 100644
--- a/graphics/java/android/graphics/BLASTBufferQueue.java
+++ b/graphics/java/android/graphics/BLASTBufferQueue.java
@@ -26,15 +26,17 @@
// Note: This field is accessed by native code.
private long mNativeObject; // BLASTBufferQueue*
- private static native long nativeCreate(long surfaceControl, long width, long height);
+ private static native long nativeCreate(long surfaceControl, long width, long height,
+ boolean tripleBufferingEnabled);
private static native void nativeDestroy(long ptr);
private static native Surface nativeGetSurface(long ptr);
private static native void nativeSetNextTransaction(long ptr, long transactionPtr);
private static native void nativeUpdate(long ptr, long surfaceControl, long width, long height);
/** Create a new connection with the surface flinger. */
- public BLASTBufferQueue(SurfaceControl sc, int width, int height) {
- mNativeObject = nativeCreate(sc.mNativeObject, width, height);
+ public BLASTBufferQueue(SurfaceControl sc, int width, int height,
+ boolean tripleBufferingEnabled) {
+ mNativeObject = nativeCreate(sc.mNativeObject, width, height, tripleBufferingEnabled);
}
public void destroy() {
@@ -64,4 +66,3 @@
}
}
}
-
diff --git a/identity/java/android/security/identity/AccessControlProfileId.java b/identity/java/android/security/identity/AccessControlProfileId.java
index 3d59450..6caac0a 100644
--- a/identity/java/android/security/identity/AccessControlProfileId.java
+++ b/identity/java/android/security/identity/AccessControlProfileId.java
@@ -25,6 +25,8 @@
/**
* Constructs a new object holding a numerical identifier.
*
+ * <p>The identifier must be a non-negative number and less than 32.
+ *
* @param id the identifier.
*/
public AccessControlProfileId(int id) {
diff --git a/identity/java/android/security/identity/IdentityCredentialStore.java b/identity/java/android/security/identity/IdentityCredentialStore.java
index 4f834d2..3843d92 100644
--- a/identity/java/android/security/identity/IdentityCredentialStore.java
+++ b/identity/java/android/security/identity/IdentityCredentialStore.java
@@ -46,7 +46,7 @@
* access control profile IDs. Names are strings and values are typed and can be any
* value supported by <a href="http://cbor.io/">CBOR</a>.</li>
*
- * <li>A set of access control profiles, each with a profile ID and a specification
+ * <li>A set of access control profiles (up to 32), each with a profile ID and a specification
* of the conditions which satisfy the profile's requirements.</li>
*
* <li>An asymmetric key pair which is used to authenticate the credential to the Issuing
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index fb95e98..72162c4 100644
--- a/media/java/android/media/MediaRoute2ProviderService.java
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -519,6 +519,7 @@
requestCreateSession));
}
+ //TODO: Ignore requests with unknown session ID.
@Override
public void selectRoute(long requestId, String sessionId, String routeId) {
if (!checkCallerisSystem()) {
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 2c65cc4..7d14ef5 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -604,7 +604,8 @@
RoutingController oldController = getCurrentController();
if (!oldController.releaseInternal(
- /* shouldReleaseSession= */ true, /* shouldNotifyStop= */ false)) {
+ /* shouldReleaseSession= */ matchingRequest != null,
+ /* shouldNotifyStop= */ false)) {
// Could not release the controller since it was just released by other thread.
oldController = getSystemController();
}
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 3b570b6..5d61dd0 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -314,7 +314,6 @@
//TODO: Ignore unknown route.
if (sessionInfo.getTransferableRoutes().contains(route.getId())) {
- //TODO: callbacks must be called after this.
transferToRoute(sessionInfo, route);
return;
}
@@ -340,7 +339,6 @@
} catch (RemoteException ex) {
Log.e(TAG, "Unable to select media route", ex);
}
- releaseSession(sessionInfo);
}
}
@@ -489,6 +487,7 @@
notifyTransferFailed(matchingRequest.mOldSessionInfo, requestedRoute);
return;
}
+ releaseSession(matchingRequest.mOldSessionInfo);
notifyTransferred(matchingRequest.mOldSessionInfo, sessionInfo);
}
diff --git a/media/jni/soundpool/Stream.cpp b/media/jni/soundpool/Stream.cpp
index 809e81b..e3152d6 100644
--- a/media/jni/soundpool/Stream.cpp
+++ b/media/jni/soundpool/Stream.cpp
@@ -330,7 +330,9 @@
AudioTrack::TRANSFER_DEFAULT,
nullptr /*offloadInfo*/, -1 /*uid*/, -1 /*pid*/,
mStreamManager->getAttributes());
-
+ // Set caller name so it can be logged in destructor.
+ // MediaMetricsConstants.h: AMEDIAMETRICS_PROP_CALLERNAME_VALUE_SOUNDPOOL
+ newTrack->setCallerName("soundpool");
oldTrack = mAudioTrack;
status = newTrack->initCheck();
if (status != NO_ERROR) {
diff --git a/mime/java-res/android.mime.types b/mime/java-res/android.mime.types
index c1f8b3f..05a2e92 100644
--- a/mime/java-res/android.mime.types
+++ b/mime/java-res/android.mime.types
@@ -72,7 +72,7 @@
?application/x-x509-server-cert crt
?application/x-x509-user-cert crt
-?audio/3gpp 3gpp 3ga
+?audio/3gpp 3ga 3gpp
?audio/aac-adts aac
?audio/ac3 ac3 a52
?audio/amr amr
diff --git a/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml
index d0916b5..a8c7098 100644
--- a/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_left_navigation_bar.xml
@@ -33,7 +33,6 @@
android:gravity="top"
android:paddingTop="30dp"
android:layout_weight="1"
- android:background="@drawable/system_bar_background"
android:animateLayoutChanges="true">
<com.android.systemui.car.navigationbar.CarNavigationButton
diff --git a/packages/CarSystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml
index de5a150..9e6dd11 100644
--- a/packages/CarSystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml
+++ b/packages/CarSystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml
@@ -33,7 +33,6 @@
android:gravity="top"
android:paddingTop="30dp"
android:layout_weight="1"
- android:background="@drawable/system_bar_background"
android:animateLayoutChanges="true">
<com.android.systemui.car.navigationbar.CarNavigationButton
diff --git a/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml
index d386ce3..fd75570 100644
--- a/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_right_navigation_bar.xml
@@ -36,7 +36,6 @@
android:gravity="top"
android:paddingTop="30dp"
android:layout_weight="1"
- android:background="@drawable/system_bar_background"
android:animateLayoutChanges="true">
<com.android.systemui.car.navigationbar.CarNavigationButton
diff --git a/packages/CarSystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml
index de5a150..9e6dd11 100644
--- a/packages/CarSystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml
+++ b/packages/CarSystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml
@@ -33,7 +33,6 @@
android:gravity="top"
android:paddingTop="30dp"
android:layout_weight="1"
- android:background="@drawable/system_bar_background"
android:animateLayoutChanges="true">
<com.android.systemui.car.navigationbar.CarNavigationButton
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java
index 2b5cab7..3b64369 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBar.java
@@ -19,7 +19,6 @@
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.InsetsState.containsType;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
@@ -28,11 +27,9 @@
import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.inputmethodservice.InputMethodService;
-import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.view.Display;
import android.view.Gravity;
import android.view.View;
@@ -47,6 +44,7 @@
import com.android.systemui.SystemUI;
import com.android.systemui.car.CarDeviceProvisionedController;
import com.android.systemui.car.CarDeviceProvisionedListener;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.statusbar.AutoHideUiElement;
@@ -76,6 +74,8 @@
private final AutoHideController mAutoHideController;
private final ButtonSelectionStateListener mButtonSelectionStateListener;
private final Handler mMainHandler;
+ private final Handler mBgHandler;
+ private final IStatusBarService mBarService;
private final Lazy<KeyguardStateController> mKeyguardStateControllerLazy;
private final ButtonSelectionStateController mButtonSelectionStateController;
private final PhoneStatusBarPolicy mIconPolicy;
@@ -84,7 +84,6 @@
private final int mDisplayId;
private StatusBarSignalPolicy mSignalPolicy;
- private IStatusBarService mBarService;
private ActivityManagerWrapper mActivityManagerWrapper;
// If the nav bar should be hidden when the soft keyboard is visible.
@@ -121,6 +120,8 @@
AutoHideController autoHideController,
ButtonSelectionStateListener buttonSelectionStateListener,
@Main Handler mainHandler,
+ @Background Handler bgHandler,
+ IStatusBarService barService,
Lazy<KeyguardStateController> keyguardStateControllerLazy,
ButtonSelectionStateController buttonSelectionStateController,
PhoneStatusBarPolicy iconPolicy,
@@ -135,6 +136,8 @@
mAutoHideController = autoHideController;
mButtonSelectionStateListener = buttonSelectionStateListener;
mMainHandler = mainHandler;
+ mBgHandler = bgHandler;
+ mBarService = barService;
mKeyguardStateControllerLazy = keyguardStateControllerLazy;
mButtonSelectionStateController = buttonSelectionStateController;
mIconPolicy = iconPolicy;
@@ -150,10 +153,6 @@
com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard);
mBottomNavBarVisible = false;
- // Get bar service.
- mBarService = IStatusBarService.Stub.asInterface(
- ServiceManager.getService(Context.STATUS_BAR_SERVICE));
-
// Connect into the status bar manager service
mCommandQueue.addCallback(this);
@@ -233,11 +232,15 @@
mActivityManagerWrapper = ActivityManagerWrapper.getInstance();
mActivityManagerWrapper.registerTaskStackListener(mButtonSelectionStateListener);
- mCarNavigationBarController.connectToHvac();
+ mBgHandler.post(() -> mCarNavigationBarController.connectToHvac());
// Lastly, call to the icon policy to install/update all the icons.
- mIconPolicy.init();
- mSignalPolicy = new StatusBarSignalPolicy(mContext, mIconController);
+ // Must be called on the main thread due to the use of observeForever() in
+ // mIconPolicy.init().
+ mMainHandler.post(() -> {
+ mIconPolicy.init();
+ mSignalPolicy = new StatusBarSignalPolicy(mContext, mIconController);
+ });
}
private void restartNavBarsIfNecessary() {
@@ -349,33 +352,38 @@
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
height,
- WindowManager.LayoutParams.TYPE_STATUS_BAR,
+ WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
- | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+ | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
PixelFormat.TRANSLUCENT);
- lp.token = new Binder();
- lp.gravity = Gravity.TOP;
- lp.setFitInsetsTypes(0 /* types */);
lp.setTitle("TopCarNavigationBar");
- lp.packageName = mContext.getPackageName();
- lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ lp.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR};
+ lp.setFitInsetsTypes(0);
+ lp.windowAnimations = 0;
+ lp.gravity = Gravity.TOP;
mWindowManager.addView(mTopNavigationBarWindow, lp);
}
if (mBottomNavigationBarWindow != null && !mBottomNavBarVisible) {
mBottomNavBarVisible = true;
+ int height = mResources.getDimensionPixelSize(
+ com.android.internal.R.dimen.navigation_bar_height);
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ height,
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
PixelFormat.TRANSLUCENT);
lp.setTitle("BottomCarNavigationBar");
+ lp.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR};
lp.windowAnimations = 0;
+ lp.gravity = Gravity.BOTTOM;
mWindowManager.addView(mBottomNavigationBarWindow, lp);
}
@@ -392,11 +400,10 @@
PixelFormat.TRANSLUCENT);
leftlp.setTitle("LeftCarNavigationBar");
leftlp.windowAnimations = 0;
- leftlp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
leftlp.gravity = Gravity.LEFT;
- leftlp.setFitInsetsTypes(0 /* types */);
mWindowManager.addView(mLeftNavigationBarWindow, leftlp);
}
+
if (mRightNavigationBarWindow != null) {
int width = mResources.getDimensionPixelSize(
R.dimen.car_right_navigation_bar_width);
@@ -410,9 +417,7 @@
PixelFormat.TRANSLUCENT);
rightlp.setTitle("RightCarNavigationBar");
rightlp.windowAnimations = 0;
- rightlp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
rightlp.gravity = Gravity.RIGHT;
- rightlp.setFitInsetsTypes(0 /* types */);
mWindowManager.addView(mRightNavigationBarWindow, rightlp);
}
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java
index 55c1153..9e194fb 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarController.java
@@ -20,7 +20,6 @@
import android.view.View;
import android.view.ViewGroup;
-import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.systemui.R;
@@ -160,8 +159,12 @@
}
/** Gets the top navigation bar with the appropriate listeners set. */
- @NonNull
+ @Nullable
public CarNavigationBarView getTopBar(boolean isSetUp) {
+ if (!mShowTop) {
+ return null;
+ }
+
mTopView = mNavigationBarViewFactory.getTopBar(isSetUp);
setupBar(mTopView, mTopBarTouchListener, mNotificationsShadeController);
return mTopView;
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java
index 46a720b..20fc1bc 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationBarView.java
@@ -16,10 +16,14 @@
package com.android.systemui.car.navigationbar;
+import static android.view.WindowInsets.Type.systemBars;
+
import android.content.Context;
+import android.graphics.Insets;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
+import android.view.WindowInsets;
import android.widget.LinearLayout;
import com.android.systemui.Dependency;
@@ -44,7 +48,6 @@
// used to wire in open/close gestures for notifications
private OnTouchListener mStatusBarWindowTouchListener;
-
public CarNavigationBarView(Context context, AttributeSet attrs) {
super(context, attrs);
mConsumeTouchWhenPanelOpen = getResources().getBoolean(
@@ -75,6 +78,30 @@
setClickable(true);
}
+ @Override
+ public WindowInsets onApplyWindowInsets(WindowInsets windowInsets) {
+ applyMargins(windowInsets.getInsets(systemBars()));
+ return windowInsets;
+ }
+
+ private void applyMargins(Insets insets) {
+ final int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ View child = getChildAt(i);
+ if (child.getLayoutParams() instanceof LayoutParams) {
+ LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ if (lp.rightMargin != insets.right || lp.leftMargin != insets.left
+ || lp.topMargin != insets.top || lp.bottomMargin != insets.bottom) {
+ lp.rightMargin = insets.right;
+ lp.leftMargin = insets.left;
+ lp.topMargin = insets.top;
+ lp.bottomMargin = insets.bottom;
+ child.requestLayout();
+ }
+ }
+ }
+ }
+
// Used to forward touch events even if the touch was initiated from a child component
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/TopNotificationPanelViewMediator.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/TopNotificationPanelViewMediator.java
index 09a4621..8d3eb4c 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/TopNotificationPanelViewMediator.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/TopNotificationPanelViewMediator.java
@@ -53,7 +53,7 @@
@Override
public void registerListeners() {
super.registerListeners();
- getCarNavigationBarController().registerBottomBarTouchListener(
+ getCarNavigationBarController().registerTopBarTouchListener(
getNotificationPanelViewController().getDragOpenTouchListener());
}
}
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarTest.java
index 6620e9d..adf4359 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/navigationbar/CarNavigationBarTest.java
@@ -33,6 +33,7 @@
import androidx.test.filters.SmallTest;
+import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.car.CarDeviceProvisionedController;
@@ -57,6 +58,7 @@
private CarNavigationBar mCarNavigationBar;
private TestableResources mTestableResources;
private Handler mHandler;
+ private Handler mBackgroundHandler;
@Mock
private CarNavigationBarController mCarNavigationBarController;
@@ -69,6 +71,8 @@
@Mock
private ButtonSelectionStateListener mButtonSelectionStateListener;
@Mock
+ private IStatusBarService mBarService;
+ @Mock
private KeyguardStateController mKeyguardStateController;
@Mock
private ButtonSelectionStateController mButtonSelectionStateController;
@@ -82,11 +86,12 @@
MockitoAnnotations.initMocks(this);
mTestableResources = mContext.getOrCreateTestableResources();
mHandler = Handler.getMain();
+ mBackgroundHandler = Handler.createAsync(TestableLooper.get(this).getLooper());
mCarNavigationBar = new CarNavigationBar(mContext, mTestableResources.getResources(),
mCarNavigationBarController, mWindowManager, mDeviceProvisionedController,
new CommandQueue(mContext), mAutoHideController, mButtonSelectionStateListener,
- mHandler, () -> mKeyguardStateController, mButtonSelectionStateController,
- mIconPolicy, mIconController);
+ mHandler, mBackgroundHandler, mBarService, () -> mKeyguardStateController,
+ mButtonSelectionStateController, mIconPolicy, mIconController);
}
@Test
@@ -103,7 +108,7 @@
verify(mDeviceProvisionedController).addCallback(deviceProvisionedCallbackCaptor.capture());
deviceProvisionedCallbackCaptor.getValue().onUserSwitched();
- waitForIdleSync(mHandler);
+ waitForIdleSync(mBackgroundHandler);
verify(mButtonSelectionStateListener).onTaskStackChanged();
}
@@ -123,7 +128,7 @@
verify(mDeviceProvisionedController).addCallback(deviceProvisionedCallbackCaptor.capture());
deviceProvisionedCallbackCaptor.getValue().onUserSwitched();
- waitForIdleSync(mHandler);
+ waitForIdleSync(mBackgroundHandler);
verify(mCarNavigationBarController).showAllKeyguardButtons(false);
}
@@ -142,12 +147,12 @@
when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(false);
verify(mDeviceProvisionedController).addCallback(deviceProvisionedCallbackCaptor.capture());
deviceProvisionedCallbackCaptor.getValue().onUserSwitched();
- waitForIdleSync(mHandler);
+ waitForIdleSync(mBackgroundHandler);
when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true);
when(mKeyguardStateController.isShowing()).thenReturn(false);
deviceProvisionedCallbackCaptor.getValue().onUserSetupChanged();
- waitForIdleSync(mHandler);
+ waitForIdleSync(mBackgroundHandler);
verify(mCarNavigationBarController).hideAllKeyguardButtons(true);
}
diff --git a/packages/CtsShim/apk/arm/CtsShim.apk b/packages/CtsShim/apk/arm/CtsShim.apk
index 54ee0d0..6480dea 100644
--- a/packages/CtsShim/apk/arm/CtsShim.apk
+++ b/packages/CtsShim/apk/arm/CtsShim.apk
Binary files differ
diff --git a/packages/CtsShim/apk/arm/CtsShimPriv.apk b/packages/CtsShim/apk/arm/CtsShimPriv.apk
index ca34af9..0814501 100644
--- a/packages/CtsShim/apk/arm/CtsShimPriv.apk
+++ b/packages/CtsShim/apk/arm/CtsShimPriv.apk
Binary files differ
diff --git a/packages/CtsShim/apk/x86/CtsShim.apk b/packages/CtsShim/apk/x86/CtsShim.apk
index 54ee0d0..6480dea 100644
--- a/packages/CtsShim/apk/x86/CtsShim.apk
+++ b/packages/CtsShim/apk/x86/CtsShim.apk
Binary files differ
diff --git a/packages/CtsShim/apk/x86/CtsShimPriv.apk b/packages/CtsShim/apk/x86/CtsShimPriv.apk
index edbb151..b52dc9d 100644
--- a/packages/CtsShim/apk/x86/CtsShimPriv.apk
+++ b/packages/CtsShim/apk/x86/CtsShimPriv.apk
Binary files differ
diff --git a/packages/CtsShim/build/Android.bp b/packages/CtsShim/build/Android.bp
index be79010..d41c672 100644
--- a/packages/CtsShim/build/Android.bp
+++ b/packages/CtsShim/build/Android.bp
@@ -76,6 +76,7 @@
"com.android.apex.cts.shim.v2_no_hashtree",
"com.android.apex.cts.shim.v2_legacy",
"com.android.apex.cts.shim.v2_sdk_target_p",
+ "com.android.apex.cts.shim.v2_unsigned_payload",
"com.android.apex.cts.shim.v3",
],
}
@@ -148,6 +149,7 @@
"com.android.apex.cts.shim.v2_no_hashtree",
"com.android.apex.cts.shim.v2_legacy",
"com.android.apex.cts.shim.v2_sdk_target_p",
+ "com.android.apex.cts.shim.v2_unsigned_payload",
"com.android.apex.cts.shim.v3",
],
}
diff --git a/packages/CtsShim/build/jni/Android.bp b/packages/CtsShim/build/jni/Android.bp
index 7a5b07e..4477582 100644
--- a/packages/CtsShim/build/jni/Android.bp
+++ b/packages/CtsShim/build/jni/Android.bp
@@ -25,6 +25,7 @@
"com.android.apex.cts.shim.v2_no_hashtree",
"com.android.apex.cts.shim.v2_legacy",
"com.android.apex.cts.shim.v2_sdk_target_p",
+ "com.android.apex.cts.shim.v2_unsigned_payload",
"com.android.apex.cts.shim.v3",
],
}
diff --git a/packages/DynamicSystemInstallationService/res/values-af/strings.xml b/packages/DynamicSystemInstallationService/res/values-af/strings.xml
new file mode 100644
index 0000000..1829d34
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-af/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Voer asseblief jou wagwoord in en gaan voort na Dinamiese Steselopdaterings"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Dinamiese stelsel is gereed. Herbegin jou toestel om dit te begin gebruik."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Installeer tans"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Kon nie installeer nie"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Kon nie prent bekragtig nie. Staak installering."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Laat loop tans \'n dinamiese stelsel. Herbegin om die oorspronklike Android-weergawe te gebruik."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Kanselleer"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Gooi weg"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Herbegin"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Herbegin"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Het dinamiese stelsel weggegooi"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Kan nie dinamiese stelsel herbegin of laai nie"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-am/strings.xml b/packages/DynamicSystemInstallationService/res/values-am/strings.xml
new file mode 100644
index 0000000..7fcc40d
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-am/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"እባክዎ የእርስዎን የይለፍ ቃል ያስገቡ እና ወደ የተለዋዋጭ ሥርዓት ዝማኔዎች ይቀጥሉ"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"የተለዋዋጭ ሥርዓት ዝግጁ ነው። እሱን መጠቀም ለመጀመር የእርስዎን መሣሪያ ዳግም ያስጀምሩ።"</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"ጭነት በሂደት ላይ ነው"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"መጫን አልተሳካም"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"የምስል ማረጋገጫ አልተሳካም። ጭነትን አጨናግፍ።"</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"የተለዋዋጭ ሥርዓትን አሁን ላይ በማስሄድ ላይ ኦርጂናሉን የAndroid ስሪት በመጠቀም ዳግም ያስጀምሩ።"</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"ይቅር"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"አስወግድ"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"ዳግም ጀምር"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"ዳግም ጀምር"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"የተጣለ ተለዋዋጭ ሥርዓት"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"ዳግም ማስጀመር አይቻልም ወይም ተለዋዋጭ ሥርዓትን ይስቀሉ"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-ar/strings.xml b/packages/DynamicSystemInstallationService/res/values-ar/strings.xml
new file mode 100644
index 0000000..be705c3
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-ar/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"يُرجى إدخال كلمة مرور والمتابعة إلى \"تحديثات النظام الديناميكية\""</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"النظام الديناميكي جاهز. لبدء استخدامه، يجب إعادة تشغيل الجهاز."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"التثبيت قيد التقدّم."</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"تعذّر التثبيت."</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"تعذّر التحقّق من الصورة. يجب إلغاء التثبيت."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"يتم الآن تشغيل نظام ديناميكي. يجب إعادة التشغيل لاستخدام الإصدار الأصلي لنظام Android."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"إلغاء"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"تجاهل"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"إعادة التشغيل"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"إعادة التشغيل"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"تم تجاهل النظام الديناميكي."</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"لا يمكن إعادة التشغيل أو تحميل النظام الديناميكي."</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-as/strings.xml b/packages/DynamicSystemInstallationService/res/values-as/strings.xml
new file mode 100644
index 0000000..14eead9
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-as/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"অনুগ্ৰহ কৰি আপোনাৰ পাছৱৰ্ডটো দিয়ক আৰু ডায়নামিক ছিষ্টেম আপডে\'টসমূহলৈ অব্যাহত ৰাখক"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"ডায়নামিক ছিষ্টেমটো সাজু। এইটো ব্যৱহাৰ কৰা আৰম্ভ কৰিবলৈ আপোনাৰ ডিভাইচটো ৰিষ্টাৰ্ট কৰক।"</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"ইনষ্টল চলি আছে"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"ইনষ্টল কৰিব পৰা নগ’ল"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"প্ৰতিচ্ছবিখন সত্যাপন কৰিব পৰা নগ’ল। ইনষ্টলেশ্বন বাতিল কৰক।"</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"বৰ্তমান এটা ডায়নামিক ছিষ্টেম চলি আছে। মূল Android সংস্কৰণটো ব্যৱহাৰ কৰিবলৈ ৰিষ্টাৰ্ট কৰক।"</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"বাতিল কৰক"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"বাতিল কৰক"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"ৰিষ্টাৰ্ট কৰক"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"ৰিষ্টাৰ্ট কৰক"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"বাতিল কৰা ডায়নামিক ছিষ্টেম"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"ডায়নামিক ছিষ্টেম ৰিষ্টার্ট অথবা ল\'ড কৰিব নোৱাৰি"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-az/strings.xml b/packages/DynamicSystemInstallationService/res/values-az/strings.xml
new file mode 100644
index 0000000..d1f0a4b
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-az/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Parolunuzu daxil edin və Dinamik Sistem Güncəlləməsinə keçin"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Dinamik sistem hazırdır. İstifadəyə başlamaq üçün cihazınızı yenidən başladın."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Quraşdırılır"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Quraşdırılmadı"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Şəkil təsdiqlənmədi. Quraşdırmanı dayandırın."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Hazırda dinamik sistem icra olunur. Orijinal Android versiyasından istifadə etmək üçün yenidən başladın."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Ləğv edin"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"İmtina edin"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Yenidən başladın"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Yenidən başladın"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Dinamik sistemdən imtina edildi"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Dinamik sistemi yenidən başlatmaq və ya yükləmək mümkün deyil"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-b+sr+Latn/strings.xml b/packages/DynamicSystemInstallationService/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..ea23a28
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Unesite lozinku i nastavite do dinamičnih ažuriranja sistema"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Dinamični sistem je spreman. Da biste počeli da ga koristite, restartujte uređaj."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Instalira se"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Instaliranje nije uspelo"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Validacija slike nije uspela. Otkažite instalaciju."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Trenutno je pokrenut dinamični sistem. Restartujte da biste koristili originalnu verziju Android-a."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Otkaži"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Odbaci"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Restartuj"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Restartuj"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Dinamični sistem je odbačen"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Restartovanje ili učitavanje dinamičnog sistema nije uspelo"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-be/strings.xml b/packages/DynamicSystemInstallationService/res/values-be/strings.xml
new file mode 100644
index 0000000..7eef297
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-be/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Увядзіце пароль і перайдзіце ў Дынамічныя абнаўленні сістэмы"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Дынамічная сістэма гатовая. Каб пачаць выкарыстоўваць яе, перазапусціце прыладу."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Ідзе ўсталёўка"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Збой усталёўкі"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Збой пры праверцы відарыса. Усталёўка спынена."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Цяпер запушчана дынамічная сістэма. Перазапусціце, каб скарыстаць арыгінальную версію Android."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Скасаваць"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Адхіліць"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Перазапусціць"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Перазапусціць"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Дынамічная сістэма адхілена"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Не ўдалося перазапусціць або загрузіць дынамічную сістэму"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-bg/strings.xml b/packages/DynamicSystemInstallationService/res/values-bg/strings.xml
new file mode 100644
index 0000000..9176676
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-bg/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Моля, въведете паролата си и продължете към функцията за динамични актуализации на системата"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Динамичната система е готова. Рестартирайте устройството си, за да я използвате."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Инсталиране – в ход"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Инсталирането не бе успешно"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Проверката на изображението не бе успешна. Прекратяване на инсталирането."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Понастоящем се изпълнява динамична система. Рестартирайте, за да използвате оригиналната версия на Android."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Отказ"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Отхвърляне"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Рестартиране"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Рестартиране"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Динамичната система е отхвърлена"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Динамичната система не може да се рестартира или зареди"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-bn/strings.xml b/packages/DynamicSystemInstallationService/res/values-bn/strings.xml
new file mode 100644
index 0000000..38ef649
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-bn/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"আপনার পাসওয়ার্ড লিখে ডায়নামিক সিস্টেম আপডেট করা চালিয়ে যান"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"ডায়নামিক সিস্টেম রেডি হয়ে গেছে। সেটি ব্যবহার করা শুরু করতে আপনার ডিভাইস রিস্টার্ট করুন।"</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"ইনস্টল করা হচ্ছে"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"ইনস্টল করা যায়নি"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"ছবি যাচাই করা যায়নি। ইনস্টলেশন বন্ধ করুন।"</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"বর্তমানে ডায়নামিক সিস্টেম চালানো হচ্ছে। আসল Android ভার্সন ব্যবহার করার জন্য রিস্টার্ট করুন।"</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"বাতিল করুন"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"বাতিল করুন"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"রিস্টার্ট করুন"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"রিস্টার্ট করুন"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"ডায়নামিক সিস্টেম বাতিল করা হয়েছে"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"ডায়নামিক সিস্টেম রিস্টার্ট বা লোড করা যাচ্ছে না"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-bs/strings.xml b/packages/DynamicSystemInstallationService/res/values-bs/strings.xml
new file mode 100644
index 0000000..84ba540
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-bs/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Unesite lozinku i nastavite na dinamična ažuriranja sistema"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Dinamični sistem je spreman. Da ga počnete koristiti, ponovo pokrenite uređaj."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Instaliranje je u toku"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Instaliranje nije uspjelo"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Potvrda slike sistema nije uspjela. Prekini instalaciju."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Dinamični sistem je sada aktivan. Ponovo pokrenite da koristite originalnu verziju Androida."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Otkaži"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Odbaci"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Ponovo pokreni"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Ponovo pokreni"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Dinamični sistem je odbačen"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Nije moguće ponovo pokrenuti ili učitati dinamični sistem"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-ca/strings.xml b/packages/DynamicSystemInstallationService/res/values-ca/strings.xml
new file mode 100644
index 0000000..787e496
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-ca/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Introdueix la contrasenya i continua cap a Actualització dinàmica del sistema"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"El sistema dinàmic ja està a punt. Per començar a utilitzar-lo, reinicia el teu dispositiu."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Instal·lació en curs"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Ha fallat la instal·lació"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"No s\'ha pogut validar la imatge. Anul·la la instal·lació."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"S\'està executant un sistema dinàmic. Reinicia per utilitzar la versió original d\'Android."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Cancel·la"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Descarta"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Reinicia"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Reinicia"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"S\'ha descartat el sistema dinàmic"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"No es pot reiniciar ni carregar el sistema dinàmic"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-cs/strings.xml b/packages/DynamicSystemInstallationService/res/values-cs/strings.xml
new file mode 100644
index 0000000..3dfb23f
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-cs/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Zadejte heslo a pokračujte k dynamickým aktualizacím systému"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Dynamický systém je připraven. Chcete-li ho začít používat, restartujte zařízení."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Probíhá instalace"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Instalace se nezdařila"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Obraz se nepodařilo ověřit. Zrušte instalaci."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Je spuštěn dynamický systém. Chcete-li použít původní verzi systému Android, restartujte zařízení."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Zrušit"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Zahodit"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Restartovat"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Restartovat"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Zahodit dynamický systém"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Dynamický systém nelze znovu spustit nebo načíst"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-da/strings.xml b/packages/DynamicSystemInstallationService/res/values-da/strings.xml
new file mode 100644
index 0000000..20005e7
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-da/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Angiv din adgangskode, og fortsæt til Dynamiske systemopdateringer"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Det dynamiske system er klar. Hvis du vil begynde at bruge det, skal du genstarte din enhed."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Installation i gang"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Installationen mislykkedes"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Billedet kunne ikke valideres. Afbryd installationen."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Der køres i øjeblikket et dynamisk system. Genstart for at bruge den oprindelige Android-version."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Annuller"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Afbryd"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Genstart"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Genstart"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Det dynamiske system blev slettet"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Det dynamiske system kan ikke genstartes eller indlæses"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-de/strings.xml b/packages/DynamicSystemInstallationService/res/values-de/strings.xml
new file mode 100644
index 0000000..3f000ea
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-de/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Gib dein Passwort ein und fahre mit Dynamic System Updates fort"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Das dynamische System kann verwendet werden. Starte dazu dein Gerät neu."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Installation läuft"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Fehler bei der Installation"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Fehler bei der Imagevalidierung. Die Installation wird abgebrochen."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Momentan wird ein dynamisches System ausgeführt. Durch einen Neustart kannst du zur ursprünglichen Android-Version zurückkehren."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Abbrechen"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Verwerfen"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Neu starten"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Neu starten"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Dynamisches System verworfen"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Neustart und Laden des dynamischen Systems nicht möglich"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-el/strings.xml b/packages/DynamicSystemInstallationService/res/values-el/strings.xml
new file mode 100644
index 0000000..4d830dd
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-el/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Πληκτρολογήστε τον κωδικό σας και συνεχίστε στις Δυναμικές ενημερώσεις συστήματος."</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Οι δυναμικές συστήματος είναι έτοιμες. Για να ξεκινήσετε να τις χρησιμοποιείτε, επανεκκινήστε τη συσκευή σας."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Εγκατάσταση σε εξέλιξη"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Η εγκατάσταση απέτυχε."</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Η επαλήθευση εικόνας απέτυχε. Ακύρωση εγκατάστασης."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Αυτήν τη στιγμή εκτελούνται δυναμικές συστήματος. Επανεκκινήστε για να χρησιμοποιήσετε την αρχική έκδοση Android."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Ακύρωση"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Απόρριψη"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Επανεκκίνηση"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Επανεκκίνηση"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Οι δυναμικές συστήματος απορρίφθηκαν."</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Δεν είναι δυνατή η επανεκκίνηση ή η φόρτωση δυναμικών συστήματος"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-en-rAU/strings.xml b/packages/DynamicSystemInstallationService/res/values-en-rAU/strings.xml
new file mode 100644
index 0000000..d728631
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-en-rAU/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Please enter your password and continue to Dynamic System Updates"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Dynamic system is ready. To start using it, restart your device."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Installation in progress"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Installation failed"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Image validation failed. Abort installation."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Currently running a dynamic system. Restart to use the original Android version."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Cancel"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Discard"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Restart"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Restart"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Discarded dynamic system"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Can’t restart or load dynamic system"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-en-rCA/strings.xml b/packages/DynamicSystemInstallationService/res/values-en-rCA/strings.xml
new file mode 100644
index 0000000..d728631
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-en-rCA/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Please enter your password and continue to Dynamic System Updates"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Dynamic system is ready. To start using it, restart your device."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Installation in progress"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Installation failed"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Image validation failed. Abort installation."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Currently running a dynamic system. Restart to use the original Android version."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Cancel"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Discard"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Restart"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Restart"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Discarded dynamic system"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Can’t restart or load dynamic system"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-en-rGB/strings.xml b/packages/DynamicSystemInstallationService/res/values-en-rGB/strings.xml
new file mode 100644
index 0000000..d728631
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-en-rGB/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Please enter your password and continue to Dynamic System Updates"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Dynamic system is ready. To start using it, restart your device."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Installation in progress"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Installation failed"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Image validation failed. Abort installation."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Currently running a dynamic system. Restart to use the original Android version."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Cancel"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Discard"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Restart"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Restart"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Discarded dynamic system"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Can’t restart or load dynamic system"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-en-rIN/strings.xml b/packages/DynamicSystemInstallationService/res/values-en-rIN/strings.xml
new file mode 100644
index 0000000..d728631
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-en-rIN/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Please enter your password and continue to Dynamic System Updates"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Dynamic system is ready. To start using it, restart your device."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Installation in progress"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Installation failed"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Image validation failed. Abort installation."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Currently running a dynamic system. Restart to use the original Android version."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Cancel"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Discard"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Restart"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Restart"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Discarded dynamic system"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Can’t restart or load dynamic system"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-en-rXC/strings.xml b/packages/DynamicSystemInstallationService/res/values-en-rXC/strings.xml
new file mode 100644
index 0000000..6ac3763
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-en-rXC/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Please enter your password and continue to Dynamic System Updates"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Dynamic system is ready. To start using it, restart your device."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Install in progress"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Install failed"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Image validation failed. Abort installation."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Currently running a dynamic system. Restart to use the original Android version."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Cancel"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Discard"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Restart"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Restart"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Discarded dynamic system"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Can’t restart or load dynamic system"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-es-rUS/strings.xml b/packages/DynamicSystemInstallationService/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..9ec8196
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-es-rUS/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Ingresa la contraseña y continúa en Actualizaciones dinámicas del sistema"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"El sistema dinámico está listo. Para comenzar a usarlo, reinicia el dispositivo."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Instalación en curso"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Error de instalación"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Se produjo un error con la validación de la imagen. Anula la instalación."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Se está ejecutando el sistema dinámico. Reinicia para usar la versión original de Android."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Cancelar"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Descartar"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Reiniciar"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Reiniciar"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Se descartó el sistema dinámico"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"No se puede reiniciar o cargar el sistema dinámico"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-es/strings.xml b/packages/DynamicSystemInstallationService/res/values-es/strings.xml
new file mode 100644
index 0000000..cd9db07
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-es/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Introduce la contraseña y entra en Actualización Dinámica del Sistema"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"El sistema dinámico está listo. Para comenzar a usarlo, reinicia el dispositivo."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Instalación en curso"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"No se ha podido instalar"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"No se ha podido validar la imagen. Anula la instalación."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Se está ejecutando el sistema dinámico. Reinícialo para usar la versión original de Android."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Cancelar"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Descartar"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Reiniciar"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Reiniciar"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Se ha descartado el sistema dinámico"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"No se ha podido reiniciar o cargar el sistema dinámico"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-et/strings.xml b/packages/DynamicSystemInstallationService/res/values-et/strings.xml
new file mode 100644
index 0000000..64968b60
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-et/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Sisestage oma parool ja minge edasi rakendusse Dünaamiline süsteemivärskendus"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Dünaamiline süsteem on valmis. Selle kasutamise alustamiseks taaskäivitage seade."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Installimine on pooleli"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Installimine ebaõnnestus"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Kujutise kinnitamine ebaõnnestus. Katkestage installimine."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Praegu kasutatakse dünaamilist süsteemi. Algse Androidi versiooni kasutamiseks taaskäivitage."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Tühista"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Loobu"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Taaskäivita"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Taaskäivita"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Dünaamilisest süsteemist loobuti"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Dünaamilist süsteemi ei saa taaskäivitada ega laadida"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-eu/strings.xml b/packages/DynamicSystemInstallationService/res/values-eu/strings.xml
new file mode 100644
index 0000000..7c4a67d
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-eu/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Idatzi pasahitza eta joan Sistemaren eguneratze dinamikoa eginbidera"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Prest dago sistema dinamikoa. Erabiltzen hasteko, berrabiarazi gailua."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Instalatzen"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Ezin izan da instalatu"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Ezin izan da balidatu irudia. Utzi bertan behera instalazioa."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Sistema dinamiko bat abian da. Berrabiarazi Android-en jatorrizko bertsioa erabiltzeko."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Utzi"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Baztertu"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Berrabiarazi"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Berrabiarazi"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Baztertu da sistema dinamikoa"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Ezin da berrabiarazi edo kargatu sistema dinamikoa"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-fa/strings.xml b/packages/DynamicSystemInstallationService/res/values-fa/strings.xml
new file mode 100644
index 0000000..7533e71
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-fa/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"لطفاً گذرواژه را وارد کنید و «بهروزرسانیهای سیستم پویا» را ادامه دهید"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"سیستم پویا آماده است. برای استفاده از آن، دستگاه را بازراهاندازی کنید."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"درحال نصب"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"نصب نشد"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"راستیآزمایی تصویر انجام نشد. نصب را لغو کنید."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"درحالحاضر سیستم پویا اجرا میشود. برای استفاده از نسخه اصلی Android، بازراهاندازی کنید."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"لغو کردن"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"صرفنظر کردن"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"بازراهاندازی"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"بازراهاندازی"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"از سیستم پویا صرفنظر شد"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"نمیتوان سیستم پویا را بازراهاندازی یا بار کرد"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-fi/strings.xml b/packages/DynamicSystemInstallationService/res/values-fi/strings.xml
new file mode 100644
index 0000000..948c333
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-fi/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Lisää salasanasi ja jatka dynaamisiin järjestelmäpäivityksiin"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Dynaaminen järjestelmä on valmis. Aloita sen käyttö käynnistämällä laite uudelleen."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Asennus käynnissä"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Asennus epäonnistui"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Kuvavahvistus epäonnistui. Keskeytä asennus."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Käyttää tällä hetkellä dynaamista järjestelmää. Käynnistä uudelleen käyttääksesi alkuperäistä Android-versiota."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Peruuta"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Hylkää"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Käynn. uudelleen"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Käynn. uudelleen"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Dynaaminen järjestelmä hylätty"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Dynaamista järjestelmää ei voi käynnistää uudelleen tai ladata"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-fr-rCA/strings.xml b/packages/DynamicSystemInstallationService/res/values-fr-rCA/strings.xml
new file mode 100644
index 0000000..6e2f235
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-fr-rCA/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Veuillez entrer votre mot de passe et continuer vers les mises à jour système dynamiques"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Le système dynamique est prêt. Pour commencer à l\'utiliser, redémarrez votre appareil."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Installation en cours…"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Échec de l\'installation"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Impossible de valider l\'image. Annulation de l\'installation."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Un système dynamique est en cours d\'exécution. Pour utiliser la version originale d\'Android, redémarrez l\'appareil."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Annuler"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Annuler"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Redémarrer"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Redémarrer"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Système dynamique supprimé"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Impossible de redémarrer ou de charger le système dynamique"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-fr/strings.xml b/packages/DynamicSystemInstallationService/res/values-fr/strings.xml
new file mode 100644
index 0000000..67f7997
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-fr/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Veuillez saisir votre mot de passe pour accéder aux Mises à jour système dynamiques"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Le système dynamique est prêt. Pour commencer à l\'utiliser, redémarrez votre appareil."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Installation…"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Échec de l\'installation"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Échec de la validation de l\'image. Annulez l\'installation."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Un système dynamique est en cours d\'exécution. Redémarrez pour utiliser la version Android d\'origine."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Annuler"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Supprimer"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Redémarrer"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Redémarrer"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Système dynamique supprimé"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Impossible de redémarrer ou de charger le système dynamique"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-gl/strings.xml b/packages/DynamicSystemInstallationService/res/values-gl/strings.xml
new file mode 100644
index 0000000..8ea6d1c
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-gl/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Pon o teu contrasinal e vai a Dynamic System Updates"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"O sistema dinámico está listo. Para utilizalo, reinicia o dispositivo."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Instalación en curso"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Produciuse un erro durante a instalación"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Produciuse un erro ao validar a imaxe. Aborta a instalación."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Estase executando un sistema dinámico. Reinicia o dispositivo para utilizar a versión de Android orixinal."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Cancelar"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Descartar"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Reiniciar"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Reiniciar"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Descartouse o sistema dinámico"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Non se puido reiniciar nin cargar o sistema dinámico"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-gu/strings.xml b/packages/DynamicSystemInstallationService/res/values-gu/strings.xml
new file mode 100644
index 0000000..aec1804
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-gu/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"કૃપા કરીને તમારો પાસવર્ડ દાખલ કરો અને ડાઇનૅમિક સિસ્ટમ અપડેટ પર ચાલુ રાખો"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"ડાઇનૅમિક સિસ્ટમ તૈયાર છે. તેનો ઉપયોગ કરવા માટે, તમારા ડિવાઇસને ફરી શરૂ કરો."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"ઇન્સ્ટૉલ થઈ રહ્યું છે"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"ઇન્સ્ટૉલ કરવામાં નિષ્ફળ રહ્યાં"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"છબીની ચકાસણી નિષ્ફળ રહી. ઇન્સ્ટૉલેશન રદ કરો."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"હાલમાં ડાઇનૅમિક સિસ્ટમ ચાલી રહી છે. ઑરિજિનલ Android વર્ઝનનો ઉપયોગ કરવા માટે ફરી શરૂ કરો."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"રદ કરો"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"કાઢી નાખો"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"ફરી શરૂ કરો"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"ફરી શરૂ કરો"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"ડાઇનૅમિક સિસ્ટમ કાઢી નાખવામાં આવી"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"ડાઇનૅમિક સિસ્ટમને ફરી શરૂ અથવા લોડ કરી શકાતી નથી"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-hi/strings.xml b/packages/DynamicSystemInstallationService/res/values-hi/strings.xml
new file mode 100644
index 0000000..efedbe8
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-hi/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"डाइनैमिक सिस्टम अपडेट की सुविधा जारी रखने के लिए कृपया अपना पासवर्ड डालें"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"डाइनैमिक सिस्टम तैयार है. इसका इस्तेमाल करने के लिए, अपना डिवाइस रीस्टार्ट करें."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"इंस्टॉल हो रहा है"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"इंस्टॉल नहीं हो सका"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"इमेज की पुष्टि नहीं हो सकी. इंस्टॉल करने की प्रक्रिया रद्द करें."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"फ़िलहाल, हम एक डाइनैमिक सिस्टम चला रहे हैं. Android का मूल वर्शन इस्तेमाल करने के लिए रीस्टार्ट करें."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"रद्द करें"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"खारिज करें"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"रीस्टार्ट करें"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"रीस्टार्ट करें"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"डाइनैमिक सिस्टम खारिज किया गया"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"डाइनैमिक सिस्टम रीस्टार्ट या लोड नहीं हो सका"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-hr/strings.xml b/packages/DynamicSystemInstallationService/res/values-hr/strings.xml
new file mode 100644
index 0000000..50ceaa1
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-hr/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Unesite zaporku i nastavite do dinamičnih ažuriranja sustava"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Dinamični sustav je spreman. Da biste ga počeli upotrebljavati, ponovno pokrenite svoj uređaj."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Instalacija u tijeku"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Instaliranje nije uspjelo"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Provjera slike nije uspjela. Prekini instalaciju."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Trenutačno je pokrenut dinamični sustav. Ponovno pokrenite kako biste upotrebljavali izvornu verziju Androida."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Otkaži"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Odbaci"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Pokreni"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Pokreni"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Odbačeni dinamični sustav"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Nije moguće ponovno pokretanje ili učitavanje dinamičnog sustava"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-hu/strings.xml b/packages/DynamicSystemInstallationService/res/values-hu/strings.xml
new file mode 100644
index 0000000..94afa3b
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-hu/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Adja meg jelszavát, hogy használhassa a Dinamikus rendszerfrissítések funkciót"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"A dinamikus rendszer készen áll. A használatához indítsa újra az eszközt."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Telepítés…"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Sikertelen telepítés"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"A kép ellenőrzése nem sikerült. A telepítés megszakad."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Jelenleg dinamikus rendszert futtat. Az eredeti Android-verzió használatához indítsa újra az eszközt."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Mégse"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Elvetés"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Újraindítás"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Újraindítás"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Elvetett dinamikus rendszer"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Nem lehet újraindítani vagy betölteni a dinamikus rendszert"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-hy/strings.xml b/packages/DynamicSystemInstallationService/res/values-hy/strings.xml
new file mode 100644
index 0000000..b0cd740
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-hy/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Մուտքագրեք ձեր գաղտնաբառը և անցեք համակարգի դինամիկ թարմացումների էջ"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Դինամիկ համակարգը պատրաստ է։ Այն օգտագործելու համար վերագործարկեք ձեր սարքը։"</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Տեղադրում"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Չհաջողվեց տեղադրել"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Չհաջողվեց հաստատել պատկերը։ Չեղարկել տեղադրումը։"</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Աշխատեցվում է դինամիկ համակարգը։ Վերագործարկեք՝ Android-ի նախկին տարբերակին անցնելու համար։"</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Չեղարկել"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Հրաժարվել"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"վերականգնել"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Վերագործարկել"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Դինամիկ համակարգի գործարկումը չեղարկվեց"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Չհաջողվեց վերագործարկել կամ բեռնել դինամիկ համակարգը"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-in/strings.xml b/packages/DynamicSystemInstallationService/res/values-in/strings.xml
new file mode 100644
index 0000000..44b4aee
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-in/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Masukkan sandi Anda dan lanjutkan ke Dynamic System Updates"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Dynamic System sudah siap. Mulai ulang perangkat untuk mulai menggunakannya."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Sedang diinstal"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Penginstalan gagal"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Validasi image gagal. Batalkan penginstalan."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Saat ini sedang menjalankan Dynamic System. Mulai ulang untuk menggunakan Android versi asli."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Batal"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Hapus"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Mulai ulang"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Mulai ulang"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Dynamic System dihapus"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Tidak dapat memulai ulang atau memuat Dynamic System"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-is/strings.xml b/packages/DynamicSystemInstallationService/res/values-is/strings.xml
new file mode 100644
index 0000000..048d1bc
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-is/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Sláðu inn aðgangsorð og haltu áfram í „Breytilegar kerfisuppfærslur“"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Breytilegt kerfi er tilbúið. Endurræstu tækið til að byrja að nota það."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Uppsetning stendur yfir"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Uppsetning mistókst"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Ekki tókst að staðfesta mynd. Hættu við uppsetninguna."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Tækið keyrir á breytilegu kerfi. Endurræstu til að nota upprunalega Android útgáfu."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Hætta við"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Fleygja"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Endurræsa"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Endurræsa"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Breytilegu kerfi fleygt"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Ekki tókst að endurræsa eða hlaða breytilegu kerfi"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-it/strings.xml b/packages/DynamicSystemInstallationService/res/values-it/strings.xml
new file mode 100644
index 0000000..f70b381
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-it/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Inserisci la password e continua su Aggiornamenti di sistema dinamici"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Sistema dinamico pronto. Per iniziare a usarlo, riavvia il dispositivo."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Installazione in corso"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Installazione non riuscita"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Impossibile convalidare l\'immagine. Interrompi l\'installazione."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Sistema dinamico in uso. Riavvia per usare la versione di Android originale."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Annulla"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Annulla"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Riavvia"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Riavvia"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Sistema dinamico annullato"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Impossibile riavviare o caricare il sistema dinamico"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-iw/strings.xml b/packages/DynamicSystemInstallationService/res/values-iw/strings.xml
new file mode 100644
index 0000000..aff7c82
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-iw/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"יש להזין את הסיסמה ולהמשיך אל עדכוני המערכת הדינמיים"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"המערכת הדינמית מוכנה. כדי להתחיל להשתמש בה, יש להפעיל מחדש את המכשיר."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"ההתקנה מתבצעת"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"ההתקנה נכשלה"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"אימות התמונה נכשל. יש לבטל את ההתקנה."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"בשלב זה פועלת מערכת דינמית. כדי להשתמש בגרסת Android המקורית, יש לבצע הפעלה מחדש."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"ביטול"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"סגירה"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"הפעלה מחדש"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"הפעלה מחדש"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"המערכת הדינמית נסגרה"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"לא ניתן להפעיל מחדש או לטעון את המערכת הדינמית"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-ja/strings.xml b/packages/DynamicSystemInstallationService/res/values-ja/strings.xml
new file mode 100644
index 0000000..46c0930
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-ja/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"パスワードを入力し、Dynamic System Updates に移動してください"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"動的システムの準備ができました。使い始めるには、デバイスを再起動してください。"</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"インストールしています"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"インストールに失敗しました"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"イメージの検証に失敗しました。インストールを中止してください。"</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"現在、動的システムを実行しています。元の Android バージョンを使用するには再起動してください。"</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"キャンセル"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"破棄"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"再起動"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"再起動"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"動的システムを破棄しました"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"動的システムの再起動や読み込みを行えません"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-ka/strings.xml b/packages/DynamicSystemInstallationService/res/values-ka/strings.xml
new file mode 100644
index 0000000..f841a59
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-ka/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"გთხოვთ, შეიყვანოთ პაროლი სისტემის დინამიურ განახლებებზე გადასასვლელად"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"დინამიური სისტემა მზადაა. გადატვირთეთ მოწყობილობა მის გამოსაყენებლად."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"ინსტალირდება"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"ინსტალაცია ვერ მოხერხდა"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"სურათის ვალიდაცია ვერ მოხერხდა. ინსტალაციის შეწყვეტა."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"ამჟამად გამოიყენება დინამიური სისტემა. გადატვირთეთ Android-ის ორიგინალი ვერსიის გამოსაყენებლად."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"გაუქმება"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"გაუქმება"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"გადატვირთვა"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"გადატვირთვა"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"გაუქმებული დინამიური სისტემა"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"დინამიური სისტემის გადატვირთვა ან ჩატვირთვა ვერ ხერხდება"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-kk/strings.xml b/packages/DynamicSystemInstallationService/res/values-kk/strings.xml
new file mode 100644
index 0000000..d367b61
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-kk/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Құпия сөзіңізді енгізіңіз және Dynamic System Updates функциясына өтіңіз."</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Динамикалық жүйе дайын. Оны пайдалану үшін құрылғыңызды қайта қосыңыз."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Орнатылып жатыр."</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Орнатылмады."</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Кескін тексерілмеді. Орнатуды доғарыңыз."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Әзірге динамикалық жүйе пайдаланылуда. Бастапқы Android нұсқасын пайдалану үшін қайта қосыңыз."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Бас тарту"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Жабу"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Қайта қосу"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Қайта қосу"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Динамикалық жүйе өшірілді."</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Динамикалық жүйені қайта қосу не жүктеу мүмкін емес."</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-km/strings.xml b/packages/DynamicSystemInstallationService/res/values-km/strings.xml
new file mode 100644
index 0000000..56a3716
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-km/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"សូមបញ្ចូលពាក្យសម្ងាត់របស់អ្នក រួចបន្តទៅការដំឡើងកំណែប្រព័ន្ធឌីណាមិច"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"ប្រព័ន្ធឌីណាមិចអាចប្រើបានហើយ។ ដើម្បីចាប់ផ្ដើមប្រើប្រព័ន្ធឌីណាមិច សូមចាប់ផ្ដើមឧបករណ៍របស់អ្នកឡើងវិញ។"</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"ការដំឡើងកំពុងដំណើរការ"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"ការដំឡើងមិនបានសម្រេច"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"បញ្ជាក់ភាពត្រឹមត្រូវនៃរូបភាពមិនបានសម្រេច។ បោះបង់ការដំឡើង។"</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"បច្ចុប្បន្នកំពុងដំណើរការប្រព័ន្ធឌីណាមិច។ ចាប់ផ្ដើមឡើងវិញ ដើម្បីប្រើកំណែ Android ដើម។"</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"បោះបង់"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"លុបចោល"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"ចាប់ផ្ដើមឡើងវិញ"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"ចាប់ផ្ដើមឡើងវិញ"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"បានលុបចោលប្រព័ន្ធឌីណាមិច"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"មិនអាចចាប់ផ្ដើមឡើងវិញ ឬផ្ទុកប្រព័ន្ធឌីណាមិចបានទេ"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-kn/strings.xml b/packages/DynamicSystemInstallationService/res/values-kn/strings.xml
new file mode 100644
index 0000000..b4063df
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-kn/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"ನಿಮ್ಮ ಪಾಸ್ವರ್ಡ್ ನಮೂದಿಸಿ ನಂತರ ಡೈನಾಮಿಕ್ ಸಿಸ್ಟಂ ಅಪ್ಡೇಟ್ಗಳಿಗೆ ಮುಂದುವರಿಯಿರಿ"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"ಡೈನಾಮಿಕ್ ಸಿಸ್ಟಂ ಸಿದ್ದವಾಗಿದೆ. ಇದನ್ನು ಬಳಸಲು, ನಿಮ್ಮ ಸಾಧನವನ್ನು ಮರುಪ್ರಾರಂಭಿಸಿ."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"ಇನ್ಸ್ಟಾಲ್ ಆಗುತ್ತಿದೆ"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"ಇನ್ಸ್ಟಾಲ್ ಮಾಡಲು ವಿಫಲವಾಗಿದೆ"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"ಚಿತ್ರದ ಮೌಲ್ಯೀಕರಣ ವಿಫಲವಾಗಿದೆ. ಇನ್ಸ್ಟಾಲ್ ಮಾಡುವಿಕೆಯನ್ನು ರದ್ದುಗೊಳಿಸಿ."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"ಪ್ರಸ್ತುತವಾಗಿ ಡೈನಾಮಿಕ್ ಸಿಸ್ಟಂ ರನ್ ಆಗುತ್ತಿದೆ ಮೂಲ Android ಆವೃತ್ತಿ ಬಳಸಲು, ಮರುಪ್ರಾರಂಭಿಸಿ."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"ರದ್ದುಗೊಳಿಸಿ"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"ತ್ಯಜಿಸಿ"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"ಮರುಪ್ರಾರಂಭಿಸಿ"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"ಮರುಪ್ರಾರಂಭಿಸಿ"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"ಡೈನಮಿಕ್ ಸಿಸ್ಟಂ ಅನ್ನು ತ್ಯಜಿಸಲಾಗಿದೆ"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"ಡೈನಾಮಿಕ್ ಸಿಸ್ಟಂ ಅನ್ನು ಮರುಪ್ರಾರಂಭಿಸಲು ಅಥವಾ ಲೋಡ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-ko/strings.xml b/packages/DynamicSystemInstallationService/res/values-ko/strings.xml
new file mode 100644
index 0000000..24ac924
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-ko/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"비밀번호를 입력하고 동적 시스템 업데이트를 계속 진행하세요."</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"동적 시스템이 준비되었습니다. 사용을 시작하려면 기기를 다시 시작하세요."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"설치 진행 중"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"설치할 수 없음"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"이미지를 확인할 수 없습니다. 설치를 취소합니다."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"현재 동적 시스템을 실행 중입니다. 기존의 Android 버전을 사용하려면 다시 시작하세요."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"취소"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"삭제"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"다시 시작"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"다시 시작"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"동적 시스템 삭제됨"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"동적 시스템을 다시 시작하거나 로드할 수 없음"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-ky/strings.xml b/packages/DynamicSystemInstallationService/res/values-ky/strings.xml
new file mode 100644
index 0000000..a4387e7
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-ky/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Сырсөзүңүздү киргизип, системаны динамикалык жаңыртууга өтүңүз"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Динамикалык система даяр. Аны колдонуу үчүн, түзмөктү өчүрүп күйгүзүңүз."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Орнотулууда"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Орнотулбай койду"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Сүрөт текшерилбей калды. Орнотууну токтотуңуз."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Учурда динамикалык система колдонулууда. Android\'дин түпнуска версиясын колдонуу үчүн, өчүрүп күйгүзүңүз."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Жок"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Жоюу"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Өчүрүп күйгүзүү"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Өчүрүп күйгүзүү"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Динамикалык система жоюлду"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Динамикалык система өчүрүлүп күйгүзүлбөй же жүктөлбөй жатат"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-lo/strings.xml b/packages/DynamicSystemInstallationService/res/values-lo/strings.xml
new file mode 100644
index 0000000..f17ca16
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-lo/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"ກະລຸນາໃສ່ລະຫັດຜ່ານຂອງທ່ານ ແລະ ດຳເນີນການຕໍ່ຫາອັບເດດລະບົບແບບໄດນາມິກ"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"ລະບົບໄດນາມິກພ້ອມແລ້ວ. ກະລຸນາຣີສະຕາດອຸປະກອນຂອງທ່ານເພື່ອເລີ່ມນຳໃຊ້ມັນ."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"ກຳລັງຕິດຕັ້ງຢູ່"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"ຕິດຕັ້ງບໍ່ສຳເລັດ"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"ການກວດສອບໄຟລ໌ຮູບບໍ່ສຳເລັດ. ຍົກເລີກການຕິດຕັ້ງ."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"ຕອນນີ້ກຳລັງໃຊ້ລະບົບໄດນາມິກ. ກະລຸນາຣີສະຕາດເພື່ອໃຊ້ເວີຊັນ Android ຕົ້ນສະບັບ."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"ຍົກເລີກ"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"ປິດໄວ້"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"ຣີສະຕາດ"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"ຣີສະຕາດ"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"ລະບົບໄດນາມິກທີ່ຍົກເລີກແລ້ວ"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"ບໍ່ສາມາດຣີສະຕາດ ຫຼື ໂຫຼດລະບົບໄດນາມິກໄດ້"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-lt/strings.xml b/packages/DynamicSystemInstallationService/res/values-lt/strings.xml
new file mode 100644
index 0000000..8128eb7
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-lt/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Įveskite slaptažodį ir eikite į Dinaminių sistemos atnaujinimų funkciją"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Dinaminė sistema paruošta. Jei norite pradėti ją naudoti, paleiskite įrenginį iš naujo."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Diegiama"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Įdiegti nepavyko"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Nepavyko patvirtinti vaizdo. Nutraukti diegimą."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Šiuo metu paleista dinaminė sistema. Paleiskite iš naujo, jei norite naudoti pradinę „Android“ versiją."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Atšaukti"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Atmesti"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Pal. iš naujo"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Pal. iš naujo"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Dinaminė sistema atmesta"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Nepavyko paleisti iš naujo ar įkelti dinaminės sistemos"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-lv/strings.xml b/packages/DynamicSystemInstallationService/res/values-lv/strings.xml
new file mode 100644
index 0000000..cfe7a08
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-lv/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Ievadiet paroli un pārejiet uz funkciju Dinamiskie sistēmas atjauninājumi"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Dinamiskā sistēma ir gatava. Lai sāktu to izmantot, restartējiet ierīci."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Notiek instalēšana"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Instalēšana neizdevās"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Attēla validācija neizdevās. Instalācija tiek priekšlaikus pārtraukta."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Pašlaik darbojas dinamiska sistēma. Lai izmantotu sākotnējo Android versiju, restartējiet ierīci."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Atcelt"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Atmest"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Restartēt"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Restartēt"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Dinamiskā sistēma tika atmesta"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Nevar restartēt vai ielādēt dinamisko sistēmu"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-mk/strings.xml b/packages/DynamicSystemInstallationService/res/values-mk/strings.xml
new file mode 100644
index 0000000..21215aa
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-mk/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Внесете ја вашата лозинка и продолжете на „Динамични системски ажурирања“"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Динамичниот систем е подготвен. За да започнете со користење, рестартирајте го уредот."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Инсталирањето е во тек"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Неуспешно инсталирање"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Проверката на сликата не успеа. Прекини ја инсталацијата."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Во моментов се извршува динамичен систем. Рестартирајте за да ја користите оригиналната верзија на Android."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Откажи"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Отфрли"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Рестартирај"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Рестартирај"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Отфрлен динамичен систем"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Не може да го рестартира или вчита динамичниот систем"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-ml/strings.xml b/packages/DynamicSystemInstallationService/res/values-ml/strings.xml
new file mode 100644
index 0000000..951a0b9
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-ml/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"നിങ്ങളുടെ പാസ്വേഡ് നൽകി ഡൈനാമിക് സിസ്റ്റം അപ്ഡേറ്റുകളിലേക്ക് പോകുക"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"ഡെെനാമിക് സിസ്റ്റം തയ്യാറാണ്. അത് ഉപയോഗിച്ച് തുടങ്ങാൻ നിങ്ങളുടെ ഉപകരണം റീസ്റ്റാർട്ട് ചെയ്യുക."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"ഇൻസ്റ്റാൾ ചെയ്യൽ പുരോഗതിയിലാണ്"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"ഇൻസ്റ്റാൾ ചെയ്യാനായില്ല"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"ചിത്രത്തിന്റെ മൂല്യനിർണ്ണയം നടത്താനായില്ല. ഇൻസ്റ്റലേഷൻ റദ്ദാക്കുക."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"നിലവിൽ ഒരു ഡെെനാമിക് സിസ്റ്റം റൺ ചെയ്യുന്നുണ്ട്. ഒറിജിനൽ Android പതിപ്പ് ഉപയോഗിക്കാൻ റീസ്റ്റാർട്ട് ചെയ്യുക."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"റദ്ദാക്കുക"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"നിരസിക്കുക"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"റീസ്റ്റാർട്ട് ചെയ്യൂ"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"റീസ്റ്റാർട്ട് ചെയ്യൂ"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"ഡെെനാമിക് സിസ്റ്റം നിരസിച്ചു"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"റീസ്റ്റാർട്ട് ചെയ്യാനോ ഡെെനാമിക് സിസ്റ്റം ലോഡ് ചെയ്യാനോ ആവില്ല"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-mn/strings.xml b/packages/DynamicSystemInstallationService/res/values-mn/strings.xml
new file mode 100644
index 0000000..d0965d0
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-mn/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Нууц үгээ оруулаад Динамик системийн шинэчлэлтийг үргэлжлүүлнэ үү"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Динамик систем бэлэн боллоо. Түүнийг ашиглаж эхлэхийн тулд төхөөрөмжөө дахин эхлүүлнэ үү."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Суулгаж байна"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Суулгаж чадсангүй"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Системийн хуулбарын баталгаажуулалт амжилтгүй боллоо. Суулгах үйлдлийг зогсооно уу."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Одоогоор динамик системийг ажиллуулж байна. Android-н эх хувилбарыг ашиглахын тулд дахин эхлүүлнэ үү."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Болих"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Болих"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Дахин эхлүүлэх"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Дахин эхлүүлэх"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Динамик системийг устгасан"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Динамик системийг дахин эхлүүлэх эсвэл ачаалах боломжгүй байна"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-mr/strings.xml b/packages/DynamicSystemInstallationService/res/values-mr/strings.xml
new file mode 100644
index 0000000..268e1d3
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-mr/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"कृपया तुमचा पासवर्ड एंटर करा आणि डायनॅमिक सिस्टम अपडेट वर जा"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"डायनॅमिक सिस्टम तयार आहे. ती वापरणे सुरू करण्यासाठी, तुमचे डिव्हाइस रीस्टार्ट करा."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"इंस्टॉल प्रगतीपथावर आहे"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"इंस्टॉल करता आली नाही"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"इमेज प्रमाणीकरण करता आले नाही. इंस्टॉलेशन रद्द करा."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"सध्या डायनॅमिक सिस्टम रन करत आहे. मूळ Android आवृत्ती वापरण्यासाठी रीस्टार्ट करा."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"रद्द करा"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"काढून टाका"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"रीस्टार्ट करा"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"रीस्टार्ट करा"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"डायनॅमिक सिस्टम काढून टाकली"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"डायनॅमिक सिस्टम रीस्टार्ट किंवा लोड करू शकत नाही"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-ms/strings.xml b/packages/DynamicSystemInstallationService/res/values-ms/strings.xml
new file mode 100644
index 0000000..bba8b97
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-ms/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Sila masukkan kata laluan dan teruskan ke Kemas Kini Sistem Dinamik"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Sistem dinamik sudah sedia. Untuk mula menggunakan sistem ini, mulakan semula peranti anda"</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Pemasangan sedang dijalankan"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Pemasangan gagal"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Gagal mengesahkan imej. Henti paksa pemasangan."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Sedang menjalankan sistem dinamik. Mulakan semula peranti untuk menggunakan versi asal Android."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Batal"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Buang"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Mulakan semula"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Mulakan semula"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Sistem dinamik dibuang"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Tidak dapat memulakan semula atau memuatkan sistem dinamik"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-my/strings.xml b/packages/DynamicSystemInstallationService/res/values-my/strings.xml
new file mode 100644
index 0000000..b2488ec
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-my/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"သင့်စကားဝှက်ထည့်ပြီး ပြောင်းလဲနိုင်သော စနစ်အပ်ဒိတ်များသို့ ရှေ့ဆက်ပါ"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"ပြောင်းလဲနိုင်သောစနစ် အသင့်ဖြစ်ပါပြီ။ ၎င်းကို စတင်အသုံးပြုရန် သင့်စက်ကို ပြန်စပါ။"</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"ထည့်သွင်းနေဆဲဖြစ်သည်"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"ထည့်သွင်း၍မရပါ"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"ပုံအတည်ပြု၍ မရပါ။ ထည့်သွင်းမှုကို ရပ်ပါ။"</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"လက်ရှိတွင် ပြောင်းလဲနိုင်သောစနစ်ကို အသုံးပြုနေသည်။ မူလ Android ဗားရှင်း အသုံးပြုရန် ပြန်စတင်ပါ။"</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"မလုပ်တော့"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"ဖယ်ပစ်ရန်"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"ပြန်စရန်"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"ပြန်စရန်"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"ပြောင်းလဲနိုင်သောစနစ်ကို ဖယ်လိုက်သည်"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"ပြန်စ၍ မရပါ (သို့) ပြောင်းလဲနိုင်သောစနစ် ဖွင့်၍မရပါ"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-nb/strings.xml b/packages/DynamicSystemInstallationService/res/values-nb/strings.xml
new file mode 100644
index 0000000..36e3d69
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-nb/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Skriv inn passordet ditt, og fortsett til dynamiske systemoppdateringer"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Det dynamiske systemet er klart. Start enheten din på nytt for å begynne å bruke det."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Installeringen pågår"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Installeringen mislyktes"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Bildebekreftelsen mislyktes. Avbryt installeringen."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Kjører et dynamisk system nå. Start på nytt for å bruke den opprinnelige Android-versjonen."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Avbryt"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Forkast"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Start på nytt"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Start på nytt"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Det dynamiske systemet er forkastet"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Det dynamiske systemet kan ikke startes på nytt eller lastes inn"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-ne/strings.xml b/packages/DynamicSystemInstallationService/res/values-ne/strings.xml
new file mode 100644
index 0000000..ee92678
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-ne/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"कृपया आफ्नो पासवर्ड प्रविष्टि गर्नुहोस् र Dynamic System Updates को प्रक्रियालाई निरन्तरता दिनुहोस्"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Dynamic System तयार छ। यसको प्रयोग सुरु गर्न आफ्नो यन्त्र रिस्टार्ट गर्नुहोस्।"</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"इन्स्टल हुँदै छ"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"स्थापना गर्न सकिएन"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"छवि पुष्टि गर्न सकिएन। स्थापना गर्ने प्रक्रिया रद्द गर्नुहोस्।"</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"हाल Dynamic System चलिरहेको छ। Android को मूल संस्करण प्रयोग गर्न यन्त्र रिस्टार्ट गर्नुहोस्।"</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"रद्द गर्नुहोस्"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"खारेज गर्नुहोस्"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"रिस्टार्ट गर्नु…"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"रिस्टार्ट गर्नु…"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Dynamic System खारेज गरियो"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"रिस्टार्ट गर्न वा Dynamic System लोड गर्न सकिएन"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-nl/strings.xml b/packages/DynamicSystemInstallationService/res/values-nl/strings.xml
new file mode 100644
index 0000000..47eeb83
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-nl/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Geef je wachtwoord op en ga door naar \'Dynamische systeemupdates\'"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Dynamisch systeem is gereed. Start je apparaat opnieuw op om het te gebruiken."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Installatie wordt uitgevoerd"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Installatie mislukt"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Valideren van afbeelding mislukt. Installatie afbreken."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Er is momenteel een dynamisch systeem actief. Start je apparaat opnieuw op om de oorspronkelijke Android-versie te gebruiken."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Annuleren"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Niet opslaan"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Herstarten"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Herstarten"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Dynamisch systeem niet opgeslagen"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Kan dynamisch systeem niet opnieuw opstarten of laden"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-or/strings.xml b/packages/DynamicSystemInstallationService/res/values-or/strings.xml
new file mode 100644
index 0000000..e0c8470
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-or/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"ଦୟାକରି ଆପଣଙ୍କ ପାସୱାର୍ଡ ଲେଖନ୍ତୁ ଏବଂ ଡାଇନାମିକ୍ ସିଷ୍ଟମ୍ ଅପଡେଟ୍ ଜାରି ରଖନ୍ତୁ"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"ଡାଇନାମିକ୍ ସିଷ୍ଟମ୍ ପ୍ରସ୍ତୁତ ଅଛି। ଏହାକୁ ବ୍ୟବହାର କରିବା ଆରମ୍ଭ କରିବାକୁ, ଆପଣଙ୍କ ଡିଭାଇସକୁ ରିଷ୍ଟାର୍ଟ କରନ୍ତୁ।"</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"ଇନଷ୍ଟଲ୍ ହେଉଛି"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"ଇନଷ୍ଟଲ୍ କରିବା ବିଫଳ ହୋଇଛି"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"ଛବି ବୈଧକରଣ ବିଫଳ ହୋଇଛି। ଇନଷ୍ଟଲେସନ୍ ରଦ୍ଦ କରନ୍ତୁ।"</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"ବର୍ତ୍ତମାନ ଏକ ଡାଇନାମିକ୍ ସିଷ୍ଟମ୍ ଚାଲୁଛି। ମୂଳ Android ସଂସ୍କରଣ ବ୍ୟବହାର କରିବାକୁ ରିଷ୍ଟାର୍ଟ କରନ୍ତୁ।"</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"ବାତିଲ୍ କରନ୍ତୁ"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"ଖାରଜ କରନ୍ତୁ"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"ରିଷ୍ଟାର୍ଟ କରନ୍ତୁ"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"ରିଷ୍ଟାର୍ଟ କରନ୍ତୁ"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"ଡାଇନାମିକ୍ ସିଷ୍ଟମ୍ ଖାରଜ କରାଯାଇଛି"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"ଡାଇନାମିକ୍ ସିଷ୍ଟମ୍ ରିଷ୍ଟାର୍ଟ କିମ୍ବା ଲୋଡ୍ କରାଯାଇପାରିବ ନାହିଁ"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-pa/strings.xml b/packages/DynamicSystemInstallationService/res/values-pa/strings.xml
new file mode 100644
index 0000000..c5f7a3d
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-pa/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"ਕਿਰਪਾ ਕਰਕੇ ਆਪਣਾ ਪਾਸਵਰਡ ਦਾਖਲ ਕਰੋ ਅਤੇ ਪਰਿਵਰਤਨਸ਼ੀਲ ਸਿਸਟਮ ਅੱਪਡੇਟ \'ਤੇ ਜਾਰੀ ਰੱਖੋ"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"ਪਰਿਵਰਤਨਸ਼ੀਲ ਸਿਸਟਮ ਤਿਆਰ ਹੈ। ਇਸ ਦੀ ਵਰਤੋਂ ਸ਼ੁਰੂ ਕਰਨ ਲਈ, ਆਪਣਾ ਡੀਵਾਈਸ ਮੁੜ-ਸ਼ੁਰੂ ਕਰੋ।"</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"ਸਥਾਪਨਾ ਜਾਰੀ ਹੈ"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"ਸਥਾਪਤ ਕਰਨਾ ਅਸਫਲ ਰਿਹਾ"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"ਚਿੱਤਰ ਪ੍ਰਮਾਣਿਕਤਾ ਅਸਫਲ ਰਹੀ। ਸਥਾਪਨਾ ਨੂੰ ਰੱਦ ਕਰੋ।"</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"ਫ਼ਿਲਹਾਲ ਪਰਿਵਰਤਨਸ਼ੀਲ ਸਿਸਟਮ ਚੱਲ ਰਿਹਾ ਹੈ। ਮੂਲ Android ਵਰਜਨ ਵਰਤਣ ਲਈ ਮੁੜ-ਸ਼ੁਰੂ ਕਰੋ।"</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"ਰੱਦ ਕਰੋ"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"ਖਾਰਜ ਕਰੋ"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"ਮੁੜ-ਸ਼ੁਰੂ ਕਰੋ"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"ਮੁੜ-ਸ਼ੁਰੂ ਕਰੋ"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"ਪਰਿਵਰਤਨਸ਼ੀਲ ਸਿਸਟਮ ਰੱਦ ਕੀਤਾ ਗਿਆ"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"ਪਰਿਵਰਤਨਸ਼ੀਲ ਸਿਸਟਮ ਮੁੜ-ਸ਼ੁਰੂ ਜਾਂ ਲੋਡ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-pl/strings.xml b/packages/DynamicSystemInstallationService/res/values-pl/strings.xml
new file mode 100644
index 0000000..bc7d5fe
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-pl/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Wpisz hasło i przejdź do dynamicznych aktualizacji systemu"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"System dynamiczny jest gotowy. Aby zacząć go używać, uruchom urządzenie ponownie."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Instaluję"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Nie udało się zainstalować"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Nie udało się zweryfikować obrazu. Przerwij instalację."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Obecnie urządzenie korzysta z systemu dynamicznego. Aby powrócić do oryginalnej wersji Androida, uruchom urządzenie ponownie."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Anuluj"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Odrzuć"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Uruchom ponownie"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Uruchom ponownie"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Usunięto system dynamiczny"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Nie można ponownie uruchomić lub wczytać systemu dynamicznego"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-pt-rBR/strings.xml b/packages/DynamicSystemInstallationService/res/values-pt-rBR/strings.xml
new file mode 100644
index 0000000..31a9bb4
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-pt-rBR/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Digite sua senha e prossiga para Atualizações dinâmicas do sistema"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"O sistema dinâmico está pronto. Para começar a usá-lo, reinicie o dispositivo."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Instalação em andamento"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Falha na instalação"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Falha ao validar imagem. Cancele a instalação."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Um sistema dinâmico está sendo executado no momento. Reinicie para usar a versão original do Android."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Cancelar"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Descartar"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Reiniciar"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Reiniciar"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Sistema dinâmico descartado"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Não é possível reiniciar ou carregar o sistema dinâmico"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-pt-rPT/strings.xml b/packages/DynamicSystemInstallationService/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..d917c6a
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-pt-rPT/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Introduza a sua palavra-passe e continue para a Atualização Dinâmica do Sistema."</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"O sistema dinâmico está pronto. Para o começar a utilizar, reinicie o dispositivo."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Instalação em curso"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Falha na instalação"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Falha ao validar a imagem. A instalação foi interrompida."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Está atualmente em execução um sistema dinâmico. Reinicie para utilizar a versão original do Android."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Cancelar"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Rejeitar"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Reiniciar"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Reiniciar"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Sistema dinâmico rejeitado"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Não é possível reiniciar ou carregar o sistema dinâmico."</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-pt/strings.xml b/packages/DynamicSystemInstallationService/res/values-pt/strings.xml
new file mode 100644
index 0000000..31a9bb4
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-pt/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Digite sua senha e prossiga para Atualizações dinâmicas do sistema"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"O sistema dinâmico está pronto. Para começar a usá-lo, reinicie o dispositivo."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Instalação em andamento"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Falha na instalação"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Falha ao validar imagem. Cancele a instalação."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Um sistema dinâmico está sendo executado no momento. Reinicie para usar a versão original do Android."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Cancelar"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Descartar"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Reiniciar"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Reiniciar"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Sistema dinâmico descartado"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Não é possível reiniciar ou carregar o sistema dinâmico"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-ro/strings.xml b/packages/DynamicSystemInstallationService/res/values-ro/strings.xml
new file mode 100644
index 0000000..c211318
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-ro/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Introduceți parola și accesați Actualizările de sistem dinamice"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Sistemul dinamic este pregătit. Ca să începeți să-l folosiți, reporniți dispozitivul."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Se instalează"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Instalarea nu a reușit"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Nu s-a validat imaginea. Abandonați instalarea."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Rulăm un sistem dinamic. Reporniți pentru a folosi versiunea Android inițială."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Anulați"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Renunțați"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Reporniți"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Reporniți"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"S-a renunțat la sistemul dinamic"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Nu se poate reporni sau încărca sistemul dinamic"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-ru/strings.xml b/packages/DynamicSystemInstallationService/res/values-ru/strings.xml
new file mode 100644
index 0000000..bf94c99
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-ru/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Введите пароль и перейдите к динамическим обновлениям системы."</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Динамическая система готова. Чтобы использовать ее, перезапустите устройство."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Идет установка…"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Ошибка установки."</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Не удалось проверить образ. Отмените установку."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Используется динамическая система. Чтобы вернуться к исходной версии Android, перезапустите устройство."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Отмена"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Отменить"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Перезапустить"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Перезапустить"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Динамическая система удалена."</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Не удается запустить или загрузить динамическую систему."</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-si/strings.xml b/packages/DynamicSystemInstallationService/res/values-si/strings.xml
new file mode 100644
index 0000000..e6a6ea2
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-si/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"කරුණාකර ඔබගේ මුරපදය ඇතුළත් කර ගතික පද්ධති යාවත්කාලීන කිරීම් වෙත යන්න"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"ගතික පද්ධතිය සූදානම්ය. එය භාවිතා කිරීම ආරම්භ කිරීමට, ඔබගේ උපාංගය නැවත ආරම්භ කරන්න."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"ස්ථාපනය කෙරෙමින් පවතී"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"ස්ථාපනය අසාර්ථක විය"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"රූප වලංගු කිරීම අසාර්ථක විය. ස්ථාපනය අවලංගු කරන්න"</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"දැනට ගතික පද්ධතියක් ක්රියාත්මක කරයි. මුල් Android අනුවාදය භාවිතා කිරීමට නැවත ආරම්භ කරන්න."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"අවලංගු කරන්න"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"ඉවත ලන්න"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"යළි අරඹන්න"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"යළි අරඹන්න"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"ගතික පද්ධතිය ඉවත දමන ලදි"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"ගතික පද්ධතිය නැවත ආරම්භ කිරීමට හෝ පූරණය කිරීමට නොහැක"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-sk/strings.xml b/packages/DynamicSystemInstallationService/res/values-sk/strings.xml
new file mode 100644
index 0000000..99390cf
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-sk/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Zadajte svoje heslo a pokračujte na dynamické aktualizácie systému"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Dynamický systém je k dispozícii. Ak ho chcete začať používať, reštartujte zariadenie."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Prebieha inštalácia"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Nepodarilo sa nainštalovať"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Obrázok sa nepodarilo overiť. Prerušte inštaláciu."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Momentálne je spustený dynamický systém. Ak chcete používať pôvodnú verziu Androidu, reštartujte."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Zrušiť"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Zahodiť"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Reštartovať"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Reštartovať"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Zahodený dynamický systém"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Nie je možné reštartovať alebo načítať dynamický systém"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-sl/strings.xml b/packages/DynamicSystemInstallationService/res/values-sl/strings.xml
new file mode 100644
index 0000000..3ffd741
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-sl/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Vnesite geslo in nadaljujte na dinamične posodobitve sistema"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Dinamični sistem je pripravljen. Znova zaženite napravo, da ga boste lahko začeli uporabljati."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Namestitev poteka"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Namestitev ni uspela"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Preverjanje slike ni uspelo. Prekinite namestitev."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Trenutno se izvaja dinamični sistem. Znova zaženite, če želite uporabljati prvotno različico Androida."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Prekliči"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Zavrzi"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Znova zaženi"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Znova zaženi"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Dinamični sistem je bil zavržen"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Dinamičnega sistema ni mogoče znova zagnati ali naložiti"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-sq/strings.xml b/packages/DynamicSystemInstallationService/res/values-sq/strings.xml
new file mode 100644
index 0000000..704b512
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-sq/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Fut fjalëkalimin tënd dhe vazhdo te përditësimet e sistemit dinamik"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Sistemi dinamik është gati. Për të nisur përdorimin e tij, rinise pajisjen."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Instalimi në vazhdim"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Instalimi dështoi"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Verifikimi i imazhit dështoi. Ndërprit instalimin."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Aktualisht po ekzekutohet në një sistem dinamik. Rinise për të përdorur versionin origjinal të Android."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Anulo"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Hiq"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Rinis"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Rinis"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Sistemi dinamik u hoq"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Sistemi dinamik nuk mund të rinisej ose të ngarkohej"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-sr/strings.xml b/packages/DynamicSystemInstallationService/res/values-sr/strings.xml
new file mode 100644
index 0000000..5e4540a
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-sr/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Унесите лозинку и наставите до динамичних ажурирања система"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Динамични систем је спреман. Да бисте почели да га користите, рестартујте уређај."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Инсталира се"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Инсталирање није успело"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Валидација слике није успела. Откажите инсталацију."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Тренутно је покренут динамични систем. Рестартујте да бисте користили оригиналну верзију Android-а."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Откажи"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Одбаци"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Рестартуј"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Рестартуј"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Динамични систем је одбачен"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Рестартовање или учитавање динамичног система није успело"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-sv/strings.xml b/packages/DynamicSystemInstallationService/res/values-sv/strings.xml
new file mode 100644
index 0000000..546ffdd
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-sv/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Ange lösenordet och fortsätt till Dynamiska systemuppdateringar"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Det dynamiska systemet är klart. Om du vill använda det startar du om enheten."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Installation pågår"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Installationen misslyckades"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Bildvalideringen misslyckades. Avbryt installationen."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Ett dynamiskt system körs. Om du vill använda den ursprungliga Android-versionen startar du om."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Avbryt"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Ignorera"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Starta om"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Starta om"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Det dynamiska systemet ignorerades"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Det gick inte att starta om eller läsa in det dynamiska systemet"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-sw/strings.xml b/packages/DynamicSystemInstallationService/res/values-sw/strings.xml
new file mode 100644
index 0000000..53414d5
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-sw/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Tafadhali weka nenosiri lako na uende kwenye Dynamic System Updates"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Dynamic system iko tayari. Ili uanze kuitumia, zima kisha uwashe kifaa chako."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Inasakinisha"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Imeshindwa kusakinisha"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Imeshindwa kuthibitisha picha. Ghairi usakinishaji."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Inatumia Dynamic System kwa sasa. Zima kisha uwashe ili utumie toleo halisi la Android."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Ghairi"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Ondoa"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Zima kisha uwashe"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Zima kisha uwashe"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Umeondoa Dynamic System"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Imeshindwa kuzima na kuwasha au kupakia Dynamic System"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-ta/strings.xml b/packages/DynamicSystemInstallationService/res/values-ta/strings.xml
new file mode 100644
index 0000000..e0aaaf7
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-ta/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Dynamic System Updatesஸுக்குத் தொடர உங்கள் கடவுச்சொல்லை உள்ளிடவும்"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Dynamic system தயாராக உள்ளது. இதைப் பயன்படுத்தத் தொடங்க உங்கள் சாதனத்தை மீண்டும் தொடங்கவும்."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"நிறுவப்படுகிறது"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"நிறுவ முடியவில்லை"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"படத்தைச் சரிபார்க்க முடியவில்லை. நிறுவலை ரத்துசெய்யவும்."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Dynamic system தற்போது இயக்கத்தில் உள்ளது. அசல் Android பதிப்பைப் பயன்படுத்த மீண்டும் தொடங்கவும்."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"ரத்துசெய்"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"நிராகரி"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"மீண்டும் தொடங்கு"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"மீண்டும் தொடங்கு"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Dynamic system நிராகரிக்கப்பட்டது"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Dynamic systemமை மீண்டும் தொடங்கவோ ஏற்றவோ முடியவில்லை"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-te/strings.xml b/packages/DynamicSystemInstallationService/res/values-te/strings.xml
new file mode 100644
index 0000000..d497630
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-te/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"దయచేసి మీ పాస్వర్డ్ను ఎంటర్ చేసి, డైనమిక్ సిస్టమ్ అప్డేట్స్ను కొనసాగించండి"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"డైనమిక్ సిస్టమ్ సిద్ధంగా ఉంది. దీనిని ఉపయోగించడానికి, మీ పరికరాన్ని రీస్టార్ట్ చేయండి."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"ఇన్స్టాల్ అవుతోంది"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"ఇన్స్టాల్ చేయడం విఫలమైంది"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"ఇమేజ్ నిర్ధారణ విఫలమైంది. ఇన్స్టాలేషన్ను రద్దు చేయండి."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"ప్రస్తుతం డైనమిక్ సిస్టమ్ రన్నవుతోంది. ఒరిజినల్ Android వెర్షన్ను ఉపయోగించడానికి రీస్టార్ట్ చేయండి."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"రద్దు చేయండి"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"విస్మరించండి"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"రీస్టార్ట్ చేయి"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"రీస్టార్ట్ చేయి"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"డైనమిక్ సిస్టమ్ విస్మరించబడింది"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"డైనమిక్ సిస్టమ్ను రీస్టార్ట్ చేయడం లేదా లోడ్ చేయడం సాధ్యపడలేదు"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-th/strings.xml b/packages/DynamicSystemInstallationService/res/values-th/strings.xml
new file mode 100644
index 0000000..786324f
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-th/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"โปรดป้อนรหัสผ่านแล้วไปที่ \"อัปเดตระบบแบบไดนามิก\""</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"ระบบแบบไดนามิกพร้อมแล้ว โปรดรีสตาร์ทอุปกรณ์เพื่อเริ่มใช้"</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"กำลังติดตั้ง"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"ติดตั้งไม่สำเร็จ"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"ตรวจสอบรูปภาพไม่สำเร็จ ล้มเลิกการติดตั้ง"</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"ปัจจุบันใช้ระบบแบบไดนามิกอยู่ รีสตาร์ทเพื่อใช้ Android เวอร์ชันดั้งเดิม"</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"ยกเลิก"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"ยกเลิก"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"รีสตาร์ท"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"รีสตาร์ท"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"ยกเลิกระบบแบบไดนามิกแล้ว"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"รีสตาร์ทหรือโหลดระบบแบบไดนามิกไม่ได้"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-tl/strings.xml b/packages/DynamicSystemInstallationService/res/values-tl/strings.xml
new file mode 100644
index 0000000..df39f7b
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-tl/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Pakilagay ang iyong password at ipagpatuloy ang Dynamic System Updates"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Handa na ang dynamic system. Para simulang gamitin ito, i-restart ang iyong device."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Ini-install"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Hindi na-install"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Hindi na-validate ang image. I-abort ang pag-install."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Kasalukuyang pinapatakbo ang dynamic system. Mag-restart para gamitin ang orihinal na Android na bersyon."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Kanselahin"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"I-discard"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"I-restart"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"I-restart"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Na-discard ang dynamic system"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Hindi ma-restart o ma-load ang dynamic system"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-tr/strings.xml b/packages/DynamicSystemInstallationService/res/values-tr/strings.xml
new file mode 100644
index 0000000..1446f96
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-tr/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Lütfen şifrenizi girip Dinamik Sistem Güncellemesi\'ne gidin"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Dinamik sistem hazır. Kullanmaya başlamak için cihazınızı yeniden başlatın."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Yükleme devam ediyor"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Yükleme başarısız oldu"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Resim doğrulanamadı. Yüklemeyi iptal edin."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Şu anda dinamik sistem çalıştırılıyor. Orijinal Android sürümünü kullanmak için cihazı yeniden başlatın."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"İptal"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Sil"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Yeniden başlat"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Yeniden başlat"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Dinamik sistem silindi"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Dinamik sistem yeniden başlatılamıyor veya yüklenemiyor"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-uk/strings.xml b/packages/DynamicSystemInstallationService/res/values-uk/strings.xml
new file mode 100644
index 0000000..9a44d97
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-uk/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Введіть пароль і перейдіть до Динамічних оновлень системи"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Динамічна система готова. Щоб перейти до неї, перезапустіть пристрій."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Триває встановлення"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Не вдалося встановити"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Не вдалося перевірити образ. Встановлення скасовано."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Запущено динамічну систему. Перезапустіть пристрій, щоб повернутися до попередньої версії Android."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Скасувати"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Видалити"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Перезапустити"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Перезапустити"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Динамічну систему видалено"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Не вдається перезапустити пристрій або завантажити динамічну систему"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-ur/strings.xml b/packages/DynamicSystemInstallationService/res/values-ur/strings.xml
new file mode 100644
index 0000000..48dddbe
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-ur/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"براہ کرم اپنا پاس ورڈ درج کریں اور ڈائنیمک سسٹم اپ ڈیٹس پر جائیں"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"ڈائنیمک سسٹم تیار ہے۔ اس کا استعمال شروع کرنے کے لیے، اپنا آلہ ری سٹارٹ کریں۔"</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"انسٹال جاری ہے"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"انسٹال ناکام ہو گیا"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"تصویر کی توثیق ناکام ہو گئی۔ انسٹالیشن منسوخ کریں-"</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"فی الحال ہم ایک ڈائنیمک سسٹم چلا رہے ہیں۔ Android کا اصل ورژن استعمال کرنے کے لیے ری سٹارٹ کریں۔"</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"منسوخ کریں"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"مسترد کریں"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"ری سٹارٹ کریں"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"ری سٹارٹ کریں"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"مسترد کردہ ڈائنیمک سسٹم"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"ڈائنیمک سسٹم کو ری سٹارٹ یا لوڈ نہیں کر سکتے"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-uz/strings.xml b/packages/DynamicSystemInstallationService/res/values-uz/strings.xml
new file mode 100644
index 0000000..3f0227c
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-uz/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Parolni kiriting va dinamik tizim yangilanishlarini davom ettiring"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Dinamik tizim tayyor. Foydalanishni boshlash uchun qurilmani qayta ishga tushiring."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Oʻrnatilmoqda"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Oʻrnatilmadi"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Rasm tekshiruvi amalga oshmadi Oʻrnatishni bekor qilish."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Hozirda dinamik tizim ishga tushirilgan. Asl Android versiyasidan foydlanish uchun qayta ishga tushiring."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Bekor qilish"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Bekor qilish"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Boshidan"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Boshidan"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Dinamik tizim bekor qilindi"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Dinamik tizim qayta ishga tushmadi yoki yuklanmadi"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-vi/strings.xml b/packages/DynamicSystemInstallationService/res/values-vi/strings.xml
new file mode 100644
index 0000000..18c051c
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-vi/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Vui lòng nhập mật khẩu và tiếp tục dùng Bản cập nhật hệ thống động"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Hệ thống động đã sẵn sàng. Để bắt đầu sử dụng hệ thống này, hãy khởi động lại thiết bị của bạn."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Đang cài đặt"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Không cài đặt được"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Không xác thực được hình ảnh. Hủy cài đặt."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Thiết bị đang chạy một hệ thống động. Hãy khởi động lại để sử dụng Android phiên bản gốc."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Hủy"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Hủy"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Khởi động lại"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Khởi động lại"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Đã hủy hệ thống động"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Không thể khởi động lại hoặc tải hệ thống động"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-zh-rCN/strings.xml b/packages/DynamicSystemInstallationService/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..b41d4e2
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-zh-rCN/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"请输入您的密码,以便继续使用动态系统更新"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"动态系统已准备就绪。重启您的设备即可开始使用动态系统。"</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"正在安装"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"安装失败"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"图片验证失败。安装将中止。"</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"目前正在运行动态系统。需重启才能使用原 Android 版本。"</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"取消"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"舍弃"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"重启"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"重启"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"已舍弃动态系统"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"无法重启或加载动态系统"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-zh-rHK/strings.xml b/packages/DynamicSystemInstallationService/res/values-zh-rHK/strings.xml
new file mode 100644
index 0000000..c830dae
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-zh-rHK/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"請輸入密碼,以使用「動態系統更新」"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"動態系統已可供使用。如要開始使用,請重新啟動裝置。"</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"安裝中"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"無法安裝"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"圖片驗證失敗,系統將取消安裝。"</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"系統目前正在執行動態系統。如要使用原本的 Android 版本,請重新啟動裝置。"</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"取消"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"捨棄"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"重新啟動"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"重新啟動"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"已捨棄動態系統"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"無法重新啟動或載入動態系統"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-zh-rTW/strings.xml b/packages/DynamicSystemInstallationService/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..e43c0f2
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-zh-rTW/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"請輸入密碼,以便繼續使用動態系統更新"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"動態系統已可供使用。如要開始使用,請重新啟動裝置。"</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"安裝中"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"無法安裝"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"圖片驗證失敗,系統將取消安裝作業。"</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"系統目前正在執行動態系統。如要使用原本的 Android 版本,請重新啟動裝置。"</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"取消"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"捨棄"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"重新啟動"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"重新啟動"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"已捨棄動態系統"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"無法重新啟動或載入動態系統"</string>
+</resources>
diff --git a/packages/DynamicSystemInstallationService/res/values-zu/strings.xml b/packages/DynamicSystemInstallationService/res/values-zu/strings.xml
new file mode 100644
index 0000000..4a48444
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/res/values-zu/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="keyguard_description" msgid="8582605799129954556">"Sicela ufake iphasiwedi yakho uphinde uqhubekele Ekubuyekezweni Kohlole Okunhlobonhlobo"</string>
+ <string name="notification_install_completed" msgid="6252047868415172643">"Uhlole Okunhlobonhlobo kulungile. Ukuze uqale ukuyisebenzisa, qalisa kabusha idivayisi yakho."</string>
+ <string name="notification_install_inprogress" msgid="7383334330065065017">"Ukufaka kuyaqhubeka"</string>
+ <string name="notification_install_failed" msgid="4066039210317521404">"Ukufaka kwehlulekile"</string>
+ <string name="notification_image_validation_failed" msgid="2720357826403917016">"Ukuqinisekiswa kwesithombe kuhlulekile. Yekisa ukufakwa."</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Manje iqalisa uhlole olunhlobonhlobo. Qalisa kabusha ukuze usebenzise inguqulo yangempela ye-Android."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Khansela"</string>
+ <string name="notification_action_discard" msgid="1817481003134947493">"Lahla"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Qala kabusha"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Qala kabusha"</string>
+ <string name="toast_dynsystem_discarded" msgid="1733249860276017050">"Kulahlwe uhlole olunhlobonhlobo"</string>
+ <string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Ayikwazi ukuqalisa kabusha noma ukulayisha uhlole olunhlobonhlobo"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 1eef0b7..7e0ff81 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"Sal waarskynlik hou tot omtrent <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"Tot <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Battery kan teen <xliff:g id="TIME">%1$s</xliff:g> afloop"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Minder as <xliff:g id="THRESHOLD">%1$s</xliff:g> oor"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Minder as <xliff:g id="THRESHOLD">%1$s</xliff:g> oor (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Meer as <xliff:g id="TIME_REMAINING">%1$s</xliff:g> oor (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Meer as <xliff:g id="TIME_REMAINING">%1$s</xliff:g> oor"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Foon sal dalk binnekort afgaan"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Tablet sal dalk binnekort afgaan"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Toestel sal dalk binnekort afgaan"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index f8dcb3a..391ceb7 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"እስከ <xliff:g id="TIME">%1$s</xliff:g> ገደማ መቆየት አለበት"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"እስከ <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"ባትሪ እስከ <xliff:g id="TIME">%1$s</xliff:g> ድረስ ሊያልቅ ይችላል"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"ከ <xliff:g id="THRESHOLD">%1$s</xliff:g> ያነሰ ይቀራል"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"ከ <xliff:g id="THRESHOLD">%1$s</xliff:g> ያነሰ ይቀራል (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"ከ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> የበለጠ ይቀራል (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"ከ <xliff:g id="TIME_REMAINING">%1$s</xliff:g> የበለጠ ይቀራል"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"ስልኩ በቅርቡ ሊዘጋ ይችላል"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"ጡባዊው በቅርቡ ሊዘጋ ይችላል"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"መሣሪያው በቅርቡ ሊዘጋ ይችላል"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index aee5061..4f1024b 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"قد تكفي طاقة البطارية حتى حوالي الساعة <xliff:g id="TIME">%1$s</xliff:g>."</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"حتى <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"قد ينفد شحن البطارية قبل <xliff:g id="TIME">%1$s</xliff:g>."</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"سيبقى شحن البطارية أقل من <xliff:g id="THRESHOLD">%1$s</xliff:g>."</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"سيبقى شحن البطارية أقل من <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)."</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"سيبقى شحن البطارية أكثر من <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)."</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"سيبقى شحن البطارية أكثر من <xliff:g id="TIME_REMAINING">%1$s</xliff:g>."</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"قد يتم إغلاق الهاتف قريبًا"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"قد يتم إغلاق الجهاز اللوحي قريبًا"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"قد يتم إغلاق الجهاز قريبًا"</string>
@@ -516,7 +512,7 @@
<string name="alarm_template_far" msgid="6382760514842998629">"يوم <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"المدة"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"الطلب في كل مرة"</string>
- <string name="zen_mode_forever" msgid="3339224497605461291">"إلى أن توقف الوضع يدويًا"</string>
+ <string name="zen_mode_forever" msgid="3339224497605461291">"إلى أن يتم إيقاف الوضع"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"للتو"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"مكبر صوت الهاتف"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"حدثت مشكلة أثناء الاتصال. يُرجى إيقاف الجهاز ثم إعادة تشغيله."</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 40eb00b..738480f 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"Təxminən <xliff:g id="TIME">%1$s</xliff:g> olana qədər davam edəcək"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"<xliff:g id="TIME">%1$s</xliff:g> olana qədər"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Batareya <xliff:g id="TIME">%1$s</xliff:g> radələrinə qədər boşala bilər"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Maksimum <xliff:g id="THRESHOLD">%1$s</xliff:g> qalıb"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Maksimum <xliff:g id="THRESHOLD">%1$s</xliff:g> qalıb (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Minimum <xliff:g id="TIME_REMAINING">%1$s</xliff:g> qalıb (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Minimum <xliff:g id="TIME_REMAINING">%1$s</xliff:g> qalıb"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Telefon tezliklə sönə bilər"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Planşet tezliklə sönə bilər"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Cihaz tezliklə sönə bilər"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index e9d314b..6187f93 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"Trajaće približno do <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"Do <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Baterija će se možda isprazniti do <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Još manje od <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Još manje od <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Još više od <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Još više od <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Telefon će se uskoro isključiti"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Tablet će se uskoro isključiti"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Uređaj će se uskoro isključiti"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 713c7cd..6cca97d 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"Зараду хопіць прыблізна да <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"Да <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Акумулятар разрадзіцца ў <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Засталося менш за <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Засталося менш за <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Засталося больш за <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Засталося больш за <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Тэлефон у хуткім часе выключыцца"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Планшэт у хуткім часе выключыцца"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Прылада ў хуткім часе выключыцца"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 57456d9..c128f3d 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"Следва да издържи до около <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"До <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Батерията може да се изтощи до <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Остава(т) по-малко от <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Остава(т) по-малко от <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Остава(т) повече от <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Остава(т) повече от <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Възможно е телефонът да се изключи скоро"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Възможно е таблетът да се изключи скоро"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Възможно е устройството да се изключи скоро"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 0a9d260..6f9188d 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -61,7 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Srednja brzina"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Brzo"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Veoma brzo"</string>
- <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Istekao"</string>
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Isteklo"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Isključen"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Prekidanje veze…"</string>
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"Trebala bi trajati otprilike do <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"Do <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Baterija bi se mogla isprazniti do <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Preostalo je manje od <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Preostalo je manje od <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Preostalo je više od <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Preostalo je više od <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Telefon će se uskoro isključiti"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Tablet će se uskoro isključiti"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Uređaj će se uskoro isključiti"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index ffa55af..da70d3b 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"Hauria de durar aproximadament fins a les <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"Fins a les <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"És possible que la bateria s\'esgoti a les <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Temps restant inferior a <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Temps restant inferior a <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Temps restant superior a <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Temps restant superior a <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"És possible que el telèfon s\'apagui aviat"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"És possible que la tauleta s\'apagui aviat"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"És possible que el dispositiu s\'apagui aviat"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 504cc5e..4d071b0 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"Vydrží asi do <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"Do <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Baterie se může vybít do <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Zbývá méně než <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Zbývá méně než <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Zbývá více než <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Zbývá více než <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Telefon se brzy vypne"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Tablet se brzy vypne"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Zařízení se brzy vypne"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 2f929f7..ba56d32 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"Bør holde indtil ca. <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"Indtil <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Enheden løber muligvis tør for batteri inden <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Der er mindre end <xliff:g id="THRESHOLD">%1$s</xliff:g> tilbage"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Der er mindre end <xliff:g id="THRESHOLD">%1$s</xliff:g> tilbage (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Der er mere end <xliff:g id="TIME_REMAINING">%1$s</xliff:g> tilbage (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Der er mere end <xliff:g id="TIME_REMAINING">%1$s</xliff:g> tilbage"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Telefonen lukker muligvis snart ned"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Denne tablet lukker muligvis snart ned"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Enheden lukker muligvis snart ned"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index dcfee98..8744cb1 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"Debería durar aproximadamente hasta: <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"Hasta <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Es posible que la batería se agote para las <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Tiempo restante: menos de <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Tiempo restante: menos de <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Tiempo restante: más de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Tiempo restante: más de <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Es posible que pronto se apague el teléfono"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Es posible que pronto se apague la tablet"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Es posible que pronto se apague el dispositivo"</string>
@@ -455,7 +451,7 @@
<string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar la carga"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Desconocido"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Cargando"</string>
- <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carga rápida"</string>
+ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Cargando rápido"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Carga lenta"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"No se está cargando."</string>
<string name="battery_info_status_not_charging" msgid="8330015078868707899">"Conectado. No se puede cargar en este momento"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index e7a8475..f1540e6 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"Debería durar hasta las <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"Hasta: <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Puede que se agote la batería sobre las <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Tiempo restante: menos de <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Tiempo restante: menos de <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Tiempo restante: más de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Tiempo restante: más de <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Es posible que el teléfono se apague pronto"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Es posible que el tablet se apague pronto"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Es posible que el dispositivo se apague pronto"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index f42111d..206917e 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"Peaks kestma kuni <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"Kuni <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Aku võib tühjaks saada kell <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Vähem kui <xliff:g id="THRESHOLD">%1$s</xliff:g> jäänud"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Vähem kui <xliff:g id="THRESHOLD">%1$s</xliff:g> jäänud (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Rohkem kui <xliff:g id="TIME_REMAINING">%1$s</xliff:g> jäänud (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Rohkem kui <xliff:g id="TIME_REMAINING">%1$s</xliff:g> jäänud"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Telefon võib peagi välja lülituda"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Tahvelarvuti võib peagi välja lülituda"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Seade võib peagi välja lülituda"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 85260fe..d2c2ae4 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"Ordu honetara arte iraungo du, gutxi gorabehera: <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"<xliff:g id="TIME">%1$s</xliff:g> arte"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Baliteke bateria ordu honetan agortzea: <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"<xliff:g id="THRESHOLD">%1$s</xliff:g> baino gutxiago geratzen dira"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"<xliff:g id="THRESHOLD">%1$s</xliff:g> baino gutxiago geratzen dira (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> baino gehiago geratzen dira (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> baino gehiago geratzen dira"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Baliteke telefonoa laster itzaltzea"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Baliteke tableta laster itzaltzea"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Baliteke gailua laster itzaltzea"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 6afa889..fba9ae5 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -435,15 +435,11 @@
<string name="power_discharge_by" msgid="4113180890060388350">"باید حدوداً تا <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>) شارژ داشته باشید"</string>
<string name="power_discharge_by_only" msgid="92545648425937000">"باید حدوداً تا <xliff:g id="TIME">%1$s</xliff:g> شارژ داشته باشید"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"تا <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"ممکن است باتری در <xliff:g id="TIME">%1$s</xliff:g> تمام شود"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"ممکن است باتری <xliff:g id="TIME">%1$s</xliff:g> تمام شود"</string>
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"کمتر از <xliff:g id="THRESHOLD">%1$s</xliff:g> شارژ باقی مانده است"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"کمتر از <xliff:g id="THRESHOLD">%1$s</xliff:g> شارژ باقی مانده است (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"بیش از <xliff:g id="TIME_REMAINING">%1$s</xliff:g> شارژ باقی مانده است (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"بیش از <xliff:g id="TIME_REMAINING">%1$s</xliff:g> شارژ باقی مانده است"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"ممکن است تلفن بهزودی خاموش شود"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"ممکن است رایانه لوحی بهزودی خاموش شود"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"ممکن است دستگاه بهزودی خاموش شود"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 9e1fa76..969180f 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"Varaus loppuu noin <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"<xliff:g id="TIME">%1$s</xliff:g> saakka"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Akku voi loppua <xliff:g id="TIME">%1$s</xliff:g> mennessä"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Alle <xliff:g id="THRESHOLD">%1$s</xliff:g> jäljellä"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Alle <xliff:g id="THRESHOLD">%1$s</xliff:g> jäljellä (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Yli <xliff:g id="TIME_REMAINING">%1$s</xliff:g> jäljellä (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Yli <xliff:g id="TIME_REMAINING">%1$s</xliff:g> jäljellä"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Puhelin voi sammua pian"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Tabletti voi sammua pian"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Laite voi sammua pian"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index bc113cd..51f79ba 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -218,7 +218,7 @@
<string name="adb_paired_devices_title" msgid="5268997341526217362">"Appareils associés"</string>
<string name="adb_wireless_device_connected_summary" msgid="3039660790249148713">"Actuellement connecté"</string>
<string name="adb_wireless_device_details_title" msgid="7129369670526565786">"Renseignements sur l\'appareil"</string>
- <string name="adb_device_forget" msgid="193072400783068417">"Oublier"</string>
+ <string name="adb_device_forget" msgid="193072400783068417">"Supprimer"</string>
<string name="adb_device_fingerprint_title_format" msgid="291504822917843701">"Empreinte digitale d\'appareil : <xliff:g id="FINGERPRINT_PARAM">%1$s</xliff:g>"</string>
<string name="adb_wireless_connection_failed_title" msgid="664211177427438438">"Échec de la connexion"</string>
<string name="adb_wireless_connection_failed_message" msgid="9213896700171602073">"Vérifiez que <xliff:g id="DEVICE_NAME">%1$s</xliff:g> est connecté au bon réseau"</string>
@@ -435,7 +435,7 @@
<string name="power_discharge_by" msgid="4113180890060388350">"Devrait durer jusqu\'à environ <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="92545648425937000">"Devrait durer jusqu\'à environ <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"Jusqu\'à <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"La pile risque d\'être épuisée à <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"La pile risque d\'être épuisée d\'ici <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Il reste moins de <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="318215464914990578">"Il reste moins de <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Il reste plus de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index c2a8d49..5e6ee86 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"Devrait durer jusqu\'à environ <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"Jusqu\'à <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"La batterie risque d\'être épuisée à <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Il reste moins de <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Il reste moins de <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Il reste plus de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Il reste plus de <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Le téléphone va bientôt s\'éteindre"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"La tablette va bientôt s\'éteindre"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"L\'appareil va bientôt s\'éteindre"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 2a6bcfc..caa1f1b 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -435,15 +435,11 @@
<string name="power_discharge_by" msgid="4113180890060388350">"Debería durar aproximadamente ata a seguinte hora: <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="92545648425937000">"Debería durar aproximadamente ata a seguinte hora: <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"Ata: <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"A batería pode esgotarse á seguinte hora: <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"A batería pode esgotarse a esta hora: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Tempo restante: menos de <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Tempo restante: menos de <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Tempo restante: máis de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Tempo restante: máis de <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"O teléfono pode apagarse en breve"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"A tableta pode apagarse en breve"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"O dispositivo pode apagarse en breve"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index f6a2374..5bfe3a3 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"લગભગ <xliff:g id="TIME">%1$s</xliff:g> સુધી ચાલવી જોઈએ"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"<xliff:g id="TIME">%1$s</xliff:g> સુધી"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"બૅટરી <xliff:g id="TIME">%1$s</xliff:g> સુધીમાં પૂરી થઈ શકે છે"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"<xliff:g id="THRESHOLD">%1$s</xliff:g> કરતાં ઓછી બાકી છે"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"<xliff:g id="THRESHOLD">%1$s</xliff:g> કરતાં ઓછી (<xliff:g id="LEVEL">%2$s</xliff:g>) બાકી છે"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> કરતાં વધુ (<xliff:g id="LEVEL">%2$s</xliff:g>) બાકી છે"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> કરતાં વધુ બાકી છે"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"ફોન થોડીક જ વારમાં બંધ થઈ શકે છે"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"ટૅબ્લેટ થોડીક જ વારમાં બંધ થઈ શકે છે"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"ડિવાઇસ થોડીક જ વારમાં બંધ થઈ શકે છે"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index b084123..999e344 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -161,7 +161,7 @@
<string name="tts_default_pitch_title" msgid="6988592215554485479">"पिच"</string>
<string name="tts_default_pitch_summary" msgid="9132719475281551884">"कृत्रिम बोली के लहजे को प्रभावित करता है"</string>
<string name="tts_default_lang_title" msgid="4698933575028098940">"भाषा"</string>
- <string name="tts_lang_use_system" msgid="6312945299804012406">"सिस्टम भाषा का उपयोग करें"</string>
+ <string name="tts_lang_use_system" msgid="6312945299804012406">"सिस्टम की भाषा का इस्तेमाल करें"</string>
<string name="tts_lang_not_selected" msgid="7927823081096056147">"भाषा नहीं चुनी गई है"</string>
<string name="tts_default_lang_summary" msgid="9042620014800063470">"बोले गए लेख के लिए भाषा-विशिष्ट ध्वनि सेट करता है"</string>
<string name="tts_play_example_title" msgid="1599468547216481684">"एक उदाहरण सुनें"</string>
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"बैटरी करीब <xliff:g id="TIME">%1$s</xliff:g> तक चलेगी"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"<xliff:g id="TIME">%1$s</xliff:g> तक"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"बैटरी <xliff:g id="TIME">%1$s</xliff:g> तक खत्म हो जाएगी"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"<xliff:g id="THRESHOLD">%1$s</xliff:g> से कम बैटरी बची है"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"<xliff:g id="THRESHOLD">%1$s</xliff:g> से कम बैटरी बची है (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> से ज़्यादा चलने लायक बैटरी बची है (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> से ज़्यादा चलने लायक बैटरी बची है"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"फ़ोन जल्द ही बंद हो सकता है"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"टैबलेट जल्द ही बंद हो सकता है"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"डिवाइस जल्द ही बंद हो सकता है"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 115fef8..391d62c 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"Trebala bi trajati do <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"Do <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Baterija bi se mogla isprazniti do <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Preostalo je manje od <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Preostalo je manje od <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Preostalo je više od <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Preostalo je više od <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Telefon bi se uskoro mogao isključiti"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Tablet bi se uskoro mogao isključiti"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Uređaj bi se uskoro mogao isključiti"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index a223b4d..02a7b67 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"Nagyjából eddig bírja: <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"Eddig: <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Az akkumulátor lemerülhet a következő időpontig: <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Kevesebb mint <xliff:g id="THRESHOLD">%1$s</xliff:g> van hátra"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Kevesebb mint <xliff:g id="THRESHOLD">%1$s</xliff:g> van hátra (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Több mint <xliff:g id="TIME_REMAINING">%1$s</xliff:g> van hátra (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Több mint <xliff:g id="TIME_REMAINING">%1$s</xliff:g> van hátra"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Előfordulhat, hogy a telefon hamarosan kikapcsol"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Előfordulhat, hogy a táblagép hamarosan kikapcsol"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Előfordulhat, hogy az eszköz hamarosan kikapcsol"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 8d40abc..8b7679a 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"Լիցքը պետք է որ բավականացնի մինչև <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"Մինչև <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Մարտկոցի լիցքը կարող է սպառվել մինչև <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Մնացել է <xliff:g id="THRESHOLD">%1$s</xliff:g>-ից քիչ"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Մնացել է <xliff:g id="THRESHOLD">%1$s</xliff:g>-ից քիչ (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Մնացել է ավելի քան <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Մնացել է ավելի քան <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Հեռախոսը շուտով կանջատվի"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Պլանշետը շուտով կանջատվի"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Սարքը շուտով կանջատվի"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index d925df3..543403a 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -61,7 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Miðlungshratt"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Hratt"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Mjög hratt"</string>
- <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Útrunninn"</string>
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Útrunnin"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Aftengt"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Aftengist…"</string>
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"Ætti að endast til u.þ.b. <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"Til klukkan <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Rafhlaðan gæti tæmst kl. <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Minna en <xliff:g id="THRESHOLD">%1$s</xliff:g> eftir"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Minna en <xliff:g id="THRESHOLD">%1$s</xliff:g> eftir (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Meira en <xliff:g id="TIME_REMAINING">%1$s</xliff:g> eftir (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Meira en <xliff:g id="TIME_REMAINING">%1$s</xliff:g> eftir"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Síminn gæti slökkt á sér fljótlega"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Spjaldtölvan gæti slökkt á sér fljótlega"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Tækið gæti slökkt á sér fljótlega"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 3f38597..dd597d0 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -61,7 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Media"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Veloce"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Molto veloce"</string>
- <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Scaduto"</string>
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Scaduta"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Disconnesso"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Disconnessione..."</string>
@@ -195,7 +195,7 @@
</string-array>
<string name="choose_profile" msgid="343803890897657450">"Scegli profilo"</string>
<string name="category_personal" msgid="6236798763159385225">"Personali"</string>
- <string name="category_work" msgid="4014193632325996115">"Lavoro"</string>
+ <string name="category_work" msgid="4014193632325996115">"Di lavoro"</string>
<string name="development_settings_title" msgid="140296922921597393">"Opzioni sviluppatore"</string>
<string name="development_settings_enable" msgid="4285094651288242183">"Attiva Opzioni sviluppatore"</string>
<string name="development_settings_summary" msgid="8718917813868735095">"Imposta opzioni per lo sviluppo di applicazioni"</string>
@@ -435,15 +435,11 @@
<string name="power_discharge_by" msgid="4113180890060388350">"Ora stimata esaurimento batteria: <xliff:g id="TIME">%1$s</xliff:g> circa (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="92545648425937000">"Ora stimata esaurimento batteria: <xliff:g id="TIME">%1$s</xliff:g> circa"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"Fino alle ore <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"La batteria potrebbe esaurirsi entro <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"La batteria potrebbe esaurirsi entro le <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Carica residua: meno di <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Carica residua: meno di <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Tempo residuo: più di <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Tempo residuo: più di <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Il telefono potrebbe spegnersi a breve"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Il tablet potrebbe spegnersi a breve"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Il dispositivo potrebbe spegnersi a breve"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 53f82eb..a7862a1 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"אמורה להחזיק מעמד בערך עד <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"עד <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"ייתכן שהסוללה תתרוקן עד <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"יש פחות מ-<xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"יש פחות מ-<xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"יש יותר מ-<xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"יש יותר מ-<xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"הטלפון עלול להיכבות בקרוב"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"הטאבלט עלול להיכבות בקרוב"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"המכשיר עלול להיכבות בקרוב"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 2f91ea6..9947b18 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"電池が切れる推定時刻: <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"<xliff:g id="TIME">%1$s</xliff:g> まで"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"<xliff:g id="TIME">%1$s</xliff:g> 頃に電池がなくなります"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"残り時間: <xliff:g id="THRESHOLD">%1$s</xliff:g>未満"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"残り時間: <xliff:g id="THRESHOLD">%1$s</xliff:g>(<xliff:g id="LEVEL">%2$s</xliff:g>)未満"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"残り時間: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>(<xliff:g id="LEVEL">%2$s</xliff:g>)以上"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"残り時間: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>以上"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"スマートフォンの電源がもうすぐ切れます"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"タブレットの電源がもうすぐ切れます"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"デバイスの電源がもうすぐ切れます"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 965a76d..5240b0c 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -435,7 +435,7 @@
<string name="power_discharge_by" msgid="4113180890060388350">"უნდა იმუშაოს დაახლოებით <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)-მდე"</string>
<string name="power_discharge_by_only" msgid="92545648425937000">"უნდა იმუშაოს დაახლოებით <xliff:g id="TIME">%1$s</xliff:g>-მდე"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"<xliff:g id="TIME">%1$s</xliff:g>-მდე"</string>
- <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"ბატარეა შესაძლოა ამოიწუროს <xliff:g id="TIME">%1$s</xliff:g>-ისთვის"</string>
+ <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"ბატარეა შესაძლოა ამოიწუროს <xliff:g id="TIME">%1$s</xliff:g>-მდე"</string>
<string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"დარჩა <xliff:g id="THRESHOLD">%1$s</xliff:g>-ზე ნაკლები"</string>
<string name="power_remaining_less_than_duration" msgid="318215464914990578">"დარჩა <xliff:g id="THRESHOLD">%1$s</xliff:g>-ზე ნაკლები (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="446388082266121894">"დარჩა <xliff:g id="TIME_REMAINING">%1$s</xliff:g>-ზე მეტი (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 5aec86b..84d0bb6 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"Шамамен <xliff:g id="TIME">%1$s</xliff:g> дейін жетеді"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"<xliff:g id="TIME">%1$s</xliff:g> дейін"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Батарея заряды сағат <xliff:g id="TIME">%1$s</xliff:g> қарай бітуі мүмкін."</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Ең азы <xliff:g id="THRESHOLD">%1$s</xliff:g> қалды"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Ең азы <xliff:g id="THRESHOLD">%1$s</xliff:g> қалды (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Ең көбі <xliff:g id="TIME_REMAINING">%1$s</xliff:g> қалды (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Ең көбі <xliff:g id="TIME_REMAINING">%1$s</xliff:g> қалды"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Телефон көп ұзамай өшуі мүмкін"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Планшет көп ұзамай өшуі мүмкін"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Құрылғы көп ұзамай өшуі мүмкін"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index aa52052..f1e42db 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -61,7 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"មធ្យម"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"លឿន"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"លឿនខ្លាំង"</string>
- <string name="wifi_passpoint_expired" msgid="6540867261754427561">"ផុតកំណត់"</string>
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"បានផុតកំណត់"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g> / <xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"បានផ្ដាច់"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"កំពុងផ្ដាច់…"</string>
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"គួរតែអាចប្រើបានរហូតដល់ម៉ោងប្រហែល <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"រហូតដល់ម៉ោង <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"អាចនឹងអស់ថ្មត្រឹមម៉ោង <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"នៅសល់តិចជាង <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"នៅសល់តិចជាង <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"នៅសល់ច្រើនជាង <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"នៅសល់ច្រើនជាង <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"ទូរសព្ទអាចនឹងបិទក្នុងពេលបន្តិចទៀត"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"ថេប្លេតអាចនឹងបិទក្នុងពេលបន្តិចទៀត"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"ឧបករណ៍អាចនឹងបិទក្នុងពេលបន្តិចទៀត"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 9dbeb45..7c20c94 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"대략 <xliff:g id="TIME">%1$s</xliff:g>까지 사용 가능"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"<xliff:g id="TIME">%1$s</xliff:g>까지"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"예상 배터리 종료 시간: <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"<xliff:g id="THRESHOLD">%1$s</xliff:g> 미만 남음"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"<xliff:g id="THRESHOLD">%1$s</xliff:g> 미만 남음(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> 이상 남음(<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> 이상 남음"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"휴대전화가 곧 종료될 수 있음"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"태블릿이 곧 종료될 수 있음"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"기기가 곧 종료될 수 있음"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 66c5c4f..8ec5f3a 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -61,7 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Орто"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Ылдам"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Абдан ылдам"</string>
- <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Мөөнөтү бүткөн"</string>
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Эскирип калган"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Ажыратылган"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"Ажыратылууда…"</string>
@@ -76,7 +76,7 @@
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Туташып турат (телефониясыз), батареянын деңгээли – <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp_battery_level" msgid="6499078454894324287">"Туташып турат (медиасыз), батареянын деңгээли – <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp_battery_level" msgid="8477440576953067242">"Туташып турат (телефониясыз же медиасыз), батареянын деңгээли – <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
- <string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Жигердүү, батареянын деңгээли: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Иштеп жатат, батареянын деңгээли: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Активдүү, сол: Батареянын деңгээли <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, оң: Батареянын деңгээли <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"Батареянын деңгээли: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"Сол: Батареянын деңгээли <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, оң: Батареянын деңгээли <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"Болжол менен саат <xliff:g id="TIME">%1$s</xliff:g> өчөт"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"<xliff:g id="TIME">%1$s</xliff:g> чейин"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Батарея <xliff:g id="TIME">%1$s</xliff:g> отуруп калышы мүмкүн"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"<xliff:g id="THRESHOLD">%1$s</xliff:g> азыраак калды"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"<xliff:g id="THRESHOLD">%1$s</xliff:g> азыраак калды (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> көбүрөөк калды (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> көбүрөөк калды"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Телефон бир аздан кийин өчүп калышы мүмкүн"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Планшет бир аздан кийин өчүп калышы мүмкүн"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Түзмөк бир аздан кийин өчүп калышы мүмкүн"</string>
@@ -458,7 +454,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ыкчам кубатталууда"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Жай кубатталууда"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Кубат алган жок"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Сайылып турат, учурда кубаттоо мүмкүн эмес"</string>
+ <string name="battery_info_status_not_charging" msgid="8330015078868707899">"сайылып турат, бирок кубатталган жок"</string>
<string name="battery_info_status_full" msgid="4443168946046847468">"Толук"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Администратор тарабынан көзөмөлдөнөт"</string>
<string name="disabled" msgid="8017887509554714950">"Өчүрүлгөн"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 4772124..cc43d5a 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"Turėtų išsikrauti maždaug po <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"Iki <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Akumuliatoriaus energija gali išsekti <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Liko mažiau nei <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Liko mažiau nei <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Liko daugiau nei <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Liko daugiau nei <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Telefonas netrukus gali būti išjungtas"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Planšetinis komp. netrukus gali būti išjungtas"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Įrenginys netrukus gali būti išjungtas"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 3380645..249489e 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"Ойролцоогоор <xliff:g id="TIME">%1$s</xliff:g> хүртэл барих ёстой"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"<xliff:g id="TIME">%1$s</xliff:g> хүртэл"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Батарейн цэнэг <xliff:g id="TIME">%1$s</xliff:g> гээд дуусаж болзошгүй"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"<xliff:g id="THRESHOLD">%1$s</xliff:g>-с бага үлдсэн"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"<xliff:g id="THRESHOLD">%1$s</xliff:g>-с бага үлдсэн (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g>-с их үлдсэн (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g>-с их үлдсэн"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Утас удахгүй унтарч болзошгүй"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Таблет удахгүй унтарч болзошгүй"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Төхөөрөмж удахгүй унтарч болзошгүй"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index f34e2b8..fa36975 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"Seharusnya boleh digunakan hingga kira-kira <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"Hingga <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Bateri mungkin habis menjelang <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Kurang daripada <xliff:g id="THRESHOLD">%1$s</xliff:g> lagi"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Kurang daripada <xliff:g id="THRESHOLD">%1$s</xliff:g> lagi (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Lebih daripada <xliff:g id="TIME_REMAINING">%1$s</xliff:g> lagi (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Lebih daripada <xliff:g id="TIME_REMAINING">%1$s</xliff:g> lagi"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Telefon mungkin ditutup tidak lama lagi"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Tablet mungkin ditutup tidak lama lagi"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Peranti mungkin ditutup tidak lama lagi"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 3f38771..e374d65 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"Skal vare til omtrent <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"Til <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Batteriet kan gå tomt <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Mindre enn <xliff:g id="THRESHOLD">%1$s</xliff:g> gjenstår"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Mindre enn <xliff:g id="THRESHOLD">%1$s</xliff:g> gjenstår (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Mer enn <xliff:g id="TIME_REMAINING">%1$s</xliff:g> gjenstår (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Mer enn <xliff:g id="TIME_REMAINING">%1$s</xliff:g> gjenstår"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Telefonen slås kanskje av snart"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Nettbrettet slås kanskje av snart"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Enheten slås kanskje av snart"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 9a839fa..d6856a7 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -251,8 +251,7 @@
<string name="wifi_display_certification" msgid="1805579519992520381">"ताररहित प्रदर्शन प्रमाणीकरण"</string>
<string name="wifi_verbose_logging" msgid="1785910450009679371">"Wi-Fi वर्बोज लग सक्षम पार्नुहोस्"</string>
<string name="wifi_scan_throttling" msgid="2985624788509913617">"Wi‑Fi स्क्यान थ्रोटलिङ"</string>
- <!-- no translation found for wifi_enhanced_mac_randomization (5437378364995776979) -->
- <skip />
+ <string name="wifi_enhanced_mac_randomization" msgid="5437378364995776979">"Wi‑Fi द्वारा परिष्कृत MAC ठेगाना बदल्ने सुविधा"</string>
<string name="mobile_data_always_on" msgid="8275958101875563572">"मोबाइल डेटा सधैँ सक्रिय राख्नुहोस्"</string>
<string name="tethering_hardware_offload" msgid="4116053719006939161">"टेदरिङको लागि हार्डवेयरको प्रवेग"</string>
<string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"नामकरण नगरिएका ब्लुटुथ यन्त्रहरू देखाउनुहोस्"</string>
@@ -285,8 +284,7 @@
<string name="wifi_display_certification_summary" msgid="8111151348106907513">"ताररहित प्रदर्शन प्रमाणीकरणका लागि विकल्पहरू देखाउनुहोस्"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Wi-Fi लग स्तर बढाउनुहोस्, Wi-Fi चयनकर्तामा प्रति SSID RSSI देखाइन्छ"</string>
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"ब्याट्रीको खपत कम गरी नेटवर्कको कार्यसम्पादनमा सुधार गर्दछ"</string>
- <!-- no translation found for wifi_enhanced_mac_randomization_summary (7925425746373704991) -->
- <skip />
+ <string name="wifi_enhanced_mac_randomization_summary" msgid="7925425746373704991">"यो टगलले क्लाइन्ट मोडमा मात्र MAC ठेगाना बदल्ने सुविधामा असर पार्न सक्छ।\nयो मोड सक्रिय हुँदा MAC बदल्ने सुविधा सक्षम पारेको कुनै पनि नेटवर्कको MAC ठेगाना पुनः बदल्न सकिन्छ। यसका लागि नेटवर्क कनेक्ट भएको हुनु पर्छ। यो कुरा क्लाइन्टले उक्त नेटवर्क पछिल्लो पटक कहिले डिस्कनेक्ट गरेको थियो भन्ने कुरामा पनि भर पर्छ। यन्त्र ४ घन्टा वा सोभन्दा कम समयमा फेरि कनेक्ट हुन्छ भने MAC ठेगाना पुनः बदलिँदैन।"</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"सशुल्क वाइफाइ"</string>
<string name="wifi_unmetered_label" msgid="6174142840934095093">"मिटर नगरिएको"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"लगर बफर आकारहरू"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 176a8b7..d034b49 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"Is nog genoeg tot ongeveer <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"Tot <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Batterij is waarschijnlijk leeg om <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Minder dan <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Nog minder dan <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Nog meer dan <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Meer dan <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Telefoon wordt binnenkort mogelijk uitgeschakeld"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Tablet wordt binnenkort mogelijk uitgeschakeld"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Apparaat wordt binnenkort mogelijk uitgeschakeld"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index e1610fb..cedf0bf 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -154,9 +154,9 @@
<string name="running_process_item_user_label" msgid="3988506293099805796">"ଉପଯୋଗକର୍ତ୍ତା: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
<string name="launch_defaults_some" msgid="3631650616557252926">"କିଛି ପୂର୍ବ-ନିର୍ଦ୍ଧାରିତ ମାନ ସେଟ୍ ହୋଇଛି"</string>
<string name="launch_defaults_none" msgid="8049374306261262709">"କୌଣସି ପୂର୍ବ-ନିର୍ଦ୍ଧାରଣ ସେଟ୍ ହୋଇନାହିଁ"</string>
- <string name="tts_settings" msgid="8130616705989351312">"ଲେଖା-ରୁ-କଥା ସେଟିଙ୍ଗ୍"</string>
- <string name="tts_settings_title" msgid="7602210956640483039">"ଲେଖା-ରୁ-କଥା ଆଉଟପୁଟ୍"</string>
- <string name="tts_default_rate_title" msgid="3964187817364304022">"ସ୍ପୀଚ୍ ବେଗ"</string>
+ <string name="tts_settings" msgid="8130616705989351312">"ଟେକ୍ସଟ-ରୁ-ସ୍ପିଚ୍ ସେଟିଂସ୍"</string>
+ <string name="tts_settings_title" msgid="7602210956640483039">"ଟେକ୍ସଟ-ରୁ-ସ୍ପିଚ୍ ଆଉଟପୁଟ୍"</string>
+ <string name="tts_default_rate_title" msgid="3964187817364304022">"ସ୍ପିଚ୍ ରେଟ୍"</string>
<string name="tts_default_rate_summary" msgid="3781937042151716987">"ଲେଖା ପଢ଼ିବାର ବେଗ"</string>
<string name="tts_default_pitch_title" msgid="6988592215554485479">"ପିଚ୍"</string>
<string name="tts_default_pitch_summary" msgid="9132719475281551884">"ସଂଶ୍ଳେଷିତ ସ୍ପିଚ୍ର ଟୋନ୍ରେ ପ୍ରଭାବ ପକାଏ"</string>
@@ -421,7 +421,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"ପ୍ରୋଟାନୋମାଲି (ଲାଲ୍-ସବୁଜ)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomaly (ନୀଳ-ହଳଦିଆ)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"ରଙ୍ଗ ସଂଶୋଧନ"</string>
- <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1284746051652993443">"ଆପଣଙ୍କ ଡିଭାଇସରେ ରଙ୍ଗଗୁଡ଼ିକ କିପରି ଡିସପ୍ଲେ ହୁଏ ତାହା ଆଡଜଷ୍ଟ କରିବାକୁ ’କଲର୍ କରେକ୍ସନ୍’ ଆପଣଙ୍କୁ ଅନୁମତି ଦିଏ"</string>
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1284746051652993443">"ଆପଣଙ୍କ ଡିଭାଇସରେ ରଙ୍ଗଗୁଡ଼ିକ କିପରି ଡିସପ୍ଲେ ହୁଏ, ତାହା ଆଡଜଷ୍ଟ କରିବାକୁ \'ରଙ୍ଗ ସଂଶୋଧନ’ ଆପଣଙ୍କୁ ଅନୁମତି ଦିଏ"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"<xliff:g id="TITLE">%1$s</xliff:g> ଦ୍ୱାରା ଓଭର୍ରାଇଡ୍ କରାଯାଇଛି"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"ପାଖାପାଖି <xliff:g id="TIME_REMAINING">%1$s</xliff:g> ବଳକା ଅଛି"</string>
@@ -536,16 +536,16 @@
<string name="user_add_profile_item_summary" msgid="5418602404308968028">"ନିଜ ଆକାଉଣ୍ଟରୁ ଆପ୍ ତଥା କଣ୍ଟେଣ୍ଟକୁ ଆପଣ ଆକ୍ସେସ୍ ରୋକିପାରିବେ"</string>
<string name="user_add_user_item_title" msgid="2394272381086965029">"ଉପଯୋଗକର୍ତ୍ତା"</string>
<string name="user_add_profile_item_title" msgid="3111051717414643029">"ସୀମିତ ସୁବିଧା ଥିବା ପ୍ରୋଫାଇଲ୍"</string>
- <string name="user_add_user_title" msgid="5457079143694924885">"ନୂତନ ୟୁଜର୍ ଯୋଡ଼ିବେ?"</string>
- <string name="user_add_user_message_long" msgid="1527434966294733380">"ଅତିରିକ୍ତ ୟୁଜର୍ ତିଆରିକରି ଆପଣ ଏହି ଡିଭାଇସ୍କୁ ଅନ୍ୟ ଲୋକମାନଙ୍କ ସହିତ ସେୟାର୍ କରିପାରିବେ। ପ୍ରତ୍ୟେକ ୟୁଜର୍ଙ୍କର ନିଜର ସ୍ପେସ୍ ଅଛି ଯାହାକୁ ସେମାନେ ଆପ୍, ୱାଲପେପର୍ ଓ ଏପରି ଅନେକ ସହିତ କଷ୍ଟମାଇଜ୍ କରିପାରିବେ। ୟୁଜର୍ ୱାଇ-ଫାଇ ଭଳି ଡିଭାଇସ୍ ସେଟିଙ୍ଗକୁ ମଧ୍ୟ ଆଡଜଷ୍ଟ କରିପାରିବେ ଯାହା ସମସ୍ତଙ୍କୁ ପ୍ରଭାବିତ କରିଥାଏ। \n\nଯେତେବେଳେ ଆପଣ ଗୋଟିଏ ନୂଆ ୟୁଜର୍ଙ୍କୁ ଯୋଡ଼ିବେ ସେତେବେଳେ ସେହି ବ୍ୟକ୍ତି ଜଣଙ୍କୁ ନିଜର ସ୍ପେସ୍କୁ ସେଟଅପ୍ କରିବାକୁ ପଡ଼ିବ। \n\nଅନ୍ୟ ୟୁଜରଙ୍କ ପାଇଁ ଯେକୌଣସି ୟୁଜର୍ ଆପ୍କୁ ଅପଡେଟ୍ କରିପାରିବେ। ଆକ୍ସେସ୍ କରିବା ପାଇଁ ସେଟିଙ୍ଗ ଏବଂ ସେବା ନୂଆ ୟୁଜର୍ଙ୍କୁ ଟ୍ରନ୍ସଫର୍ ନହୋଇପାରେ।"</string>
+ <string name="user_add_user_title" msgid="5457079143694924885">"ନୂତନ ଉପଯୋଗକର୍ତ୍ତାଙ୍କୁ ଯୋଗ କରିବେ?"</string>
+ <string name="user_add_user_message_long" msgid="1527434966294733380">"ଅତିରିକ୍ତ ଉପଯୋଗକର୍ତ୍ତା ତିଆରି କରି ଆପଣ ଏହି ଡିଭାଇସ୍କୁ ଅନ୍ୟ ଲୋକମାନଙ୍କ ସହିତ ସେୟାର୍ କରିପାରିବେ। ପ୍ରତ୍ୟେକ ଉପଯୋଗକର୍ତ୍ତାଙ୍କ ନିଜର ସ୍ପେସ୍ ଅଛି ଯାହାକୁ ସେମାନେ ଆପ୍, ୱାଲପେପର୍ ଓ ଏପରି ଅନେକ କିଛି ସହିତ କଷ୍ଟମାଇଜ୍ କରିପାରିବେ। ଉପଯୋଗକର୍ତ୍ତା ୱାଇ-ଫାଇ ଭଳି ଡିଭାଇସ୍ ସେଟିଂସକୁ ମଧ୍ୟ ଆଡଜଷ୍ଟ କରିପାରିବେ ଯାହା ସମସ୍ତଙ୍କୁ ପ୍ରଭାବିତ କରିଥାଏ। \n\nଯେତେବେଳେ ଆପଣ ଗୋଟିଏ ନୂଆ ଉପଯୋଗକର୍ତ୍ତାଙ୍କୁ ଯୋଗ କରିବେ, ସେତେବେଳେ ସେହି ବ୍ୟକ୍ତିଙ୍କୁ ନିଜର ସ୍ପେସ୍କୁ ସେଟଅପ୍ କରିବାକୁ ପଡ଼ିବ। \n\nଅନ୍ୟ ଉପଯୋଗକର୍ତ୍ତାଙ୍କ ପାଇଁ ଯେ କୌଣସି ଉପଯୋଗକର୍ତ୍ତା ଆପ୍କୁ ଅପଡେଟ୍ କରିପାରିବେ। ଆକ୍ସେସିବିଲିଟୀ ସେଟିଂସ୍ ଏବଂ ସେବା ନୂଆ ଉପଯୋଗକର୍ତ୍ତାଙ୍କୁ ସ୍ଥାନାନ୍ତର ହୋଇନପାରେ।"</string>
<string name="user_add_user_message_short" msgid="3295959985795716166">"ଜଣେ ନୂଆ ଉପଯୋଗକର୍ତ୍ତାଙ୍କୁ ଯୋଡ଼ିବାବେଳେ, ସେହି ବ୍ୟକ୍ତିଙ୍କୁ ସ୍ଥାନ ସେଟ୍ କରିବାକୁ ପଡ଼ିବ।\n\nଅନ୍ୟ ସମସ୍ତ ଉପଯୋଗକର୍ତ୍ତାଙ୍କ ପାଇଁ ଯେକୌଣସି ଉପଯୋଗକର୍ତ୍ତା ଆପ୍ଗୁଡ଼ିକୁ ଅପ୍ଡେଟ୍ କରିପାରିବେ।"</string>
- <string name="user_setup_dialog_title" msgid="8037342066381939995">"ଏବେ ୟୁଜର୍ଙ୍କୁ ସେଟ୍ କରିବେ?"</string>
- <string name="user_setup_dialog_message" msgid="269931619868102841">"ସୁନିଶ୍ଚିତ କରନ୍ତୁ ଯେ, ବ୍ୟକ୍ତି ଜଣକ ଡିଭାଇସ୍ ଓ ନିଜର ସ୍ଥାନ ସେଟ୍ କରିବା ପାଇଁ ଉପଲବ୍ଧ ଅଛନ୍ତି।"</string>
+ <string name="user_setup_dialog_title" msgid="8037342066381939995">"ଏବେ ଉପଯୋଗକର୍ତ୍ତା ସେଟଅପ କରିବେ?"</string>
+ <string name="user_setup_dialog_message" msgid="269931619868102841">"ସୁନିଶ୍ଚିତ କରନ୍ତୁ ଯେ, ବ୍ୟକ୍ତି ଜଣକ ଡିଭାଇସ୍ ଓ ନିଜର ସ୍ଥାନ ସେଟଅପ୍ କରିବା ପାଇଁ ଉପଲବ୍ଧ ଅଛନ୍ତି।"</string>
<string name="user_setup_profile_dialog_message" msgid="4788197052296962620">"ପ୍ରୋଫାଇଲ୍କୁ ଏବେ ସେଟ୍ କରିବେ?"</string>
- <string name="user_setup_button_setup_now" msgid="1708269547187760639">"ଏବେ ସେଟ୍ କରନ୍ତୁ"</string>
+ <string name="user_setup_button_setup_now" msgid="1708269547187760639">"ଏବେ ସେଟଅପ୍ କରନ୍ତୁ"</string>
<string name="user_setup_button_setup_later" msgid="8712980133555493516">"ଏବେ ନୁହେଁଁ"</string>
<string name="user_add_user_type_title" msgid="551279664052914497">"ଯୋଡନ୍ତୁ"</string>
- <string name="user_new_user_name" msgid="60979820612818840">"ନୂଆ ୟୁଜର୍"</string>
+ <string name="user_new_user_name" msgid="60979820612818840">"ନୂଆ ଉପଯୋଗକର୍ତ୍ତା"</string>
<string name="user_new_profile_name" msgid="2405500423304678841">"ନୂଆ ପ୍ରୋଫାଇଲ୍"</string>
<string name="user_info_settings_title" msgid="6351390762733279907">"ଉପଯୋଗକର୍ତ୍ତା ସୂଚନା"</string>
<string name="profile_info_settings_title" msgid="105699672534365099">"ପ୍ରୋଫାଇଲ୍ ସୂଚନା"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index d371c52..cc0543e 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -195,7 +195,7 @@
</string-array>
<string name="choose_profile" msgid="343803890897657450">"Wybierz profil"</string>
<string name="category_personal" msgid="6236798763159385225">"Osobiste"</string>
- <string name="category_work" msgid="4014193632325996115">"Praca"</string>
+ <string name="category_work" msgid="4014193632325996115">"Służbowe"</string>
<string name="development_settings_title" msgid="140296922921597393">"Opcje programistyczne"</string>
<string name="development_settings_enable" msgid="4285094651288242183">"Włącz opcje dla programistów"</string>
<string name="development_settings_summary" msgid="8718917813868735095">"Ustaw opcje związane z programowaniem aplikacji."</string>
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"Powinno wystarczyć do <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"Do <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Bateria może się wyczerpać do <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Pozostało mniej niż <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Pozostało mniej niż <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Pozostało mniej niż <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Pozostało mniej niż <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Wkrótce telefon może się wyłączyć"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Tablet może się wkrótce wyłączyć"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Urządzenie może się wkrótce wyłączyć"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index e3adb38..6318c6d 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -61,7 +61,7 @@
<string name="speed_label_medium" msgid="9078405312828606976">"Média"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Rápida"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Muito rápida"</string>
- <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Expirado"</string>
+ <string name="wifi_passpoint_expired" msgid="6540867261754427561">"Expirada"</string>
<string name="preference_summary_default_combination" msgid="2644094566845577901">"<xliff:g id="STATE">%1$s</xliff:g>/<xliff:g id="DESCRIPTION">%2$s</xliff:g>"</string>
<string name="bluetooth_disconnected" msgid="7739366554710388701">"Desligado"</string>
<string name="bluetooth_disconnecting" msgid="7638892134401574338">"A desligar..."</string>
@@ -508,7 +508,7 @@
<string name="alarm_template_far" msgid="6382760514842998629">"no(a) <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="zen_mode_duration_settings_title" msgid="1553451650289651489">"Duração"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="3212996860498119555">"Perguntar sempre"</string>
- <string name="zen_mode_forever" msgid="3339224497605461291">"Até ser desativado"</string>
+ <string name="zen_mode_forever" msgid="3339224497605461291">"Até desativar"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"Agora mesmo"</string>
<string name="media_transfer_this_device_name" msgid="2716555073132169240">"Altifalante do telemóvel"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problema ao ligar. Desligue e volte a ligar o dispositivo."</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index fa43c13..f64cfbf 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"Ar trebui să reziste până la <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"Până la <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Bateria se poate descărca până la <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"A mai rămas mai puțin de <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"A mai rămas mai puțin de <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"A mai rămas mai mult de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"A mai rămas mai mult de <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Telefonul se poate închide în curând"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Tableta se poate închide în curând"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Dispozitivul se poate închide în curând"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index bb06071..71c7cf3 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"Заряда хватит примерно до <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"До <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Батарея может разрядиться к <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Осталось менее <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Уровень заряда батареи: <xliff:g id="LEVEL">%2$s</xliff:g> (хватит менее чем на <xliff:g id="THRESHOLD">%1$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Уровень заряда батареи: <xliff:g id="LEVEL">%2$s</xliff:g> (хватит более чем на <xliff:g id="TIME_REMAINING">%1$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Осталось более <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Телефон скоро выключится"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Планшет скоро выключится"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Устройство скоро выключится"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index ea8cf73..f988d46 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -435,15 +435,11 @@
<string name="power_discharge_by" msgid="4113180890060388350">"Vydrží asi do <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="92545648425937000">"Vydrží asi do <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"Do <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Batéria sa môže do <xliff:g id="TIME">%1$s</xliff:g> minúť"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Batéria sa môže do <xliff:g id="TIME">%1$s</xliff:g> vybiť"</string>
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Zostáva menej ako <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Zostáva menej ako <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Zostáva viac ako <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Zostáva viac ako <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Telefón sa môže čoskoro vypnúť"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Tablet sa môže čoskoro vypnúť"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Zariadenie sa môže čoskoro vypnúť"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 893b207..eb2077c 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"Moralo bi zadostovati do približno <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"Do <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Energije baterije lahko zmanjka do <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Preostaja manj kot <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Preostaja manj kot <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Preostaja več kot <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Preostaja več kot <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Telefon se bo morda kmalu zaustavil"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Tablični računalnik se bo morda kmalu zaustavil"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Naprava se bo morda kmalu zaustavila"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 72b28ce..685be99 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -454,7 +454,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Po ngarkon me shpejtësi"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Po karikohet ngadalë"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Nuk po karikohet"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Në prizë, por nuk mund të ngarkohet për momentin"</string>
+ <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Në prizë, por nuk mund të karikohet për momentin"</string>
<string name="battery_info_status_full" msgid="4443168946046847468">"E mbushur"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Kontrolluar nga administratori"</string>
<string name="disabled" msgid="8017887509554714950">"Çaktivizuar"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index e47969c..9188a76 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"Трајаће приближно до <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"До <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Батерија ће се можда испразнити до <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Још мање од <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Још мање од <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Још више од <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Још више од <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Телефон ће се ускоро искључити"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Таблет ће се ускоро искључити"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Уређај ће се ускоро искључити"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index c21e624..c383e577 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"Bör räcka ungefär till klockan <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"Till kl. <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Batteriet kan ta slut klockan <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Mindre än <xliff:g id="THRESHOLD">%1$s</xliff:g> återstår"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Mindre än <xliff:g id="THRESHOLD">%1$s</xliff:g> återstår (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Mer än <xliff:g id="TIME_REMAINING">%1$s</xliff:g> återstår (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Mer än <xliff:g id="TIME_REMAINING">%1$s</xliff:g> återstår"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Telefonen kanske stängs av snart"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Surfplattan kanske stängs av snart"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Enheten kanske stängs av snart"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 844a329..315fab9 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"น่าจะใช้งานได้ถึงเวลาประมาณ <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"จนถึง <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"แบตเตอรี่อาจหมดภายใน <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"เหลือน้อยกว่า <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"เหลือน้อยกว่า <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"เหลืออีกกว่า <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"เหลืออีกกว่า <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"โทรศัพท์อาจปิดเครื่องในไม่ช้า"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"แท็บเล็ตอาจปิดเครื่องในไม่ช้า"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"อุปกรณ์อาจปิดเครื่องในไม่ช้า"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 412be0f..2ea03a4 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"Tatagal hanggang mga <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"Hanggang <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Posibleng maubos ang baterya sa loob ng <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Wala nang <xliff:g id="THRESHOLD">%1$s</xliff:g> ang natitira"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Wala nang <xliff:g id="THRESHOLD">%1$s</xliff:g> ang natitira (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Mahigit <xliff:g id="TIME_REMAINING">%1$s</xliff:g> pa ang natitira (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Mahigit <xliff:g id="TIME_REMAINING">%1$s</xliff:g> pa ang natitira"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Baka mag-shut down na ang telepono"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Baka mag-shut down na ang tablet"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Baka mag-shut down na ang device"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 6f6b3fc..a6021d2 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"Вистачить приблизно до <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"До <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Акумулятор може розрядитися до <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Залишилося менше ніж <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Залишилося менше ніж <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Залишилося понад <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Залишилося понад <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Телефон може невдовзі вимкнутися"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Планшет може невдовзі вимкнутися"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Пристрій може невдовзі вимкнутися"</string>
@@ -458,7 +454,7 @@
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Швидке заряджання"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Повільне заряджання"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Не заряджається"</string>
- <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Підключено. Не вдається зарядити"</string>
+ <string name="battery_info_status_not_charging" msgid="8330015078868707899">"Підключено, не заряджається"</string>
<string name="battery_info_status_full" msgid="4443168946046847468">"Акумулятор заряджено"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Керується адміністратором"</string>
<string name="disabled" msgid="8017887509554714950">"Вимкнено"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 215cb4c..3089b70 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -76,7 +76,7 @@
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"<xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> ulandi (telefondan tashqari), batareya quvvati: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp_battery_level" msgid="6499078454894324287">"<xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> ulandi (mediadan tashqari), batareya quvvati: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp_battery_level" msgid="8477440576953067242">"<xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> ulandi (telefon yoki mediadan tashqari), batareya quvvati: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Faol, batariya quvvati: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_active_battery_level" msgid="3450745316700494425">"Faol, batareya quvvati: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"Faol, L: batareya quvvati: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, R: batareya quvvati: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"Batareya quvvati: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"L: batareya quvvati: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, R: batareya quvvati: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
@@ -129,8 +129,8 @@
<string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string>
<string name="bluetooth_hearingaid_left_pairing_message" msgid="8561855779703533591">"Chap eshitish apparati ulanmoqda…"</string>
<string name="bluetooth_hearingaid_right_pairing_message" msgid="2655347721696331048">"O‘ng eshitish apparati ulanmoqda…"</string>
- <string name="bluetooth_hearingaid_left_battery_level" msgid="7375621694748104876">"Chap eshitish apparati – batariya quvvati: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <string name="bluetooth_hearingaid_right_battery_level" msgid="1850094448499089312">"O‘ng eshitish apparati – batariya quvvati: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_hearingaid_left_battery_level" msgid="7375621694748104876">"Chap eshitish apparati, batareya quvvati: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+ <string name="bluetooth_hearingaid_right_battery_level" msgid="1850094448499089312">"O‘ng eshitish apparati, batareya quvvati: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="accessibility_wifi_off" msgid="1195445715254137155">"Wi-Fi o‘chiq."</string>
<string name="accessibility_no_wifi" msgid="5297119459491085771">"Wi-Fi o‘chiq."</string>
<string name="accessibility_wifi_one_bar" msgid="6025652717281815212">"Wi-Fi: bitta ustun"</string>
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"Taxminan <xliff:g id="TIME">%1$s</xliff:g> gacha davom etadi"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"<xliff:g id="TIME">%1$s</xliff:g> gacha"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Batareya quvvati tugash vaqti: <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"<xliff:g id="THRESHOLD">%1$s</xliff:g>dan kam qoldi"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"<xliff:g id="THRESHOLD">%1$s</xliff:g>dan kam qoldi (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g>dan koʻproq qoldi (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g>dan koʻproq qoldi"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Telefon tez orada oʻchib qolishi mumkin"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Planshet tez orada oʻchib qolishi mumkin"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Qurilma tez orada oʻchib qolishi mumkin"</string>
@@ -451,8 +447,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7703677921000858479">"Planshet tez orada oʻchib qolishi mumkin (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="4374784375644214578">"Qurilma tez orada oʻchib qolishi mumkin (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> ichida toʻliq quvvat oladi"</string>
- <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> ichida toʻliq quvvat oladi"</string>
+ <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> ichida toʻladi"</string>
+ <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> ichida toʻladi"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Noma’lum"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Quvvat olmoqda"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Tezkor quvvat olmoqda"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index e118ec0..32fbcc9 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"Pin sẽ hết vào khoảng <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"Cho đến <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Điện thoại có thể hết pin vào <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Còn chưa đến <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Còn chưa đến <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Còn hơn <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Còn hơn <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Điện thoại có thể sắp tắt"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Máy tính bảng có thể sắp tắt"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Thiết bị có thể sắp tắt"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 9edcff2..7270c2e 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"估计能用到<xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"直到<xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"电池电量可能在<xliff:g id="TIME">%1$s</xliff:g> 前耗尽"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"剩余电池续航时间不到 <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"剩余电池续航时间不到 <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"剩余电池续航时间超过 <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"剩余电池续航时间超过 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"手机可能即将关机"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"平板电脑可能即将关机"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"设备可能即将关机"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 974831f..5d73f3e 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -435,7 +435,7 @@
<string name="power_discharge_by" msgid="4113180890060388350">"電量剩餘約 <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only" msgid="92545648425937000">"電量大約可用到<xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"還可用到<xliff:g id="TIME">%1$s</xliff:g>"</string>
- <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"電池電量可能將於<xliff:g id="TIME">%1$s</xliff:g>耗盡"</string>
+ <string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"電池電量可能將於<xliff:g id="TIME">%1$s</xliff:g> 耗盡"</string>
<string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"剩餘電量少於 <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="318215464914990578">"剩餘電量少於 <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="446388082266121894">"剩餘電量時間超過 <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 04b5cb6..43caf5a 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"預估電力大約可使用到<xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"還能持續使用到<xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"電池電力可能於<xliff:g id="TIME">%1$s</xliff:g> 前耗盡"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"電池可用時間不到 <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"電池可用時間不到 <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"電池可用時間超過 <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"電池可用時間超過 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"手機可能即將關機"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"平板電腦可能即將關機"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"裝置可能即將關機"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 7e84918..6cb24e6 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -436,14 +436,10 @@
<string name="power_discharge_by_only" msgid="92545648425937000">"Kumele ihlale cishe kube ngu-<xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_discharge_by_only_short" msgid="5883041507426914446">"Kuze kube ngu-<xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_suggestion_battery_run_out" msgid="6332089307827787087">"Ibhethri lingaphela ngo-<xliff:g id="TIME">%1$s</xliff:g>"</string>
- <!-- no translation found for power_remaining_less_than_duration_only (8956656616031395152) -->
- <skip />
- <!-- no translation found for power_remaining_less_than_duration (318215464914990578) -->
- <skip />
- <!-- no translation found for power_remaining_more_than_subtext (446388082266121894) -->
- <skip />
- <!-- no translation found for power_remaining_only_more_than_subtext (4873750633368888062) -->
- <skip />
+ <string name="power_remaining_less_than_duration_only" msgid="8956656616031395152">"Kusele okungaphansi kuka-<xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
+ <string name="power_remaining_less_than_duration" msgid="318215464914990578">"Kusele okungaphansi kuka-<xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_more_than_subtext" msgid="446388082266121894">"Kusele okungaphezu kuka-<xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_only_more_than_subtext" msgid="4873750633368888062">"Kusele okungaphezu kuka-<xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="137330009791560774">"Ifoni ingacisha maduze"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="145489081521468132">"Ithebulethi ingacisha maduze"</string>
<string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="1070562682853942350">"Idivayisi ingacisha maduze"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index f485793..eb02a1c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -279,8 +279,12 @@
}
public static int getThemeAttr(Context context, int attr) {
+ return getThemeAttr(context, attr, 0);
+ }
+
+ public static int getThemeAttr(Context context, int attr, int defaultValue) {
TypedArray ta = context.obtainStyledAttributes(new int[]{attr});
- int theme = ta.getResourceId(0, 0);
+ int theme = ta.getResourceId(0, defaultValue);
ta.recycle();
return theme;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
index 57e6808..ab7b54d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
@@ -159,6 +159,25 @@
return null;
}
+ /**
+ * Gets an icon from active dream.
+ */
+ public Drawable getActiveIcon() {
+ final ComponentName cn = getActiveDream();
+ if (cn != null) {
+ final PackageManager pm = mContext.getPackageManager();
+ try {
+ final ServiceInfo ri = pm.getServiceInfo(cn, 0);
+ if (ri != null) {
+ return ri.loadIcon(pm);
+ }
+ } catch (PackageManager.NameNotFoundException exc) {
+ return null;
+ }
+ }
+ return null;
+ }
+
public @WhenToDream int getWhenToDreamSetting() {
if (!isEnabled()) {
return NEVER;
diff --git a/packages/SettingsProvider/AndroidManifest.xml b/packages/SettingsProvider/AndroidManifest.xml
index 839899e..04d3f94 100644
--- a/packages/SettingsProvider/AndroidManifest.xml
+++ b/packages/SettingsProvider/AndroidManifest.xml
@@ -21,5 +21,10 @@
android:singleUser="true"
android:initOrder="100"
android:visibleToInstantApps="true" />
+
+ <service
+ android:name="WriteFallbackSettingsFilesJobService"
+ android:permission="android.permission.BIND_JOB_SERVICE"
+ android:exported="true"/>
</application>
</manifest>
diff --git a/packages/SettingsProvider/OWNERS b/packages/SettingsProvider/OWNERS
index 2054129..b2ac4f4 100644
--- a/packages/SettingsProvider/OWNERS
+++ b/packages/SettingsProvider/OWNERS
@@ -1,3 +1,5 @@
hackbod@google.com
+narayan@google.com
svetoslavganov@google.com
-moltmann@google.com
+schfan@google.com
+toddke@google.com
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 2245ee4..94509dd 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -23,12 +23,16 @@
import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON_OVERLAY;
+import static com.android.providers.settings.SettingsState.FALLBACK_FILE_SUFFIX;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.backup.BackupManager;
+import android.app.job.JobInfo;
+import android.app.job.JobScheduler;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentProvider;
@@ -55,12 +59,14 @@
import android.os.Bundle;
import android.os.DropBoxManager;
import android.os.Environment;
+import android.os.FileUtils;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IUserRestrictionsListener;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteCallback;
import android.os.RemoteException;
@@ -95,6 +101,7 @@
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
+import java.io.IOException;
import java.io.PrintWriter;
import java.nio.ByteBuffer;
import java.security.InvalidKeyException;
@@ -154,9 +161,11 @@
private static final String LOG_TAG = "SettingsProvider";
- private static final String TABLE_SYSTEM = "system";
- private static final String TABLE_SECURE = "secure";
- private static final String TABLE_GLOBAL = "global";
+ public static final String TABLE_SYSTEM = "system";
+ public static final String TABLE_SECURE = "secure";
+ public static final String TABLE_GLOBAL = "global";
+ public static final String TABLE_SSAID = "ssaid";
+ public static final String TABLE_CONFIG = "config";
// Old tables no longer exist.
private static final String TABLE_FAVORITES = "favorites";
@@ -205,6 +214,10 @@
public static final String RESULT_ROWS_DELETED = "result_rows_deleted";
public static final String RESULT_SETTINGS_LIST = "result_settings_list";
+ // Used for scheduling jobs to make a copy for the settings files
+ public static final int WRITE_FALLBACK_SETTINGS_FILES_JOB_ID = 1;
+ public static final long ONE_DAY_INTERVAL_MILLIS = 24 * 60 * 60 * 1000L;
+
// Overlay specified settings whitelisted for Instant Apps
private static final Set<String> OVERLAY_ALLOWED_GLOBAL_INSTANT_APP_SETTINGS = new ArraySet<>();
private static final Set<String> OVERLAY_ALLOWED_SYSTEM_INSTANT_APP_SETTINGS = new ArraySet<>();
@@ -2484,6 +2497,68 @@
}
}
+ /**
+ * Schedule the job service to make a copy of all the settings files.
+ */
+ public void scheduleWriteFallbackFilesJob() {
+ final Context context = getContext();
+ final JobScheduler jobScheduler =
+ (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
+ if (jobScheduler == null) {
+ // Might happen: SettingsProvider is created before JobSchedulerService in system server
+ return;
+ }
+ // Check if the job is already scheduled. If so, skip scheduling another one
+ if (jobScheduler.getPendingJob(WRITE_FALLBACK_SETTINGS_FILES_JOB_ID) != null) {
+ return;
+ }
+ // Back up all settings files
+ final PersistableBundle bundle = new PersistableBundle();
+ final File globalSettingsFile = mSettingsRegistry.getSettingsFile(
+ makeKey(SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM));
+ final File systemSettingsFile = mSettingsRegistry.getSettingsFile(
+ makeKey(SETTINGS_TYPE_SYSTEM, UserHandle.USER_SYSTEM));
+ final File secureSettingsFile = mSettingsRegistry.getSettingsFile(
+ makeKey(SETTINGS_TYPE_SECURE, UserHandle.USER_SYSTEM));
+ final File ssaidSettingsFile = mSettingsRegistry.getSettingsFile(
+ makeKey(SETTINGS_TYPE_SSAID, UserHandle.USER_SYSTEM));
+ final File configSettingsFile = mSettingsRegistry.getSettingsFile(
+ makeKey(SETTINGS_TYPE_CONFIG, UserHandle.USER_SYSTEM));
+ bundle.putString(TABLE_GLOBAL, globalSettingsFile.getAbsolutePath());
+ bundle.putString(TABLE_SYSTEM, systemSettingsFile.getAbsolutePath());
+ bundle.putString(TABLE_SECURE, secureSettingsFile.getAbsolutePath());
+ bundle.putString(TABLE_SSAID, ssaidSettingsFile.getAbsolutePath());
+ bundle.putString(TABLE_CONFIG, configSettingsFile.getAbsolutePath());
+ // Schedule the job to write the fallback files, once daily when phone is charging
+ jobScheduler.schedule(new JobInfo.Builder(WRITE_FALLBACK_SETTINGS_FILES_JOB_ID,
+ new ComponentName(context, WriteFallbackSettingsFilesJobService.class))
+ .setExtras(bundle)
+ .setPeriodic(ONE_DAY_INTERVAL_MILLIS)
+ .setRequiresCharging(true)
+ .setPersisted(true)
+ .build());
+ }
+
+ /**
+ * For each file in the given list, if it exists, copy it to a back up file. Ignore failures.
+ * @param filePaths List of paths of files that need to be backed up
+ */
+ public static void writeFallBackSettingsFiles(List<String> filePaths) {
+ final int numFiles = filePaths.size();
+ for (int i = 0; i < numFiles; i++) {
+ final String filePath = filePaths.get(i);
+ final File originalFile = new File(filePath);
+ if (SettingsState.stateFileExists(originalFile)) {
+ final File fallBackFile = new File(filePath + FALLBACK_FILE_SUFFIX);
+ try {
+ FileUtils.copy(originalFile, fallBackFile);
+ } catch (IOException ex) {
+ Slog.w(LOG_TAG, "Failed to write fallback file for: " + filePath);
+ }
+ }
+ }
+ }
+
final class SettingsRegistry {
private static final String DROPBOX_TAG_USERLOG = "restricted_profile_ssaid";
@@ -3431,6 +3506,7 @@
case MSG_NOTIFY_DATA_CHANGED: {
mBackupManager.dataChanged();
+ scheduleWriteFallbackFilesJob();
} break;
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 6b8219e..6678cf6 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -27,6 +27,7 @@
import android.content.pm.Signature;
import android.os.Binder;
import android.os.Build;
+import android.os.FileUtils;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -101,6 +102,8 @@
public static final int VERSION_UNDEFINED = -1;
+ public static final String FALLBACK_FILE_SUFFIX = ".fallback";
+
private static final String TAG_SETTINGS = "settings";
private static final String TAG_SETTING = "setting";
private static final String ATTR_PACKAGE = "package";
@@ -266,7 +269,7 @@
public SettingsState(Context context, Object lock, File file, int key,
int maxBytesPerAppPackage, Looper looper) {
// It is important that we use the same lock as the settings provider
- // to ensure multiple mutations on this state are atomicaly persisted
+ // to ensure multiple mutations on this state are atomically persisted
// as the async persistence should be blocked while we make changes.
mContext = context;
mLock = lock;
@@ -998,24 +1001,56 @@
}
@GuardedBy("mLock")
- private void readStateSyncLocked() {
+ private void readStateSyncLocked() throws IllegalStateException {
FileInputStream in;
+ AtomicFile file = new AtomicFile(mStatePersistFile);
try {
- in = new AtomicFile(mStatePersistFile).openRead();
+ in = file.openRead();
} catch (FileNotFoundException fnfe) {
- Slog.i(LOG_TAG, "No settings state " + mStatePersistFile);
+ Slog.w(LOG_TAG, "No settings state " + mStatePersistFile);
logSettingsDirectoryInformation(mStatePersistFile);
addHistoricalOperationLocked(HISTORICAL_OPERATION_INITIALIZE, null);
return;
}
+ if (parseStateFromXmlStreamLocked(in)) {
+ return;
+ }
+
+ // Settings file exists but is corrupted. Retry with the fallback file
+ final File statePersistFallbackFile = new File(
+ mStatePersistFile.getAbsolutePath() + FALLBACK_FILE_SUFFIX);
+ Slog.i(LOG_TAG, "Failed parsing settings file: " + mStatePersistFile
+ + ", retrying with fallback file: " + statePersistFallbackFile);
+ try {
+ in = new AtomicFile(statePersistFallbackFile).openRead();
+ } catch (FileNotFoundException fnfe) {
+ final String message = "No fallback file found for: " + mStatePersistFile;
+ Slog.wtf(LOG_TAG, message);
+ throw new IllegalStateException(message);
+ }
+ if (parseStateFromXmlStreamLocked(in)) {
+ // Parsed state from fallback file. Restore original file with fallback file
+ try {
+ FileUtils.copy(statePersistFallbackFile, mStatePersistFile);
+ } catch (IOException ignored) {
+ // Failed to copy, but it's okay because we already parsed states from fallback file
+ }
+ } else {
+ final String message = "Failed parsing settings file: " + mStatePersistFile;
+ Slog.wtf(LOG_TAG, message);
+ throw new IllegalStateException(message);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private boolean parseStateFromXmlStreamLocked(FileInputStream in) {
try {
XmlPullParser parser = Xml.newPullParser();
parser.setInput(in, StandardCharsets.UTF_8.name());
parseStateLocked(parser);
+ return true;
} catch (XmlPullParserException | IOException e) {
- String message = "Failed parsing settings file: " + mStatePersistFile;
- Slog.wtf(LOG_TAG, message);
- throw new IllegalStateException(message, e);
+ return false;
} finally {
IoUtils.closeQuietly(in);
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/WriteFallbackSettingsFilesJobService.java b/packages/SettingsProvider/src/com/android/providers/settings/WriteFallbackSettingsFilesJobService.java
new file mode 100644
index 0000000..6e5b889
--- /dev/null
+++ b/packages/SettingsProvider/src/com/android/providers/settings/WriteFallbackSettingsFilesJobService.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.providers.settings;
+
+import static com.android.providers.settings.SettingsProvider.TABLE_CONFIG;
+import static com.android.providers.settings.SettingsProvider.TABLE_GLOBAL;
+import static com.android.providers.settings.SettingsProvider.TABLE_SECURE;
+import static com.android.providers.settings.SettingsProvider.TABLE_SSAID;
+import static com.android.providers.settings.SettingsProvider.TABLE_SYSTEM;
+import static com.android.providers.settings.SettingsProvider.WRITE_FALLBACK_SETTINGS_FILES_JOB_ID;
+
+import android.app.job.JobParameters;
+import android.app.job.JobService;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * JobService to make a copy of a list of files, given their paths.
+ */
+public class WriteFallbackSettingsFilesJobService extends JobService {
+ @Override
+ public boolean onStartJob(final JobParameters params) {
+ switch (params.getJobId()) {
+ case WRITE_FALLBACK_SETTINGS_FILES_JOB_ID:
+ final List<String> settingsFiles = new ArrayList<>();
+ settingsFiles.add(params.getExtras().getString(TABLE_GLOBAL, ""));
+ settingsFiles.add(params.getExtras().getString(TABLE_SYSTEM, ""));
+ settingsFiles.add(params.getExtras().getString(TABLE_SECURE, ""));
+ settingsFiles.add(params.getExtras().getString(TABLE_SSAID, ""));
+ settingsFiles.add(params.getExtras().getString(TABLE_CONFIG, ""));
+ SettingsProvider.writeFallBackSettingsFiles(settingsFiles);
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters params) {
+ return false;
+ }
+
+}
diff --git a/packages/SystemUI/res/drawable/ic_hardware_speaker.xml b/packages/SystemUI/res/drawable/ic_hardware_speaker.xml
new file mode 100644
index 0000000..0081e56
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_hardware_speaker.xml
@@ -0,0 +1,24 @@
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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="18dp"
+ android:height="18dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M17,2L7,2c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,1.99 2,1.99L17,22c1.1,0 2,-0.9 2,-2L19,4c0,-1.1 -0.9,-2 -2,-2zM7,20L7,4h10v16L7,20zM12,9c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2c-1.11,0 -2,0.9 -2,2s0.89,2 2,2zM12,11c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zM12,17c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2z"
+ android:fillColor="#000000"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/global_actions_grid_v2.xml b/packages/SystemUI/res/layout/global_actions_grid_v2.xml
index 66f57fd..fb57b47 100644
--- a/packages/SystemUI/res/layout/global_actions_grid_v2.xml
+++ b/packages/SystemUI/res/layout/global_actions_grid_v2.xml
@@ -30,6 +30,7 @@
>
<RelativeLayout
android:id="@+id/global_actions_overflow_button"
+ android:contentDescription="@string/accessibility_menu"
android:layout_width="48dp"
android:layout_height="48dp"
>
diff --git a/packages/SystemUI/res/layout/hybrid_conversation_notification.xml b/packages/SystemUI/res/layout/hybrid_conversation_notification.xml
new file mode 100644
index 0000000..e6f2790
--- /dev/null
+++ b/packages/SystemUI/res/layout/hybrid_conversation_notification.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<com.android.systemui.statusbar.notification.row.HybridConversationNotificationView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical|start"
+ android:paddingEnd="12dp">
+
+ <FrameLayout
+ android:layout_width="@*android:dimen/conversation_content_start"
+ android:layout_height="36dp"
+ >
+ <ImageView
+ android:id="@*android:id/conversation_icon"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_gravity="center"
+ />
+
+ <ViewStub
+ android:id="@*android:id/conversation_face_pile"
+ android:layout="@*android:layout/conversation_face_pile_layout"
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:layout_gravity="center"
+ />
+ </FrameLayout>
+
+ <TextView
+ android:id="@+id/notification_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+
+ android:paddingEnd="8dp"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Notification.Title"
+ />
+
+ <TextView
+ android:id="@+id/conversation_notification_sender"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ style="?attr/hybridNotificationTextStyle"
+ />
+
+ <TextView
+ android:id="@+id/notification_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ style="?attr/hybridNotificationTextStyle"
+ />
+</com.android.systemui.statusbar.notification.row.HybridConversationNotificationView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_media_panel.xml b/packages/SystemUI/res/layout/qs_media_panel.xml
index e5ac5f8..a194569 100644
--- a/packages/SystemUI/res/layout/qs_media_panel.xml
+++ b/packages/SystemUI/res/layout/qs_media_panel.xml
@@ -119,6 +119,7 @@
android:id="@+id/media_seamless"
android:background="@*android:drawable/media_seamless_background"
android:layout_weight="1"
+ android:forceHasOverlappingRendering="false"
>
<ImageView
android:layout_width="@dimen/qs_seamless_icon_size"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index bad18cf..eca2557 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -625,6 +625,15 @@
<!-- The height of the gap between adjacent notification sections. -->
<dimen name="notification_section_divider_height">@dimen/notification_side_paddings</dimen>
+ <!-- Size of the face pile shown on one-line (children of a group) conversation notifications -->
+ <dimen name="conversation_single_line_face_pile_size">36dp</dimen>
+
+ <!-- Size of an avatar shown on one-line (children of a group) conversation notifications -->
+ <dimen name="conversation_single_line_avatar_size">24dp</dimen>
+
+ <!-- Border width for avatars in the face pile shown on one-line (children of a group) conversation notifications -->
+ <dimen name="conversation_single_line_face_pile_protection_width">1dp</dimen>
+
<!-- The minimum amount of top overscroll to go to the quick settings. -->
<dimen name="min_top_overscroll_to_qs">36dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index d639ed0..9f0f482 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1021,6 +1021,9 @@
<!-- QuickSettings: Text to prompt the user to stop an ongoing recording [CHAR LIMIT=20] -->
<string name="quick_settings_screen_record_stop">Stop</string>
+ <!-- Default name for the media device shown in the output switcher when the name is not available [CHAR LIMIT=30] -->
+ <string name="media_seamless_remote_device">Device</string>
+
<!-- Recents: Text that shows above the navigation bar after launching a few apps. [CHAR LIMIT=NONE] -->
<string name="recents_swipe_up_onboarding">Swipe up to switch apps</string>
<!-- Recents: Text that shows above the navigation bar after launching several apps. [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 26ae790..304cf85 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -475,7 +475,6 @@
<item name="android:textColor">?android:attr/textColorTertiary</item>
</style>
-
<style name="VolumeButtons" parent="@android:style/Widget.Material.Button.Borderless">
<item name="android:background">@drawable/btn_borderless_rect</item>
</style>
@@ -670,6 +669,7 @@
<item name="android:colorBackground">@android:color/black</item>
<item name="android:windowAnimationStyle">@null</item>
<item name="android:statusBarColor">@*android:color/transparent</item>
+ <item name="wallpaperTextColor">@*android:color/primary_text_material_dark</item>
</style>
<style name="TextAppearance.Control">
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
index b8997c2..ed9c98b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
@@ -257,12 +257,8 @@
if (DEBUG) Log.d(TAG, "queryAll " + mAction);
for (int i = mPlugins.size() - 1; i >= 0; i--) {
PluginInfo<T> plugin = mPlugins.get(i);
- mListener.onPluginDisconnected(plugin.mPlugin);
- if (!(plugin.mPlugin instanceof PluginFragment)) {
- // Only call onDestroy for plugins that aren't fragments, as fragments
- // will get the onDestroy as part of the fragment lifecycle.
- plugin.mPlugin.onDestroy();
- }
+ mMainHandler.obtainMessage(
+ mMainHandler.PLUGIN_DISCONNECTED, plugin).sendToTarget();
}
mPlugins.clear();
handleQueryPlugins(null);
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
index 9e9b9dc..dd5cc7c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
@@ -38,7 +38,7 @@
public void onActivityPinned(String packageName, int userId, int taskId, int stackId) { }
public void onActivityUnpinned() { }
public void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible,
- boolean clearedTask) { }
+ boolean clearedTask, boolean wasVisible) { }
public void onActivityForcedResizable(String packageName, int taskId, int reason) { }
public void onActivityDismissingDockedStack() { }
public void onActivityLaunchOnSecondaryDisplayFailed() { }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
index ce9cbab..a76a901 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
@@ -122,11 +122,12 @@
@Override
public void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible,
- boolean clearedTask) throws RemoteException {
+ boolean clearedTask, boolean wasVisible) throws RemoteException {
final SomeArgs args = SomeArgs.obtain();
args.arg1 = task;
args.argi1 = homeTaskVisible ? 1 : 0;
args.argi2 = clearedTask ? 1 : 0;
+ args.argi3 = wasVisible ? 1 : 0;
mHandler.removeMessages(H.ON_ACTIVITY_RESTART_ATTEMPT);
mHandler.obtainMessage(H.ON_ACTIVITY_RESTART_ATTEMPT, args).sendToTarget();
}
@@ -305,9 +306,10 @@
final RunningTaskInfo task = (RunningTaskInfo) args.arg1;
final boolean homeTaskVisible = args.argi1 != 0;
final boolean clearedTask = args.argi2 != 0;
+ final boolean wasVisible = args.argi3 != 0;
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
mTaskStackListeners.get(i).onActivityRestartAttempt(task,
- homeTaskVisible, clearedTask);
+ homeTaskVisible, clearedTask, wasVisible);
}
break;
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
index 1f27ae2..73dfd32 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
@@ -18,6 +18,8 @@
import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_GLOBAL_ACTIONS;
+import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME;
+
import android.accessibilityservice.AccessibilityService;
import android.app.PendingIntent;
import android.app.RemoteAction;
@@ -25,6 +27,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.res.Configuration;
import android.graphics.drawable.Icon;
import android.hardware.input.InputManager;
import android.os.Handler;
@@ -32,6 +35,7 @@
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.util.Log;
import android.view.Display;
import android.view.IWindowManager;
@@ -43,12 +47,15 @@
import android.view.accessibility.AccessibilityManager;
import com.android.internal.R;
+import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity;
import com.android.internal.util.ScreenshotHelper;
import com.android.systemui.Dependency;
import com.android.systemui.SystemUI;
import com.android.systemui.recents.Recents;
import com.android.systemui.statusbar.phone.StatusBar;
+import java.util.Locale;
+
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -58,7 +65,6 @@
@Singleton
public class SystemActions extends SystemUI {
private static final String TAG = "SystemActions";
- // TODO(b/147916452): add implementation on launcher side to register this action.
/**
* Action ID to go back.
@@ -96,12 +102,6 @@
AccessibilityService.GLOBAL_ACTION_POWER_DIALOG; // = 6
/**
- * Action ID to toggle docking the current app's window
- */
- private static final int SYSTEM_ACTION_ID_TOGGLE_SPLIT_SCREEN =
- AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN; // = 7
-
- /**
* Action ID to lock the screen
*/
private static final int SYSTEM_ACTION_ID_LOCK_SCREEN =
@@ -114,13 +114,22 @@
AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT; // = 9
/**
- * Action ID to show accessibility menu
+ * Action ID to trigger the accessibility button
*/
- private static final int SYSTEM_ACTION_ID_ACCESSIBILITY_MENU = 10;
+ public static final int SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON =
+ AccessibilityService.GLOBAL_ACTION_ACCESSIBILITY_BUTTON; // 11
+
+ /**
+ * Action ID to show accessibility button's menu of services
+ */
+ public static final int SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON_CHOOSER =
+ AccessibilityService.GLOBAL_ACTION_ACCESSIBILITY_BUTTON_CHOOSER; // 12
private Recents mRecents;
private StatusBar mStatusBar;
private SystemActionsBroadcastReceiver mReceiver;
+ private Locale mLocale;
+ private AccessibilityManager mA11yManager;
@Inject
public SystemActions(Context context) {
@@ -128,96 +137,139 @@
mRecents = Dependency.get(Recents.class);
mStatusBar = Dependency.get(StatusBar.class);
mReceiver = new SystemActionsBroadcastReceiver();
+ mLocale = mContext.getResources().getConfiguration().getLocales().get(0);
+ mA11yManager = (AccessibilityManager) mContext.getSystemService(
+ Context.ACCESSIBILITY_SERVICE);
}
@Override
public void start() {
mContext.registerReceiverForAllUsers(mReceiver, mReceiver.createIntentFilter(), null, null);
+ registerActions();
+ }
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ final Locale locale = mContext.getResources().getConfiguration().getLocales().get(0);
+ if (!locale.equals(mLocale)) {
+ mLocale = locale;
+ registerActions();
+ }
+ }
+
+ private void registerActions() {
+ RemoteAction actionBack = createRemoteAction(
+ R.string.accessibility_system_action_back_label,
+ SystemActionsBroadcastReceiver.INTENT_ACTION_BACK);
+
+ RemoteAction actionHome = createRemoteAction(
+ R.string.accessibility_system_action_home_label,
+ SystemActionsBroadcastReceiver.INTENT_ACTION_HOME);
+
+ RemoteAction actionRecents = createRemoteAction(
+ R.string.accessibility_system_action_recents_label,
+ SystemActionsBroadcastReceiver.INTENT_ACTION_RECENTS);
+
+ RemoteAction actionNotifications = createRemoteAction(
+ R.string.accessibility_system_action_notifications_label,
+ SystemActionsBroadcastReceiver.INTENT_ACTION_NOTIFICATIONS);
+
+ RemoteAction actionQuickSettings = createRemoteAction(
+ R.string.accessibility_system_action_quick_settings_label,
+ SystemActionsBroadcastReceiver.INTENT_ACTION_QUICK_SETTINGS);
+
+ RemoteAction actionPowerDialog = createRemoteAction(
+ R.string.accessibility_system_action_power_dialog_label,
+ SystemActionsBroadcastReceiver.INTENT_ACTION_POWER_DIALOG);
+
+ RemoteAction actionLockScreen = createRemoteAction(
+ R.string.accessibility_system_action_lock_screen_label,
+ SystemActionsBroadcastReceiver.INTENT_ACTION_LOCK_SCREEN);
+
+ RemoteAction actionTakeScreenshot = createRemoteAction(
+ R.string.accessibility_system_action_screenshot_label,
+ SystemActionsBroadcastReceiver.INTENT_ACTION_TAKE_SCREENSHOT);
+
+ mA11yManager.registerSystemAction(actionBack, SYSTEM_ACTION_ID_BACK);
+ mA11yManager.registerSystemAction(actionHome, SYSTEM_ACTION_ID_HOME);
+ mA11yManager.registerSystemAction(actionRecents, SYSTEM_ACTION_ID_RECENTS);
+ mA11yManager.registerSystemAction(actionNotifications, SYSTEM_ACTION_ID_NOTIFICATIONS);
+ mA11yManager.registerSystemAction(actionQuickSettings, SYSTEM_ACTION_ID_QUICK_SETTINGS);
+ mA11yManager.registerSystemAction(actionPowerDialog, SYSTEM_ACTION_ID_POWER_DIALOG);
+ mA11yManager.registerSystemAction(actionLockScreen, SYSTEM_ACTION_ID_LOCK_SCREEN);
+ mA11yManager.registerSystemAction(actionTakeScreenshot, SYSTEM_ACTION_ID_TAKE_SCREENSHOT);
+ }
+
+ /**
+ * Register a system action.
+ * @param actionId the action ID to register.
+ */
+ public void register(int actionId) {
+ int labelId;
+ String intent;
+ switch (actionId) {
+ case SYSTEM_ACTION_ID_BACK:
+ labelId = R.string.accessibility_system_action_back_label;
+ intent = SystemActionsBroadcastReceiver.INTENT_ACTION_BACK;
+ break;
+ case SYSTEM_ACTION_ID_HOME:
+ labelId = R.string.accessibility_system_action_home_label;
+ intent = SystemActionsBroadcastReceiver.INTENT_ACTION_HOME;
+ break;
+ case SYSTEM_ACTION_ID_RECENTS:
+ labelId = R.string.accessibility_system_action_recents_label;
+ intent = SystemActionsBroadcastReceiver.INTENT_ACTION_RECENTS;
+ break;
+ case SYSTEM_ACTION_ID_NOTIFICATIONS:
+ labelId = R.string.accessibility_system_action_notifications_label;
+ intent = SystemActionsBroadcastReceiver.INTENT_ACTION_NOTIFICATIONS;
+ break;
+ case SYSTEM_ACTION_ID_QUICK_SETTINGS:
+ labelId = R.string.accessibility_system_action_quick_settings_label;
+ intent = SystemActionsBroadcastReceiver.INTENT_ACTION_QUICK_SETTINGS;
+ break;
+ case SYSTEM_ACTION_ID_POWER_DIALOG:
+ labelId = R.string.accessibility_system_action_power_dialog_label;
+ intent = SystemActionsBroadcastReceiver.INTENT_ACTION_POWER_DIALOG;
+ break;
+ case SYSTEM_ACTION_ID_LOCK_SCREEN:
+ labelId = R.string.accessibility_system_action_lock_screen_label;
+ intent = SystemActionsBroadcastReceiver.INTENT_ACTION_LOCK_SCREEN;
+ break;
+ case SYSTEM_ACTION_ID_TAKE_SCREENSHOT:
+ labelId = R.string.accessibility_system_action_screenshot_label;
+ intent = SystemActionsBroadcastReceiver.INTENT_ACTION_TAKE_SCREENSHOT;
+ break;
+ case SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON:
+ labelId = R.string.accessibility_system_action_accessibility_button_label;
+ intent = SystemActionsBroadcastReceiver.INTENT_ACTION_ACCESSIBILITY_BUTTON;
+ break;
+ case SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON_CHOOSER:
+ labelId = R.string.accessibility_system_action_accessibility_button_chooser_label;
+ intent = SystemActionsBroadcastReceiver.INTENT_ACTION_ACCESSIBILITY_BUTTON_CHOOSER;
+ break;
+ default:
+ return;
+ }
+ mA11yManager.registerSystemAction(createRemoteAction(labelId, intent), actionId);
+ }
+
+ private RemoteAction createRemoteAction(int labelId, String intent) {
// TODO(b/148087487): update the icon used below to a valid one
- RemoteAction actionBack = new RemoteAction(
+ return new RemoteAction(
Icon.createWithResource(mContext, R.drawable.ic_info),
- mContext.getString(R.string.accessibility_system_action_back_label),
- mContext.getString(R.string.accessibility_system_action_back_label),
- mReceiver.createPendingIntent(
- mContext, SystemActionsBroadcastReceiver.INTENT_ACTION_BACK));
- RemoteAction actionHome = new RemoteAction(
- Icon.createWithResource(mContext, R.drawable.ic_info),
- mContext.getString(R.string.accessibility_system_action_home_label),
- mContext.getString(R.string.accessibility_system_action_home_label),
- mReceiver.createPendingIntent(
- mContext, SystemActionsBroadcastReceiver.INTENT_ACTION_HOME));
+ mContext.getString(labelId),
+ mContext.getString(labelId),
+ mReceiver.createPendingIntent(mContext, intent));
+ }
- RemoteAction actionRecents = new RemoteAction(
- Icon.createWithResource(mContext, R.drawable.ic_info),
- mContext.getString(R.string.accessibility_system_action_recents_label),
- mContext.getString(R.string.accessibility_system_action_recents_label),
- mReceiver.createPendingIntent(
- mContext, SystemActionsBroadcastReceiver.INTENT_ACTION_RECENTS));
-
- RemoteAction actionNotifications = new RemoteAction(
- Icon.createWithResource(mContext, R.drawable.ic_info),
- mContext.getString(R.string.accessibility_system_action_notifications_label),
- mContext.getString(R.string.accessibility_system_action_notifications_label),
- mReceiver.createPendingIntent(
- mContext, SystemActionsBroadcastReceiver.INTENT_ACTION_NOTIFICATIONS));
-
- RemoteAction actionQuickSettings = new RemoteAction(
- Icon.createWithResource(mContext, R.drawable.ic_info),
- mContext.getString(R.string.accessibility_system_action_quick_settings_label),
- mContext.getString(R.string.accessibility_system_action_quick_settings_label),
- mReceiver.createPendingIntent(
- mContext, SystemActionsBroadcastReceiver.INTENT_ACTION_QUICK_SETTINGS));
-
- RemoteAction actionPowerDialog = new RemoteAction(
- Icon.createWithResource(mContext, R.drawable.ic_info),
- mContext.getString(R.string.accessibility_system_action_power_dialog_label),
- mContext.getString(R.string.accessibility_system_action_power_dialog_label),
- mReceiver.createPendingIntent(
- mContext, SystemActionsBroadcastReceiver.INTENT_ACTION_POWER_DIALOG));
-
- RemoteAction actionToggleSplitScreen = new RemoteAction(
- Icon.createWithResource(mContext, R.drawable.ic_info),
- mContext.getString(R.string.accessibility_system_action_toggle_split_screen_label),
- mContext.getString(R.string.accessibility_system_action_toggle_split_screen_label),
- mReceiver.createPendingIntent(
- mContext,
- SystemActionsBroadcastReceiver.INTENT_ACTION_TOGGLE_SPLIT_SCREEN));
-
- RemoteAction actionLockScreen = new RemoteAction(
- Icon.createWithResource(mContext, R.drawable.ic_info),
- mContext.getString(R.string.accessibility_system_action_lock_screen_label),
- mContext.getString(R.string.accessibility_system_action_lock_screen_label),
- mReceiver.createPendingIntent(
- mContext, SystemActionsBroadcastReceiver.INTENT_ACTION_LOCK_SCREEN));
-
- RemoteAction actionTakeScreenshot = new RemoteAction(
- Icon.createWithResource(mContext, R.drawable.ic_info),
- mContext.getString(R.string.accessibility_system_action_screenshot_label),
- mContext.getString(R.string.accessibility_system_action_screenshot_label),
- mReceiver.createPendingIntent(
- mContext, SystemActionsBroadcastReceiver.INTENT_ACTION_TAKE_SCREENSHOT));
-
- RemoteAction actionAccessibilityMenu = new RemoteAction(
- Icon.createWithResource(mContext, R.drawable.ic_info),
- mContext.getString(R.string.accessibility_system_action_accessibility_menu_label),
- mContext.getString(R.string.accessibility_system_action_accessibility_menu_label),
- mReceiver.createPendingIntent(
- mContext, SystemActionsBroadcastReceiver.INTENT_ACTION_ACCESSIBILITY_MENU));
-
- AccessibilityManager am = (AccessibilityManager) mContext.getSystemService(
- Context.ACCESSIBILITY_SERVICE);
-
- am.registerSystemAction(actionBack, SYSTEM_ACTION_ID_BACK);
- am.registerSystemAction(actionHome, SYSTEM_ACTION_ID_HOME);
- am.registerSystemAction(actionRecents, SYSTEM_ACTION_ID_RECENTS);
- am.registerSystemAction(actionNotifications, SYSTEM_ACTION_ID_NOTIFICATIONS);
- am.registerSystemAction(actionQuickSettings, SYSTEM_ACTION_ID_QUICK_SETTINGS);
- am.registerSystemAction(actionPowerDialog, SYSTEM_ACTION_ID_POWER_DIALOG);
- am.registerSystemAction(actionToggleSplitScreen, SYSTEM_ACTION_ID_TOGGLE_SPLIT_SCREEN);
- am.registerSystemAction(actionLockScreen, SYSTEM_ACTION_ID_LOCK_SCREEN);
- am.registerSystemAction(actionTakeScreenshot, SYSTEM_ACTION_ID_TAKE_SCREENSHOT);
- am.registerSystemAction(actionAccessibilityMenu, SYSTEM_ACTION_ID_ACCESSIBILITY_MENU);
+ /**
+ * Unregister a system action.
+ * @param actionId the action ID to unregister.
+ */
+ public void unregister(int actionId) {
+ mA11yManager.unregisterSystemAction(actionId);
}
private void handleBack() {
@@ -266,10 +318,6 @@
}
}
- private void handleToggleSplitScreen() {
- mStatusBar.toggleSplitScreen();
- }
-
private void handleLockScreen() {
IWindowManager windowManager = WindowManagerGlobal.getWindowManagerService();
@@ -288,11 +336,19 @@
SCREENSHOT_GLOBAL_ACTIONS, new Handler(Looper.getMainLooper()), null);
}
- private void handleAccessibilityMenu() {
+ private void handleAccessibilityButton() {
AccessibilityManager.getInstance(mContext).notifyAccessibilityButtonClicked(
Display.DEFAULT_DISPLAY);
}
+ private void handleAccessibilityButtonChooser() {
+ final Intent intent = new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ final String chooserClassName = AccessibilityButtonChooserActivity.class.getName();
+ intent.setClassName(CHOOSER_PACKAGE_NAME, chooserClassName);
+ mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+ }
+
private class SystemActionsBroadcastReceiver extends BroadcastReceiver {
private static final String INTENT_ACTION_BACK = "SYSTEM_ACTION_BACK";
private static final String INTENT_ACTION_HOME = "SYSTEM_ACTION_HOME";
@@ -300,12 +356,12 @@
private static final String INTENT_ACTION_NOTIFICATIONS = "SYSTEM_ACTION_NOTIFICATIONS";
private static final String INTENT_ACTION_QUICK_SETTINGS = "SYSTEM_ACTION_QUICK_SETTINGS";
private static final String INTENT_ACTION_POWER_DIALOG = "SYSTEM_ACTION_POWER_DIALOG";
- private static final String INTENT_ACTION_TOGGLE_SPLIT_SCREEN =
- "SYSTEM_ACTION_TOGGLE_SPLIT_SCREEN";
private static final String INTENT_ACTION_LOCK_SCREEN = "SYSTEM_ACTION_LOCK_SCREEN";
private static final String INTENT_ACTION_TAKE_SCREENSHOT = "SYSTEM_ACTION_TAKE_SCREENSHOT";
- private static final String INTENT_ACTION_ACCESSIBILITY_MENU =
- "SYSTEM_ACTION_ACCESSIBILITY_MENU";
+ private static final String INTENT_ACTION_ACCESSIBILITY_BUTTON =
+ "SYSTEM_ACTION_ACCESSIBILITY_BUTTON";
+ private static final String INTENT_ACTION_ACCESSIBILITY_BUTTON_CHOOSER =
+ "SYSTEM_ACTION_ACCESSIBILITY_BUTTON_MENU";
private PendingIntent createPendingIntent(Context context, String intentAction) {
switch (intentAction) {
@@ -315,10 +371,10 @@
case INTENT_ACTION_NOTIFICATIONS:
case INTENT_ACTION_QUICK_SETTINGS:
case INTENT_ACTION_POWER_DIALOG:
- case INTENT_ACTION_TOGGLE_SPLIT_SCREEN:
case INTENT_ACTION_LOCK_SCREEN:
case INTENT_ACTION_TAKE_SCREENSHOT:
- case INTENT_ACTION_ACCESSIBILITY_MENU: {
+ case INTENT_ACTION_ACCESSIBILITY_BUTTON:
+ case INTENT_ACTION_ACCESSIBILITY_BUTTON_CHOOSER: {
Intent intent = new Intent(intentAction);
return PendingIntent.getBroadcast(context, 0, intent, 0);
}
@@ -336,10 +392,10 @@
intentFilter.addAction(INTENT_ACTION_NOTIFICATIONS);
intentFilter.addAction(INTENT_ACTION_QUICK_SETTINGS);
intentFilter.addAction(INTENT_ACTION_POWER_DIALOG);
- intentFilter.addAction(INTENT_ACTION_TOGGLE_SPLIT_SCREEN);
intentFilter.addAction(INTENT_ACTION_LOCK_SCREEN);
intentFilter.addAction(INTENT_ACTION_TAKE_SCREENSHOT);
- intentFilter.addAction(INTENT_ACTION_ACCESSIBILITY_MENU);
+ intentFilter.addAction(INTENT_ACTION_ACCESSIBILITY_BUTTON);
+ intentFilter.addAction(INTENT_ACTION_ACCESSIBILITY_BUTTON_CHOOSER);
return intentFilter;
}
@@ -371,10 +427,6 @@
handlePowerDialog();
break;
}
- case INTENT_ACTION_TOGGLE_SPLIT_SCREEN: {
- handleToggleSplitScreen();
- break;
- }
case INTENT_ACTION_LOCK_SCREEN: {
handleLockScreen();
break;
@@ -383,8 +435,12 @@
handleTakeScreenshot();
break;
}
- case INTENT_ACTION_ACCESSIBILITY_MENU: {
- handleAccessibilityMenu();
+ case INTENT_ACTION_ACCESSIBILITY_BUTTON: {
+ handleAccessibilityButton();
+ break;
+ }
+ case INTENT_ACTION_ACCESSIBILITY_BUTTON_CHOOSER: {
+ handleAccessibilityButtonChooser();
break;
}
default:
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 4d7eb75..e8fd7e0 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -669,8 +669,11 @@
mStackView.onThemeChanged();
}
mBubbleIconFactory = new BubbleIconFactory(mContext);
+ // Reload each bubble
for (Bubble b: mBubbleData.getBubbles()) {
- // Reload each bubble
+ b.inflate(null /* callback */, mContext, mStackView, mBubbleIconFactory);
+ }
+ for (Bubble b: mBubbleData.getOverflowBubbles()) {
b.inflate(null /* callback */, mContext, mStackView, mBubbleIconFactory);
}
}
@@ -1236,7 +1239,7 @@
@Override
public void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible,
- boolean clearedTask) {
+ boolean clearedTask, boolean wasVisible) {
for (Bubble b : mBubbleData.getBubbles()) {
if (b.getDisplayId() == task.displayId) {
expandStackAndSelectBubble(b.getKey());
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 9a2ccb5..25271e1 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
@@ -16,6 +16,8 @@
package com.android.systemui.controls.management
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
import android.app.ActivityOptions
import android.content.ComponentName
import android.content.Intent
@@ -163,7 +165,23 @@
pageIndicator.visibility =
if (listOfStructures.size > 1) View.VISIBLE else View.GONE
- ControlsAnimations.enterAnimation(pageIndicator).start()
+ ControlsAnimations.enterAnimation(pageIndicator).apply {
+ addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator?) {
+ // Position the tooltip if necessary after animations are complete
+ // so we can get the position on screen. The tooltip is not
+ // rooted in the layout root.
+ if (pageIndicator.visibility == View.VISIBLE &&
+ mTooltipManager != null) {
+ val p = IntArray(2)
+ pageIndicator.getLocationOnScreen(p)
+ val x = p[0] + pageIndicator.width / 2
+ val y = p[1] + pageIndicator.height
+ mTooltipManager?.show(R.string.controls_structure_tooltip, x, y)
+ }
+ }
+ })
+ }.start()
ControlsAnimations.enterAnimation(structurePager).start()
}
}, Consumer { runnable -> cancelLoadRunnable = runnable })
@@ -225,27 +243,6 @@
}
pageIndicator = requireViewById<ManagementPageIndicator>(
R.id.structure_page_indicator).apply {
- addOnLayoutChangeListener(object : View.OnLayoutChangeListener {
- override fun onLayoutChange(
- v: View,
- left: Int,
- top: Int,
- right: Int,
- bottom: Int,
- oldLeft: Int,
- oldTop: Int,
- oldRight: Int,
- oldBottom: Int
- ) {
- if (v.visibility == View.VISIBLE && mTooltipManager != null) {
- val p = IntArray(2)
- v.getLocationOnScreen(p)
- val x = p[0] + (right - left) / 2
- val y = p[1] + bottom - top
- mTooltipManager?.show(R.string.controls_structure_tooltip, x, y)
- }
- }
- })
visibilityListener = {
if (it != View.VISIBLE) {
mTooltipManager?.hide(true)
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 42db005..f3693c1 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -536,7 +536,7 @@
override fun hide() {
Log.d(ControlsUiController.TAG, "hide()")
hidden = true
- popup?.dismiss()
+ popup?.dismissImmediate()
activeDialog?.dismiss()
ControlActionCoordinator.closeDialog()
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java b/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
index 3f88f25..554457b 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java
@@ -75,6 +75,12 @@
public void onEvent(int dockState) {
if (DEBUG) Log.d(TAG, "dock event = " + dockState);
+ // Only act upon state changes, otherwise we might overwrite other transitions,
+ // like proximity sensor initialization.
+ if (mDockState == dockState) {
+ return;
+ }
+
mDockState = dockState;
if (isPulsing()) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index 18bfd89..490890f 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -159,6 +159,15 @@
mDozeHost = dozeHost;
}
+ /**
+ * Clean ourselves up.
+ */
+ public void destroy() {
+ for (Part part : mParts) {
+ part.destroy();
+ }
+ }
+
/** Initializes the set of {@link Part}s. Must be called exactly once after construction. */
public void setParts(Part[] parts) {
Preconditions.checkState(mParts == null);
@@ -411,6 +420,9 @@
/** Dump current state. For debugging only. */
default void dump(PrintWriter pw) {}
+
+ /** Give the Part a chance to clean itself up. */
+ default void destroy() {}
}
/** A wrapper interface for {@link android.service.dreams.DreamService} */
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 700a861..10776c9 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -164,6 +164,17 @@
}
/**
+ * Unregister any sensors.
+ */
+ public void destroy() {
+ // Unregisters everything, which is enough to allow gc.
+ for (TriggerSensor triggerSensor : mSensors) {
+ triggerSensor.setListening(false);
+ }
+ mProximitySensor.pause();
+ }
+
+ /**
* Temporarily disable some sensors to avoid turning on the device while the user is
* turning it off.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
index 7cbbdd7..529b016 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
@@ -65,6 +65,7 @@
mPluginManager.removePluginListener(this);
}
super.onDestroy();
+ mDozeMachine.destroy();
mDozeMachine = null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index b329991..1be4d43 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -111,6 +111,11 @@
mBroadcastDispatcher = broadcastDispatcher;
}
+ @Override
+ public void destroy() {
+ mDozeSensors.destroy();
+ }
+
private void onNotification(Runnable onPulseSuppressedListener) {
if (DozeMachine.DEBUG) {
Log.d(TAG, "requestNotificationPulse");
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 6aa4f86..9bd32ff 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -484,8 +484,7 @@
*/
@VisibleForTesting
protected int getMaxShownPowerItems() {
- // TODO: Overflow disabled on keyguard while we solve for touch blocking issues.
- if (shouldUseControlsLayout() && !mKeyguardShowing) {
+ if (shouldUseControlsLayout()) {
return mResources.getInteger(com.android.systemui.R.integer.power_menu_max_columns);
} else {
return Integer.MAX_VALUE;
@@ -2246,7 +2245,7 @@
mShowing = false;
resetOrientation();
dismissPanel();
- dismissOverflow();
+ dismissOverflow(true);
if (mControlsUiController != null) mControlsUiController.hide();
mNotificationShadeWindowController.setForceHasTopUi(mHadTopUi);
mDepthController.updateGlobalDialogVisibility(0, null /* view */);
@@ -2259,9 +2258,13 @@
}
}
- private void dismissOverflow() {
+ private void dismissOverflow(boolean immediate) {
if (mOverflowPopup != null) {
- mOverflowPopup.dismiss();
+ if (immediate) {
+ mOverflowPopup.dismissImmediate();
+ } else {
+ mOverflowPopup.dismiss();
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 1012a52..b26dc5f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -1717,9 +1717,9 @@
resetKeyguardDonePendingLocked();
}
- mUpdateMonitor.clearBiometricRecognized();
if (mGoingToSleep) {
+ mUpdateMonitor.clearBiometricRecognized();
Log.i(TAG, "Device is going to sleep, aborting keyguardDone");
return;
}
@@ -1740,6 +1740,7 @@
}
handleHide();
+ mUpdateMonitor.clearBiometricRecognized();
Trace.endSection();
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index 683c793..b12d02d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -19,6 +19,7 @@
import android.annotation.LayoutRes;
import android.app.PendingIntent;
import android.content.ComponentName;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
@@ -35,10 +36,12 @@
import android.media.MediaMetadata;
import android.media.ThumbnailUtils;
import android.media.session.MediaController;
+import android.media.session.MediaController.PlaybackInfo;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
import android.net.Uri;
import android.service.media.MediaBrowserService;
+import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -94,6 +97,7 @@
public static final String MEDIA_PREFERENCE_KEY = "browser_components";
private SharedPreferences mSharedPrefs;
private boolean mCheckedForResumption = false;
+ private boolean mIsRemotePlayback;
// Button IDs used in notifications
protected static final int[] NOTIF_ACTION_IDS = {
@@ -245,7 +249,7 @@
// Try to find a browser service component for this app
// TODO also check for a media button receiver intended for restarting (b/154127084)
// Only check if we haven't tried yet or the session token changed
- String pkgName = mController.getPackageName();
+ final String pkgName = mController.getPackageName();
if (mServiceComponent == null && !mCheckedForResumption) {
Log.d(TAG, "Checking for service component");
PackageManager pm = mContext.getPackageManager();
@@ -281,18 +285,29 @@
// Transfer chip
mSeamless = mMediaNotifView.findViewById(R.id.media_seamless);
- if (mSeamless != null && mLocalMediaManager != null) {
- mSeamless.setVisibility(View.VISIBLE);
- updateDevice(mLocalMediaManager.getCurrentConnectedDevice());
- mSeamless.setOnClickListener(v -> {
- final Intent intent = new Intent()
- .setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT)
- .putExtra(MediaOutputSliceConstants.EXTRA_PACKAGE_NAME,
- mController.getPackageName())
- .putExtra(MediaOutputSliceConstants.KEY_MEDIA_SESSION_TOKEN, mToken);
- mActivityStarter.startActivity(intent, false, true /* dismissShade */,
- Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- });
+ if (mSeamless != null) {
+ if (mLocalMediaManager != null) {
+ mSeamless.setVisibility(View.VISIBLE);
+ updateDevice(mLocalMediaManager.getCurrentConnectedDevice());
+ mSeamless.setOnClickListener(v -> {
+ final Intent intent = new Intent()
+ .setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT)
+ .putExtra(MediaOutputSliceConstants.EXTRA_PACKAGE_NAME,
+ mController.getPackageName())
+ .putExtra(MediaOutputSliceConstants.KEY_MEDIA_SESSION_TOKEN, mToken);
+ mActivityStarter.startActivity(intent, false, true /* dismissShade */,
+ Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ });
+ } else {
+ Log.d(TAG, "LocalMediaManager is null. Not binding output chip for pkg=" + pkgName);
+ }
+ }
+ PlaybackInfo playbackInfo = mController.getPlaybackInfo();
+ if (playbackInfo != null) {
+ mIsRemotePlayback = playbackInfo.getPlaybackType() == PlaybackInfo.PLAYBACK_TYPE_REMOTE;
+ } else {
+ Log.d(TAG, "PlaybackInfo was null. Defaulting to local playback.");
+ mIsRemotePlayback = false;
}
makeActive();
@@ -431,7 +446,7 @@
// First look in URI fields
for (String field : ART_URIS) {
String uriString = metadata.getString(field);
- if (uriString != null) {
+ if (!TextUtils.isEmpty(uriString)) {
albumArt = loadBitmapFromUri(Uri.parse(uriString));
if (albumArt != null) {
Log.d(TAG, "loaded art from " + field);
@@ -459,6 +474,17 @@
* @return bitmap, or null if couldn't be loaded
*/
private Bitmap loadBitmapFromUri(Uri uri) {
+ // ImageDecoder requires a scheme of the following types
+ if (uri.getScheme() == null) {
+ return null;
+ }
+
+ if (!uri.getScheme().equals(ContentResolver.SCHEME_CONTENT)
+ && !uri.getScheme().equals(ContentResolver.SCHEME_ANDROID_RESOURCE)
+ && !uri.getScheme().equals(ContentResolver.SCHEME_FILE)) {
+ return null;
+ }
+
ImageDecoder.Source source = ImageDecoder.createSource(mContext.getContentResolver(), uri);
try {
return ImageDecoder.decodeBitmap(source);
@@ -528,7 +554,16 @@
TextView deviceName = mSeamless.findViewById(R.id.media_seamless_text);
deviceName.setTextColor(fgTintList);
- if (device != null) {
+ if (mIsRemotePlayback) {
+ mSeamless.setEnabled(false);
+ mSeamless.setAlpha(0.38f);
+ iconView.setImageResource(R.drawable.ic_hardware_speaker);
+ iconView.setVisibility(View.VISIBLE);
+ iconView.setImageTintList(fgTintList);
+ deviceName.setText(R.string.media_seamless_remote_device);
+ } else if (device != null) {
+ mSeamless.setEnabled(true);
+ mSeamless.setAlpha(1f);
Drawable icon = device.getIcon();
iconView.setVisibility(View.VISIBLE);
iconView.setImageTintList(fgTintList);
@@ -543,6 +578,9 @@
deviceName.setText(device.getName());
} else {
// Reset to default
+ Log.d(TAG, "device is null. Not binding output chip.");
+ mSeamless.setEnabled(true);
+ mSeamless.setAlpha(1f);
iconView.setVisibility(View.GONE);
deviceName.setText(com.android.internal.R.string.ext_media_seamless_action);
}
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 7897573..a86a884 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -135,8 +135,8 @@
@Override
public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
- boolean homeTaskVisible, boolean clearedTask) {
- if (task.configuration.windowConfiguration.getWindowingMode()
+ boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
+ if (!wasVisible || task.configuration.windowConfiguration.getWindowingMode()
!= WINDOWING_MODE_PINNED) {
return;
}
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 6c5312d..fae8af4 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -708,15 +708,15 @@
mActiveMediaSessionListener, null);
updateMediaController(mMediaSessionManager.getActiveSessions(null));
for (int i = mListeners.size() - 1; i >= 0; i--) {
- mListeners.get(i).onPipEntered();
+ mListeners.get(i).onPipEntered(packageName);
}
updatePipVisibility(true);
}
@Override
public void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible,
- boolean clearedTask) {
- if (task.configuration.windowConfiguration.getWindowingMode()
+ boolean clearedTask, boolean wasVisible) {
+ if (!wasVisible || task.configuration.windowConfiguration.getWindowingMode()
!= WINDOWING_MODE_PINNED) {
return;
}
@@ -758,7 +758,7 @@
* because there's no guarantee for the PIP manager be return relavent information
* correctly. (e.g. {@link isPipShown}).
*/
- void onPipEntered();
+ void onPipEntered(String packageName);
/** Invoked when a PIPed activity is closed. */
void onPipActivityClosed();
/** Invoked when the PIP menu gets shown. */
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java
index c7e77cc..158be45 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java
@@ -137,8 +137,8 @@
}
@Override
- public void onPipEntered() {
- if (DEBUG) Log.d(TAG, "onPipEntered()");
+ public void onPipEntered(String packageName) {
+ if (DEBUG) Log.d(TAG, "onPipEntered(), packageName=" + packageName);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java
index b01c2f4..30ec296 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java
@@ -23,6 +23,8 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -50,6 +52,8 @@
private static final String ACTION_MENU = "PipNotification.menu";
private static final String ACTION_CLOSE = "PipNotification.close";
+ private final PackageManager mPackageManager;
+
private final PipManager mPipManager;
private final NotificationManager mNotificationManager;
@@ -59,13 +63,16 @@
private String mDefaultTitle;
private int mDefaultIconResId;
+ /** Package name for the application that owns PiP window. */
+ private String mPackageName;
private boolean mNotified;
- private String mTitle;
+ private String mMediaTitle;
private Bitmap mArt;
private PipManager.Listener mPipListener = new PipManager.Listener() {
@Override
- public void onPipEntered() {
+ public void onPipEntered(String packageName) {
+ mPackageName = packageName;
updateMediaControllerMetadata();
notifyPipNotification();
}
@@ -73,6 +80,7 @@
@Override
public void onPipActivityClosed() {
dismissPipNotification();
+ mPackageName = null;
}
@Override
@@ -88,6 +96,7 @@
@Override
public void onMoveToFullscreen() {
dismissPipNotification();
+ mPackageName = null;
}
@Override
@@ -146,6 +155,8 @@
public PipNotification(Context context, BroadcastDispatcher broadcastDispatcher,
PipManager pipManager) {
+ mPackageManager = context.getPackageManager();
+
mNotificationManager = (NotificationManager) context.getSystemService(
Context.NOTIFICATION_SERVICE);
@@ -188,7 +199,7 @@
.setShowWhen(true)
.setWhen(System.currentTimeMillis())
.setSmallIcon(mDefaultIconResId)
- .setContentTitle(!TextUtils.isEmpty(mTitle) ? mTitle : mDefaultTitle);
+ .setContentTitle(getNotificationTitle());
if (mArt != null) {
mNotificationBuilder.setStyle(new Notification.BigPictureStyle()
.bigPicture(mArt));
@@ -220,14 +231,36 @@
}
}
}
- if (!TextUtils.equals(title, mTitle) || art != mArt) {
- mTitle = title;
+ if (!TextUtils.equals(title, mMediaTitle) || art != mArt) {
+ mMediaTitle = title;
mArt = art;
return true;
}
return false;
}
+ private String getNotificationTitle() {
+ if (!TextUtils.isEmpty(mMediaTitle)) {
+ return mMediaTitle;
+ }
+
+ final String applicationTitle = getApplicationLabel(mPackageName);
+ if (!TextUtils.isEmpty(applicationTitle)) {
+ return applicationTitle;
+ }
+
+ return mDefaultTitle;
+ }
+
+ private String getApplicationLabel(String packageName) {
+ try {
+ final ApplicationInfo appInfo = mPackageManager.getApplicationInfo(packageName, 0);
+ return mPackageManager.getApplicationLabel(appInfo).toString();
+ } catch (PackageManager.NameNotFoundException e) {
+ return null;
+ }
+ }
+
private static PendingIntent createPendingIntent(Context context, String action) {
return PendingIntent.getBroadcast(context, 0,
new Intent(action), PendingIntent.FLAG_CANCEL_CURRENT);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index c7ce1af..cb3d511 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -343,6 +343,11 @@
return;
}
+ if (desc == null || desc.getTitle() == null) {
+ Log.e(TAG, "Description incomplete");
+ return;
+ }
+
Log.d(TAG, "adding track from browser: " + desc + ", " + component);
QSMediaPlayer player = new QSMediaPlayer(mContext, QSPanel.this,
null, mForegroundExecutor, mBackgroundExecutor, mActivityStarter);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
index 8051998..f3e2f10 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
@@ -20,8 +20,6 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.trust.TrustManager;
@@ -39,7 +37,6 @@
import com.android.systemui.R;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -66,22 +63,6 @@
private TrustManager mTrustManager;
private OverviewProxyService mOverviewProxyService;
- private TaskStackChangeListener mListener = new TaskStackChangeListener() {
- @Override
- public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
- boolean homeTaskVisible, boolean clearedTask) {
- if (task.configuration.windowConfiguration.getWindowingMode()
- != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY || !mDividerOptional.isPresent()) {
- return;
- }
-
- final Divider divider = mDividerOptional.get();
- if (divider.isMinimized()) {
- divider.onUndockingTask();
- }
- }
- };
-
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
@Inject
public OverviewProxyRecentsImpl(Optional<Lazy<StatusBar>> statusBarLazy,
@@ -96,7 +77,6 @@
mHandler = new Handler();
mTrustManager = (TrustManager) context.getSystemService(Context.TRUST_SERVICE);
mOverviewProxyService = Dependency.get(OverviewProxyService.class);
- ActivityManagerWrapper.getInstance().registerTaskStackListener(mListener);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 70454d4..290816b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -48,7 +48,6 @@
import android.graphics.drawable.Icon;
import android.media.MediaActionSound;
import android.net.Uri;
-import android.os.AsyncTask;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -188,7 +187,9 @@
private final ImageView mDismissImage;
private Bitmap mScreenBitmap;
+ private SaveImageInBackgroundTask mSaveInBgTask;
private Animator mScreenshotAnimation;
+ private Runnable mOnCompleteRunnable;
private boolean mInDarkMode = false;
private float mScreenshotOffsetXPx;
@@ -197,8 +198,6 @@
private float mDismissButtonSize;
private float mCornerSizeX;
- private AsyncTask<Void, Void, Void> mSaveInBgTask;
-
private MediaActionSound mCameraSound;
// standard material ease
@@ -211,6 +210,7 @@
case MESSAGE_CORNER_TIMEOUT:
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_INTERACTION_TIMEOUT);
GlobalScreenshot.this.clearScreenshot("timeout");
+ mOnCompleteRunnable.run();
break;
default:
break;
@@ -252,6 +252,7 @@
mDismissButton.setOnClickListener(view -> {
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_EXPLICIT_DISMISSAL);
clearScreenshot("dismiss_button");
+ mOnCompleteRunnable.run();
});
mDismissImage = mDismissButton.findViewById(R.id.global_screenshot_dismiss_image);
@@ -325,19 +326,19 @@
data.finisher = finisher;
data.mActionsReadyListener = actionsReadyListener;
data.createDeleteAction = false;
+
if (mSaveInBgTask != null) {
- mSaveInBgTask.cancel(false);
+ mSaveInBgTask.ignoreResult();
}
- mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data).execute();
+ mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data);
+ mSaveInBgTask.execute();
}
/**
* Takes a screenshot of the current display and shows an animation.
*/
private void takeScreenshot(Consumer<Uri> finisher, Rect crop) {
- clearScreenshot("new screenshot requested");
-
int rot = mDisplay.getRotation();
int width = crop.width();
int height = crop.height();
@@ -349,10 +350,12 @@
private void takeScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect) {
mScreenBitmap = screenshot;
+
if (mScreenBitmap == null) {
mNotificationsController.notifyScreenshotError(
R.string.screenshot_failed_to_capture_text);
finisher.accept(null);
+ mOnCompleteRunnable.run();
return;
}
@@ -366,11 +369,13 @@
mScreenshotLayout.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
// Start the post-screenshot animation
- startAnimation(finisher, screenRect.width(), screenRect.height(),
- screenRect);
+ startAnimation(finisher, screenRect.width(), screenRect.height(), screenRect);
}
- void takeScreenshot(Consumer<Uri> finisher) {
+ void takeScreenshot(Consumer<Uri> finisher, Runnable onComplete) {
+ clearScreenshot("new screenshot requested");
+ mOnCompleteRunnable = onComplete;
+
mDisplay.getRealMetrics(mDisplayMetrics);
takeScreenshot(
finisher,
@@ -378,9 +383,11 @@
}
void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds,
- Insets visibleInsets, int taskId, Consumer<Uri> finisher) {
+ Insets visibleInsets, int taskId, Consumer<Uri> finisher, Runnable onComplete) {
// TODO use taskId and visibleInsets
clearScreenshot("new screenshot requested");
+ mOnCompleteRunnable = onComplete;
+
takeScreenshot(screenshot, finisher, screenshotScreenBounds);
}
@@ -388,7 +395,10 @@
* Displays a screenshot selector
*/
@SuppressLint("ClickableViewAccessibility")
- void takeScreenshotPartial(final Consumer<Uri> finisher) {
+ void takeScreenshotPartial(final Consumer<Uri> finisher, Runnable onComplete) {
+ clearScreenshot("new screenshot requested");
+ mOnCompleteRunnable = onComplete;
+
mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
mScreenshotSelectorView.setOnTouchListener(new View.OnTouchListener() {
@Override
@@ -660,6 +670,7 @@
() -> {
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SMART_ACTION_TAPPED);
clearScreenshot("chip tapped");
+ mOnCompleteRunnable.run();
});
mActionsView.addView(actionChip);
}
@@ -671,6 +682,7 @@
shareChip.setPendingIntent(imageData.shareAction.actionIntent, () -> {
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SHARE_TAPPED);
clearScreenshot("chip tapped");
+ mOnCompleteRunnable.run();
});
mActionsView.addView(shareChip);
@@ -681,6 +693,7 @@
editChip.setPendingIntent(imageData.editAction.actionIntent, () -> {
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_EDIT_TAPPED);
clearScreenshot("chip tapped");
+ mOnCompleteRunnable.run();
});
mActionsView.addView(editChip);
@@ -689,6 +702,7 @@
imageData.editAction.actionIntent.send();
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_PREVIEW_TAPPED);
clearScreenshot("screenshot preview tapped");
+ mOnCompleteRunnable.run();
} catch (PendingIntent.CanceledException e) {
Log.e(TAG, "Intent cancelled", e);
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index 170174d..bc3c33d 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -130,11 +130,6 @@
Resources r = mContext.getResources();
try {
- CompletableFuture<List<Notification.Action>> smartActionsFuture =
- ScreenshotSmartActions.getSmartActionsFuture(
- mScreenshotId, mImageFileName, image, mSmartActionsProvider,
- mSmartActionsEnabled, isManagedProfile(mContext));
-
// Save the screenshot to the MediaStore
final ContentValues values = new ContentValues();
values.put(MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES
@@ -148,6 +143,11 @@
final Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
+ CompletableFuture<List<Notification.Action>> smartActionsFuture =
+ ScreenshotSmartActions.getSmartActionsFuture(
+ mScreenshotId, uri, image, mSmartActionsProvider,
+ mSmartActionsEnabled, isManagedProfile(mContext));
+
try {
// First, write the actual data for our screenshot
try (OutputStream out = resolver.openOutputStream(uri)) {
@@ -203,7 +203,7 @@
1000);
smartActions.addAll(buildSmartActions(
ScreenshotSmartActions.getSmartActions(
- mScreenshotId, mImageFileName, smartActionsFuture, timeoutMs,
+ mScreenshotId, smartActionsFuture, timeoutMs,
mSmartActionsProvider),
mContext));
}
@@ -230,6 +230,19 @@
return null;
}
+ /**
+ * If we get a new screenshot request while this one is saving, we want to continue saving in
+ * the background but not return anything.
+ */
+ void ignoreResult() {
+ mParams.mActionsReadyListener = new GlobalScreenshot.ActionsReadyListener() {
+ @Override
+ void onActionsReady(GlobalScreenshot.SavedImageData imageData) {
+ // do nothing
+ }
+ };
+ }
+
@Override
protected void onCancelled(Void params) {
// If we are cancelled while the task is running in the background, we may get null
@@ -311,6 +324,7 @@
editIntent.setData(uri);
editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
editIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
// 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.
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java
index 09a0644..3edb33d 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java
@@ -19,6 +19,7 @@
import android.app.Notification;
import android.content.ComponentName;
import android.graphics.Bitmap;
+import android.net.Uri;
import android.util.Log;
import java.util.Collections;
@@ -67,7 +68,7 @@
*/
public CompletableFuture<List<Notification.Action>> getActions(
String screenshotId,
- String screenshotFileName,
+ Uri screenshotUri,
Bitmap bitmap,
ComponentName componentName,
boolean isManagedProfile) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java
index d313444..c228fe2 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java
@@ -23,6 +23,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.graphics.Bitmap;
+import android.net.Uri;
import android.os.Handler;
import android.os.SystemClock;
import android.util.Slog;
@@ -45,7 +46,7 @@
@VisibleForTesting
static CompletableFuture<List<Notification.Action>> getSmartActionsFuture(
- String screenshotId, String screenshotFileName, Bitmap image,
+ String screenshotId, Uri screenshotUri, Bitmap image,
ScreenshotNotificationSmartActionsProvider smartActionsProvider,
boolean smartActionsEnabled, boolean isManagedProfile) {
if (!smartActionsEnabled) {
@@ -70,7 +71,7 @@
? runningTask.topActivity
: new ComponentName("", "");
smartActionsFuture = smartActionsProvider.getActions(
- screenshotId, screenshotFileName, image, componentName, isManagedProfile);
+ screenshotId, screenshotUri, image, componentName, isManagedProfile);
} catch (Throwable e) {
long waitTimeMs = SystemClock.uptimeMillis() - startTimeMs;
smartActionsFuture = CompletableFuture.completedFuture(Collections.emptyList());
@@ -84,7 +85,7 @@
}
@VisibleForTesting
- static List<Notification.Action> getSmartActions(String screenshotId, String screenshotFileName,
+ static List<Notification.Action> getSmartActions(String screenshotId,
CompletableFuture<List<Notification.Action>> smartActionsFuture, int timeoutMs,
ScreenshotNotificationSmartActionsProvider smartActionsProvider) {
long startTimeMs = SystemClock.uptimeMillis();
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index f68cb74..98030d4 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -16,6 +16,9 @@
package com.android.systemui.screenshot;
+import static com.android.internal.util.ScreenshotHelper.SCREENSHOT_MSG_PROCESS_COMPLETE;
+import static com.android.internal.util.ScreenshotHelper.SCREENSHOT_MSG_URI;
+
import android.app.Service;
import android.content.Intent;
import android.graphics.Bitmap;
@@ -51,8 +54,15 @@
@Override
public void handleMessage(Message msg) {
final Messenger callback = msg.replyTo;
- Consumer<Uri> finisher = uri -> {
- Message reply = Message.obtain(null, 1, uri);
+ Consumer<Uri> uriConsumer = uri -> {
+ Message reply = Message.obtain(null, SCREENSHOT_MSG_URI, uri);
+ try {
+ callback.send(reply);
+ } catch (RemoteException e) {
+ }
+ };
+ Runnable onComplete = () -> {
+ Message reply = Message.obtain(null, SCREENSHOT_MSG_PROCESS_COMPLETE);
try {
callback.send(reply);
} catch (RemoteException e) {
@@ -64,7 +74,8 @@
// animation and error notification.
if (!mUserManager.isUserUnlocked()) {
Log.w(TAG, "Skipping screenshot because storage is locked!");
- post(() -> finisher.accept(null));
+ post(() -> uriConsumer.accept(null));
+ post(onComplete);
return;
}
@@ -79,19 +90,19 @@
switch (msg.what) {
case WindowManager.TAKE_SCREENSHOT_FULLSCREEN:
if (useCornerFlow) {
- mScreenshot.takeScreenshot(finisher);
+ mScreenshot.takeScreenshot(uriConsumer, onComplete);
} else {
mScreenshotLegacy.takeScreenshot(
- finisher, screenshotRequest.getHasStatusBar(),
+ uriConsumer, screenshotRequest.getHasStatusBar(),
screenshotRequest.getHasNavBar());
}
break;
case WindowManager.TAKE_SCREENSHOT_SELECTED_REGION:
if (useCornerFlow) {
- mScreenshot.takeScreenshotPartial(finisher);
+ mScreenshot.takeScreenshotPartial(uriConsumer, onComplete);
} else {
mScreenshotLegacy.takeScreenshotPartial(
- finisher, screenshotRequest.getHasStatusBar(),
+ uriConsumer, screenshotRequest.getHasStatusBar(),
screenshotRequest.getHasNavBar());
}
break;
@@ -102,10 +113,10 @@
int taskId = screenshotRequest.getTaskId();
if (useCornerFlow) {
mScreenshot.handleImageAsScreenshot(
- screenshot, screenBounds, insets, taskId, finisher);
+ screenshot, screenBounds, insets, taskId, uriConsumer, onComplete);
} else {
mScreenshotLegacy.handleImageAsScreenshot(
- screenshot, screenBounds, insets, taskId, finisher);
+ screenshot, screenBounds, insets, taskId, uriConsumer);
}
break;
default:
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index b71c4eb..f36f8c13 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -21,23 +21,25 @@
import static android.content.res.Configuration.SCREEN_WIDTH_DP_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
+import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Handler;
-import android.os.RemoteException;
import android.provider.Settings;
import android.util.Slog;
-import android.window.TaskOrganizer;
-import android.window.WindowContainerToken;
import android.view.LayoutInflater;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.View;
+import android.window.TaskOrganizer;
+import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import android.window.WindowOrganizer;
@@ -48,6 +50,8 @@
import com.android.systemui.SystemUI;
import com.android.systemui.TransactionPool;
import com.android.systemui.recents.Recents;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.wm.DisplayChangeController;
import com.android.systemui.wm.DisplayController;
@@ -415,6 +419,21 @@
}
private final DividerImeController mImePositionProcessor = new DividerImeController();
+ private TaskStackChangeListener mActivityRestartListener = new TaskStackChangeListener() {
+ @Override
+ public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
+ boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
+ if (!wasVisible || task.configuration.windowConfiguration.getWindowingMode()
+ != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY || !mSplits.isSplitScreenSupported()) {
+ return;
+ }
+
+ if (isMinimized()) {
+ onUndockingTask();
+ }
+ }
+ };
+
public Divider(Context context, Optional<Lazy<Recents>> recentsOptionalLazy,
DisplayController displayController, SystemWindows systemWindows,
DisplayImeController imeController, Handler handler,
@@ -485,6 +504,7 @@
removeDivider();
return;
}
+ ActivityManagerWrapper.getInstance().registerTaskStackListener(mActivityRestartListener);
update(mDisplayController.getDisplayContext(displayId).getResources().getConfiguration());
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 1229881..2647c04 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -19,11 +19,13 @@
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
import static com.android.systemui.DejankUtils.whitelistIpcs;
+import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_MEDIA_CONTROLS;
import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT;
import android.app.ActivityManager;
import android.app.KeyguardManager;
import android.app.Notification;
+import android.app.NotificationManager;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -182,7 +184,7 @@
protected final Context mContext;
private final Handler mMainHandler;
protected final SparseArray<UserInfo> mCurrentProfiles = new SparseArray<>();
- protected final ArrayList<UserInfo> mCurrentManagedProfiles = new ArrayList<>();
+ protected final SparseArray<UserInfo> mCurrentManagedProfiles = new SparseArray<>();
protected int mCurrentUserId = 0;
protected NotificationPresenter mPresenter;
@@ -351,7 +353,10 @@
boolean exceedsPriorityThreshold;
if (NotificationUtils.useNewInterruptionModel(mContext)
&& hideSilentNotificationsOnLockscreen()) {
- exceedsPriorityThreshold = entry.getBucket() != BUCKET_SILENT;
+ exceedsPriorityThreshold =
+ entry.getBucket() == BUCKET_MEDIA_CONTROLS
+ || (entry.getBucket() != BUCKET_SILENT
+ && entry.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT);
} else {
exceedsPriorityThreshold = !entry.getRanking().isAmbient();
}
@@ -425,8 +430,9 @@
*/
public boolean allowsManagedPrivateNotificationsInPublic() {
synchronized (mLock) {
- for (UserInfo profile : mCurrentManagedProfiles) {
- if (!userAllowsPrivateNotificationsInPublic(profile.id)) {
+ for (int i = mCurrentManagedProfiles.size() - 1; i >= 0; i--) {
+ if (!userAllowsPrivateNotificationsInPublic(
+ mCurrentManagedProfiles.valueAt(i).id)) {
return false;
}
}
@@ -490,15 +496,22 @@
public boolean needsRedaction(NotificationEntry ent) {
int userId = ent.getSbn().getUserId();
- boolean currentUserWantsRedaction = !userAllowsPrivateNotificationsInPublic(mCurrentUserId);
- boolean notiUserWantsRedaction = !userAllowsPrivateNotificationsInPublic(userId);
- boolean redactedLockscreen = currentUserWantsRedaction || notiUserWantsRedaction;
+ boolean isCurrentUserRedactingNotifs =
+ !userAllowsPrivateNotificationsInPublic(mCurrentUserId);
+ boolean isNotifForManagedProfile = mCurrentManagedProfiles.contains(userId);
+ boolean isNotifUserRedacted = !userAllowsPrivateNotificationsInPublic(userId);
+
+ // redact notifications if the current user is redacting notifications; however if the
+ // notification is associated with a managed profile, we rely on the managed profile
+ // setting to determine whether to redact it
+ boolean isNotifRedacted = (!isNotifForManagedProfile && isCurrentUserRedactingNotifs)
+ || isNotifUserRedacted;
boolean notificationRequestsRedaction =
ent.getSbn().getNotification().visibility == Notification.VISIBILITY_PRIVATE;
boolean userForcesRedaction = packageHasVisibilityOverride(ent.getSbn().getKey());
- return userForcesRedaction || notificationRequestsRedaction && redactedLockscreen;
+ return userForcesRedaction || notificationRequestsRedaction && isNotifRedacted;
}
private boolean packageHasVisibilityOverride(String key) {
@@ -519,7 +532,7 @@
for (UserInfo user : mUserManager.getProfiles(mCurrentUserId)) {
mCurrentProfiles.put(user.id, user);
if (UserManager.USER_TYPE_PROFILE_MANAGED.equals(user.userType)) {
- mCurrentManagedProfiles.add(user);
+ mCurrentManagedProfiles.put(user.id, user);
}
}
}
@@ -551,7 +564,7 @@
public boolean isAnyManagedProfilePublicMode() {
synchronized (mLock) {
for (int i = mCurrentManagedProfiles.size() - 1; i >= 0; i--) {
- if (isLockscreenPublicMode(mCurrentManagedProfiles.get(i).id)) {
+ if (isLockscreenPublicMode(mCurrentManagedProfiles.valueAt(i).id)) {
return true;
}
}
@@ -668,8 +681,8 @@
}
pw.print(" mCurrentManagedProfiles=");
synchronized (mLock) {
- for (UserInfo userInfo : mCurrentManagedProfiles) {
- pw.print("" + userInfo.id + " ");
+ for (int i = mCurrentManagedProfiles.size() - 1; i >= 0; i--) {
+ pw.print("" + mCurrentManagedProfiles.valueAt(i).id + " ");
}
}
pw.println();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
index ec17f4e..9738bcc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
@@ -34,12 +34,13 @@
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_HEADS_UP
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_PEOPLE
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT
+
import com.android.systemui.statusbar.phone.NotificationGroupManager
import com.android.systemui.statusbar.policy.HeadsUpManager
import dagger.Lazy
-import java.util.Comparator
-import java.util.Objects
+import java.util.Objects;
import javax.inject.Inject
+import kotlin.Comparator
private const val TAG = "NotifRankingManager"
@@ -90,19 +91,13 @@
val aIsHighPriority = a.isHighPriority()
val bIsHighPriority = b.isHighPriority()
-
when {
aHeadsUp != bHeadsUp -> if (aHeadsUp) -1 else 1
// Provide consistent ranking with headsUpManager
aHeadsUp -> headsUpManager.compare(a, b)
- usePeopleFiltering && aPersonType != bPersonType -> when (aPersonType) {
- TYPE_IMPORTANT_PERSON -> -1
- TYPE_PERSON -> when (bPersonType) {
- TYPE_IMPORTANT_PERSON -> 1
- else -> -1
- }
- else -> 1
- }
+
+ usePeopleFiltering && aPersonType != bPersonType ->
+ peopleNotificationIdentifier.compareTo(aPersonType, bPersonType)
// Upsort current media notification.
aMedia != bMedia -> if (aMedia) -1 else 1
// Upsort PRIORITY_MAX system notifications
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
index 5879c15..d36627f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
@@ -20,6 +20,7 @@
import android.service.notification.NotificationListenerService.Ranking
import android.service.notification.StatusBarNotification
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.PeopleNotificationType
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_FULL_PERSON
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_IMPORTANT_PERSON
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_PERSON
@@ -33,21 +34,28 @@
/**
* Identifies if the given notification can be classified as a "People" notification.
*
- * @return [TYPE_NON_PERSON] if not a people notification, [TYPE_PERSON] if a standard people
- * notification, and [TYPE_IMPORTANT_PERSON] if an "important" people notification.
+ * @return [TYPE_NON_PERSON] if not a people notification, [TYPE_PERSON] if it is a people
+ * notification that doesn't use shortcuts, [TYPE_FULL_PERSON] if it is a person notification
+ * that users shortcuts, and [TYPE_IMPORTANT_PERSON] if an "important" people notification
+ * that users shortcuts.
*/
@PeopleNotificationType
fun getPeopleNotificationType(sbn: StatusBarNotification, ranking: Ranking): Int
+ fun compareTo(@PeopleNotificationType a: Int,
+ @PeopleNotificationType b: Int): Int
+
companion object {
@Retention(AnnotationRetention.SOURCE)
- @IntDef(prefix = ["TYPE_"], value = [TYPE_NON_PERSON, TYPE_PERSON, TYPE_IMPORTANT_PERSON])
+ @IntDef(prefix = ["TYPE_"], value = [TYPE_NON_PERSON, TYPE_PERSON, TYPE_FULL_PERSON,
+ TYPE_IMPORTANT_PERSON])
annotation class PeopleNotificationType
const val TYPE_NON_PERSON = 0
const val TYPE_PERSON = 1
- const val TYPE_IMPORTANT_PERSON = 2
+ const val TYPE_FULL_PERSON = 2
+ const val TYPE_IMPORTANT_PERSON = 3
}
}
@@ -69,6 +77,11 @@
}
}
+ override fun compareTo(@PeopleNotificationType a: Int,
+ @PeopleNotificationType b: Int): Int {
+ return b.compareTo(a);
+ }
+
/**
* Given two [PeopleNotificationType]s, determine the upper bound. Used to constrain a
* notification to a type given multiple signals, i.e. notification groups, where each child
@@ -84,8 +97,9 @@
private val Ranking.personTypeInfo
get() = when {
!isConversation -> TYPE_NON_PERSON
+ shortcutInfo == null -> TYPE_PERSON
channel?.isImportantConversation == true -> TYPE_IMPORTANT_PERSON
- else -> TYPE_PERSON
+ else -> TYPE_FULL_PERSON
}
private fun extractPersonTypeInfo(sbn: StatusBarNotification) =
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 ba72e28..a9bb416 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
@@ -535,6 +535,12 @@
return isNonblockable;
}
+ private boolean isConversation() {
+ return mPeopleNotificationIdentifier
+ .getPeopleNotificationType(mEntry.getSbn(), mEntry.getRanking())
+ != PeopleNotificationIdentifier.TYPE_NON_PERSON;
+ }
+
public void onNotificationUpdated() {
for (NotificationContentView l : mLayouts) {
l.onNotificationUpdated(mEntry);
@@ -547,7 +553,7 @@
mMenuRow.setAppName(mAppName);
}
if (mIsSummaryWithChildren) {
- mChildrenContainer.recreateNotificationHeader(mExpandClickListener);
+ mChildrenContainer.recreateNotificationHeader(mExpandClickListener, isConversation());
mChildrenContainer.onNotificationUpdated();
}
if (mIconAnimationRunning) {
@@ -2349,8 +2355,7 @@
mIsSummaryWithChildren = mChildrenContainer != null
&& mChildrenContainer.getNotificationChildCount() > 0;
if (mIsSummaryWithChildren && mChildrenContainer.getHeaderView() == null) {
- mChildrenContainer.recreateNotificationHeader(mExpandClickListener
- );
+ mChildrenContainer.recreateNotificationHeader(mExpandClickListener, isConversation());
}
getShowingLayout().updateBackgroundColor(false /* animate */);
mPrivateLayout.updateExpandButtons(isExpandable());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
new file mode 100644
index 0000000..32477a6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.row;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.drawable.Icon;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.internal.widget.ConversationLayout;
+import com.android.systemui.R;
+
+/**
+ * A hybrid view which may contain information about one ore more conversations.
+ */
+public class HybridConversationNotificationView extends HybridNotificationView {
+
+ private ImageView mConversationIconView;
+ private TextView mConversationSenderName;
+ private View mConversationFacePile;
+ private int mConversationIconSize;
+ private int mFacePileSize;
+ private int mFacePileProtectionWidth;
+
+ public HybridConversationNotificationView(Context context) {
+ this(context, null);
+ }
+
+ public HybridConversationNotificationView(Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public HybridConversationNotificationView(
+ Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public HybridConversationNotificationView(
+ Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mConversationIconView = requireViewById(com.android.internal.R.id.conversation_icon);
+ mConversationFacePile = requireViewById(com.android.internal.R.id.conversation_face_pile);
+ mConversationSenderName = requireViewById(R.id.conversation_notification_sender);
+ mFacePileSize = getResources()
+ .getDimensionPixelSize(R.dimen.conversation_single_line_face_pile_size);
+ mConversationIconSize = getResources()
+ .getDimensionPixelSize(R.dimen.conversation_single_line_avatar_size);
+ mFacePileProtectionWidth = getResources().getDimensionPixelSize(
+ R.dimen.conversation_single_line_face_pile_protection_width);
+ mTransformationHelper.addViewTransformingToSimilar(mConversationIconView);
+ }
+
+ @Override
+ public void bind(@Nullable CharSequence title, @Nullable CharSequence text,
+ @Nullable View contentView) {
+ if (!(contentView instanceof ConversationLayout)) {
+ super.bind(title, text, contentView);
+ return;
+ }
+
+ ConversationLayout conversationLayout = (ConversationLayout) contentView;
+ Icon conversationIcon = conversationLayout.getConversationIcon();
+ if (conversationIcon != null) {
+ mConversationFacePile.setVisibility(GONE);
+ mConversationIconView.setVisibility(VISIBLE);
+ mConversationIconView.setImageIcon(conversationIcon);
+ } else {
+ // If there isn't an icon, generate a "face pile" based on the sender avatars
+ mConversationIconView.setVisibility(GONE);
+ mConversationFacePile.setVisibility(VISIBLE);
+
+ mConversationFacePile =
+ requireViewById(com.android.internal.R.id.conversation_face_pile);
+ ImageView facePileBottomBg = mConversationFacePile.requireViewById(
+ com.android.internal.R.id.conversation_face_pile_bottom_background);
+ ImageView facePileBottom = mConversationFacePile.requireViewById(
+ com.android.internal.R.id.conversation_face_pile_bottom);
+ ImageView facePileTop = mConversationFacePile.requireViewById(
+ com.android.internal.R.id.conversation_face_pile_top);
+ conversationLayout.bindFacePile(facePileBottomBg, facePileBottom, facePileTop);
+ setSize(mConversationFacePile, mFacePileSize);
+ setSize(facePileBottom, mConversationIconSize);
+ setSize(facePileTop, mConversationIconSize);
+ setSize(facePileBottomBg, mConversationIconSize + 2 * mFacePileProtectionWidth);
+ mTransformationHelper.addViewTransformingToSimilar(facePileTop);
+ mTransformationHelper.addViewTransformingToSimilar(facePileBottom);
+ mTransformationHelper.addViewTransformingToSimilar(facePileBottomBg);
+ }
+ CharSequence conversationTitle = conversationLayout.getConversationTitle();
+ if (TextUtils.isEmpty(conversationTitle)) {
+ conversationTitle = title;
+ }
+ if (conversationLayout.isOneToOne()) {
+ mConversationSenderName.setVisibility(GONE);
+ } else {
+ mConversationSenderName.setVisibility(VISIBLE);
+ mConversationSenderName.setText(conversationLayout.getConversationSenderName());
+ }
+ CharSequence conversationText = conversationLayout.getConversationText();
+ if (TextUtils.isEmpty(conversationText)) {
+ conversationText = text;
+ }
+ super.bind(conversationTitle, conversationText, conversationLayout);
+ }
+
+ private static void setSize(View view, int size) {
+ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) view.getLayoutParams();
+ lp.width = size;
+ lp.height = size;
+ view.setLayoutParams(lp);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java
index fe81957..0ccebc13 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java
@@ -16,18 +16,20 @@
package com.android.systemui.statusbar.notification.row;
+import android.annotation.Nullable;
import android.app.Notification;
import android.content.Context;
import android.content.res.Resources;
+import android.service.notification.StatusBarNotification;
import android.util.TypedValue;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
+import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
+import com.android.internal.widget.ConversationLayout;
import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.NotificationDozeHelper;
-import com.android.systemui.statusbar.notification.NotificationUtils;
/**
* A class managing hybrid groups that include {@link HybridNotificationView} and the notification
@@ -36,41 +38,41 @@
public class HybridGroupManager {
private final Context mContext;
- private final ViewGroup mParent;
private float mOverflowNumberSize;
private int mOverflowNumberPadding;
private int mOverflowNumberColor;
- public HybridGroupManager(Context ctx, ViewGroup parent) {
+ public HybridGroupManager(Context ctx) {
mContext = ctx;
- mParent = parent;
initDimens();
}
public void initDimens() {
Resources res = mContext.getResources();
- mOverflowNumberSize = res.getDimensionPixelSize(
- R.dimen.group_overflow_number_size);
- mOverflowNumberPadding = res.getDimensionPixelSize(
- R.dimen.group_overflow_number_padding);
+ mOverflowNumberSize = res.getDimensionPixelSize(R.dimen.group_overflow_number_size);
+ mOverflowNumberPadding = res.getDimensionPixelSize(R.dimen.group_overflow_number_padding);
}
- private HybridNotificationView inflateHybridViewWithStyle(int style) {
+ private HybridNotificationView inflateHybridViewWithStyle(int style,
+ View contentView, ViewGroup parent) {
LayoutInflater inflater = new ContextThemeWrapper(mContext, style)
.getSystemService(LayoutInflater.class);
- HybridNotificationView hybrid = (HybridNotificationView) inflater.inflate(
- R.layout.hybrid_notification, mParent, false);
- mParent.addView(hybrid);
+ int layout = contentView instanceof ConversationLayout
+ ? R.layout.hybrid_conversation_notification
+ : R.layout.hybrid_notification;
+ HybridNotificationView hybrid = (HybridNotificationView)
+ inflater.inflate(layout, parent, false);
+ parent.addView(hybrid);
return hybrid;
}
- private TextView inflateOverflowNumber() {
+ private TextView inflateOverflowNumber(ViewGroup parent) {
LayoutInflater inflater = mContext.getSystemService(LayoutInflater.class);
TextView numberView = (TextView) inflater.inflate(
- R.layout.hybrid_overflow_number, mParent, false);
- mParent.addView(numberView);
+ R.layout.hybrid_overflow_number, parent, false);
+ parent.addView(numberView);
updateOverFlowNumberColor(numberView);
return numberView;
}
@@ -87,22 +89,26 @@
}
public HybridNotificationView bindFromNotification(HybridNotificationView reusableView,
- Notification notification) {
- return bindFromNotificationWithStyle(reusableView, notification,
- R.style.HybridNotification);
+ View contentView, StatusBarNotification notification,
+ ViewGroup parent) {
+ return bindFromNotificationWithStyle(reusableView, contentView, notification,
+ R.style.HybridNotification, parent);
}
private HybridNotificationView bindFromNotificationWithStyle(
- HybridNotificationView reusableView, Notification notification, int style) {
+ HybridNotificationView reusableView, View contentView,
+ StatusBarNotification notification,
+ int style, ViewGroup parent) {
if (reusableView == null) {
- reusableView = inflateHybridViewWithStyle(style);
+ reusableView = inflateHybridViewWithStyle(style, contentView, parent);
}
- CharSequence titleText = resolveTitle(notification);
- CharSequence contentText = resolveText(notification);
- reusableView.bind(titleText, contentText);
+ CharSequence titleText = resolveTitle(notification.getNotification());
+ CharSequence contentText = resolveText(notification.getNotification());
+ reusableView.bind(titleText, contentText, contentView);
return reusableView;
}
+ @Nullable
private CharSequence resolveText(Notification notification) {
CharSequence contentText = notification.extras.getCharSequence(Notification.EXTRA_TEXT);
if (contentText == null) {
@@ -111,6 +117,7 @@
return contentText;
}
+ @Nullable
private CharSequence resolveTitle(Notification notification) {
CharSequence titleText = notification.extras.getCharSequence(Notification.EXTRA_TITLE);
if (titleText == null) {
@@ -119,9 +126,10 @@
return titleText;
}
- public TextView bindOverflowNumber(TextView reusableView, int number) {
+ public TextView bindOverflowNumber(TextView reusableView, int number,
+ ViewGroup parent) {
if (reusableView == null) {
- reusableView = inflateOverflowNumber();
+ reusableView = inflateOverflowNumber(parent);
}
String text = mContext.getResources().getString(
R.string.notification_group_overflow_indicator, number);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java
index be25d63..2071449 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java
@@ -36,8 +36,7 @@
public class HybridNotificationView extends AlphaOptimizedLinearLayout
implements TransformableView {
- private ViewTransformationHelper mTransformationHelper;
-
+ protected final ViewTransformationHelper mTransformationHelper = new ViewTransformationHelper();
protected TextView mTitleView;
protected TextView mTextView;
@@ -69,9 +68,8 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mTitleView = (TextView) findViewById(R.id.notification_title);
- mTextView = (TextView) findViewById(R.id.notification_text);
- mTransformationHelper = new ViewTransformationHelper();
+ mTitleView = findViewById(R.id.notification_title);
+ mTextView = findViewById(R.id.notification_text);
mTransformationHelper.setCustomTransformation(
new ViewTransformationHelper.CustomTransformation() {
@Override
@@ -106,11 +104,8 @@
mTransformationHelper.addTransformedView(TRANSFORMING_VIEW_TEXT, mTextView);
}
- public void bind(CharSequence title) {
- bind(title, null);
- }
-
- public void bind(CharSequence title, CharSequence text) {
+ public void bind(@Nullable CharSequence title, @Nullable CharSequence text,
+ @Nullable View contentView) {
mTitleView.setText(title);
mTitleView.setVisibility(TextUtils.isEmpty(title) ? GONE : VISIBLE);
if (TextUtils.isEmpty(text)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index bd1745e..f23f3bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -177,7 +177,7 @@
public NotificationContentView(Context context, AttributeSet attrs) {
super(context, attrs);
- mHybridGroupManager = new HybridGroupManager(getContext(), this);
+ mHybridGroupManager = new HybridGroupManager(getContext());
mMediaTransferManager = new MediaTransferManager(getContext());
mSmartReplyConstants = Dependency.get(SmartReplyConstants.class);
mSmartReplyController = Dependency.get(SmartReplyController.class);
@@ -1163,7 +1163,7 @@
if (mIsChildInGroup) {
boolean isNewView = mSingleLineView == null;
mSingleLineView = mHybridGroupManager.bindFromNotification(
- mSingleLineView, mStatusBarNotification.getNotification());
+ mSingleLineView, mContractedChild, mStatusBarNotification, this);
if (isNewView) {
updateViewVisibility(mVisibleType, VISIBLE_TYPE_SINGLELINE,
mSingleLineView, mSingleLineView);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
index 13e7fe5..15499b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
@@ -46,13 +46,13 @@
)
private val conversationLayout: ConversationLayout = view as ConversationLayout
- private lateinit var conversationIcon: CachingIconView
+ private lateinit var conversationIconView: CachingIconView
private lateinit var conversationBadgeBg: View
private lateinit var expandButton: View
private lateinit var expandButtonContainer: View
private lateinit var imageMessageContainer: ViewGroup
private lateinit var messagingLinearLayout: MessagingLinearLayout
- private lateinit var conversationTitle: View
+ private lateinit var conversationTitleView: View
private lateinit var importanceRing: View
private lateinit var appName: View
private var facePileBottomBg: View? = null
@@ -63,7 +63,7 @@
messagingLinearLayout = conversationLayout.messagingLinearLayout
imageMessageContainer = conversationLayout.imageMessageContainer
with(conversationLayout) {
- conversationIcon = requireViewById(com.android.internal.R.id.conversation_icon)
+ conversationIconView = requireViewById(com.android.internal.R.id.conversation_icon)
conversationBadgeBg =
requireViewById(com.android.internal.R.id.conversation_icon_badge_bg)
expandButton = requireViewById(com.android.internal.R.id.expand_button)
@@ -71,7 +71,7 @@
requireViewById(com.android.internal.R.id.expand_button_container)
importanceRing = requireViewById(com.android.internal.R.id.conversation_icon_badge_ring)
appName = requireViewById(com.android.internal.R.id.app_name_text)
- conversationTitle = requireViewById(com.android.internal.R.id.conversation_text)
+ conversationTitleView = requireViewById(com.android.internal.R.id.conversation_text)
facePileTop = findViewById(com.android.internal.R.id.conversation_face_pile_top)
facePileBottom = findViewById(com.android.internal.R.id.conversation_face_pile_bottom)
facePileBottomBg =
@@ -93,7 +93,7 @@
addTransformedViews(
messagingLinearLayout,
appName,
- conversationTitle)
+ conversationTitleView)
// Let's ignore the image message container since that is transforming as part of the
// messages already
@@ -124,7 +124,7 @@
)
addViewsTransformingToSimilar(
- conversationIcon,
+ conversationIconView,
conversationBadgeBg,
expandButton,
importanceRing,
@@ -136,29 +136,27 @@
override fun setShelfIconVisible(visible: Boolean) {
if (conversationLayout.isImportantConversation) {
- if (conversationIcon.visibility != GONE) {
- conversationIcon.setForceHidden(visible);
+ if (conversationIconView.visibility != GONE) {
+ conversationIconView.isForceHidden = visible
// We don't want the small icon to be hidden by the extended wrapper, as force
// hiding the conversationIcon will already do that via its listener.
- return;
+ return
}
}
super.setShelfIconVisible(visible)
}
- override fun getShelfTransformationTarget(): View? {
- if (conversationLayout.isImportantConversation) {
- if (conversationIcon.visibility != GONE) {
- return conversationIcon
- } else {
- // A notification with a fallback icon was set to important. Currently
- // the transformation doesn't work for these and needs to be fixed. In the meantime
- // those are using the icon.
- return super.getShelfTransformationTarget();
- }
- }
- return super.getShelfTransformationTarget()
- }
+ override fun getShelfTransformationTarget(): View? =
+ if (conversationLayout.isImportantConversation)
+ if (conversationIconView.visibility != GONE)
+ conversationIconView
+ else
+ // A notification with a fallback icon was set to important. Currently
+ // the transformation doesn't work for these and needs to be fixed.
+ // In the meantime those are using the icon.
+ super.getShelfTransformationTarget()
+ else
+ super.getShelfTransformationTarget()
override fun setRemoteInputVisible(visible: Boolean) =
conversationLayout.showHistoricMessages(visible)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index e20be2b..f8b7831 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -27,11 +27,13 @@
import android.view.ViewGroup;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
+import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import com.android.internal.widget.CachingIconView;
import com.android.internal.widget.NotificationExpandButton;
+import com.android.settingslib.Utils;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.statusbar.TransformableView;
@@ -60,12 +62,14 @@
private NotificationExpandButton mExpandButton;
protected NotificationHeaderView mNotificationHeader;
private TextView mHeaderText;
+ private TextView mAppNameText;
private ImageView mWorkProfileImage;
private View mCameraIcon;
private View mMicIcon;
private View mOverlayIcon;
private View mAppOps;
private View mAudiblyAlertedIcon;
+ private FrameLayout mIconContainer;
private boolean mIsLowPriority;
private boolean mTransformLowPriorityTitle;
@@ -108,8 +112,10 @@
}
protected void resolveHeaderViews() {
+ mIconContainer = mView.findViewById(com.android.internal.R.id.header_icon_container);
mIcon = mView.findViewById(com.android.internal.R.id.icon);
mHeaderText = mView.findViewById(com.android.internal.R.id.header_text);
+ mAppNameText = mView.findViewById(com.android.internal.R.id.app_name_text);
mExpandButton = mView.findViewById(com.android.internal.R.id.expand_button);
mWorkProfileImage = mView.findViewById(com.android.internal.R.id.profile_badge);
mNotificationHeader = mView.findViewById(com.android.internal.R.id.notification_header);
@@ -182,6 +188,61 @@
}
}
+ public void applyConversationSkin() {
+ if (mAppNameText != null) {
+ mAppNameText.setTextAppearance(
+ com.android.internal.R.style
+ .TextAppearance_DeviceDefault_Notification_Conversation_AppName);
+ ViewGroup.MarginLayoutParams layoutParams =
+ (ViewGroup.MarginLayoutParams) mAppNameText.getLayoutParams();
+ layoutParams.setMarginStart(0);
+ }
+ if (mIconContainer != null) {
+ ViewGroup.MarginLayoutParams layoutParams =
+ (ViewGroup.MarginLayoutParams) mIconContainer.getLayoutParams();
+ layoutParams.width =
+ mIconContainer.getContext().getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.conversation_content_start);
+ final int marginStart =
+ mIconContainer.getContext().getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.notification_content_margin_start);
+ layoutParams.setMarginStart(marginStart * -1);
+ }
+ if (mIcon != null) {
+ ViewGroup.MarginLayoutParams layoutParams =
+ (ViewGroup.MarginLayoutParams) mIcon.getLayoutParams();
+ layoutParams.setMarginEnd(0);
+ }
+ }
+
+ public void clearConversationSkin() {
+ if (mAppNameText != null) {
+ final int textAppearance = Utils.getThemeAttr(
+ mAppNameText.getContext(),
+ com.android.internal.R.attr.notificationHeaderTextAppearance,
+ com.android.internal.R.style.TextAppearance_DeviceDefault_Notification_Info);
+ mAppNameText.setTextAppearance(textAppearance);
+ ViewGroup.MarginLayoutParams layoutParams =
+ (ViewGroup.MarginLayoutParams) mAppNameText.getLayoutParams();
+ final int marginStart = mAppNameText.getContext().getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.notification_header_app_name_margin_start);
+ layoutParams.setMarginStart(marginStart);
+ }
+ if (mIconContainer != null) {
+ ViewGroup.MarginLayoutParams layoutParams =
+ (ViewGroup.MarginLayoutParams) mIconContainer.getLayoutParams();
+ layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT;
+ layoutParams.setMarginStart(0);
+ }
+ if (mIcon != null) {
+ ViewGroup.MarginLayoutParams layoutParams =
+ (ViewGroup.MarginLayoutParams) mIcon.getLayoutParams();
+ final int marginEnd = mIcon.getContext().getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.notification_header_icon_margin_end);
+ layoutParams.setMarginEnd(marginEnd);
+ }
+ }
+
/**
* Adds the remaining TransformTypes to the TransformHelper. This is done to make sure that each
* child is faded automatically and doesn't have to be manually added.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 351a3ef..c9b1318 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -40,6 +40,7 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.HybridGroupManager;
import com.android.systemui.statusbar.notification.row.HybridNotificationView;
+import com.android.systemui.statusbar.notification.row.wrapper.NotificationHeaderViewWrapper;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
import java.util.ArrayList;
@@ -99,6 +100,7 @@
private boolean mIsLowPriority;
private OnClickListener mHeaderClickListener;
private ViewGroup mCurrentHeader;
+ private boolean mIsConversation;
private boolean mShowDividersWhenExpanded;
private boolean mHideDividersDuringExpand;
@@ -122,7 +124,7 @@
public NotificationChildrenContainer(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- mHybridGroupManager = new HybridGroupManager(getContext(), this);
+ mHybridGroupManager = new HybridGroupManager(getContext());
initDimens();
setClipChildren(false);
}
@@ -308,8 +310,9 @@
return mAttachedChildren.size();
}
- public void recreateNotificationHeader(OnClickListener listener) {
+ public void recreateNotificationHeader(OnClickListener listener, boolean isConversation) {
mHeaderClickListener = listener;
+ mIsConversation = isConversation;
StatusBarNotification notification = mContainingNotification.getEntry().getSbn();
final Notification.Builder builder = Notification.Builder.recoverBuilder(getContext(),
notification.getNotification());
@@ -328,7 +331,16 @@
header.reapply(getContext(), mNotificationHeader);
}
mNotificationHeaderWrapper.onContentUpdated(mContainingNotification);
- recreateLowPriorityHeader(builder);
+ if (mNotificationHeaderWrapper instanceof NotificationHeaderViewWrapper) {
+ NotificationHeaderViewWrapper headerWrapper =
+ (NotificationHeaderViewWrapper) mNotificationHeaderWrapper;
+ if (isConversation) {
+ headerWrapper.applyConversationSkin();
+ } else {
+ headerWrapper.clearConversationSkin();
+ }
+ }
+ recreateLowPriorityHeader(builder, isConversation);
updateHeaderVisibility(false /* animate */);
updateChildrenHeaderAppearance();
}
@@ -338,7 +350,7 @@
*
* @param builder a builder to reuse. Otherwise the builder will be recovered.
*/
- private void recreateLowPriorityHeader(Notification.Builder builder) {
+ private void recreateLowPriorityHeader(Notification.Builder builder, boolean isConversation) {
RemoteViews header;
StatusBarNotification notification = mContainingNotification.getEntry().getSbn();
if (mIsLowPriority) {
@@ -362,6 +374,15 @@
header.reapply(getContext(), mNotificationHeaderLowPriority);
}
mNotificationHeaderWrapperLowPriority.onContentUpdated(mContainingNotification);
+ if (mNotificationHeaderWrapper instanceof NotificationHeaderViewWrapper) {
+ NotificationHeaderViewWrapper headerWrapper =
+ (NotificationHeaderViewWrapper) mNotificationHeaderWrapper;
+ if (isConversation) {
+ headerWrapper.applyConversationSkin();
+ } else {
+ headerWrapper.clearConversationSkin();
+ }
+ }
resetHeaderVisibilityIfNeeded(mNotificationHeaderLowPriority, calculateDesiredHeader());
} else {
removeView(mNotificationHeaderLowPriority);
@@ -378,7 +399,7 @@
int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
if (mUntruncatedChildCount > maxAllowedVisibleChildren) {
int number = mUntruncatedChildCount - maxAllowedVisibleChildren;
- mOverflowNumber = mHybridGroupManager.bindOverflowNumber(mOverflowNumber, number);
+ mOverflowNumber = mHybridGroupManager.bindOverflowNumber(mOverflowNumber, number, this);
if (mGroupOverFlowState == null) {
mGroupOverFlowState = new ViewState();
mNeverAppliedGroupState = true;
@@ -1141,7 +1162,7 @@
removeView(mNotificationHeaderLowPriority);
mNotificationHeaderLowPriority = null;
}
- recreateNotificationHeader(listener);
+ recreateNotificationHeader(listener, mIsConversation);
initDimens();
for (int i = 0; i < mDividers.size(); i++) {
View prevDivider = mDividers.get(i);
@@ -1225,7 +1246,7 @@
public void setIsLowPriority(boolean isLowPriority) {
mIsLowPriority = isLowPriority;
if (mContainingNotification != null) { /* we're not yet set up yet otherwise */
- recreateLowPriorityHeader(null /* existingBuilder */);
+ recreateLowPriorityHeader(null /* existingBuilder */, mIsConversation);
updateHeaderVisibility(false /* animate */);
}
if (mUserLocked) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index a19d35a..ec54b30 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -65,7 +65,6 @@
mPredrawRegistered = false;
int newState = mState;
- mOldState = mState;
Drawable icon = getIcon(newState);
setImageDrawable(icon, false);
@@ -135,6 +134,7 @@
}
void update(int newState, boolean pulsing, boolean dozing, boolean keyguardJustShown) {
+ mOldState = mState;
mState = newState;
mPulsing = pulsing;
mDozing = dozing;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
index a633e19..a2e7306 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
@@ -470,8 +470,10 @@
}
private int getState() {
- if ((mKeyguardStateController.canDismissLockScreen() || !mKeyguardShowing
- || mKeyguardStateController.isKeyguardGoingAway()) && !mSimLocked) {
+ if ((mKeyguardStateController.canDismissLockScreen()
+ || !mKeyguardStateController.isShowing()
+ || mKeyguardStateController.isKeyguardGoingAway()
+ || mKeyguardStateController.isKeyguardFadingAway()) && !mSimLocked) {
return STATE_LOCK_OPEN;
} else if (mTransientBiometricsError) {
return STATE_BIOMETRICS_ERROR;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 9ee204e..b2aa769 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -98,6 +98,7 @@
import com.android.internal.util.LatencyTracker;
import com.android.internal.view.AppearanceRegion;
import com.android.systemui.R;
+import com.android.systemui.accessibility.SystemActions;
import com.android.systemui.assist.AssistHandleViewController;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -185,6 +186,7 @@
private WindowManager mWindowManager;
private final CommandQueue mCommandQueue;
private long mLastLockToAppLongPress;
+ private final SystemActions mSystemActions;
private Locale mLocale;
private int mLayoutDirection;
@@ -373,6 +375,7 @@
Optional<Recents> recentsOptional, Lazy<StatusBar> statusBarLazy,
ShadeController shadeController,
NotificationRemoteInputManager notificationRemoteInputManager,
+ SystemActions systemActions,
@Main Handler mainHandler) {
mAccessibilityManagerWrapper = accessibilityManagerWrapper;
mDeviceProvisionedController = deviceProvisionedController;
@@ -391,6 +394,7 @@
mCommandQueue = commandQueue;
mDivider = divider;
mRecentsOptional = recentsOptional;
+ mSystemActions = systemActions;
mHandler = mainHandler;
}
@@ -1168,6 +1172,16 @@
.setFlag(SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE, longClickable)
.setFlag(SYSUI_STATE_NAV_BAR_HIDDEN, !isNavBarWindowVisible())
.commitUpdate(mDisplayId);
+ registerAction(clickable, SystemActions.SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON);
+ registerAction(longClickable, SystemActions.SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON_CHOOSER);
+ }
+
+ private void registerAction(boolean register, int actionId) {
+ if (register) {
+ mSystemActions.register(actionId);
+ } else {
+ mSystemActions.unregister(actionId);
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java b/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java
index 899aabb..0b6e4b2 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java
@@ -201,6 +201,14 @@
attrs.flags |= FLAG_HARDWARE_ACCELERATED;
viewRoot.setView(view, attrs);
mViewRoots.put(view, viewRoot);
+
+ try {
+ mWmService.setShellRootAccessibilityWindow(mDisplayId, windowType,
+ viewRoot.getWindowToken());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error setting accessibility window for " + mDisplayId + ":"
+ + windowType, e);
+ }
}
SysUiWindowManager addRoot(int windowType) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
index 9985d21..dc02799 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
@@ -19,6 +19,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
@@ -83,7 +84,16 @@
}
@Test
+ public void onEvent_noneWhileEnabledAod_ignoresIfAlreadyNone() {
+ mDockManagerFake.setDockEvent(DockManager.STATE_NONE);
+
+ verify(mMachine, never()).requestState(eq(State.DOZE_AOD));
+ }
+
+ @Test
public void onEvent_noneWhileEnabledAod_requestsAodState() {
+ mDockManagerFake.setDockEvent(DockManager.STATE_DOCKED);
+ clearInvocations(mMachine);
mDockManagerFake.setDockEvent(DockManager.STATE_NONE);
verify(mMachine).requestState(eq(State.DOZE_AOD));
@@ -91,6 +101,8 @@
@Test
public void onEvent_noneWhileDisabledAod_requestsDozeState() {
+ mDockManagerFake.setDockEvent(DockManager.STATE_DOCKED);
+ clearInvocations(mMachine);
doReturn(false).when(mConfig).alwaysOnEnabled(anyInt());
mDockManagerFake.setDockEvent(DockManager.STATE_NONE);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
index ff03fba..317500c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
@@ -158,6 +158,13 @@
verify(mTriggerSensor).setListening(eq(false));
}
+ @Test
+ public void testDestroy() {
+ mDozeSensors.destroy();
+
+ verify(mTriggerSensor).setListening(false);
+ }
+
private class TestableDozeSensors extends DozeSensors {
TestableDozeSensors() {
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 ffe3cd5..2c4304d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
@@ -83,8 +83,9 @@
eq(false))).thenThrow(
RuntimeException.class);
CompletableFuture<List<Notification.Action>> smartActionsFuture =
- ScreenshotSmartActions.getSmartActionsFuture("", "", bitmap,
- smartActionsProvider, true, false);
+ ScreenshotSmartActions.getSmartActionsFuture(
+ "", Uri.parse("content://authority/data"), bitmap, smartActionsProvider,
+ true, false);
assertNotNull(smartActionsFuture);
List<Notification.Action> smartActions = smartActionsFuture.get(5, TimeUnit.MILLISECONDS);
assertEquals(Collections.emptyList(), smartActions);
@@ -101,7 +102,7 @@
when(smartActionsFuture.get(timeoutMs, TimeUnit.MILLISECONDS)).thenThrow(
RuntimeException.class);
List<Notification.Action> actions = ScreenshotSmartActions.getSmartActions(
- "", "", smartActionsFuture, timeoutMs, mSmartActionsProvider);
+ "", smartActionsFuture, timeoutMs, mSmartActionsProvider);
assertEquals(Collections.emptyList(), actions);
}
@@ -122,8 +123,9 @@
Bitmap bitmap = mock(Bitmap.class);
when(bitmap.getConfig()).thenReturn(Bitmap.Config.RGB_565);
CompletableFuture<List<Notification.Action>> smartActionsFuture =
- ScreenshotSmartActions.getSmartActionsFuture("", "", bitmap,
- mSmartActionsProvider, true, true);
+ ScreenshotSmartActions.getSmartActionsFuture(
+ "", Uri.parse("content://autority/data"), bitmap, mSmartActionsProvider,
+ true, true);
verify(mSmartActionsProvider, never()).getActions(any(), any(), any(), any(),
eq(false));
assertNotNull(smartActionsFuture);
@@ -136,8 +138,9 @@
public void testScreenshotNotificationSmartActionsProviderInvokedOnce() {
Bitmap bitmap = mock(Bitmap.class);
when(bitmap.getConfig()).thenReturn(Bitmap.Config.HARDWARE);
- ScreenshotSmartActions.getSmartActionsFuture("", "", bitmap, mSmartActionsProvider,
- true, true);
+ ScreenshotSmartActions.getSmartActionsFuture(
+ "", Uri.parse("content://autority/data"), bitmap, mSmartActionsProvider, true,
+ true);
verify(mSmartActionsProvider, times(1))
.getActions(any(), any(), any(), any(), eq(true));
}
@@ -152,7 +155,7 @@
SystemUIFactory.getInstance().createScreenshotNotificationSmartActionsProvider(
mContext, null, mHandler);
CompletableFuture<List<Notification.Action>> smartActionsFuture =
- ScreenshotSmartActions.getSmartActionsFuture("", "", bitmap,
+ ScreenshotSmartActions.getSmartActionsFuture("", null, bitmap,
actionsProvider,
true, true);
assertNotNull(smartActionsFuture);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index c6d57e6..d124bad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -20,6 +20,8 @@
import static android.content.Intent.ACTION_USER_SWITCHED;
import static android.provider.Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL;
+import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_MEDIA_CONTROLS;
+import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_PEOPLE;
import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT;
import static junit.framework.Assert.assertFalse;
@@ -42,6 +44,7 @@
import android.database.ContentObserver;
import android.os.Handler;
import android.os.Looper;
+import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
@@ -99,6 +102,9 @@
private UserInfo mSecondaryUser;
private UserInfo mWorkUser;
private TestNotificationLockscreenUserManager mLockscreenUserManager;
+ private NotificationEntry mCurrentUserNotif;
+ private NotificationEntry mSecondaryUserNotif;
+ private NotificationEntry mWorkProfileNotif;
@Before
public void setUp() {
@@ -116,6 +122,21 @@
mDependency.injectTestDependency(Dependency.MAIN_HANDLER,
Handler.createAsync(Looper.myLooper()));
+ Notification notifWithPrivateVisibility = new Notification();
+ notifWithPrivateVisibility.visibility = Notification.VISIBILITY_PRIVATE;
+ mCurrentUserNotif = new NotificationEntryBuilder()
+ .setNotification(notifWithPrivateVisibility)
+ .setUser(new UserHandle(mCurrentUser.id))
+ .build();
+ mSecondaryUserNotif = new NotificationEntryBuilder()
+ .setNotification(notifWithPrivateVisibility)
+ .setUser(new UserHandle(mSecondaryUser.id))
+ .build();
+ mWorkProfileNotif = new NotificationEntryBuilder()
+ .setNotification(notifWithPrivateVisibility)
+ .setUser(new UserHandle(mWorkUser.id))
+ .build();
+
mLockscreenUserManager = new TestNotificationLockscreenUserManager(mContext);
mLockscreenUserManager.setUpWithPresenter(mPresenter);
}
@@ -155,7 +176,7 @@
Settings.Secure.putIntForUser(mContext.getContentResolver(),
Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mCurrentUser.id);
mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
- assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
+ assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mCurrentUser.id));
}
@Test
@@ -163,7 +184,7 @@
Settings.Secure.putIntForUser(mContext.getContentResolver(),
Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mWorkUser.id);
mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
- assertFalse(mLockscreenUserManager.allowsManagedPrivateNotificationsInPublic());
+ assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mWorkUser.id));
}
@Test
@@ -171,7 +192,107 @@
Settings.Secure.putIntForUser(mContext.getContentResolver(),
Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mWorkUser.id);
mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
- assertTrue(mLockscreenUserManager.allowsManagedPrivateNotificationsInPublic());
+ assertTrue(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mWorkUser.id));
+ }
+
+ @Test
+ public void testCurrentUserPrivateNotificationsNotRedacted() {
+ // GIVEN current user doesn't allow private notifications to show
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mCurrentUser.id);
+ mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+
+ // THEN current user's notification is redacted
+ assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
+ }
+
+ @Test
+ public void testCurrentUserPrivateNotificationsRedacted() {
+ // GIVEN current user allows private notifications to show
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mCurrentUser.id);
+ mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+
+ // THEN current user's notification isn't redacted
+ assertFalse(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
+ }
+
+ @Test
+ public void testWorkPrivateNotificationsRedacted() {
+ // GIVEN work profile doesn't private notifications to show
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mWorkUser.id);
+ mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+
+ // THEN work profile notification is redacted
+ assertTrue(mLockscreenUserManager.needsRedaction(mWorkProfileNotif));
+ }
+
+ @Test
+ public void testWorkPrivateNotificationsNotRedacted() {
+ // GIVEN work profile allows private notifications to show
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mWorkUser.id);
+ mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+
+ // THEN work profile notification isn't redacted
+ assertFalse(mLockscreenUserManager.needsRedaction(mWorkProfileNotif));
+ }
+
+ @Test
+ public void testWorkPrivateNotificationsNotRedacted_otherUsersRedacted() {
+ // GIVEN work profile allows private notifications to show but the other users don't
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mWorkUser.id);
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mCurrentUser.id);
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+ mSecondaryUser.id);
+ mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+
+ // THEN the work profile notification doesn't need to be redacted
+ assertFalse(mLockscreenUserManager.needsRedaction(mWorkProfileNotif));
+
+ // THEN the current user and secondary user notifications do need to be redacted
+ assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
+ assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif));
+ }
+
+ @Test
+ public void testWorkProfileRedacted_otherUsersNotRedacted() {
+ // GIVEN work profile doesn't allow private notifications to show but the other users do
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mWorkUser.id);
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1, mCurrentUser.id);
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+ mSecondaryUser.id);
+ mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+
+ // THEN the work profile notification needs to be redacted
+ assertTrue(mLockscreenUserManager.needsRedaction(mWorkProfileNotif));
+
+ // THEN the current user and secondary user notifications don't need to be redacted
+ assertFalse(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
+ assertFalse(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif));
+ }
+
+ @Test
+ public void testSecondaryUserNotRedacted_currentUserRedacted() {
+ // GIVEN secondary profile allows private notifications to show but the current user
+ // doesn't allow private notifications to show
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, mCurrentUser.id);
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+ mSecondaryUser.id);
+ mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+
+ // THEN the secondary profile notification still needs to be redacted because the current
+ // user's setting takes precedence
+ assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif));
}
@Test
@@ -233,6 +354,45 @@
assertFalse(mLockscreenUserManager.shouldShowOnKeyguard(entry));
}
+ @Test
+ public void testShowSilentNotificationsPeopleBucket_settingSaysHide() {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ NOTIFICATION_NEW_INTERRUPTION_MODEL, 1);
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0);
+
+ final Notification notification = mock(Notification.class);
+ when(notification.isForegroundService()).thenReturn(true);
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setImportance(IMPORTANCE_LOW)
+ .setNotification(notification)
+ .build();
+ entry.setBucket(BUCKET_PEOPLE);
+ assertFalse(mLockscreenUserManager.shouldShowOnKeyguard(entry));
+ }
+
+ @Test
+ public void testShowSilentNotificationsMediaBucket_settingSaysHide() {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ NOTIFICATION_NEW_INTERRUPTION_MODEL, 1);
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0);
+
+ final Notification notification = mock(Notification.class);
+ when(notification.isForegroundService()).thenReturn(true);
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setImportance(IMPORTANCE_LOW)
+ .setNotification(notification)
+ .build();
+ entry.setBucket(BUCKET_MEDIA_CONTROLS);
+ // always show media controls, even if they're silent
+ assertTrue(mLockscreenUserManager.shouldShowOnKeyguard(entry));
+ }
+
private class TestNotificationLockscreenUserManager
extends NotificationLockscreenUserManagerImpl {
public TestNotificationLockscreenUserManager(Context context) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
index a2599ec..b4cabfd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
@@ -32,6 +32,7 @@
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_FULL_PERSON
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_IMPORTANT_PERSON
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_PERSON
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
@@ -228,6 +229,56 @@
whenever(personNotificationIdentifier.getPeopleNotificationType(b.sbn, b.ranking))
.thenReturn(TYPE_IMPORTANT_PERSON)
+ whenever(personNotificationIdentifier.compareTo(TYPE_PERSON, TYPE_IMPORTANT_PERSON))
+ .thenReturn(TYPE_IMPORTANT_PERSON.compareTo(TYPE_PERSON))
+ whenever(personNotificationIdentifier.compareTo(TYPE_IMPORTANT_PERSON, TYPE_PERSON))
+ .thenReturn(TYPE_PERSON.compareTo(TYPE_IMPORTANT_PERSON))
+
+ assertEquals(
+ listOf(b, a),
+ rankingManager.updateRanking(null, listOf(a, b), "test"))
+ }
+
+ @Test
+ fun testSort_fullPeople() {
+ whenever(sectionsManager.isFilteringEnabled()).thenReturn(true)
+ val aN = Notification.Builder(mContext, "test")
+ .setStyle(Notification.MessagingStyle(""))
+ .build()
+ val a = NotificationEntryBuilder()
+ .setImportance(IMPORTANCE_HIGH)
+ .setPkg("pkg")
+ .setOpPkg("pkg")
+ .setTag("tag")
+ .setNotification(aN)
+ .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
+ .setUser(mContext.user)
+ .setOverrideGroupKey("")
+ .build()
+ whenever(personNotificationIdentifier.getPeopleNotificationType(a.sbn, a.ranking))
+ .thenReturn(TYPE_PERSON)
+
+ val bN = Notification.Builder(mContext, "test")
+ .setStyle(Notification.MessagingStyle(""))
+ .build()
+ val b = NotificationEntryBuilder()
+ .setImportance(IMPORTANCE_HIGH)
+ .setPkg("pkg2")
+ .setOpPkg("pkg2")
+ .setTag("tag")
+ .setNotification(bN)
+ .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT))
+ .setUser(mContext.user)
+ .setOverrideGroupKey("")
+ .build()
+ whenever(personNotificationIdentifier.getPeopleNotificationType(b.sbn, b.ranking))
+ .thenReturn(TYPE_FULL_PERSON)
+
+ whenever(personNotificationIdentifier.compareTo(TYPE_PERSON, TYPE_FULL_PERSON))
+ .thenReturn(TYPE_FULL_PERSON.compareTo(TYPE_PERSON))
+ whenever(personNotificationIdentifier.compareTo(TYPE_FULL_PERSON, TYPE_PERSON))
+ .thenReturn(TYPE_PERSON.compareTo(TYPE_FULL_PERSON))
+
assertEquals(
listOf(b, a),
rankingManager.updateRanking(null, listOf(a, b), "test"))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
index 7037891..b9055ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
@@ -146,7 +146,7 @@
@Test
public void testRecreateNotificationHeader_hasHeader() {
- mChildrenContainer.recreateNotificationHeader(null);
+ mChildrenContainer.recreateNotificationHeader(null, false);
Assert.assertNotNull("Children container must have a header after recreation",
mChildrenContainer.getCurrentHeaderView());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
index 9aa0fdd..a77f8c6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
@@ -62,6 +62,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.SysuiBaseFragmentTest;
import com.android.systemui.SysuiTestableContext;
+import com.android.systemui.accessibility.SystemActions;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.model.SysUiState;
@@ -102,6 +103,8 @@
private Divider mDivider;
@Mock
private Recents mRecents;
+ @Mock
+ private SystemActions mSystemActions;
private AccessibilityManagerWrapper mAccessibilityWrapper =
new AccessibilityManagerWrapper(mContext) {
@@ -257,6 +260,7 @@
() -> mock(StatusBar.class),
mock(ShadeController.class),
mock(NotificationRemoteInputManager.class),
+ mock(SystemActions.class),
mHandler);
}
diff --git a/packages/Tethering/jarjar-rules.txt b/packages/Tethering/jarjar-rules.txt
index c6efa41..e90a2cc 100644
--- a/packages/Tethering/jarjar-rules.txt
+++ b/packages/Tethering/jarjar-rules.txt
@@ -8,7 +8,6 @@
rule com.android.internal.util.IndentingPrintWriter.java* com.android.networkstack.tethering.util.IndentingPrintWriter.java@1
rule com.android.internal.util.IState.java* com.android.networkstack.tethering.util.IState.java@1
rule com.android.internal.util.MessageUtils* com.android.networkstack.tethering.util.MessageUtils@1
-rule com.android.internal.util.Preconditions* com.android.networkstack.tethering.util.Preconditions@1
rule com.android.internal.util.State* com.android.networkstack.tethering.util.State@1
rule com.android.internal.util.StateMachine* com.android.networkstack.tethering.util.StateMachine@1
rule com.android.internal.util.TrafficStatsConstants* com.android.networkstack.tethering.util.TrafficStatsConstants@1
diff --git a/packages/Tethering/res/values-mcc310-mnc004-ne/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ne/strings.xml
index 2a73300..12d6c2c 100644
--- a/packages/Tethering/res/values-mcc310-mnc004-ne/strings.xml
+++ b/packages/Tethering/res/values-mcc310-mnc004-ne/strings.xml
@@ -16,13 +16,9 @@
<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="no_upstream_notification_title" msgid="5030042590486713460">"Tethering has no internet"</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-ta/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ta/strings.xml
index ea04821..f4b15aa 100644
--- a/packages/Tethering/res/values-mcc310-mnc004-ta/strings.xml
+++ b/packages/Tethering/res/values-mcc310-mnc004-ta/strings.xml
@@ -16,13 +16,9 @@
<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="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-mcc311-mnc480-ne/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ne/strings.xml
index 617c50d..0a0aa21 100644
--- a/packages/Tethering/res/values-mcc311-mnc480-ne/strings.xml
+++ b/packages/Tethering/res/values-mcc311-mnc480-ne/strings.xml
@@ -16,13 +16,9 @@
<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="no_upstream_notification_title" msgid="611650570559011140">"Tethering has no internet"</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-ta/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ta/strings.xml
index 0e43759..2ea2467 100644
--- a/packages/Tethering/res/values-mcc311-mnc480-ta/strings.xml
+++ b/packages/Tethering/res/values-mcc311-mnc480-ta/strings.xml
@@ -16,13 +16,9 @@
<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="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/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java b/packages/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
index 25ddce4..320427c 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
@@ -43,7 +43,6 @@
import android.util.SparseIntArray;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
import com.android.internal.util.StateMachine;
import java.util.HashMap;
@@ -591,7 +590,9 @@
// Map from type to transports.
final int notFound = -1;
final int transport = sLegacyTypeToTransport.get(type, notFound);
- Preconditions.checkArgument(transport != notFound, "unknown legacy type: " + type);
+ if (transport == notFound) {
+ throw new IllegalArgumentException("unknown legacy type: " + type);
+ }
builder.addTransportType(transport);
if (type == TYPE_MOBILE_DUN) {
diff --git a/packages/Tethering/tests/unit/jarjar-rules.txt b/packages/Tethering/tests/unit/jarjar-rules.txt
index 921fbed..1ea56cd 100644
--- a/packages/Tethering/tests/unit/jarjar-rules.txt
+++ b/packages/Tethering/tests/unit/jarjar-rules.txt
@@ -4,7 +4,6 @@
rule com.android.internal.util.IndentingPrintWriter.java* com.android.networkstack.tethering.util.IndentingPrintWriter.java@1
rule com.android.internal.util.IState.java* com.android.networkstack.tethering.util.IState.java@1
rule com.android.internal.util.MessageUtils* com.android.networkstack.tethering.util.MessageUtils@1
-rule com.android.internal.util.Preconditions* com.android.networkstack.tethering.util.Preconditions@1
rule com.android.internal.util.State* com.android.networkstack.tethering.util.State@1
rule com.android.internal.util.StateMachine* com.android.networkstack.tethering.util.StateMachine@1
rule com.android.internal.util.TrafficStatsConstants* com.android.networkstack.tethering.util.TrafficStatsConstants@1
diff --git a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
index a1fc3fa..3505499 100644
--- a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
+++ b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
@@ -85,7 +85,6 @@
private final AccessibilityAction mLegacyNotificationsAction;
private final AccessibilityAction mLegacyQuickSettingsAction;
private final AccessibilityAction mLegacyPowerDialogAction;
- private final AccessibilityAction mLegacyToggleSplitScreenAction;
private final AccessibilityAction mLegacyLockScreenAction;
private final AccessibilityAction mLegacyTakeScreenshotAction;
@@ -142,10 +141,6 @@
AccessibilityService.GLOBAL_ACTION_POWER_DIALOG,
mContext.getResources().getString(
R.string.accessibility_system_action_power_dialog_label));
- mLegacyToggleSplitScreenAction = new AccessibilityAction(
- AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN,
- mContext.getResources().getString(
- R.string.accessibility_system_action_toggle_split_screen_label));
mLegacyLockScreenAction = new AccessibilityAction(
AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN,
mContext.getResources().getString(
@@ -235,10 +230,6 @@
systemActions.add(mLegacyPowerDialogAction);
}
if (!mRegisteredSystemActions.containsKey(
- AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN)) {
- systemActions.add(mLegacyToggleSplitScreenAction);
- }
- if (!mRegisteredSystemActions.containsKey(
AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN)) {
systemActions.add(mLegacyLockScreenAction);
}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index bc38fbf..4fee672 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -566,6 +566,7 @@
// a given distance perform a drag.
mState.startDragging();
mDraggingPointerId = pointerId;
+ adjustEventLocationForDrag(event);
event.setEdgeFlags(mReceivedPointerTracker.getLastReceivedDownEdgeFlags());
mDispatcher.sendMotionEvent(
event, MotionEvent.ACTION_DOWN, rawEvent, pointerIdBits, policyFlags);
@@ -793,10 +794,6 @@
*/
private void handleMotionEventStateDelegating(
MotionEvent event, MotionEvent rawEvent, int policyFlags) {
- if (mGestureDetector.isMultiFingerGesturesEnabled()) {
- // Multi-finger gestures conflict with this functionality.
- return;
- }
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
Slog.e(LOG_TAG, "Delegating state can only be reached if "
diff --git a/services/art-profile b/services/art-profile
index e2974a1..559356b 100644
--- a/services/art-profile
+++ b/services/art-profile
@@ -32290,7 +32290,6 @@
HSPLcom/android/server/pm/permission/BasePermission;->getName()Ljava/lang/String;
HSPLcom/android/server/pm/permission/BasePermission;->getProtectionLevel()I
HSPLcom/android/server/pm/permission/BasePermission;->getSourcePackageName()Ljava/lang/String;
-HSPLcom/android/server/pm/permission/BasePermission;->getSourcePackageSetting()Lcom/android/server/pm/PackageSettingBase;
PLcom/android/server/pm/permission/BasePermission;->getUid()I
HSPLcom/android/server/pm/permission/BasePermission;->isAppOp()Z
HSPLcom/android/server/pm/permission/BasePermission;->isAppPredictor()Z
@@ -32329,7 +32328,6 @@
HSPLcom/android/server/pm/permission/BasePermission;->setGids([IZ)V
HSPLcom/android/server/pm/permission/BasePermission;->setPermission(Landroid/content/pm/parsing/ComponentParseUtils$ParsedPermission;)V
PLcom/android/server/pm/permission/BasePermission;->setPermission(Landroid/content/pm/parsing/component/ParsedPermission;)V
-HSPLcom/android/server/pm/permission/BasePermission;->setSourcePackageSetting(Lcom/android/server/pm/PackageSettingBase;)V
HSPLcom/android/server/pm/permission/BasePermission;->updateDynamicPermission(Ljava/util/Collection;)V
HSPLcom/android/server/pm/permission/BasePermission;->writeLPr(Lorg/xmlpull/v1/XmlSerializer;)V
HSPLcom/android/server/pm/permission/DefaultPermissionGrantPolicy$1;-><init>(Lcom/android/server/pm/permission/DefaultPermissionGrantPolicy;Landroid/os/Looper;)V
diff --git a/services/art-profile-boot b/services/art-profile-boot
index fe4178a..addc906fc 100644
--- a/services/art-profile-boot
+++ b/services/art-profile-boot
@@ -171,7 +171,6 @@
Lcom/android/server/pm/PackageUsage;->readVersion1LP(Ljava/util/Map;Ljava/io/InputStream;Ljava/lang/StringBuffer;)V
Lcom/android/server/pm/PackageUsage;->parseAsLong(Ljava/lang/String;)J
Lcom/android/server/pm/CompilerStats;->read(Ljava/io/Reader;)Z
-Lcom/android/server/pm/permission/BasePermission;->getSourcePackageSetting()Lcom/android/server/pm/PackageSettingBase;
Lcom/android/server/pm/permission/PermissionManagerService;->updatePermissionSourcePackage(Ljava/lang/String;Landroid/content/pm/PackageParser$Package;)Z
Lcom/android/server/pm/permission/BasePermission;->isDynamic()Z
Lcom/android/server/pm/permission/PermissionManagerService;->cacheBackgroundToForegoundPermissionMapping()V
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
index a86d34d..79c9efa 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
@@ -319,9 +319,10 @@
}
@Override
- public void onContent(SurfaceControlViewHost.SurfacePackage surface)
+ public void onContent(SurfaceControlViewHost.SurfacePackage surface, int width,
+ int height)
throws RemoteException {
- callback.onContent(surface);
+ callback.onContent(surface, width, height);
surface.release();
}
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 992e984..dc35c77 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -318,6 +318,9 @@
private static final String SERIAL_ID_FILE = "serial_id";
+ private static final String SKIP_USER_FACING_DATA = "backup_skip_user_facing_data";
+ private static final String WALLPAPER_PACKAGE = "com.android.wallpaperbackup";
+
private final @UserIdInt int mUserId;
private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
private final TransportManager mTransportManager;
@@ -3553,6 +3556,40 @@
}
}
+ /**
+ * We want to skip backup/restore of certain packages if 'backup_skip_user_facing_data' is
+ * set to true in secure settings. See b/153940088 for details.
+ *
+ * TODO(b/154822946): Remove this logic in the next release.
+ */
+ public List<PackageInfo> filterUserFacingPackages(List<PackageInfo> packages) {
+ if (!shouldSkipUserFacingData()) {
+ return packages;
+ }
+
+ List<PackageInfo> filteredPackages = new ArrayList<>(packages.size());
+ for (PackageInfo packageInfo : packages) {
+ if (!shouldSkipPackage(packageInfo.packageName)) {
+ filteredPackages.add(packageInfo);
+ } else {
+ Slog.i(TAG, "Will skip backup/restore for " + packageInfo.packageName);
+ }
+ }
+
+ return filteredPackages;
+ }
+
+ @VisibleForTesting
+ public boolean shouldSkipUserFacingData() {
+ return Settings.Secure.getInt(mContext.getContentResolver(), SKIP_USER_FACING_DATA,
+ /* def */ 0) != 0;
+ }
+
+ @VisibleForTesting
+ public boolean shouldSkipPackage(String packageName) {
+ return WALLPAPER_PACKAGE.equals(packageName);
+ }
+
private void updateStateForTransport(String newTransportName) {
// Publish the name change
Settings.Secure.putStringForUser(mContext.getContentResolver(),
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
index f7f0138..738dd9bf 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -61,6 +61,7 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -131,7 +132,7 @@
private UserBackupManagerService mUserBackupManagerService;
private final Object mCancelLock = new Object();
- ArrayList<PackageInfo> mPackages;
+ List<PackageInfo> mPackages;
PackageInfo mCurrentPackage;
boolean mUpdateSchedule;
CountDownLatch mLatch;
@@ -249,6 +250,8 @@
null);
}
}
+
+ mPackages = backupManagerService.filterUserFacingPackages(mPackages);
}
private void registerTask() {
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index e434be6..12113fe 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -270,6 +270,8 @@
}
}
+ mAcceptSet = backupManagerService.filterUserFacingPackages(mAcceptSet);
+
if (MORE_DEBUG) {
Slog.v(TAG, "Restore; accept set size is " + mAcceptSet.size());
for (PackageInfo info : mAcceptSet) {
diff --git a/services/core/java/com/android/server/DynamicSystemService.java b/services/core/java/com/android/server/DynamicSystemService.java
index b6bbe19..b09b260 100644
--- a/services/core/java/com/android/server/DynamicSystemService.java
+++ b/services/core/java/com/android/server/DynamicSystemService.java
@@ -51,7 +51,7 @@
mContext = context;
}
- private IGsiService getGsiService() throws RemoteException {
+ private IGsiService getGsiService() {
checkPermission();
if (mGsiService != null) {
return mGsiService;
@@ -60,8 +60,7 @@
}
private void checkPermission() {
- if (mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires MANAGE_DYNAMIC_SYSTEM permission");
}
@@ -147,12 +146,12 @@
}
@Override
- public boolean isInUse() throws RemoteException {
+ public boolean isInUse() {
return SystemProperties.getBoolean("ro.gsid.image_running", false);
}
@Override
- public boolean isInstalled() throws RemoteException {
+ public boolean isInstalled() {
boolean installed = SystemProperties.getBoolean("gsid.image_installed", false);
Slog.i(TAG, "isInstalled(): " + installed);
return installed;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 0bd134c..bb0c0cf 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5364,7 +5364,7 @@
return false;
}
- if (!didSomething) {
+ if (didSomething) {
updateOomAdjLocked(app, OomAdjuster.OOM_ADJ_REASON_PROCESS_BEGIN);
checkTime(startTime, "attachApplicationLocked: after updateOomAdjLocked");
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index ee4697f..de9b02f 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -4133,9 +4133,6 @@
throws NumberFormatException,
XmlPullParserException, IOException {
int opCode = Integer.parseInt(parser.getAttributeValue(null, "n"));
- if (isIgnoredAppOp(opCode)) {
- return;
- }
Op op = new Op(uidState, pkgName, opCode, uidState.uid);
final int mode = XmlUtils.readIntAttribute(parser, "m",
@@ -4170,16 +4167,6 @@
ops.put(op.op, op);
}
- //TODO(b/149995538): Remove once this has reached all affected devices
- private static boolean isIgnoredAppOp(int op) {
- switch (op) {
- case AppOpsManager.OP_MANAGE_EXTERNAL_STORAGE:
- return true;
- default:
- return false;
- }
- }
-
void writeState() {
synchronized (mFile) {
FileOutputStream stream;
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index e6129b9..115899a 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -1069,7 +1069,7 @@
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
| Intent.FLAG_ACTIVITY_CLEAR_TOP);
final PendingIntent keyboardLayoutIntent = PendingIntent.getActivityAsUser(mContext, 0,
- intent, 0, null, UserHandle.CURRENT);
+ intent, PendingIntent.FLAG_IMMUTABLE, null, UserHandle.CURRENT);
Resources r = mContext.getResources();
Notification notification =
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index d8bf9ed..5e865e7 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -1574,8 +1574,15 @@
}
// Succeeded
- notifySessionCreatedToRouter(matchingRequest.mRouterRecord,
- toOriginalRequestId(uniqueRequestId), sessionInfo);
+ if (sessionInfo.isSystemSession()
+ && !matchingRequest.mRouterRecord.mHasModifyAudioRoutingPermission) {
+ notifySessionCreatedToRouter(matchingRequest.mRouterRecord,
+ toOriginalRequestId(uniqueRequestId),
+ mSystemProvider.getDefaultSessionInfo());
+ } else {
+ notifySessionCreatedToRouter(matchingRequest.mRouterRecord,
+ toOriginalRequestId(uniqueRequestId), sessionInfo);
+ }
mSessionToRouterMap.put(sessionInfo.getId(), routerRecord);
}
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 41d7fff..b585b49 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -36,6 +36,7 @@
import android.media.IAudioService;
import android.media.MediaRoute2Info;
import android.media.MediaRoute2ProviderInfo;
+import android.media.MediaRoute2ProviderService;
import android.media.RouteDiscoveryPreference;
import android.media.RoutingSessionInfo;
import android.os.Bundle;
@@ -47,6 +48,7 @@
import android.util.Log;
import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
import java.util.Objects;
@@ -80,6 +82,10 @@
RoutingSessionInfo mDefaultSessionInfo;
final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo();
+ private final Object mRequestLock = new Object();
+ @GuardedBy("mRequestLock")
+ private volatile SessionCreationRequest mPendingSessionCreationRequest;
+
final IAudioRoutesObserver.Stub mAudioRoutesObserver = new IAudioRoutesObserver.Stub() {
@Override
public void dispatchAudioRoutesChanged(final AudioRoutesInfo newRoutes) {
@@ -135,10 +141,27 @@
@Override
public void requestCreateSession(long requestId, String packageName, String routeId,
Bundle sessionHints) {
+ // Assume a router without MODIFY_AUDIO_ROUTING permission can't request with
+ // a route ID different from the default route ID. The service should've filtered.
+ if (TextUtils.equals(routeId, DEFAULT_ROUTE_ID)) {
+ mCallback.onSessionCreated(this, requestId, mDefaultSessionInfo);
+ return;
+ }
+ if (TextUtils.equals(routeId, mSelectedRouteId)) {
+ mCallback.onSessionCreated(this, requestId, mSessionInfos.get(0));
+ return;
+ }
+
+ synchronized (mRequestLock) {
+ // Handle the previous request as a failure if exists.
+ if (mPendingSessionCreationRequest != null) {
+ mCallback.onRequestFailed(this, mPendingSessionCreationRequest.mRequestId,
+ MediaRoute2ProviderService.REASON_UNKNOWN_ERROR);
+ }
+ mPendingSessionCreationRequest = new SessionCreationRequest(requestId, routeId);
+ }
transferToRoute(requestId, SYSTEM_SESSION_ID, routeId);
- mCallback.onSessionCreated(this, requestId, mSessionInfos.get(0));
- //TODO: We should call after the session info is changed.
}
@Override
@@ -280,6 +303,24 @@
}
RoutingSessionInfo newSessionInfo = builder.setProviderId(mUniqueId).build();
+
+ if (mPendingSessionCreationRequest != null) {
+ SessionCreationRequest sessionCreationRequest;
+ synchronized (mRequestLock) {
+ sessionCreationRequest = mPendingSessionCreationRequest;
+ mPendingSessionCreationRequest = null;
+ }
+ if (sessionCreationRequest != null) {
+ if (TextUtils.equals(mSelectedRouteId, sessionCreationRequest.mRouteId)) {
+ mCallback.onSessionCreated(this,
+ sessionCreationRequest.mRequestId, newSessionInfo);
+ } else {
+ mCallback.onRequestFailed(this, sessionCreationRequest.mRequestId,
+ MediaRoute2ProviderService.REASON_UNKNOWN_ERROR);
+ }
+ }
+ }
+
if (Objects.equals(oldSessionInfo, newSessionInfo)) {
return false;
} else {
@@ -310,6 +351,16 @@
mCallback.onSessionUpdated(this, sessionInfo);
}
+ private static class SessionCreationRequest {
+ final long mRequestId;
+ final String mRouteId;
+
+ SessionCreationRequest(long requestId, String routeId) {
+ this.mRequestId = requestId;
+ this.mRouteId = routeId;
+ }
+ }
+
private class VolumeChangeReceiver extends BroadcastReceiver {
// This will be called in the main thread.
@Override
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 44173c6..1951fc0 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -47,6 +47,7 @@
import static android.net.NetworkTemplate.buildTemplateWifiWildcard;
import static android.net.TrafficStats.KB_IN_BYTES;
import static android.net.TrafficStats.MB_IN_BYTES;
+import static android.net.TrafficStats.UNSUPPORTED;
import static android.os.Trace.TRACE_TAG_NETWORK;
import static android.provider.Settings.Global.NETSTATS_AUGMENT_ENABLED;
import static android.provider.Settings.Global.NETSTATS_COMBINE_SUBTYPE_ENABLED;
@@ -1031,6 +1032,10 @@
@Override
public long getUidStats(int uid, int type) {
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid != android.os.Process.SYSTEM_UID && callingUid != uid) {
+ return UNSUPPORTED;
+ }
return nativeGetUidStat(uid, type, checkBpfStatsEnable());
}
diff --git a/services/core/java/com/android/server/om/OverlayActorEnforcer.java b/services/core/java/com/android/server/om/OverlayActorEnforcer.java
index 9197956..ef6655d 100644
--- a/services/core/java/com/android/server/om/OverlayActorEnforcer.java
+++ b/services/core/java/com/android/server/om/OverlayActorEnforcer.java
@@ -23,7 +23,6 @@
import android.content.pm.PackageInfo;
import android.net.Uri;
import android.os.Process;
-import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Pair;
@@ -140,7 +139,7 @@
return ActorState.MISSING_LEGACY_PERMISSION;
}
}
- } catch (RemoteException | IOException e) {
+ } catch (IOException e) {
return ActorState.ERROR_READING_OVERLAYABLE;
}
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index c81f7cd..84e43fc 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -1151,9 +1151,8 @@
@Override
public boolean doesTargetDefineOverlayable(String targetPackageName, int userId)
- throws RemoteException, IOException {
- PackageInfo packageInfo = mPackageManager.getPackageInfo(targetPackageName, 0,
- userId);
+ throws IOException {
+ PackageInfo packageInfo = getPackageInfo(targetPackageName, userId);
if (packageInfo == null) {
throw new IOException("Unable to get target package");
}
diff --git a/services/core/java/com/android/server/om/OverlayableInfoCallback.java b/services/core/java/com/android/server/om/OverlayableInfoCallback.java
index 6b81884..5066ecd 100644
--- a/services/core/java/com/android/server/om/OverlayableInfoCallback.java
+++ b/services/core/java/com/android/server/om/OverlayableInfoCallback.java
@@ -22,7 +22,6 @@
import android.content.om.OverlayableInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.os.RemoteException;
import com.android.server.pm.PackageManagerServiceUtils;
@@ -68,8 +67,7 @@
/**
* @return true if the target package has declared an overlayable
*/
- boolean doesTargetDefineOverlayable(String targetPackageName, int userId)
- throws RemoteException, IOException;
+ boolean doesTargetDefineOverlayable(String targetPackageName, int userId) throws IOException;
/**
* @throws SecurityException containing message if the caller doesn't have the given
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 1d5c304..385ace8 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -746,9 +746,8 @@
}
UserHandle user = UserHandle.of(injectCallingUserId());
- if (mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
- == PackageManager.PERMISSION_GRANTED) {
+ if (injectHasInteractAcrossUsersFullPermission(injectBinderCallingPid(),
+ injectBinderCallingUid())) {
user = null;
}
@@ -1053,29 +1052,6 @@
}
public static class ShortcutChangeHandler implements LauncherApps.ShortcutChangeCallback {
-
- static class QueryInfo {
- final long mChangedSince;
- final String mPackage;
- final List<String> mShortcutIds;
- final List<LocusId> mLocusIds;
- final ComponentName mActivity;
- final int mQueryFlags;
- final UserHandle mCallbackUser;
-
- QueryInfo(long changedSince, String packageName, List<String> shortcutIds,
- List<LocusId> locusIds, ComponentName activity, int flags,
- UserHandle callbackUser) {
- mChangedSince = changedSince;
- mPackage = packageName;
- mShortcutIds = shortcutIds;
- mLocusIds = locusIds;
- mActivity = activity;
- mQueryFlags = flags;
- mCallbackUser = callbackUser;
- }
- }
-
private final UserManagerInternal mUserManagerInternal;
ShortcutChangeHandler(UserManagerInternal userManager) {
@@ -1088,9 +1064,7 @@
public synchronized void addShortcutChangeCallback(IShortcutChangeCallback callback,
ShortcutQueryWrapper query, UserHandle user) {
mCallbacks.unregister(callback);
- mCallbacks.register(callback, new QueryInfo(query.getChangedSince(),
- query.getPackage(), query.getShortcutIds(), query.getLocusIds(),
- query.getActivity(), query.getQueryFlags(), user));
+ mCallbacks.register(callback, new Pair<>(query, user));
}
public synchronized void removeShortcutChangeCallback(
@@ -1116,16 +1090,19 @@
for (int i = 0; i < count; i++) {
final IShortcutChangeCallback callback = mCallbacks.getBroadcastItem(i);
- final QueryInfo query = (QueryInfo) mCallbacks.getBroadcastCookie(i);
+ final Pair<ShortcutQueryWrapper, UserHandle> cookie =
+ (Pair<ShortcutQueryWrapper, UserHandle>)
+ mCallbacks.getBroadcastCookie(i);
- if (query.mCallbackUser != null && !hasUserAccess(query.mCallbackUser, user)) {
+ final UserHandle callbackUser = cookie.second;
+ if (callbackUser != null && !hasUserAccess(callbackUser, user)) {
// Callback owner does not have access to the shortcuts' user.
continue;
}
// Filter the list by query, if any matches exists, send via callback.
- List<ShortcutInfo> matchedList =
- filterShortcutsByQuery(packageName, shortcuts, query);
+ List<ShortcutInfo> matchedList = filterShortcutsByQuery(packageName, shortcuts,
+ cookie.first, shortcutsRemoved);
if (!CollectionUtils.isEmpty(matchedList)) {
try {
if (shortcutsRemoved) {
@@ -1143,21 +1120,25 @@
}
public static List<ShortcutInfo> filterShortcutsByQuery(String packageName,
- List<ShortcutInfo> shortcuts, QueryInfo query) {
- if (query.mPackage != null && query.mPackage != packageName) {
+ List<ShortcutInfo> shortcuts, ShortcutQueryWrapper query,
+ boolean shortcutsRemoved) {
+ final long changedSince = query.getChangedSince();
+ final String queryPackage = query.getPackage();
+ final List<String> shortcutIds = query.getShortcutIds();
+ final List<LocusId> locusIds = query.getLocusIds();
+ final ComponentName activity = query.getActivity();
+ final int flags = query.getQueryFlags();
+
+ if (queryPackage != null && !queryPackage.equals(packageName)) {
return null;
}
List<ShortcutInfo> matches = new ArrayList<>();
- final boolean matchDynamic =
- (query.mQueryFlags & ShortcutQuery.FLAG_MATCH_DYNAMIC) != 0;
- final boolean matchPinned =
- (query.mQueryFlags & ShortcutQuery.FLAG_MATCH_PINNED) != 0;
- final boolean matchManifest =
- (query.mQueryFlags & ShortcutQuery.FLAG_MATCH_MANIFEST) != 0;
- final boolean matchCached =
- (query.mQueryFlags & ShortcutQuery.FLAG_MATCH_CACHED) != 0;
+ final boolean matchDynamic = (flags & ShortcutQuery.FLAG_MATCH_DYNAMIC) != 0;
+ final boolean matchPinned = (flags & ShortcutQuery.FLAG_MATCH_PINNED) != 0;
+ final boolean matchManifest = (flags & ShortcutQuery.FLAG_MATCH_MANIFEST) != 0;
+ final boolean matchCached = (flags & ShortcutQuery.FLAG_MATCH_CACHED) != 0;
final int shortcutFlags = (matchDynamic ? ShortcutInfo.FLAG_DYNAMIC : 0)
| (matchPinned ? ShortcutInfo.FLAG_PINNED : 0)
| (matchManifest ? ShortcutInfo.FLAG_MANIFEST : 0)
@@ -1166,24 +1147,19 @@
for (int i = 0; i < shortcuts.size(); i++) {
final ShortcutInfo si = shortcuts.get(i);
- if (query.mActivity != null && !query.mActivity.equals(si.getActivity())) {
+ if (activity != null && !activity.equals(si.getActivity())) {
continue;
}
-
- if (query.mChangedSince != 0
- && query.mChangedSince > si.getLastChangedTimestamp()) {
+ if (changedSince != 0 && changedSince > si.getLastChangedTimestamp()) {
continue;
}
-
- if (query.mShortcutIds != null && !query.mShortcutIds.contains(si.getId())) {
+ if (shortcutIds != null && !shortcutIds.contains(si.getId())) {
continue;
}
-
- if (query.mLocusIds != null && !query.mLocusIds.contains(si.getLocusId())) {
+ if (locusIds != null && !locusIds.contains(si.getLocusId())) {
continue;
}
-
- if ((shortcutFlags & si.getFlags()) != 0) {
+ if (shortcutsRemoved || (shortcutFlags & si.getFlags()) != 0) {
matches.add(si);
}
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 8e7eaf6..d9275f5 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -163,6 +163,7 @@
private static final int MSG_STREAM_VALIDATE_AND_COMMIT = 1;
private static final int MSG_INSTALL = 2;
private static final int MSG_ON_PACKAGE_INSTALLED = 3;
+ private static final int MSG_SESSION_VERIFICATION_FAILURE = 4;
/** XML constants used for persisting a session */
static final String TAG_SESSION = "session";
@@ -449,6 +450,11 @@
packageName, returnCode, message, extras);
break;
+ case MSG_SESSION_VERIFICATION_FAILURE:
+ final int error = msg.arg1;
+ final String detailMessage = (String) msg.obj;
+ onSessionVerificationFailure(error, detailMessage);
+ break;
}
return true;
@@ -1479,15 +1485,22 @@
}
private PackageManagerException onSessionVerificationFailure(PackageManagerException e) {
- // Session is sealed but could not be verified, we need to destroy it.
- destroyInternal();
- // Dispatch message to remove session from PackageInstallerService.
- dispatchSessionFinished(e.error, ExceptionUtils.getCompleteMessage(e), null);
-
+ onSessionVerificationFailure(e.error, ExceptionUtils.getCompleteMessage(e));
return e;
}
+ private void onSessionVerificationFailure(int error, String detailMessage) {
+ // Session is sealed but could not be verified, we need to destroy it.
+ destroyInternal();
+ // Dispatch message to remove session from PackageInstallerService.
+ dispatchSessionFinished(error, detailMessage, null);
+ }
+
private void onDataLoaderUnrecoverable() {
+ if (TextUtils.isEmpty(mPackageName)) {
+ // The package has not been installed.
+ return;
+ }
final PackageManagerService packageManagerService = mPm;
final String packageName = mPackageName;
mHandler.post(() -> {
@@ -2610,12 +2623,14 @@
case IDataLoaderStatusListener.DATA_LOADER_STOPPED:
case IDataLoaderStatusListener.DATA_LOADER_DESTROYED:
return;
- case IDataLoaderStatusListener.DATA_LOADER_UNRECOVERABLE:
- onDataLoaderUnrecoverable();
- return;
}
if (mDestroyed || mDataLoaderFinished) {
+ switch (status) {
+ case IDataLoaderStatusListener.DATA_LOADER_UNRECOVERABLE:
+ onDataLoaderUnrecoverable();
+ return;
+ }
return;
}
@@ -2623,9 +2638,8 @@
IDataLoader dataLoader = dataLoaderManager.getDataLoader(dataLoaderId);
if (dataLoader == null) {
mDataLoaderFinished = true;
- onSessionVerificationFailure(
- new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE,
- "Failure to obtain data loader"));
+ dispatchSessionVerificationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE,
+ "Failure to obtain data loader");
return;
}
@@ -2670,14 +2684,18 @@
}
case IDataLoaderStatusListener.DATA_LOADER_IMAGE_NOT_READY: {
mDataLoaderFinished = true;
- onSessionVerificationFailure(
- new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE,
- "Failed to prepare image."));
+ dispatchSessionVerificationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE,
+ "Failed to prepare image.");
if (manualStartAndDestroy) {
dataLoader.destroy(dataLoaderId);
}
break;
}
+ case IDataLoaderStatusListener.DATA_LOADER_UNRECOVERABLE:
+ mDataLoaderFinished = true;
+ dispatchSessionVerificationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE,
+ "DataLoader reported unrecoverable failure.");
+ return;
}
} catch (RemoteException e) {
// In case of streaming failure we don't want to fail or commit the session.
@@ -2708,6 +2726,11 @@
return false;
}
+ private void dispatchSessionVerificationFailure(int error, String detailMessage) {
+ mHandler.obtainMessage(MSG_SESSION_VERIFICATION_FAILURE, error, -1,
+ detailMessage).sendToTarget();
+ }
+
@Override
public int[] getChildSessionIds() {
final int[] childSessionIds = mChildSessionIds.copyKeys();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 8b191dd..90ca83e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -17122,7 +17122,12 @@
// "updating same package" could also involve key-rotation.
final boolean sigsOk;
final String sourcePackageName = bp.getSourcePackageName();
- final PackageSettingBase sourcePackageSetting = bp.getSourcePackageSetting();
+ final PackageSetting sourcePackageSetting;
+ synchronized (mLock) {
+ sourcePackageSetting = mSettings.getPackageLPr(sourcePackageName);
+ }
+ final SigningDetails sourceSigningDetails = (sourcePackageSetting == null
+ ? SigningDetails.UNKNOWN : sourcePackageSetting.getSigningDetails());
final KeySetManagerService ksms = mSettings.mKeySetManagerService;
if (sourcePackageName.equals(parsedPackage.getPackageName())
&& (ksms.shouldCheckUpgradeKeySetLocked(
@@ -17133,18 +17138,19 @@
// in the event of signing certificate rotation, we need to see if the
// package's certificate has rotated from the current one, or if it is an
// older certificate with which the current is ok with sharing permissions
- if (sourcePackageSetting.signatures.mSigningDetails.checkCapability(
+ if (sourceSigningDetails.checkCapability(
parsedPackage.getSigningDetails(),
PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
sigsOk = true;
} else if (parsedPackage.getSigningDetails().checkCapability(
- sourcePackageSetting.signatures.mSigningDetails,
+ sourceSigningDetails,
PackageParser.SigningDetails.CertCapabilities.PERMISSION)) {
-
// the scanned package checks out, has signing certificate rotation
// history, and is newer; bring it over
- sourcePackageSetting.signatures.mSigningDetails =
- parsedPackage.getSigningDetails();
+ synchronized (mLock) {
+ sourcePackageSetting.signatures.mSigningDetails =
+ parsedPackage.getSigningDetails();
+ }
sigsOk = true;
} else {
sigsOk = false;
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 1fc0a38..71a4bb4 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -256,7 +256,7 @@
if (shortcut != null) {
mShortcutUser.mService.removeIconLocked(shortcut);
shortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_PINNED
- | ShortcutInfo.FLAG_MANIFEST);
+ | ShortcutInfo.FLAG_MANIFEST | ShortcutInfo.FLAG_CACHED);
}
return shortcut;
}
@@ -281,8 +281,10 @@
* invisible.
*
* It checks the max number of dynamic shortcuts.
+ *
+ * @return True if it replaced an existing shortcut, False otherwise.
*/
- public void addOrReplaceDynamicShortcut(@NonNull ShortcutInfo newShortcut) {
+ public boolean addOrReplaceDynamicShortcut(@NonNull ShortcutInfo newShortcut) {
Preconditions.checkArgument(newShortcut.isEnabled(),
"add/setDynamicShortcuts() cannot publish disabled shortcuts");
@@ -291,38 +293,59 @@
final ShortcutInfo oldShortcut = mShortcuts.get(newShortcut.getId());
+ final boolean replaced;
+
final boolean wasPinned;
+ final boolean wasCached;
if (oldShortcut == null) {
+ replaced = false;
wasPinned = false;
+ wasCached = false;
} else {
// It's an update case.
// Make sure the target is updatable. (i.e. should be mutable.)
oldShortcut.ensureUpdatableWith(newShortcut, /*isUpdating=*/ false);
+ replaced = true;
wasPinned = oldShortcut.isPinned();
+ wasCached = oldShortcut.isCached();
}
// If it was originally pinned, the new one should be pinned too.
if (wasPinned) {
newShortcut.addFlags(ShortcutInfo.FLAG_PINNED);
}
+ if (wasCached) {
+ newShortcut.addFlags(ShortcutInfo.FLAG_CACHED);
+ }
forceReplaceShortcutInner(newShortcut);
+ return replaced;
}
/**
* Push a shortcut. If the max number of dynamic shortcuts is already reached, remove the
* shortcut with the lowest rank before adding the new shortcut.
+ *
+ * Any shortcut that gets altered (removed or changed) as a result of this push operation will
+ * be included and returned in changedShortcuts.
+ *
+ * @return True if a shortcut had to be removed to complete this operation, False otherwise.
*/
- public boolean pushDynamicShortcut(@NonNull ShortcutInfo newShortcut) {
+ public boolean pushDynamicShortcut(@NonNull ShortcutInfo newShortcut,
+ @NonNull List<ShortcutInfo> changedShortcuts) {
Preconditions.checkArgument(newShortcut.isEnabled(),
"pushDynamicShortcuts() cannot publish disabled shortcuts");
newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC);
+ changedShortcuts.clear();
final ShortcutInfo oldShortcut = mShortcuts.get(newShortcut.getId());
boolean wasPinned = false;
+ boolean wasCached = false;
+
+ boolean deleted = false;
if (oldShortcut == null) {
final ShortcutService service = mShortcutUser.mService;
@@ -343,10 +366,11 @@
// All shortcuts are manifest shortcuts and cannot be removed.
Slog.e(TAG, "Failed to remove manifest shortcut while pushing dynamic shortcut "
+ newShortcut.getId());
- return false;
+ return true; // poppedShortcuts is empty which indicates a failure.
}
- deleteDynamicWithId(shortcut.getId(), /*ignoreInvisible=*/ true);
+ changedShortcuts.add(shortcut);
+ deleted = deleteDynamicWithId(shortcut.getId(), /*ignoreInvisible=*/ true) != null;
}
} else {
// It's an update case.
@@ -354,15 +378,19 @@
oldShortcut.ensureUpdatableWith(newShortcut, /*isUpdating=*/ false);
wasPinned = oldShortcut.isPinned();
+ wasCached = oldShortcut.isCached();
}
- // If it was originally pinned, the new one should be pinned too.
+ // If it was originally pinned or cached, the new one should be pinned or cached too.
if (wasPinned) {
newShortcut.addFlags(ShortcutInfo.FLAG_PINNED);
}
+ if (wasCached) {
+ newShortcut.addFlags(ShortcutInfo.FLAG_CACHED);
+ }
forceReplaceShortcutInner(newShortcut);
- return true;
+ return deleted;
}
/**
@@ -371,8 +399,7 @@
* @return List of removed shortcuts.
*/
private List<ShortcutInfo> removeOrphans() {
- ArrayList<String> removeList = null; // Lazily initialize.
- List<ShortcutInfo> removedShortcuts = null;
+ List<ShortcutInfo> removeList = null;
for (int i = mShortcuts.size() - 1; i >= 0; i--) {
final ShortcutInfo si = mShortcuts.valueAt(i);
@@ -381,18 +408,16 @@
if (removeList == null) {
removeList = new ArrayList<>();
- removedShortcuts = new ArrayList<>();
}
- removeList.add(si.getId());
- removedShortcuts.add(si);
+ removeList.add(si);
}
if (removeList != null) {
for (int i = removeList.size() - 1; i >= 0; i--) {
- forceDeleteShortcutInner(removeList.get(i));
+ forceDeleteShortcutInner(removeList.get(i).getId());
}
}
- return removedShortcuts;
+ return removeList;
}
/**
@@ -424,68 +449,69 @@
* Remove a dynamic shortcut by ID. It'll be removed from the dynamic set, but if the shortcut
* is pinned or cached, it'll remain as a pinned or cached shortcut, and is still enabled.
*
- * @return true if it's removed, or false if it was not actually removed because it is either
+ * @return The deleted shortcut, or null if it was not actually removed because it is either
* pinned or cached.
*/
- public boolean deleteDynamicWithId(@NonNull String shortcutId, boolean ignoreInvisible) {
- final ShortcutInfo removed = deleteOrDisableWithId(
+ public ShortcutInfo deleteDynamicWithId(@NonNull String shortcutId, boolean ignoreInvisible) {
+ return deleteOrDisableWithId(
shortcutId, /* disable =*/ false, /* overrideImmutable=*/ false, ignoreInvisible,
ShortcutInfo.DISABLED_REASON_NOT_DISABLED);
- return removed == null;
}
/**
- * Disable a dynamic shortcut by ID. It'll be removed from the dynamic set, but if the shortcut
+ * Disable a dynamic shortcut by ID. It'll be removed from the dynamic set, but if the shortcut
* is pinned, it'll remain as a pinned shortcut, but will be disabled.
*
- * @return true if it's actually removed because it wasn't pinned, or false if it's still
- * pinned.
+ * @return Shortcut if the disabled shortcut got removed because it wasn't pinned. Or null if
+ * it's still pinned.
*/
- private boolean disableDynamicWithId(@NonNull String shortcutId, boolean ignoreInvisible,
+ private ShortcutInfo disableDynamicWithId(@NonNull String shortcutId, boolean ignoreInvisible,
int disabledReason) {
- final ShortcutInfo disabled = deleteOrDisableWithId(
- shortcutId, /* disable =*/ true, /* overrideImmutable=*/ false, ignoreInvisible,
- disabledReason);
- return disabled == null;
+ return deleteOrDisableWithId(shortcutId, /* disable =*/ true, /* overrideImmutable=*/ false,
+ ignoreInvisible, disabledReason);
}
/**
* Remove a long lived shortcut by ID. If the shortcut is pinned, it'll remain as a pinned
* shortcut, and is still enabled.
*
- * @return true if it's actually removed because it wasn't pinned, or false if it's still
- * pinned.
+ * @return The deleted shortcut, or null if it was not actually removed because it's pinned.
*/
- public boolean deleteLongLivedWithId(@NonNull String shortcutId, boolean ignoreInvisible) {
+ public ShortcutInfo deleteLongLivedWithId(@NonNull String shortcutId, boolean ignoreInvisible) {
final ShortcutInfo shortcut = mShortcuts.get(shortcutId);
if (shortcut != null) {
shortcut.clearFlags(ShortcutInfo.FLAG_CACHED);
}
- final ShortcutInfo removed = deleteOrDisableWithId(
+ return deleteOrDisableWithId(
shortcutId, /* disable =*/ false, /* overrideImmutable=*/ false, ignoreInvisible,
ShortcutInfo.DISABLED_REASON_NOT_DISABLED);
- return removed == null;
}
/**
* Disable a dynamic shortcut by ID. It'll be removed from the dynamic set, but if the shortcut
* is pinned, it'll remain as a pinned shortcut but will be disabled.
+ *
+ * @return Shortcut if the disabled shortcut got removed because it wasn't pinned. Or null if
+ * it's still pinned.
*/
- public void disableWithId(@NonNull String shortcutId, String disabledMessage,
+ public ShortcutInfo disableWithId(@NonNull String shortcutId, String disabledMessage,
int disabledMessageResId, boolean overrideImmutable, boolean ignoreInvisible,
int disabledReason) {
- final ShortcutInfo disabled = deleteOrDisableWithId(shortcutId, /* disable =*/ true,
+ final ShortcutInfo deleted = deleteOrDisableWithId(shortcutId, /* disable =*/ true,
overrideImmutable, ignoreInvisible, disabledReason);
+ // If disabled id still exists, it is pinned and we need to update the disabled message.
+ final ShortcutInfo disabled = mShortcuts.get(shortcutId);
if (disabled != null) {
if (disabledMessage != null) {
disabled.setDisabledMessage(disabledMessage);
} else if (disabledMessageResId != 0) {
disabled.setDisabledMessageResId(disabledMessageResId);
-
mShortcutUser.mService.fixUpShortcutResourceNamesAndValues(disabled);
}
}
+
+ return deleted;
}
@Nullable
@@ -521,10 +547,10 @@
oldShortcut.setActivity(null);
}
- return oldShortcut;
+ return null;
} else {
forceDeleteShortcutInner(shortcutId);
- return null;
+ return oldShortcut;
}
}
@@ -762,7 +788,7 @@
// Get the list of all dynamic shortcuts in this package.
final ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
- findAll(shortcuts, ShortcutInfo::isDynamicVisible,
+ findAll(shortcuts, ShortcutInfo::isNonManifestVisible,
ShortcutInfo.CLONE_REMOVE_FOR_APP_PREDICTION);
final List<ShortcutManager.ShareShortcutInfo> result = new ArrayList<>();
@@ -807,7 +833,8 @@
// Get the list of all dynamic shortcuts in this package
final ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
- findAll(shortcuts, ShortcutInfo::isDynamicVisible, ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
+ findAll(shortcuts, ShortcutInfo::isNonManifestVisible,
+ ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
int sharingShortcutCount = 0;
for (int i = 0; i < shortcuts.size(); i++) {
@@ -1004,7 +1031,7 @@
"%s is no longer main activity. Disabling shorcut %s.",
getPackageName(), si.getId()));
if (disableDynamicWithId(si.getId(), /*ignoreInvisible*/ false,
- ShortcutInfo.DISABLED_REASON_APP_CHANGED)) {
+ ShortcutInfo.DISABLED_REASON_APP_CHANGED) != null) {
continue; // Actually removed.
}
// Still pinned, so fall-through and possibly update the resources.
diff --git a/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
index 6fd997d..d4a02a9 100644
--- a/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
+++ b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
@@ -35,7 +35,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
-import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/**
@@ -513,10 +513,6 @@
launcher.addPinnedShortcut(appPackageName, appUserId, shortcutId,
/*forPinRequest=*/ true);
- if (changedShortcuts == null) {
- changedShortcuts = new ArrayList<>(1);
- }
- changedShortcuts.add(original);
if (current == null) {
if (DEBUG) {
@@ -526,6 +522,8 @@
}
ps.adjustRanks(); // Shouldn't be needed, but just in case.
+
+ changedShortcuts = Collections.singletonList(ps.findShortcutById(shortcutId));
}
mService.verifyStates();
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 8d53d15..e4ae007 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -1666,11 +1666,10 @@
* - Write to file
*/
void packageShortcutsChanged(@NonNull String packageName, @UserIdInt int userId,
- @Nullable List<ShortcutInfo> addedOrUpdatedShortcuts,
- @Nullable List<ShortcutInfo> removedShortcuts) {
+ @Nullable final List<ShortcutInfo> changedShortcuts,
+ @Nullable final List<ShortcutInfo> removedShortcuts) {
notifyListeners(packageName, userId);
- notifyShortcutChangeCallbacks(packageName, userId, addedOrUpdatedShortcuts,
- removedShortcuts);
+ notifyShortcutChangeCallbacks(packageName, userId, changedShortcuts, removedShortcuts);
scheduleSaveUser(userId);
}
@@ -1699,8 +1698,11 @@
}
private void notifyShortcutChangeCallbacks(@NonNull String packageName, @UserIdInt int userId,
- @Nullable List<ShortcutInfo> addedOrUpdatedShortcuts,
- @Nullable List<ShortcutInfo> removedShortcuts) {
+ @Nullable final List<ShortcutInfo> changedShortcuts,
+ @Nullable final List<ShortcutInfo> removedShortcuts) {
+ final List<ShortcutInfo> changedList = removeNonKeyFields(changedShortcuts);
+ final List<ShortcutInfo> removedList = removeNonKeyFields(removedShortcuts);
+
final UserHandle user = UserHandle.of(userId);
injectPostToHandler(() -> {
try {
@@ -1713,12 +1715,11 @@
copy = new ArrayList<>(mShortcutChangeCallbacks);
}
for (int i = copy.size() - 1; i >= 0; i--) {
- if (!CollectionUtils.isEmpty(addedOrUpdatedShortcuts)) {
- copy.get(i).onShortcutsAddedOrUpdated(packageName, addedOrUpdatedShortcuts,
- user);
+ if (!CollectionUtils.isEmpty(changedList)) {
+ copy.get(i).onShortcutsAddedOrUpdated(packageName, changedList, user);
}
- if (!CollectionUtils.isEmpty(removedShortcuts)) {
- copy.get(i).onShortcutsRemoved(packageName, removedShortcuts, user);
+ if (!CollectionUtils.isEmpty(removedList)) {
+ copy.get(i).onShortcutsRemoved(packageName, removedList, user);
}
}
} catch (Exception ignore) {
@@ -1726,6 +1727,25 @@
});
}
+ private List<ShortcutInfo> removeNonKeyFields(@Nullable List<ShortcutInfo> shortcutInfos) {
+ if (CollectionUtils.isEmpty(shortcutInfos)) {
+ return shortcutInfos;
+ }
+
+ final int size = shortcutInfos.size();
+ List<ShortcutInfo> keyFieldOnlyShortcuts = new ArrayList<>(size);
+
+ for (int i = 0; i < size; i++) {
+ final ShortcutInfo si = shortcutInfos.get(i);
+ if (si.hasKeyFieldsOnly()) {
+ keyFieldOnlyShortcuts.add(si);
+ } else {
+ keyFieldOnlyShortcuts.add(si.clone(ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO));
+ }
+ }
+ return keyFieldOnlyShortcuts;
+ }
+
/**
* Clean up / validate an incoming shortcut.
* - Make sure all mandatory fields are set.
@@ -1820,6 +1840,7 @@
final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission(
injectBinderCallingPid(), injectBinderCallingUid());
+ List<ShortcutInfo> changedShortcuts = null;
List<ShortcutInfo> removedShortcuts = null;
synchronized (mLock) {
@@ -1846,7 +1867,12 @@
fixUpIncomingShortcutInfo(newShortcuts.get(i), /* forUpdate= */ false);
}
- // First, remove all un-pinned; dynamic shortcuts
+ ArrayList<ShortcutInfo> cachedOrPinned = new ArrayList<>();
+ ps.findAll(cachedOrPinned, (ShortcutInfo si) -> si.isVisibleToPublisher()
+ && si.isDynamic() && (si.isCached() || si.isPinned()),
+ ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
+
+ // First, remove all un-pinned and non-cached; dynamic shortcuts
removedShortcuts = ps.deleteAllDynamicShortcuts(/*ignoreInvisible=*/ true);
// Then, add/update all. We need to make sure to take over "pinned" flag.
@@ -1857,8 +1883,12 @@
// Lastly, adjust the ranks.
ps.adjustRanks();
+
+ changedShortcuts = prepareChangedShortcuts(
+ cachedOrPinned, newShortcuts, removedShortcuts, ps);
}
- packageShortcutsChanged(packageName, userId, newShortcuts, removedShortcuts);
+
+ packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts);
verifyStates();
@@ -1916,6 +1946,11 @@
"ShortcutInfo.enabled cannot be changed with updateShortcuts()");
}
+ if (target.isLongLived() != source.isLongLived()) {
+ Slog.w(TAG,
+ "ShortcutInfo.longLived cannot be changed with updateShortcuts()");
+ }
+
// When updating the rank, we need to insert between existing ranks, so set
// this setRankChanged, and also copy the implicit rank fo adjustRanks().
if (source.hasRank()) {
@@ -2026,8 +2061,8 @@
verifyCaller(packageName, userId);
verifyShortcutInfoPackage(packageName, shortcut);
- final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission(
- injectBinderCallingPid(), injectBinderCallingUid());
+ List<ShortcutInfo> changedShortcuts = new ArrayList<>();
+ List<ShortcutInfo> removedShortcuts = null;
synchronized (mLock) {
throwIfUserLockedL(userId);
@@ -2052,14 +2087,22 @@
shortcut.setRankChanged();
// Push it.
- if (!ps.pushDynamicShortcut(shortcut)) {
- return;
+ boolean deleted = ps.pushDynamicShortcut(shortcut, changedShortcuts);
+
+ if (deleted) {
+ if (changedShortcuts.isEmpty()) {
+ return; // Failed to push.
+ }
+ removedShortcuts = Collections.singletonList(changedShortcuts.get(0));
+ changedShortcuts.clear();
}
+ changedShortcuts.add(shortcut);
// Lastly, adjust the ranks.
ps.adjustRanks();
}
- packageShortcutsChanged(packageName, userId, Collections.singletonList(shortcut), null);
+
+ packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts);
verifyStates();
}
@@ -2147,6 +2190,9 @@
verifyCaller(packageName, userId);
Objects.requireNonNull(shortcutIds, "shortcutIds must be provided");
+ List<ShortcutInfo> changedShortcuts = null;
+ List<ShortcutInfo> removedShortcuts = null;
+
synchronized (mLock) {
throwIfUserLockedL(userId);
@@ -2163,17 +2209,30 @@
if (!ps.isShortcutExistsAndVisibleToPublisher(id)) {
continue;
}
- ps.disableWithId(id,
+
+ final ShortcutInfo deleted = ps.disableWithId(id,
disabledMessageString, disabledMessageResId,
/* overrideImmutable=*/ false, /*ignoreInvisible=*/ true,
ShortcutInfo.DISABLED_REASON_BY_APP);
+
+ if (deleted == null) {
+ if (changedShortcuts == null) {
+ changedShortcuts = new ArrayList<>(1);
+ }
+ changedShortcuts.add(ps.findShortcutById(id));
+ } else {
+ if (removedShortcuts == null) {
+ removedShortcuts = new ArrayList<>(1);
+ }
+ removedShortcuts.add(deleted);
+ }
}
// We may have removed dynamic shortcuts which may have left a gap, so adjust the ranks.
ps.adjustRanks();
}
- // TODO: Disabling dynamic shortcuts will removed them if not pinned. Cover all cases.
- packageShortcutsChanged(packageName, userId, null, null);
+
+ packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts);
verifyStates();
}
@@ -2200,13 +2259,10 @@
}
ps.enableWithId(id);
- final ShortcutInfo si = ps.findShortcutById(id);
- if (si != null) {
- if (changedShortcuts == null) {
- changedShortcuts = new ArrayList<>(1);
- }
- changedShortcuts.add(si);
+ if (changedShortcuts == null) {
+ changedShortcuts = new ArrayList<>(1);
}
+ changedShortcuts.add(ps.findShortcutById(id));
}
}
@@ -2237,18 +2293,18 @@
if (!ps.isShortcutExistsAndVisibleToPublisher(id)) {
continue;
}
- final ShortcutInfo si = ps.findShortcutById(id);
- final boolean removed = ps.deleteDynamicWithId(id, /*ignoreInvisible=*/ true);
- if (removed) {
- if (removedShortcuts == null) {
- removedShortcuts = new ArrayList<>(1);
- }
- removedShortcuts.add(si);
- } else {
+
+ ShortcutInfo removed = ps.deleteDynamicWithId(id, /*ignoreInvisible=*/ true);
+ if (removed == null) {
if (changedShortcuts == null) {
changedShortcuts = new ArrayList<>(1);
}
- changedShortcuts.add(si);
+ changedShortcuts.add(ps.findShortcutById(id));
+ } else {
+ if (removedShortcuts == null) {
+ removedShortcuts = new ArrayList<>(1);
+ }
+ removedShortcuts.add(removed);
}
}
@@ -2264,7 +2320,7 @@
public void removeAllDynamicShortcuts(String packageName, @UserIdInt int userId) {
verifyCaller(packageName, userId);
- List<ShortcutInfo> changedShortcuts = null;
+ List<ShortcutInfo> changedShortcuts = new ArrayList<>();
List<ShortcutInfo> removedShortcuts = null;
synchronized (mLock) {
@@ -2272,10 +2328,16 @@
final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
+ // Dynamic shortcuts that are either cached or pinned will not get deleted.
+ ps.findAll(changedShortcuts, (ShortcutInfo si) -> si.isVisibleToPublisher()
+ && si.isDynamic() && (si.isCached() || si.isPinned()),
+ ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
+
removedShortcuts = ps.deleteAllDynamicShortcuts(/*ignoreInvisible=*/ true);
+ changedShortcuts = prepareChangedShortcuts(
+ changedShortcuts, null, removedShortcuts, ps);
}
- // TODO: Pinned and cached shortcuts are not removed, add those to changedShortcuts list
packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts);
verifyStates();
@@ -2300,22 +2362,21 @@
for (int i = shortcutIds.size() - 1; i >= 0; i--) {
final String id = Preconditions.checkStringNotEmpty((String) shortcutIds.get(i));
+ if (!ps.isShortcutExistsAndVisibleToPublisher(id)) {
+ continue;
+ }
- final ShortcutInfo si = ps.findShortcutById(id);
- final boolean removed = ps.deleteLongLivedWithId(id, /*ignoreInvisible=*/ true);
-
- if (si != null) {
- if (removed) {
- if (removedShortcuts == null) {
- removedShortcuts = new ArrayList<>(1);
- }
- removedShortcuts.add(si);
- } else {
- if (changedShortcuts == null) {
- changedShortcuts = new ArrayList<>(1);
- }
- changedShortcuts.add(si);
+ ShortcutInfo removed = ps.deleteLongLivedWithId(id, /*ignoreInvisible=*/ true);
+ if (removed != null) {
+ if (removedShortcuts == null) {
+ removedShortcuts = new ArrayList<>(1);
}
+ removedShortcuts.add(removed);
+ } else {
+ if (changedShortcuts == null) {
+ changedShortcuts = new ArrayList<>(1);
+ }
+ changedShortcuts.add(ps.findShortcutById(id));
}
}
@@ -2939,6 +3000,7 @@
Objects.requireNonNull(shortcutIds, "shortcutIds");
List<ShortcutInfo> changedShortcuts = null;
+ List<ShortcutInfo> removedShortcuts = null;
synchronized (mLock) {
throwIfUserLockedL(userId);
@@ -2948,24 +3010,31 @@
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId);
launcher.attemptToRestoreIfNeededAndSave();
- launcher.pinShortcuts(userId, packageName, shortcutIds, /*forPinRequest=*/ false);
-
final ShortcutPackage sp = getUserShortcutsLocked(userId)
.getPackageShortcutsIfExists(packageName);
if (sp != null) {
- for (int i = 0; i < shortcutIds.size(); i++) {
- final ShortcutInfo si = sp.findShortcutById(shortcutIds.get(i));
- if (si != null) {
- if (changedShortcuts == null) {
- changedShortcuts = new ArrayList<>(1);
- }
- changedShortcuts.add(si);
- }
+ // List the shortcuts that are pinned only, these will get removed.
+ removedShortcuts = new ArrayList<>();
+ sp.findAll(removedShortcuts, (ShortcutInfo si) -> si.isVisibleToPublisher()
+ && si.isPinned() && !si.isCached() && !si.isDynamic()
+ && !si.isDeclaredInManifest(), ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO,
+ callingPackage, launcherUserId, false);
+ }
+ // Get list of shortcuts that will get unpinned.
+ ArraySet<String> oldPinnedIds = launcher.getPinnedShortcutIds(packageName, userId);
+
+ launcher.pinShortcuts(userId, packageName, shortcutIds, /*forPinRequest=*/ false);
+
+ if (oldPinnedIds != null && removedShortcuts != null) {
+ for (int i = 0; i < removedShortcuts.size(); i++) {
+ oldPinnedIds.remove(removedShortcuts.get(i).getId());
}
}
+ changedShortcuts = prepareChangedShortcuts(
+ oldPinnedIds, new ArraySet<>(shortcutIds), removedShortcuts, sp);
}
- // TODO: Include previously pinned shortcuts since they are not pinned anymore.
- packageShortcutsChanged(packageName, userId, changedShortcuts, null);
+
+ packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts);
verifyStates();
}
@@ -3045,17 +3114,17 @@
+ "shortcut " + si.getId());
}
} else {
- boolean removed = false;
+ ShortcutInfo removed = null;
if (si.isDynamic()) {
si.clearFlags(ShortcutInfo.FLAG_CACHED);
} else {
removed = sp.deleteLongLivedWithId(id, /*ignoreInvisible=*/ true);
}
- if (removed) {
+ if (removed != null) {
if (removedShortcuts == null) {
removedShortcuts = new ArrayList<>(1);
}
- removedShortcuts.add(si);
+ removedShortcuts.add(removed);
} else {
if (changedShortcuts == null) {
changedShortcuts = new ArrayList<>(1);
@@ -3222,8 +3291,6 @@
// Grant read uri permission to the caller on behalf of the shortcut owner. All
// granted permissions are revoked when the default launcher changes, or when
// device is rebooted.
- // b/151572645 is tracking a bug where Uri permissions are persisted across
- // reboots, even when Intent#FLAG_GRANT_PERSISTABLE_URI_PERMISSION is not used.
mUriGrantsManager.grantUriPermissionFromOwner(mUriPermissionOwner, packageUid,
launcherPackage, Uri.parse(uri), Intent.FLAG_GRANT_READ_URI_PERMISSION,
userId, launcherUserId);
@@ -4872,4 +4939,61 @@
mShortcutBitmapSaver.waitForAllSavesLocked();
}
}
+
+ /**
+ * This helper method does the following 3 tasks:
+ *
+ * 1- Combines the |changed| and |updated| shortcut lists, while removing duplicates.
+ * 2- If a shortcut is deleted and added at once in the same operation, removes it from the
+ * |removed| list.
+ * 3- Reloads the final list to get the latest flags.
+ */
+ private List<ShortcutInfo> prepareChangedShortcuts(ArraySet<String> changedIds,
+ ArraySet<String> newIds, List<ShortcutInfo> deletedList, final ShortcutPackage ps) {
+ if (ps == null) {
+ // This can happen when package restore is not finished yet.
+ return null;
+ }
+ if (CollectionUtils.isEmpty(changedIds) && CollectionUtils.isEmpty(newIds)) {
+ return null;
+ }
+
+ ArraySet<String> resultIds = new ArraySet<>();
+ if (!CollectionUtils.isEmpty(changedIds)) {
+ resultIds.addAll(changedIds);
+ }
+ if (!CollectionUtils.isEmpty(newIds)) {
+ resultIds.addAll(newIds);
+ }
+
+ if (!CollectionUtils.isEmpty(deletedList)) {
+ deletedList.removeIf((ShortcutInfo si) -> resultIds.contains(si.getId()));
+ }
+
+ List<ShortcutInfo> result = new ArrayList<>();
+ ps.findAll(result, (ShortcutInfo si) -> resultIds.contains(si.getId()),
+ ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
+ return result;
+ }
+
+ private List<ShortcutInfo> prepareChangedShortcuts(List<ShortcutInfo> changedList,
+ List<ShortcutInfo> newList, List<ShortcutInfo> deletedList, final ShortcutPackage ps) {
+ ArraySet<String> changedIds = new ArraySet<>();
+ addShortcutIdsToSet(changedIds, changedList);
+
+ ArraySet<String> newIds = new ArraySet<>();
+ addShortcutIdsToSet(newIds, newList);
+
+ return prepareChangedShortcuts(changedIds, newIds, deletedList, ps);
+ }
+
+ private void addShortcutIdsToSet(ArraySet<String> ids, List<ShortcutInfo> shortcuts) {
+ if (CollectionUtils.isEmpty(shortcuts)) {
+ return;
+ }
+ final int size = shortcuts.size();
+ for (int i = 0; i < size; i++) {
+ ids.add(shortcuts.get(i).getId());
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java
index 1d7d038..cfa0449 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -31,7 +31,6 @@
import android.annotation.Nullable;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PermissionInfo;
-import android.content.pm.Signature;
import android.content.pm.parsing.component.ParsedPermission;
import android.os.UserHandle;
import android.util.Log;
@@ -86,9 +85,6 @@
String sourcePackageName;
- // TODO: Can we get rid of this? Seems we only use some signature info from the setting
- PackageSettingBase sourcePackageSetting;
-
int protectionLevel;
ParsedPermission perm;
@@ -130,12 +126,6 @@
public String getSourcePackageName() {
return sourcePackageName;
}
- public PackageSettingBase getSourcePackageSetting() {
- return sourcePackageSetting;
- }
- public Signature[] getSourceSignatures() {
- return sourcePackageSetting.getSignatures();
- }
public int getType() {
return type;
}
@@ -149,9 +139,6 @@
public void setPermission(@Nullable ParsedPermission perm) {
this.perm = perm;
}
- public void setSourcePackageSetting(PackageSettingBase sourcePackageSetting) {
- this.sourcePackageSetting = sourcePackageSetting;
- }
public int[] computeGids(int userId) {
if (perUser) {
@@ -290,7 +277,6 @@
return;
}
sourcePackageName = newPackageName;
- sourcePackageSetting = null;
perm = null;
if (pendingPermissionInfo != null) {
pendingPermissionInfo.packageName = newPackageName;
@@ -319,10 +305,9 @@
if (PackageManagerService.DEBUG_SETTINGS) Log.v(TAG, "Dynamic permission: name="
+ getName() + " pkg=" + getSourcePackageName()
+ " info=" + pendingPermissionInfo);
- if (sourcePackageSetting == null && pendingPermissionInfo != null) {
+ if (pendingPermissionInfo != null) {
final BasePermission tree = findPermissionTree(permissionTrees, name);
if (tree != null && tree.perm != null) {
- sourcePackageSetting = tree.sourcePackageSetting;
perm = new ParsedPermission(tree.perm, pendingPermissionInfo,
tree.perm.getPackageName(), name);
uid = tree.uid;
@@ -355,7 +340,6 @@
if (bp.type == BasePermission.TYPE_BUILTIN && bp.perm == null) {
// It's a built-in permission and no owner, take ownership now
p.setFlags(p.getFlags() | PermissionInfo.FLAG_INSTALLED);
- bp.sourcePackageSetting = pkgSetting;
bp.perm = p;
bp.uid = pkg.getUid();
bp.sourcePackageName = p.getPackageName();
@@ -378,7 +362,6 @@
if (tree == null
|| tree.sourcePackageName.equals(p.getPackageName())) {
p.setFlags(p.getFlags() | PermissionInfo.FLAG_INSTALLED);
- bp.sourcePackageSetting = pkgSetting;
bp.perm = p;
bp.uid = pkg.getUid();
bp.sourcePackageName = p.getPackageName();
@@ -639,9 +622,6 @@
pw.print(" flags=0x"); pw.println(Integer.toHexString(perm.getFlags()));
}
}
- if (sourcePackageSetting != null) {
- pw.print(" packageSetting="); pw.println(sourcePackageSetting);
- }
if (READ_EXTERNAL_STORAGE.equals(name)) {
pw.print(" enforced=");
pw.println(readEnforced);
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index e4e5261..c425307 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -2499,24 +2499,10 @@
synchronized (mLock) {
ArraySet<String> newImplicitPermissions = new ArraySet<>();
- // TODO ntmyren: Remove once propagated to droidfood
- int flagMask = PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED
- | PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED;
- int user = UserHandle.getUserId(pkg.getUid());
-
final int N = pkg.getRequestedPermissions().size();
for (int i = 0; i < N; i++) {
final String permName = pkg.getRequestedPermissions().get(i);
final BasePermission bp = mSettings.getPermissionLocked(permName);
-
- // TODO ntmyren: Remove once propagated to droidfood
- if (bp != null && !bp.isRuntime()) {
- PermissionState permState = permissionsState.getInstallPermissionState(bp.name);
- if (permState == null || (permState.getFlags() & flagMask) != 0) {
- permissionsState.updatePermissionFlags(bp, user, flagMask, 0);
- }
- }
-
final boolean appSupportsRuntimePermissions =
pkg.getTargetSdkVersion() >= Build.VERSION_CODES.M;
String upgradedActivityRecognitionPermission = null;
@@ -2526,7 +2512,7 @@
+ " checking " + permName + ": " + bp);
}
- if (bp == null || bp.getSourcePackageSetting() == null) {
+ if (bp == null || getSourcePackageSetting(bp) == null) {
if (packageOfInterest == null || packageOfInterest.equals(
pkg.getPackageName())) {
if (DEBUG_PERMISSIONS) {
@@ -3403,10 +3389,11 @@
// - or its signing certificate is a previous signing certificate of the defining
// package, and the defining package still trusts the old certificate for permissions
// - or it shares the above relationships with the system package
+ final PackageParser.SigningDetails sourceSigningDetails =
+ getSourcePackageSigningDetails(bp);
boolean allowed =
- pkg.getSigningDetails().hasAncestorOrSelf(
- bp.getSourcePackageSetting().getSigningDetails())
- || bp.getSourcePackageSetting().getSigningDetails().checkCapability(
+ pkg.getSigningDetails().hasAncestorOrSelf(sourceSigningDetails)
+ || sourceSigningDetails.checkCapability(
pkg.getSigningDetails(),
PackageParser.SigningDetails.CertCapabilities.PERMISSION)
|| pkg.getSigningDetails().hasAncestorOrSelf(systemPackage.getSigningDetails())
@@ -3576,6 +3563,22 @@
return allowed;
}
+ @NonNull
+ private PackageParser.SigningDetails getSourcePackageSigningDetails(
+ @NonNull BasePermission bp) {
+ final PackageSetting ps = getSourcePackageSetting(bp);
+ if (ps == null) {
+ return PackageParser.SigningDetails.UNKNOWN;
+ }
+ return ps.getSigningDetails();
+ }
+
+ @Nullable
+ private PackageSetting getSourcePackageSetting(@NonNull BasePermission bp) {
+ final String sourcePackageName = bp.getSourcePackageName();
+ return mPackageManagerInt.getPackageSetting(sourcePackageName);
+ }
+
private static boolean isProfileOwner(int uid) {
DevicePolicyManagerInternal dpmInternal =
LocalServices.getService(DevicePolicyManagerInternal.class);
@@ -4084,8 +4087,10 @@
if (bp.isDynamic()) {
bp.updateDynamicPermission(mSettings.mPermissionTrees.values());
}
- if (bp.getSourcePackageSetting() == null
- || !packageName.equals(bp.getSourcePackageName())) {
+ if (!packageName.equals(bp.getSourcePackageName())) {
+ // Not checking sourcePackageSetting because it can be null when
+ // the permission source package is the target package and the target package is
+ // being uninstalled,
continue;
}
// The target package is the source of the current permission
@@ -4123,9 +4128,6 @@
bp.getSourcePackageName());
synchronized (mLock) {
if (sourcePkg != null && sourcePs != null) {
- if (bp.getSourcePackageSetting() == null) {
- bp.setSourcePackageSetting(sourcePs);
- }
continue;
}
Slog.w(TAG, "Removing dangling permission: " + bp.getName()
@@ -4200,8 +4202,10 @@
final Iterator<BasePermission> it = mSettings.mPermissionTrees.values().iterator();
while (it.hasNext()) {
final BasePermission bp = it.next();
- if (bp.getSourcePackageSetting() == null
- || !packageName.equals(bp.getSourcePackageName())) {
+ if (!packageName.equals(bp.getSourcePackageName())) {
+ // Not checking sourcePackageSetting because it can be null when
+ // the permission source package is the target package and the target package is
+ // being uninstalled,
continue;
}
// The target package is the source of the current permission tree
@@ -4227,9 +4231,6 @@
bp.getSourcePackageName());
synchronized (mLock) {
if (sourcePkg != null && sourcePs != null) {
- if (bp.getSourcePackageSetting() == null) {
- bp.setSourcePackageSetting(sourcePs);
- }
continue;
}
Slog.w(TAG, "Removing dangling permission tree: " + bp.getName()
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
index ec394a2..4bc7744 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
@@ -47,6 +47,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
/**
* This is a decorator of an {@link ISoundTriggerMiddlewareService}, which enforces permissions and
@@ -123,7 +124,7 @@
}
}
- private Boolean mCaptureState;
+ private AtomicReference<Boolean> mCaptureState = new AtomicReference<>();
private final @NonNull ISoundTriggerMiddlewareInternal mDelegate;
private final @NonNull Context mContext;
@@ -230,10 +231,7 @@
} catch (Exception e) {
throw handleException(e);
} finally {
- // It is safe to lock here - local operation.
- synchronized (this) {
- mCaptureState = active;
- }
+ mCaptureState.set(active);
}
}
@@ -286,8 +284,9 @@
@Override
public void dump(PrintWriter pw) {
synchronized (this) {
- pw.printf("Capture state is %s\n\n", mCaptureState == null ? "uninitialized"
- : (mCaptureState ? "active" : "inactive"));
+ Boolean captureState = mCaptureState.get();
+ pw.printf("Capture state is %s\n\n", captureState == null ? "uninitialized"
+ : (captureState ? "active" : "inactive"));
if (mModules != null) {
for (int handle : mModules.keySet()) {
final ModuleState module = mModules.get(handle);
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 299592d..373cb8b 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -2951,7 +2951,7 @@
}
int processHistoricalOps(HistoricalOps histOps, int atomTag, List<StatsEvent> pulledData) {
- int counter = 0;
+ int counter = 1;
for (int uidIdx = 0; uidIdx < histOps.getUidCount(); uidIdx++) {
final HistoricalUidOps uidOps = histOps.getUidOpsAt(uidIdx);
final int uid = uidOps.getUid();
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 8edc84f..df53227 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1384,7 +1384,8 @@
final ActivityStack homeStack = targetTask.getDisplayArea().getRootHomeTask();
final boolean homeTaskVisible = homeStack != null && homeStack.shouldBeVisible(null);
mService.getTaskChangeNotificationController().notifyActivityRestartAttempt(
- targetTask.getTaskInfo(), homeTaskVisible, clearedTask);
+ targetTask.getTaskInfo(), homeTaskVisible, clearedTask,
+ targetTask.getTopNonFinishingActivity().isVisible());
}
}
diff --git a/services/core/java/com/android/server/wm/ShellRoot.java b/services/core/java/com/android/server/wm/ShellRoot.java
index 0b1760d..99f710b 100644
--- a/services/core/java/com/android/server/wm/ShellRoot.java
+++ b/services/core/java/com/android/server/wm/ShellRoot.java
@@ -43,6 +43,8 @@
private WindowToken mToken;
private final IBinder.DeathRecipient mDeathRecipient;
private SurfaceControl mSurfaceControl = null;
+ private IWindow mAccessibilityWindow;
+ private IBinder.DeathRecipient mAccessibilityWindowDeath;
ShellRoot(@NonNull IWindow client, @NonNull DisplayContent dc, final int windowType) {
mDisplayContent = dc;
@@ -112,11 +114,14 @@
if (!mDisplayContent.getDefaultTaskDisplayArea().isSplitScreenModeActivated()) {
return null;
}
+ if (mAccessibilityWindow == null) {
+ return null;
+ }
WindowInfo windowInfo = WindowInfo.obtain();
windowInfo.displayId = mToken.getDisplayArea().getDisplayContent().mDisplayId;
windowInfo.type = mToken.windowType;
windowInfo.layer = mToken.getWindowLayerFromType();
- windowInfo.token = mClient.asBinder();
+ windowInfo.token = mAccessibilityWindow.asBinder();
windowInfo.title = "Splitscreen Divider";
windowInfo.focused = false;
windowInfo.inPictureInPicture = false;
@@ -126,5 +131,29 @@
windowInfo.regionInScreen.set(regionRect);
return windowInfo;
}
+
+ void setAccessibilityWindow(IWindow window) {
+ if (mAccessibilityWindow != null) {
+ mAccessibilityWindow.asBinder().unlinkToDeath(mAccessibilityWindowDeath, 0);
+ }
+ mAccessibilityWindow = window;
+ if (mAccessibilityWindow != null) {
+ try {
+ mAccessibilityWindowDeath = () -> {
+ synchronized (mDisplayContent.mWmService.mGlobalLock) {
+ mAccessibilityWindow = null;
+ setAccessibilityWindow(null);
+ }
+ };
+ mAccessibilityWindow.asBinder().linkToDeath(mAccessibilityWindowDeath, 0);
+ } catch (RemoteException e) {
+ mAccessibilityWindow = null;
+ }
+ }
+ if (mDisplayContent.mWmService.mAccessibilityController != null) {
+ mDisplayContent.mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(
+ mDisplayContent.getDisplayId());
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 2115a59..982df4b 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -644,7 +644,7 @@
return;
}
removeImmediately();
- if (!isRootTask) {
+ if (isLeafTask()) {
mAtmService.getTaskChangeNotificationController().notifyTaskRemoved(mTaskId);
}
}
@@ -2904,7 +2904,7 @@
adjustBoundsForDisplayChangeIfNeeded(dc);
}
super.onDisplayChanged(dc);
- if (!isRootTask) {
+ if (isLeafTask()) {
final int displayId = (dc != null) ? dc.getDisplayId() : INVALID_DISPLAY;
mWmService.mAtmService.getTaskChangeNotificationController().notifyTaskDisplayChanged(
mTaskId, displayId);
@@ -3112,7 +3112,8 @@
@Override
boolean showToCurrentUser() {
- return mForceShowForAllUsers || showForAllUsers() || mWmService.isCurrentProfile(mUserId);
+ return mForceShowForAllUsers || showForAllUsers()
+ || mWmService.isCurrentProfile(getTopMostTask().mUserId);
}
void setForceShowForAllUsers(boolean forceShowForAllUsers) {
diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
index e4f10d9..4b0e293 100644
--- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
@@ -123,7 +123,7 @@
private final TaskStackConsumer mNotifyActivityRestartAttempt = (l, m) -> {
SomeArgs args = (SomeArgs) m.obj;
l.onActivityRestartAttempt((RunningTaskInfo) args.arg1, args.argi1 != 0,
- args.argi2 != 0);
+ args.argi2 != 0, args.argi3 != 0);
};
private final TaskStackConsumer mNotifyActivityForcedResizable = (l, m) -> {
@@ -368,12 +368,13 @@
* running, but the task is either brought to the front or a new Intent is delivered to it.
*/
void notifyActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible,
- boolean clearedTask) {
+ boolean clearedTask, boolean wasVisible) {
mHandler.removeMessages(NOTIFY_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG);
final SomeArgs args = SomeArgs.obtain();
args.arg1 = task;
args.argi1 = homeTaskVisible ? 1 : 0;
args.argi2 = clearedTask ? 1 : 0;
+ args.argi3 = wasVisible ? 1 : 0;
final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG,
args);
forAllLocalListeners(mNotifyActivityRestartAttempt, msg);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 8eb4b26..84d749f 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -399,12 +399,18 @@
private static final String HIERARCHICAL_ANIMATIONS_PROPERTY =
"persist.wm.hierarchical_animations";
+ private static final String DISABLE_TRIPLE_BUFFERING_PROPERTY =
+ "ro.sf.disable_triple_buffer";
+
/**
* @see #HIERARCHICAL_ANIMATIONS_PROPERTY
*/
static boolean sHierarchicalAnimations =
SystemProperties.getBoolean(HIERARCHICAL_ANIMATIONS_PROPERTY, true);
+ static boolean sEnableTripleBuffering = !SystemProperties.getBoolean(
+ DISABLE_TRIPLE_BUFFERING_PROPERTY, false);
+
// Enums for animation scale update types.
@Retention(RetentionPolicy.SOURCE)
@IntDef({WINDOW_ANIMATION_SCALE, TRANSITION_ANIMATION_SCALE, ANIMATION_DURATION_SCALE})
@@ -1604,6 +1610,14 @@
// From now on, no exceptions or errors allowed!
res = WindowManagerGlobal.ADD_OKAY;
+
+ if (mUseBLAST) {
+ res |= WindowManagerGlobal.ADD_FLAG_USE_BLAST;
+ }
+ if (sEnableTripleBuffering) {
+ res |= WindowManagerGlobal.ADD_FLAG_USE_TRIPLE_BUFFERING;
+ }
+
if (displayContent.mCurrentFocus == null) {
displayContent.mWinAddedSinceNullFocus.add(win);
}
@@ -3897,6 +3911,30 @@
}
@Override
+ public void setShellRootAccessibilityWindow(int displayId, int windowType, IWindow target) {
+ if (mContext.checkCallingOrSelfPermission(MANAGE_APP_TOKENS)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Must hold permission " + MANAGE_APP_TOKENS);
+ }
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final DisplayContent dc = mRoot.getDisplayContent(displayId);
+ if (dc == null) {
+ return;
+ }
+ ShellRoot root = dc.mShellRoots.get(windowType);
+ if (root == null) {
+ return;
+ }
+ root.setAccessibilityWindow(target);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ @Override
public void setDisplayWindowInsetsController(
int displayId, IDisplayWindowInsetsController insetsController) {
if (mContext.checkCallingOrSelfPermission(MANAGE_APP_TOKENS)
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 1784e21..c4cb4b5 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
+import static android.Manifest.permission.READ_FRAME_BUFFER;
import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED;
@@ -27,17 +28,20 @@
import android.app.WindowConfiguration;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
+import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.ArraySet;
import android.util.Slog;
+import android.view.Surface;
import android.view.SurfaceControl;
import android.window.IDisplayAreaOrganizerController;
import android.window.ITaskOrganizerController;
import android.window.IWindowContainerTransactionCallback;
import android.window.IWindowOrganizerController;
+import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import com.android.internal.util.function.pooled.PooledConsumer;
@@ -377,6 +381,40 @@
mTransactionCallbacksByPendingSyncId.remove(mSyncId);
}
+ @Override
+ public boolean takeScreenshot(WindowContainerToken token, SurfaceControl outSurfaceControl) {
+ mService.mAmInternal.enforceCallingPermission(READ_FRAME_BUFFER, "takeScreenshot()");
+ final WindowContainer wc = WindowContainer.fromBinder(token.asBinder());
+ if (wc == null) {
+ throw new RuntimeException("Invalid token in screenshot transaction");
+ }
+
+ final Rect bounds = new Rect();
+ wc.getBounds(bounds);
+ bounds.offsetTo(0, 0);
+ SurfaceControl.ScreenshotGraphicBuffer buffer = SurfaceControl.captureLayers(
+ wc.getSurfaceControl(), bounds, 1);
+
+ if (buffer == null || buffer.getGraphicBuffer() == null) {
+ return false;
+ }
+
+ SurfaceControl screenshot = mService.mWindowManager.mSurfaceControlFactory.apply(null)
+ .setName(wc.getName() + " - Organizer Screenshot")
+ .setBufferSize(bounds.width(), bounds.height())
+ .setFormat(PixelFormat.TRANSLUCENT)
+ .setParent(wc.getParentSurfaceControl())
+ .build();
+
+ Surface surface = new Surface();
+ surface.copyFrom(screenshot);
+ surface.attachAndQueueBufferWithColorSpace(buffer.getGraphicBuffer(), null);
+ surface.release();
+
+ outSurfaceControl.copyFrom(screenshot);
+ return true;
+ }
+
private void enforceStackPermission(String func) {
mService.mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, func);
}
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 55e6ab7..0a7ca5a 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -115,8 +115,8 @@
.setMetadata(METADATA_WINDOW_TYPE, windowType)
.setMetadata(METADATA_OWNER_UID, ownerUid);
- final boolean useBLAST = (win.getAttrs().privateFlags &
- WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST) != 0;
+ final boolean useBLAST = mService.mUseBLAST && ((win.getAttrs().privateFlags &
+ WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST) != 0);
if (useBLAST) {
b.setContainerLayer();
}
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index cae5027..1036858 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -761,10 +761,7 @@
std::unique_lock l2(ifs->lock);
if (ifs->bindPoints.size() <= 1) {
ifs->bindPoints.clear();
- std::thread([this, ifs, l2 = std::move(l2)]() mutable {
- mJni->initializeForCurrentThread();
- deleteStorageLocked(*ifs, std::move(l2));
- }).detach();
+ deleteStorageLocked(*ifs, std::move(l2));
} else {
const std::string savedFile = std::move(bindIt->second.savedFilename);
ifs->bindPoints.erase(bindIt);
diff --git a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
index fea61aa..dfe75ed 100644
--- a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -30,10 +30,13 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.robolectric.Shadows.shadowOf;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
import static org.testng.Assert.expectThrows;
import android.app.backup.BackupManager;
@@ -86,6 +89,7 @@
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
/**
@@ -104,6 +108,7 @@
private static final String TAG = "BMSTest";
private static final String PACKAGE_1 = "some.package.1";
private static final String PACKAGE_2 = "some.package.2";
+ private static final String USER_FACING_PACKAGE = "user.facing.package";
private static final int USER_ID = 10;
@Mock private TransportManager mTransportManager;
@@ -1186,6 +1191,47 @@
eq(packageTrackingReceiver), eq(UserHandle.of(USER_ID)), any(), any(), any());
}
+ @Test
+ public void testFilterUserFacingPackages_shouldSkipUserFacing_filtersUserFacing() {
+ List<PackageInfo> packages = Arrays.asList(getPackageInfo(USER_FACING_PACKAGE),
+ getPackageInfo(PACKAGE_1));
+ UserBackupManagerService backupManagerService = spy(
+ createUserBackupManagerServiceAndRunTasks());
+ when(backupManagerService.shouldSkipUserFacingData()).thenReturn(true);
+ when(backupManagerService.shouldSkipPackage(eq(USER_FACING_PACKAGE))).thenReturn(true);
+
+ List<PackageInfo> filteredPackages = backupManagerService.filterUserFacingPackages(
+ packages);
+
+ assertFalse(containsPackage(filteredPackages, USER_FACING_PACKAGE));
+ assertTrue(containsPackage(filteredPackages, PACKAGE_1));
+ }
+
+ @Test
+ public void testFilterUserFacingPackages_shouldNotSkipUserFacing_doesNotFilterUserFacing() {
+ List<PackageInfo> packages = Arrays.asList(getPackageInfo(USER_FACING_PACKAGE),
+ getPackageInfo(PACKAGE_1));
+ UserBackupManagerService backupManagerService = spy(
+ createUserBackupManagerServiceAndRunTasks());
+ when(backupManagerService.shouldSkipUserFacingData()).thenReturn(false);
+ when(backupManagerService.shouldSkipPackage(eq(USER_FACING_PACKAGE))).thenReturn(true);
+
+ List<PackageInfo> filteredPackages = backupManagerService.filterUserFacingPackages(
+ packages);
+
+ assertTrue(containsPackage(filteredPackages, USER_FACING_PACKAGE));
+ assertTrue(containsPackage(filteredPackages, PACKAGE_1));
+ }
+
+ private static boolean containsPackage(List<PackageInfo> packages, String targetPackage) {
+ for (PackageInfo packageInfo : packages) {
+ if (targetPackage.equals(packageInfo.packageName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private UserBackupManagerService createUserBackupManagerServiceAndRunTasks() {
return BackupManagerServiceTestUtils.createUserBackupManagerServiceAndRunTasks(
USER_ID, mContext, mBackupThread, mBaseStateDir, mDataDir, mTransportManager);
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 064e348..98f4764 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
@@ -67,7 +67,7 @@
*/
public class SystemActionPerformerTest {
private static final int LATCH_TIMEOUT_MS = 500;
- private static final int LEGACY_SYSTEM_ACTION_COUNT = 9;
+ private static final int LEGACY_SYSTEM_ACTION_COUNT = 8;
private static final int NEW_ACTION_ID = 20;
private static final String LABEL_1 = "label1";
private static final String LABEL_2 = "label2";
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index 06b344b..efa25bd 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -6734,6 +6734,21 @@
mManager.hasShareTargets(CALLING_PACKAGE_1);
}
+ public void testisSharingShortcut_permission() throws IntentFilter.MalformedMimeTypeException {
+ setCaller(LAUNCHER_1, USER_0);
+
+ IntentFilter filter_any = new IntentFilter();
+ filter_any.addDataType("*/*");
+
+ assertExpectException(SecurityException.class, "Missing permission", () ->
+ mInternal.isSharingShortcut(USER_0, LAUNCHER_1, CALLING_PACKAGE_1, "s1", USER_0,
+ filter_any));
+
+ // Has permission, now it should pass.
+ mCallerPermissions.add(permission.MANAGE_APP_PREDICTIONS);
+ mManager.hasShareTargets(CALLING_PACKAGE_1);
+ }
+
public void testDumpsys_crossProfile() {
prepareCrossProfileDataSet();
dumpsysOnLogcat("test1", /* force= */ true);
@@ -8645,6 +8660,61 @@
filter_any));
}
+ public void testIsSharingShortcut_PinnedAndCachedOnlyShortcuts()
+ throws IntentFilter.MalformedMimeTypeException {
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_share_targets);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ final ShortcutInfo s1 = makeShortcutWithCategory("s1",
+ set("com.test.category.CATEGORY1", "com.test.category.CATEGORY2"));
+ final ShortcutInfo s2 = makeShortcutWithCategory("s2",
+ set("com.test.category.CATEGORY5", "com.test.category.CATEGORY6"));
+ final ShortcutInfo s3 = makeShortcutWithCategory("s3",
+ set("com.test.category.CATEGORY5", "com.test.category.CATEGORY6"));
+ s1.setLongLived();
+ s2.setLongLived();
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(s1, s2, s3)));
+ assertShortcutIds(assertAllNotKeyFieldsOnly(mManager.getDynamicShortcuts()),
+ "s1", "s2", "s3");
+ });
+
+ IntentFilter filter_any = new IntentFilter();
+ filter_any.addDataType("*/*");
+
+ setCaller(LAUNCHER_1, USER_0);
+ mCallerPermissions.add(permission.MANAGE_APP_PREDICTIONS);
+
+ // Assert all are sharing shortcuts
+ assertTrue(mInternal.isSharingShortcut(USER_0, LAUNCHER_1, CALLING_PACKAGE_1, "s1", USER_0,
+ filter_any));
+ assertTrue(mInternal.isSharingShortcut(USER_0, LAUNCHER_1, CALLING_PACKAGE_1, "s2", USER_0,
+ filter_any));
+ assertTrue(mInternal.isSharingShortcut(USER_0, LAUNCHER_1, CALLING_PACKAGE_1, "s3", USER_0,
+ filter_any));
+
+ mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s2"), HANDLE_USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3"), HANDLE_USER_0);
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ // Remove one cached shortcut, and leave one cached-only and pinned-only shortcuts.
+ mManager.removeLongLivedShortcuts(list("s1"));
+ mManager.removeDynamicShortcuts(list("s2, s3"));
+ });
+
+ assertFalse(mInternal.isSharingShortcut(USER_0, LAUNCHER_1, CALLING_PACKAGE_1, "s1", USER_0,
+ filter_any));
+ assertTrue(mInternal.isSharingShortcut(USER_0, LAUNCHER_1, CALLING_PACKAGE_1, "s2", USER_0,
+ filter_any));
+ assertTrue(mInternal.isSharingShortcut(USER_0, LAUNCHER_1, CALLING_PACKAGE_1, "s3", USER_0,
+ filter_any));
+ }
+
private Uri getFileUriFromResource(String fileName, int resId) throws IOException {
File file = new File(getTestContext().getFilesDir(), fileName);
// Make sure we are not leaving phantom files behind.
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest11.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest11.java
new file mode 100644
index 0000000..50d290a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest11.java
@@ -0,0 +1,775 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.pm;
+
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertWith;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.ComponentName;
+import android.content.pm.LauncherApps.ShortcutChangeCallback;
+import android.content.pm.LauncherApps.ShortcutQuery;
+import android.content.pm.ShortcutInfo;
+import android.os.test.TestLooper;
+
+import org.mockito.ArgumentCaptor;
+
+import java.util.List;
+
+/**
+ * Tests for {@link android.content.pm.LauncherApps.ShortcutChangeCallback} and relevant APIs.
+ *
+ atest -c com.android.server.pm.ShortcutManagerTest11
+ */
+public class ShortcutManagerTest11 extends BaseShortcutManagerTest {
+
+ private static final ShortcutQuery QUERY_MATCH_ALL = createShortcutQuery(
+ ShortcutQuery.FLAG_MATCH_ALL_KINDS_WITH_ALL_PINNED);
+
+ private final TestLooper mTestLooper = new TestLooper();
+
+ public void testShortcutChangeCallback_setDynamicShortcuts() {
+ ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
+ mTestLooper.getNewExecutor());
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(makeShortcuts("s1", "s2")));
+ });
+
+ mTestLooper.dispatchAll();
+
+ ArgumentCaptor<List> shortcuts = ArgumentCaptor.forClass(List.class);
+ verify(callback, times(1)).onShortcutsAddedOrUpdated(
+ eq(CALLING_PACKAGE_1), shortcuts.capture(), eq(HANDLE_USER_0));
+ verify(callback, times(0)).onShortcutsRemoved(any(), any(), any());
+
+ assertWith(shortcuts.getValue())
+ .areAllWithKeyFieldsOnly()
+ .haveIds("s1", "s2");
+ }
+
+ public void testShortcutChangeCallback_setDynamicShortcuts_replaceSameId() {
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(makeShortcuts("s1", "s2")));
+ });
+
+ ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
+ mTestLooper.getNewExecutor());
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(makeShortcuts("s2", "s3")));
+ });
+
+ mTestLooper.dispatchAll();
+
+ ArgumentCaptor<List> changedShortcuts = ArgumentCaptor.forClass(List.class);
+ verify(callback, times(1)).onShortcutsAddedOrUpdated(
+ eq(CALLING_PACKAGE_1), changedShortcuts.capture(), eq(HANDLE_USER_0));
+
+ ArgumentCaptor<List> removedShortcuts = ArgumentCaptor.forClass(List.class);
+ verify(callback, times(1)).onShortcutsRemoved(
+ eq(CALLING_PACKAGE_1), removedShortcuts.capture(), eq(HANDLE_USER_0));
+
+ assertWith(changedShortcuts.getValue())
+ .areAllWithKeyFieldsOnly()
+ .haveIds("s2", "s3");
+
+ assertWith(removedShortcuts.getValue())
+ .areAllWithKeyFieldsOnly()
+ .haveIds("s1");
+ }
+
+ public void testShortcutChangeCallback_setDynamicShortcuts_pinnedAndCached() {
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(
+ list(makeShortcut("s1"), makeLongLivedShortcut("s2"))));
+ });
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s1"), HANDLE_USER_0);
+ mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0);
+ });
+
+ ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
+ mTestLooper.getNewExecutor());
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(makeShortcuts("s3", "s4")));
+ });
+
+ mTestLooper.dispatchAll();
+
+ ArgumentCaptor<List> changedShortcuts = ArgumentCaptor.forClass(List.class);
+ verify(callback, times(1)).onShortcutsAddedOrUpdated(
+ eq(CALLING_PACKAGE_1), changedShortcuts.capture(), eq(HANDLE_USER_0));
+ verify(callback, times(0)).onShortcutsRemoved(any(), any(), any());
+
+ assertWith(changedShortcuts.getValue())
+ .areAllWithKeyFieldsOnly()
+ .haveIds("s1", "s2", "s3", "s4");
+ }
+
+ public void testShortcutChangeCallback_pinShortcuts() {
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(makeShortcuts("s1", "s2")));
+ });
+
+ ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
+ mTestLooper.getNewExecutor());
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s1"), HANDLE_USER_0);
+ });
+
+ mTestLooper.dispatchAll();
+
+ ArgumentCaptor<List> shortcuts = ArgumentCaptor.forClass(List.class);
+ verify(callback, times(1)).onShortcutsAddedOrUpdated(
+ eq(CALLING_PACKAGE_1), shortcuts.capture(), eq(HANDLE_USER_0));
+ verify(callback, times(0)).onShortcutsRemoved(any(), any(), any());
+
+ assertWith(shortcuts.getValue())
+ .areAllWithKeyFieldsOnly()
+ .haveIds("s1");
+ }
+
+ public void testShortcutChangeCallback_pinShortcuts_unpinOthers() {
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(makeShortcuts("s1", "s2", "s3")));
+ });
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s1", "s2"), HANDLE_USER_0);
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ mManager.removeDynamicShortcuts(list("s1", "s2"));
+ });
+
+ ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
+ mTestLooper.getNewExecutor());
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s2", "s3"), HANDLE_USER_0);
+ });
+
+ mTestLooper.dispatchAll();
+
+ ArgumentCaptor<List> changedShortcuts = ArgumentCaptor.forClass(List.class);
+ verify(callback, times(1)).onShortcutsAddedOrUpdated(
+ eq(CALLING_PACKAGE_1), changedShortcuts.capture(), eq(HANDLE_USER_0));
+
+ ArgumentCaptor<List> removedShortcuts = ArgumentCaptor.forClass(List.class);
+ verify(callback, times(1)).onShortcutsRemoved(
+ eq(CALLING_PACKAGE_1), removedShortcuts.capture(), eq(HANDLE_USER_0));
+
+ assertWith(changedShortcuts.getValue())
+ .areAllWithKeyFieldsOnly()
+ .haveIds("s2", "s3");
+
+ assertWith(removedShortcuts.getValue())
+ .areAllWithKeyFieldsOnly()
+ .haveIds("s1");
+ }
+
+ public void testShortcutChangeCallback_cacheShortcuts() {
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(makeLongLivedShortcut("s1"),
+ makeLongLivedShortcut("s2"), makeLongLivedShortcut("s3"))));
+ });
+
+ ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
+ mTestLooper.getNewExecutor());
+ mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s3"), HANDLE_USER_0);
+ });
+
+ mTestLooper.dispatchAll();
+
+ ArgumentCaptor<List> shortcuts = ArgumentCaptor.forClass(List.class);
+ verify(callback, times(1)).onShortcutsAddedOrUpdated(
+ eq(CALLING_PACKAGE_1), shortcuts.capture(), eq(HANDLE_USER_0));
+ verify(callback, times(0)).onShortcutsRemoved(any(), any(), any());
+
+ assertWith(shortcuts.getValue())
+ .areAllWithKeyFieldsOnly()
+ .haveIds("s1", "s3");
+ }
+
+ public void testShortcutChangeCallback_uncacheShortcuts() {
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(makeLongLivedShortcut("s1"),
+ makeLongLivedShortcut("s2"), makeLongLivedShortcut("s3"))));
+ });
+
+ ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s2"), HANDLE_USER_0);
+ mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
+ mTestLooper.getNewExecutor());
+ mLauncherApps.uncacheShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0);
+ });
+
+ mTestLooper.dispatchAll();
+
+ ArgumentCaptor<List> shortcuts = ArgumentCaptor.forClass(List.class);
+ verify(callback, times(1)).onShortcutsAddedOrUpdated(
+ eq(CALLING_PACKAGE_1), shortcuts.capture(), eq(HANDLE_USER_0));
+ verify(callback, times(0)).onShortcutsRemoved(any(), any(), any());
+
+ assertWith(shortcuts.getValue())
+ .areAllWithKeyFieldsOnly()
+ .haveIds("s2");
+ }
+
+ public void testShortcutChangeCallback_uncacheShortcuts_causeDeletion() {
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(makeLongLivedShortcut("s1"),
+ makeLongLivedShortcut("s2"), makeLongLivedShortcut("s3"))));
+ });
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s2", "s3"), HANDLE_USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0);
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ mManager.removeDynamicShortcuts(list("s2", "s3"));
+ });
+
+ ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
+ mTestLooper.getNewExecutor());
+ mLauncherApps.uncacheShortcuts(CALLING_PACKAGE_1, list("s2", "s3"), HANDLE_USER_0);
+ });
+
+ mTestLooper.dispatchAll();
+
+ ArgumentCaptor<List> changedShortcuts = ArgumentCaptor.forClass(List.class);
+ verify(callback, times(1)).onShortcutsAddedOrUpdated(
+ eq(CALLING_PACKAGE_1), changedShortcuts.capture(), eq(HANDLE_USER_0));
+
+ ArgumentCaptor<List> removedShortcuts = ArgumentCaptor.forClass(List.class);
+ verify(callback, times(1)).onShortcutsRemoved(
+ eq(CALLING_PACKAGE_1), removedShortcuts.capture(), eq(HANDLE_USER_0));
+
+ assertWith(changedShortcuts.getValue())
+ .areAllWithKeyFieldsOnly()
+ .haveIds("s2");
+
+ assertWith(removedShortcuts.getValue())
+ .areAllWithKeyFieldsOnly()
+ .haveIds("s3");
+ }
+
+ public void testShortcutChangeCallback_updateShortcuts() {
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"),
+ makeShortcutWithActivity("s2", new ComponentName(CALLING_PACKAGE_1, "test")))));
+ });
+
+ ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
+ mTestLooper.getNewExecutor());
+ });
+
+ final ComponentName updatedCn = new ComponentName(CALLING_PACKAGE_1, "updated activity");
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.updateShortcuts(list(makeShortcutWithActivity("s2", updatedCn))));
+ });
+
+ mTestLooper.dispatchAll();
+
+ ArgumentCaptor<List> shortcuts = ArgumentCaptor.forClass(List.class);
+ verify(callback, times(1)).onShortcutsAddedOrUpdated(
+ eq(CALLING_PACKAGE_1), shortcuts.capture(), eq(HANDLE_USER_0));
+ verify(callback, times(0)).onShortcutsRemoved(any(), any(), any());
+
+ assertWith(shortcuts.getValue())
+ .areAllWithKeyFieldsOnly()
+ .haveIds("s2");
+ assertEquals(updatedCn, ((ShortcutInfo) shortcuts.getValue().get(0)).getActivity());
+ }
+
+ public void testShortcutChangeCallback_addDynamicShortcuts() {
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(makeShortcuts("s1")));
+ });
+
+ ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
+ mTestLooper.getNewExecutor());
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.addDynamicShortcuts(makeShortcuts("s1", "s2")));
+ });
+
+ mTestLooper.dispatchAll();
+
+ ArgumentCaptor<List> shortcuts = ArgumentCaptor.forClass(List.class);
+ verify(callback, times(1)).onShortcutsAddedOrUpdated(
+ eq(CALLING_PACKAGE_1), shortcuts.capture(), eq(HANDLE_USER_0));
+ verify(callback, times(0)).onShortcutsRemoved(any(), any(), any());
+
+ assertWith(shortcuts.getValue())
+ .areAllWithKeyFieldsOnly()
+ .haveIds("s1", "s2");
+ }
+
+ public void testShortcutChangeCallback_pushDynamicShortcut() {
+ ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
+ mTestLooper.getNewExecutor());
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ mManager.pushDynamicShortcut(makeShortcut("s1"));
+ });
+
+ mTestLooper.dispatchAll();
+
+ ArgumentCaptor<List> shortcuts = ArgumentCaptor.forClass(List.class);
+ verify(callback, times(1)).onShortcutsAddedOrUpdated(
+ eq(CALLING_PACKAGE_1), shortcuts.capture(), eq(HANDLE_USER_0));
+ verify(callback, times(0)).onShortcutsRemoved(any(), any(), any());
+
+ assertWith(shortcuts.getValue())
+ .areAllWithKeyFieldsOnly()
+ .haveIds("s1");
+ }
+
+ public void testShortcutChangeCallback_pushDynamicShortcut_existingId() {
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts((makeShortcuts("s1", "s2", "s3", "s4", "s5",
+ "s6", "s7", "s8", "s9", "s10"))));
+ });
+
+ ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
+ mTestLooper.getNewExecutor());
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ mManager.pushDynamicShortcut(makeShortcut("s5"));
+ });
+
+ mTestLooper.dispatchAll();
+
+ ArgumentCaptor<List> shortcuts = ArgumentCaptor.forClass(List.class);
+ verify(callback, times(1)).onShortcutsAddedOrUpdated(
+ eq(CALLING_PACKAGE_1), shortcuts.capture(), eq(HANDLE_USER_0));
+ verify(callback, times(0)).onShortcutsRemoved(any(), any(), any());
+
+ assertWith(shortcuts.getValue())
+ .areAllWithKeyFieldsOnly()
+ .haveIds("s5");
+ }
+
+ public void testShortcutChangeCallback_pushDynamicShortcut_causeDeletion() {
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts((makeShortcuts("s1", "s2", "s3", "s4", "s5",
+ "s6", "s7", "s8", "s9", "s10"))));
+ });
+
+ ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
+ mTestLooper.getNewExecutor());
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ mManager.pushDynamicShortcut(makeShortcut("s11"));
+ });
+
+ mTestLooper.dispatchAll();
+
+ ArgumentCaptor<List> changedShortcuts = ArgumentCaptor.forClass(List.class);
+ verify(callback, times(1)).onShortcutsAddedOrUpdated(
+ eq(CALLING_PACKAGE_1), changedShortcuts.capture(), eq(HANDLE_USER_0));
+
+ ArgumentCaptor<List> removedShortcuts = ArgumentCaptor.forClass(List.class);
+ verify(callback, times(1)).onShortcutsRemoved(
+ eq(CALLING_PACKAGE_1), removedShortcuts.capture(), eq(HANDLE_USER_0));
+
+ assertWith(changedShortcuts.getValue())
+ .areAllWithKeyFieldsOnly()
+ .haveIds("s11");
+
+ assertWith(removedShortcuts.getValue())
+ .areAllWithKeyFieldsOnly()
+ .haveIds("s10");
+ }
+
+ public void testShortcutChangeCallback_pushDynamicShortcut_causeDeletionButCached() {
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts((makeShortcuts("s1", "s2", "s3", "s4", "s5",
+ "s6", "s7", "s8", "s9"))));
+ ShortcutInfo s10 = makeLongLivedShortcut("s10");
+ s10.setRank(10);
+ mManager.pushDynamicShortcut(s10); // Add a long lived shortcut to the end of the list.
+ });
+
+ ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s10"), HANDLE_USER_0);
+ mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
+ mTestLooper.getNewExecutor());
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ mManager.pushDynamicShortcut(makeShortcut("s11"));
+ });
+
+ mTestLooper.dispatchAll();
+
+ ArgumentCaptor<List> shortcuts = ArgumentCaptor.forClass(List.class);
+ verify(callback, times(1)).onShortcutsAddedOrUpdated(
+ eq(CALLING_PACKAGE_1), shortcuts.capture(), eq(HANDLE_USER_0));
+ verify(callback, times(0)).onShortcutsRemoved(any(), any(), any());
+
+ assertWith(shortcuts.getValue())
+ .areAllWithKeyFieldsOnly()
+ .haveIds("s10", "s11");
+ }
+
+ public void testShortcutChangeCallback_disableShortcuts() {
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(makeShortcuts("s1", "s2")));
+ });
+
+ ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
+ mTestLooper.getNewExecutor());
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ mManager.disableShortcuts(list("s2"));
+ });
+
+ mTestLooper.dispatchAll();
+
+ verify(callback, times(0)).onShortcutsAddedOrUpdated(any(), any(), any());
+
+ ArgumentCaptor<List> shortcuts = ArgumentCaptor.forClass(List.class);
+ verify(callback, times(1)).onShortcutsRemoved(
+ eq(CALLING_PACKAGE_1), shortcuts.capture(), eq(HANDLE_USER_0));
+
+ assertWith(shortcuts.getValue())
+ .areAllWithKeyFieldsOnly()
+ .haveIds("s2");
+ }
+
+ public void testShortcutChangeCallback_disableShortcuts_pinnedAndCached() {
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(
+ list(makeShortcut("s1"), makeLongLivedShortcut("s2"), makeShortcut("s3"))));
+ });
+
+ ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3"), HANDLE_USER_0);
+ mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
+ mTestLooper.getNewExecutor());
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ mManager.disableShortcuts(list("s1", "s2", "s3"));
+ });
+
+ mTestLooper.dispatchAll();
+
+ ArgumentCaptor<List> changedShortcuts = ArgumentCaptor.forClass(List.class);
+ verify(callback, times(1)).onShortcutsAddedOrUpdated(
+ eq(CALLING_PACKAGE_1), changedShortcuts.capture(), eq(HANDLE_USER_0));
+
+ ArgumentCaptor<List> removedShortcuts = ArgumentCaptor.forClass(List.class);
+ verify(callback, times(1)).onShortcutsRemoved(
+ eq(CALLING_PACKAGE_1), removedShortcuts.capture(), eq(HANDLE_USER_0));
+
+ assertWith(changedShortcuts.getValue())
+ .areAllWithKeyFieldsOnly()
+ .haveIds("s2", "s3");
+
+ assertWith(removedShortcuts.getValue())
+ .areAllWithKeyFieldsOnly()
+ .haveIds("s1");
+ }
+
+ public void testShortcutChangeCallback_enableShortcuts() {
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(
+ list(makeShortcut("s1"), makeLongLivedShortcut("s2"), makeShortcut("s3"))));
+ });
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3"), HANDLE_USER_0);
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ mManager.disableShortcuts(list("s1", "s2", "s3"));
+ });
+
+ ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
+ mTestLooper.getNewExecutor());
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ mManager.enableShortcuts(list("s1", "s2", "s3"));
+ });
+
+ mTestLooper.dispatchAll();
+
+ ArgumentCaptor<List> shortcuts = ArgumentCaptor.forClass(List.class);
+ verify(callback, times(1)).onShortcutsAddedOrUpdated(
+ eq(CALLING_PACKAGE_1), shortcuts.capture(), eq(HANDLE_USER_0));
+ verify(callback, times(0)).onShortcutsRemoved(any(), any(), any());
+
+ assertWith(shortcuts.getValue())
+ .areAllWithKeyFieldsOnly()
+ .haveIds("s2", "s3");
+ }
+
+ public void testShortcutChangeCallback_removeDynamicShortcuts() {
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(makeShortcuts("s1", "s2")));
+ });
+
+ ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
+ mTestLooper.getNewExecutor());
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ mManager.removeDynamicShortcuts(list("s2"));
+ });
+
+ mTestLooper.dispatchAll();
+
+ verify(callback, times(0)).onShortcutsAddedOrUpdated(any(), any(), any());
+
+ ArgumentCaptor<List> shortcuts = ArgumentCaptor.forClass(List.class);
+ verify(callback, times(1)).onShortcutsRemoved(
+ eq(CALLING_PACKAGE_1), shortcuts.capture(), eq(HANDLE_USER_0));
+
+ assertWith(shortcuts.getValue())
+ .areAllWithKeyFieldsOnly()
+ .haveIds("s2");
+ }
+
+ public void testShortcutChangeCallback_removeDynamicShortcuts_pinnedAndCached() {
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"),
+ makeLongLivedShortcut("s2"), makeShortcut("s3"), makeShortcut("s4"))));
+ });
+
+ ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3"), HANDLE_USER_0);
+ mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
+ mTestLooper.getNewExecutor());
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ mManager.removeDynamicShortcuts(list("s1", "s2", "s3"));
+ });
+
+ mTestLooper.dispatchAll();
+
+ ArgumentCaptor<List> changedShortcuts = ArgumentCaptor.forClass(List.class);
+ verify(callback, times(1)).onShortcutsAddedOrUpdated(
+ eq(CALLING_PACKAGE_1), changedShortcuts.capture(), eq(HANDLE_USER_0));
+
+ ArgumentCaptor<List> removedShortcuts = ArgumentCaptor.forClass(List.class);
+ verify(callback, times(1)).onShortcutsRemoved(
+ eq(CALLING_PACKAGE_1), removedShortcuts.capture(), eq(HANDLE_USER_0));
+
+ assertWith(changedShortcuts.getValue())
+ .areAllWithKeyFieldsOnly()
+ .haveIds("s2", "s3");
+
+ assertWith(removedShortcuts.getValue())
+ .areAllWithKeyFieldsOnly()
+ .haveIds("s1");
+ }
+
+ public void testShortcutChangeCallback_removeAllDynamicShortcuts() {
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(makeShortcuts("s1", "s2")));
+ });
+
+ ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
+ mTestLooper.getNewExecutor());
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ mManager.removeAllDynamicShortcuts();
+ });
+
+ mTestLooper.dispatchAll();
+
+ verify(callback, times(0)).onShortcutsAddedOrUpdated(any(), any(), any());
+
+ ArgumentCaptor<List> shortcuts = ArgumentCaptor.forClass(List.class);
+ verify(callback, times(1)).onShortcutsRemoved(
+ eq(CALLING_PACKAGE_1), shortcuts.capture(), eq(HANDLE_USER_0));
+
+ assertWith(shortcuts.getValue())
+ .areAllWithKeyFieldsOnly()
+ .haveIds("s1", "s2");
+ }
+
+ public void testShortcutChangeCallback_removeAllDynamicShortcuts_pinnedAndCached() {
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(
+ list(makeShortcut("s1"), makeLongLivedShortcut("s2"), makeShortcut("s3"))));
+ });
+
+ ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3"), HANDLE_USER_0);
+ mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
+ mTestLooper.getNewExecutor());
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ mManager.removeAllDynamicShortcuts();
+ });
+
+ mTestLooper.dispatchAll();
+
+ ArgumentCaptor<List> changedShortcuts = ArgumentCaptor.forClass(List.class);
+ verify(callback, times(1)).onShortcutsAddedOrUpdated(
+ eq(CALLING_PACKAGE_1), changedShortcuts.capture(), eq(HANDLE_USER_0));
+
+ ArgumentCaptor<List> removedShortcuts = ArgumentCaptor.forClass(List.class);
+ verify(callback, times(1)).onShortcutsRemoved(
+ eq(CALLING_PACKAGE_1), removedShortcuts.capture(), eq(HANDLE_USER_0));
+
+ assertWith(changedShortcuts.getValue())
+ .areAllWithKeyFieldsOnly()
+ .haveIds("s2", "s3");
+
+ assertWith(removedShortcuts.getValue())
+ .areAllWithKeyFieldsOnly()
+ .haveIds("s1");
+ }
+
+ public void testShortcutChangeCallback_removeLongLivedShortcuts_notCached() {
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"),
+ makeLongLivedShortcut("s2"), makeShortcut("s3"))));
+ });
+
+ ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
+ mTestLooper.getNewExecutor());
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ mManager.removeLongLivedShortcuts(list("s1", "s2"));
+ });
+
+ mTestLooper.dispatchAll();
+
+ verify(callback, times(0)).onShortcutsAddedOrUpdated(any(), any(), any());
+
+ ArgumentCaptor<List> shortcuts = ArgumentCaptor.forClass(List.class);
+ verify(callback, times(1)).onShortcutsRemoved(
+ eq(CALLING_PACKAGE_1), shortcuts.capture(), eq(HANDLE_USER_0));
+
+ assertWith(shortcuts.getValue())
+ .areAllWithKeyFieldsOnly()
+ .haveIds("s1", "s2");
+ }
+
+ public void testShortcutChangeCallback_removeLongLivedShortcuts_pinnedAndCached() {
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"),
+ makeLongLivedShortcut("s2"), makeShortcut("s3"), makeShortcut("s4"))));
+ });
+
+ ShortcutChangeCallback callback = mock(ShortcutChangeCallback.class);
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s3"), HANDLE_USER_0);
+ mLauncherApps.registerShortcutChangeCallback(callback, QUERY_MATCH_ALL,
+ mTestLooper.getNewExecutor());
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ mManager.removeLongLivedShortcuts(list("s1", "s2", "s3"));
+ });
+
+ mTestLooper.dispatchAll();
+
+ ArgumentCaptor<List> changedShortcuts = ArgumentCaptor.forClass(List.class);
+ verify(callback, times(1)).onShortcutsAddedOrUpdated(
+ eq(CALLING_PACKAGE_1), changedShortcuts.capture(), eq(HANDLE_USER_0));
+
+ ArgumentCaptor<List> removedShortcuts = ArgumentCaptor.forClass(List.class);
+ verify(callback, times(1)).onShortcutsRemoved(
+ eq(CALLING_PACKAGE_1), removedShortcuts.capture(), eq(HANDLE_USER_0));
+
+ assertWith(changedShortcuts.getValue())
+ .areAllWithKeyFieldsOnly()
+ .haveIds("s3");
+
+ assertWith(removedShortcuts.getValue())
+ .areAllWithKeyFieldsOnly()
+ .haveIds("s1", "s2");
+ }
+
+ private static ShortcutQuery createShortcutQuery(int queryFlags) {
+ ShortcutQuery q = new ShortcutQuery();
+ return q.setQueryFlags(ShortcutQuery.FLAG_MATCH_ALL_KINDS_WITH_ALL_PINNED);
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index a84a0a2..edc9756 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -808,6 +808,41 @@
verify(secondaryTaskContainer, times(2)).createStack(anyInt(), anyInt(), anyBoolean());
}
+ @Test
+ public void testWasVisibleInRestartAttempt() {
+ final ActivityStarter starter = prepareStarter(
+ FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | FLAG_ACTIVITY_SINGLE_TOP, false);
+ final ActivityRecord reusableActivity =
+ new ActivityBuilder(mService).setCreateTask(true).build();
+ final ActivityRecord topActivity =
+ new ActivityBuilder(mService).setCreateTask(true).build();
+
+ // Make sure topActivity is on top
+ topActivity.getRootTask().moveToFront("testWasVisibleInRestartAttempt");
+ reusableActivity.setVisible(false);
+
+ final TaskChangeNotificationController taskChangeNotifier =
+ mService.getTaskChangeNotificationController();
+ spyOn(taskChangeNotifier);
+
+ Task task = topActivity.getTask();
+ starter.postStartActivityProcessing(
+ task.getTopNonFinishingActivity(), START_DELIVERED_TO_TOP, task.getStack());
+
+ verify(taskChangeNotifier).notifyActivityRestartAttempt(
+ any(), anyBoolean(), anyBoolean(), anyBoolean());
+ verify(taskChangeNotifier).notifyActivityRestartAttempt(
+ any(), anyBoolean(), anyBoolean(), eq(true));
+
+ Task task2 = reusableActivity.getTask();
+ starter.postStartActivityProcessing(
+ task2.getTopNonFinishingActivity(), START_TASK_TO_FRONT, task.getStack());
+ verify(taskChangeNotifier, times(2)).notifyActivityRestartAttempt(
+ any(), anyBoolean(), anyBoolean(), anyBoolean());
+ verify(taskChangeNotifier).notifyActivityRestartAttempt(
+ any(), anyBoolean(), anyBoolean(), eq(false));
+ }
+
private ActivityRecord createSingleTaskActivityOn(ActivityStack stack) {
final ComponentName componentName = ComponentName.createRelative(
DEFAULT_COMPONENT_PACKAGE_NAME,
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index 17dd26e..4b43ceb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -39,6 +39,7 @@
import android.graphics.Rect;
import android.os.IBinder;
import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
import android.view.IDisplayWindowListener;
import androidx.test.filters.MediumTest;
@@ -57,6 +58,7 @@
* Build/Install/Run:
* atest WmTests:ActivityTaskManagerServiceTests
*/
+@Presubmit
@MediumTest
@RunWith(WindowTestRunner.class)
public class ActivityTaskManagerServiceTests extends ActivityTestsBase {
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 dea9294..bce1320 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -335,7 +335,11 @@
mWmService.mConstants.dispose();
mWmService.mHighRefreshRateBlacklist.dispose();
+ // This makes sure the posted messages without delay are processed, e.g.
+ // DisplayPolicy#release, WindowManagerService#setAnimationScale.
waitUntilWindowManagerHandlersIdle();
+ // Clear all posted messages with delay, so they don't be executed at unexpected times.
+ cleanupWindowManagerHandlers();
// Needs to explicitly dispose current static threads because there could be messages
// scheduled at a later time, and all mocks are invalid when it's executed.
DisplayThread.dispose();
@@ -399,8 +403,6 @@
if (wm == null) {
return;
}
- // Removing delayed FORCE_GC message decreases time for waiting idle.
- wm.mH.removeMessages(WindowManagerService.H.FORCE_GC);
waitHandlerIdle(wm.mH);
waitHandlerIdle(wm.mAnimationHandler);
// This is a different handler object than the wm.mAnimationHandler above.
@@ -408,25 +410,8 @@
waitHandlerIdle(SurfaceAnimationThread.getHandler());
}
- private void waitHandlerIdle(Handler handler) {
- 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);
- handler.post(() -> {
- synchronized (mCurrentMessagesProcessed) {
- mCurrentMessagesProcessed.set(true);
- mCurrentMessagesProcessed.notifyAll();
- }
- });
- while (!mCurrentMessagesProcessed.get()) {
- try {
- mCurrentMessagesProcessed.wait();
- } catch (InterruptedException e) {
- }
- }
- }
+ static void waitHandlerIdle(Handler handler) {
+ handler.runWithScissors(() -> { }, 0 /* timeout */);
}
void waitUntilWindowAnimatorIdle() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
index 6e41118..9872faa 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
@@ -27,6 +27,7 @@
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.TaskDescription;
+import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
import android.app.ActivityView;
import android.app.IActivityManager;
@@ -43,6 +44,7 @@
import android.platform.test.annotations.Presubmit;
import android.support.test.uiautomator.UiDevice;
import android.text.TextUtils;
+import android.view.Display;
import android.view.ViewGroup;
import androidx.test.filters.FlakyTest;
@@ -319,15 +321,91 @@
waitForCallback(singleTaskDisplayEmptyLatch);
}
+ @Test
+ public void testTaskDisplayChanged() throws Exception {
+ final CountDownLatch activityViewReadyLatch = new CountDownLatch(1);
+ final ActivityViewTestActivity activity =
+ (ActivityViewTestActivity) startTestActivity(ActivityViewTestActivity.class);
+ final ActivityView activityView = activity.getActivityView();
+ activityView.setCallback(new ActivityView.StateCallback() {
+ @Override
+ public void onActivityViewReady(ActivityView view) {
+ activityViewReadyLatch.countDown();
+ }
+ @Override
+ public void onActivityViewDestroyed(ActivityView view) {}
+ });
+ waitForCallback(activityViewReadyLatch);
+
+ // Launch a Activity inside ActivityView.
+ final Object[] params1 = new Object[1];
+ final CountDownLatch displayChangedLatch1 = new CountDownLatch(1);
+ final int activityViewDisplayId = activityView.getVirtualDisplayId();
+ registerTaskStackChangedListener(
+ new TaskDisplayChangedListener(
+ activityViewDisplayId, params1, displayChangedLatch1));
+ int taskId1;
+ ActivityOptions options1 = ActivityOptions.makeBasic()
+ .setLaunchDisplayId(activityView.getVirtualDisplayId());
+ synchronized (sLock) {
+ taskId1 = startTestActivity(ActivityInActivityView.class, options1).getTaskId();
+ }
+ waitForCallback(displayChangedLatch1);
+
+ assertEquals(taskId1, params1[0]);
+
+ // Launch the Activity in the default display, expects that reparenting happens.
+ final Object[] params2 = new Object[1];
+ final CountDownLatch displayChangedLatch2 = new CountDownLatch(1);
+ registerTaskStackChangedListener(
+ new TaskDisplayChangedListener(
+ Display.DEFAULT_DISPLAY, params2, displayChangedLatch2));
+ int taskId2;
+ ActivityOptions options2 = ActivityOptions.makeBasic()
+ .setLaunchDisplayId(Display.DEFAULT_DISPLAY);
+ synchronized (sLock) {
+ taskId2 = startTestActivity(ActivityInActivityView.class, options2).getTaskId();
+ }
+ waitForCallback(displayChangedLatch2);
+
+ assertEquals(taskId2, params2[0]);
+ assertEquals(taskId1, taskId2); // TaskId should be same since reparenting happens.
+ }
+
+ private static class TaskDisplayChangedListener extends TaskStackListener {
+ private int mDisplayId;
+ private final Object[] mParams;
+ private final CountDownLatch mDisplayChangedLatch;
+ TaskDisplayChangedListener(
+ int displayId, Object[] params, CountDownLatch displayChangedLatch) {
+ mDisplayId = displayId;
+ mParams = params;
+ mDisplayChangedLatch = displayChangedLatch;
+ }
+ @Override
+ public void onTaskDisplayChanged(int taskId, int displayId) throws RemoteException {
+ // Filter out the events for the uninterested displays.
+ // if (displayId != mDisplayId) return;
+ mParams[0] = taskId;
+ mDisplayChangedLatch.countDown();
+ }
+ };
+
/**
* Starts the provided activity and returns the started instance.
*/
private TestActivity startTestActivity(Class<?> activityClass) throws InterruptedException {
+ return startTestActivity(activityClass, ActivityOptions.makeBasic());
+ }
+
+ private TestActivity startTestActivity(Class<?> activityClass, ActivityOptions options)
+ throws InterruptedException {
final ActivityMonitor monitor = new ActivityMonitor(activityClass.getName(), null, false);
getInstrumentation().addMonitor(monitor);
final Context context = getInstrumentation().getContext();
context.startActivity(
- new Intent(context, activityClass).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+ new Intent(context, activityClass).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
+ options.toBundle());
final TestActivity activity = (TestActivity) monitor.waitForActivityWithTimeout(1000);
if (activity == null) {
throw new RuntimeException("Timed out waiting for Activity");
@@ -337,6 +415,9 @@
}
private void registerTaskStackChangedListener(ITaskStackListener listener) throws Exception {
+ if (mTaskStackListener != null) {
+ ActivityTaskManager.getService().unregisterTaskStackListener(mTaskStackListener);
+ }
mTaskStackListener = listener;
ActivityTaskManager.getService().registerTaskStackListener(listener);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
index f354a04..9fdb9d8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
@@ -72,6 +72,13 @@
stack.positionChildAt(WindowContainer.POSITION_TOP, task2, false /* includingParents */);
assertEquals(stack.mChildren.get(0), task2);
assertEquals(stack.mChildren.get(1), task1);
+
+ // Non-leaf task should be moved to top regardless of the user id.
+ createTaskInStack((ActivityStack) task2, 0 /* userId */);
+ createTaskInStack((ActivityStack) task2, 1 /* userId */);
+ stack.positionChildAt(WindowContainer.POSITION_TOP, task2, false /* includingParents */);
+ assertEquals(stack.mChildren.get(0), task1);
+ assertEquals(stack.mChildren.get(1), task2);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java
index 926bd8c..8d5363c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java
@@ -30,6 +30,7 @@
import android.content.ContentResolver;
import android.net.Uri;
+import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
import androidx.test.filters.SmallTest;
@@ -43,6 +44,7 @@
* Build/Install/Run:
* atest WmTests:WindowManagerSettingsTests
*/
+@Presubmit
@SmallTest
@RunWith(WindowTestRunner.class)
public class WindowManagerSettingsTests extends WindowTestsBase {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 6a64d1c..94c204ab 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -52,13 +52,9 @@
import com.android.server.AttributeCache;
-import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
-import java.util.HashSet;
-import java.util.LinkedList;
-
/**
* Common base class for window manager unit test classes.
*
@@ -85,7 +81,6 @@
WindowState mAppWindow;
WindowState mChildAppWindowAbove;
WindowState mChildAppWindowBelow;
- HashSet<WindowState> mCommonWindows;
/**
* Spied {@link Transaction} class than can be used to verify calls.
@@ -115,7 +110,6 @@
mDisplayContent = createNewDisplay(true /* supportIme */);
// Set-up some common windows.
- mCommonWindows = new HashSet<>();
synchronized (mWm.mGlobalLock) {
mWallpaperWindow = createCommonWindow(null, TYPE_WALLPAPER, "wallpaperWindow");
mImeWindow = createCommonWindow(null, TYPE_INPUT_METHOD, "mImeWindow");
@@ -151,48 +145,9 @@
// Called before display is created.
}
- @After
- public void tearDownBase() {
- // If @After throws an exception, the error isn't logged. This will make sure any failures
- // in the tear down are clear. This can be removed when b/37850063 is fixed.
- try {
- // Test may schedule to perform surface placement or other messages. Wait until a
- // stable state to clean up for consistency.
- waitUntilHandlersIdle();
-
- final LinkedList<WindowState> nonCommonWindows = new LinkedList<>();
-
- synchronized (mWm.mGlobalLock) {
- mWm.mRoot.forAllWindows(w -> {
- if (!mCommonWindows.contains(w)) {
- nonCommonWindows.addLast(w);
- }
- }, true /* traverseTopToBottom */);
-
- while (!nonCommonWindows.isEmpty()) {
- nonCommonWindows.pollLast().removeImmediately();
- }
-
- // Remove app transition & window freeze timeout callbacks to prevent unnecessary
- // actions after test.
- mWm.getDefaultDisplayContentLocked().mAppTransition
- .removeAppTransitionTimeoutCallbacks();
- mWm.mH.removeMessages(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT);
- mDisplayContent.mInputMethodTarget = null;
- }
-
- // Cleaned up everything in Handler.
- cleanupWindowManagerHandlers();
- } catch (Exception e) {
- Log.e(TAG, "Failed to tear down test", e);
- throw e;
- }
- }
-
private WindowState createCommonWindow(WindowState parent, int type, String name) {
synchronized (mWm.mGlobalLock) {
final WindowState win = createWindow(parent, type, name);
- mCommonWindows.add(win);
// Prevent common windows from been IMe targets
win.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
return win;
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/RotationAnimationUtilsTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/RotationAnimationUtilsTest.java
index e5497a2..233e147 100644
--- a/services/tests/wmtests/src/com/android/server/wm/utils/RotationAnimationUtilsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/RotationAnimationUtilsTest.java
@@ -27,10 +27,12 @@
import android.graphics.Matrix;
import android.graphics.PointF;
import android.view.Surface;
+import android.platform.test.annotations.Presubmit;
import org.junit.Before;
import org.junit.Test;
+@Presubmit
public class RotationAnimationUtilsTest {
private static final int BITMAP_HEIGHT = 100;
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index 7488a1a..2edb564 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -269,10 +269,10 @@
* only contains {@link #OPERATION_DOWNLOAD} and ErrorCode is 0 implies this is an unknown
* Download error.
*
- * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_OPERATION_CODE}
- * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_ERROR_CODE}
- * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_SMDX_SUBJECT_CODE}
- * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_SMDX_REASON_CODE}
+ * @see #EXTRA_EMBEDDED_SUBSCRIPTION_OPERATION_CODE
+ * @see #EXTRA_EMBEDDED_SUBSCRIPTION_ERROR_CODE
+ * @see #EXTRA_EMBEDDED_SUBSCRIPTION_SMDX_SUBJECT_CODE
+ * @see #EXTRA_EMBEDDED_SUBSCRIPTION_SMDX_REASON_CODE
*/
public static final String EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE =
"android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DETAILED_CODE";
@@ -552,7 +552,7 @@
/**
* List of OperationCode corresponding to {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE}'s
- * value, an integer. @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ * value, an integer. @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
*
* @hide
*/
@@ -575,44 +575,44 @@
/**
* Internal system error.
- * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
*/
public static final int OPERATION_SYSTEM = 1;
/**
* SIM slot error. Failed to switch slot, failed to access the physical slot etc.
- * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
*/
public static final int OPERATION_SIM_SLOT = 2;
/**
* eUICC card error.
- * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
*/
public static final int OPERATION_EUICC_CARD = 3;
/**
* Generic switching profile error
- * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
*/
public static final int OPERATION_SWITCH = 4;
/**
* Download profile error.
- * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
*/
public static final int OPERATION_DOWNLOAD = 5;
/**
* Subscription's metadata error
- * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
*/
public static final int OPERATION_METADATA = 6;
/**
* eUICC returned an error defined in GSMA (SGP.22 v2.2) while running one of the ES10x
* functions.
- * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
*/
public static final int OPERATION_EUICC_GSMA = 7;
@@ -620,13 +620,13 @@
* The exception of failing to execute an APDU command. It can be caused by an error
* happening on opening the basic or logical channel, or the response of the APDU command is
* not success (0x9000).
- * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
*/
public static final int OPERATION_APDU = 8;
/**
* SMDX(SMDP/SMDS) error
- * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
*/
public static final int OPERATION_SMDX = 9;
@@ -655,19 +655,19 @@
* Thus the integer stored in {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} is
* 0xA8B1051(176885841)
*
- * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
*/
public static final int OPERATION_SMDX_SUBJECT_REASON_CODE = 10;
/**
* HTTP error
- * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
*/
public static final int OPERATION_HTTP = 11;
/**
* List of ErrorCode corresponding to {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE}
- * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
@@ -695,56 +695,56 @@
/**
* Operation such as downloading/switching to another profile failed due to device being
* carrier locked.
- * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
*/
public static final int ERROR_CARRIER_LOCKED = 10000;
/**
* The activation code(SGP.22 v2.2 section[4.1]) is invalid.
- * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
*/
public static final int ERROR_INVALID_ACTIVATION_CODE = 10001;
/**
* The confirmation code(SGP.22 v2.2 section[4.7]) is invalid.
- * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
*/
public static final int ERROR_INVALID_CONFIRMATION_CODE = 10002;
/**
* The profile's carrier is incompatible with the LPA.
- * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
*/
public static final int ERROR_INCOMPATIBLE_CARRIER = 10003;
/**
* There is no more space available on the eUICC for new profiles.
- * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
*/
public static final int ERROR_EUICC_INSUFFICIENT_MEMORY = 10004;
/**
* Timed out while waiting for an operation to complete. i.e restart, disable,
* switch reset etc.
- * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
*/
public static final int ERROR_TIME_OUT = 10005;
/**
* eUICC is missing or defective on the device.
- * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
*/
public static final int ERROR_EUICC_MISSING = 10006;
/**
* The eUICC card(hardware) version is incompatible with the software
- * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
*/
public static final int ERROR_UNSUPPORTED_VERSION = 10007;
/**
* No SIM card is available in the device.
- * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
*/
public static final int ERROR_SIM_MISSING = 10008;
@@ -754,52 +754,52 @@
* 2. GSMA(.22 v2.2) Profile Install Result - installFailedDueToDataMismatch
* 3. operation was interrupted
* 4. SIMalliance error in PEStatus(SGP.22 v2.2 section 2.5.6.1)
- * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
*/
public static final int ERROR_INSTALL_PROFILE = 10009;
/**
* Failed to load profile onto eUICC due to Profile Poicly Rules.
- * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
*/
public static final int ERROR_DISALLOWED_BY_PPR = 10010;
/**
* Address is missing e.g SMDS/SMDP address is missing.
- * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
*/
public static final int ERROR_ADDRESS_MISSING = 10011;
/**
* Certificate needed for authentication is not valid or missing. E.g SMDP/SMDS authentication
* failed.
- * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
*/
public static final int ERROR_CERTIFICATE_ERROR = 10012;
/**
* No profiles available.
- * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
*/
public static final int ERROR_NO_PROFILES_AVAILABLE = 10013;
/**
* Failure to create a connection.
- * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
*/
public static final int ERROR_CONNECTION_ERROR = 10014;
/**
* Response format is invalid. e.g SMDP/SMDS response contains invalid json, header or/and ASN1.
- * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
*/
public static final int ERROR_INVALID_RESPONSE = 10015;
/**
* The operation is currently busy, try again later.
- * @see {@link #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE} for details
+ * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details
*/
public static final int ERROR_OPERATION_BUSY = 10016;
diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h
index e7be434..852b1244 100644
--- a/tools/aapt2/cmd/Link.h
+++ b/tools/aapt2/cmd/Link.h
@@ -263,6 +263,10 @@
"Changes the name of the target package for instrumentation. Most useful\n"
"when used in conjunction with --rename-manifest-package.",
&options_.manifest_fixer_options.rename_instrumentation_target_package);
+ AddOptionalFlag("--rename-overlay-target-package",
+ "Changes the name of the target package for overlay. Most useful\n"
+ "when used in conjunction with --rename-manifest-package.",
+ &options_.manifest_fixer_options.rename_overlay_target_package);
AddOptionalFlagList("-0", "File suffix not to compress.",
&options_.extensions_to_not_compress);
AddOptionalSwitch("--no-compress", "Do not compress any resources.",
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index bcfce66..c813a44 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -263,6 +263,16 @@
}
}
+ if (options_.rename_overlay_target_package) {
+ if (!util::IsJavaPackageName(options_.rename_overlay_target_package.value())) {
+ diag->Error(DiagMessage()
+ << "invalid overlay target package override '"
+ << options_.rename_overlay_target_package.value()
+ << "'");
+ return false;
+ }
+ }
+
// Common <intent-filter> actions.
xml::XmlNodeAction intent_filter_action;
intent_filter_action["action"].Action(RequiredNameIsNotEmpty);
@@ -373,7 +383,17 @@
manifest_action["attribution"];
manifest_action["attribution"]["inherit-from"];
manifest_action["original-package"];
- manifest_action["overlay"];
+ manifest_action["overlay"].Action([&](xml::Element* el) -> bool {
+ if (!options_.rename_overlay_target_package) {
+ return true;
+ }
+
+ if (xml::Attribute* attr =
+ el->FindAttribute(xml::kSchemaAndroid, "targetPackage")) {
+ attr->value = options_.rename_overlay_target_package.value();
+ }
+ return true;
+ });
manifest_action["protected-broadcast"];
manifest_action["adopt-permissions"];
manifest_action["uses-permission"];
diff --git a/tools/aapt2/link/ManifestFixer.h b/tools/aapt2/link/ManifestFixer.h
index 3ef57d0..ec4367b 100644
--- a/tools/aapt2/link/ManifestFixer.h
+++ b/tools/aapt2/link/ManifestFixer.h
@@ -44,6 +44,10 @@
// <instrumentation>.
Maybe<std::string> rename_instrumentation_target_package;
+ // The Android package to use instead of the one defined in 'android:targetPackage' in
+ // <overlay>.
+ Maybe<std::string> rename_overlay_target_package;
+
// The version name to set if 'android:versionName' is not defined in <manifest> or if
// replace_version is set.
Maybe<std::string> version_name_default;
diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp
index 3af06f5..0791805 100644
--- a/tools/aapt2/link/ManifestFixer_test.cpp
+++ b/tools/aapt2/link/ManifestFixer_test.cpp
@@ -325,6 +325,32 @@
EXPECT_THAT(attr->value, StrEq("com.android"));
}
+TEST_F(ManifestFixerTest,
+ RenameManifestOverlayPackageAndFullyQualifyTarget) {
+ ManifestFixerOptions options;
+ options.rename_overlay_target_package = std::string("com.android");
+
+ std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android">
+ <overlay android:targetName="Customization" android:targetPackage="android" />
+ </manifest>)EOF",
+ options);
+ ASSERT_THAT(doc, NotNull());
+
+ xml::Element* manifest_el = doc->root.get();
+ ASSERT_THAT(manifest_el, NotNull());
+
+ xml::Element* overlay_el =
+ manifest_el->FindChild({}, "overlay");
+ ASSERT_THAT(overlay_el, NotNull());
+
+ xml::Attribute* attr =
+ overlay_el->FindAttribute(xml::kSchemaAndroid, "targetPackage");
+ ASSERT_THAT(attr, NotNull());
+ EXPECT_THAT(attr->value, StrEq("com.android"));
+}
+
TEST_F(ManifestFixerTest, UseDefaultVersionNameAndCode) {
ManifestFixerOptions options;
options.version_name_default = std::string("Beta");