From 524fe3fd6cac2d63b2286fc9101ec1f0f0ecaf4b Mon Sep 17 00:00:00 2001 From: Jing Ji Date: Tue, 25 Oct 2022 22:39:52 -0700 Subject: DO NOT MERGE: ActivityManager#killBackgroundProcesses can kill caller's own app only unless it's a system app. Bug: 239423414 Bug: 223376078 Test: atest CtsAppTestCases:ActivityManagerTest Merged-In: I35d20539ffac055a6d61260445620f45584bd9c5 Merged-In: Ieed6af77da1bc31cfecc5272b9f97971db7ae7b2 Merged-In: I8b8a427ee87339cc038e53adc0912283b05d2cfc Change-Id: Iac6baa889965b8ffecd9a43179a4c96632ad1d02 --- core/java/android/app/ActivityManager.java | 3 ++ core/res/AndroidManifest.xml | 6 +++- .../android/server/am/ActivityManagerService.java | 32 ++++++++++++++++++++-- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 1059a6f2e868..811cd5b75275 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -3933,6 +3933,9 @@ public class ActivityManager { * processes to reclaim memory; the system will take care of restarting * these processes in the future as needed. * + *

Third party applications can only use this API to kill their own processes. + *

+ * * @param packageName The name of the package whose processes are to * be killed. */ diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 046473ff09fb..b702b88b9f05 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2537,7 +2537,11 @@ android:protectionLevel="normal" /> = FIRST_APPLICATION_UID + && (proc == null || !proc.info.isSystemApp())) { + final String msg = "Permission Denial: killAllBackgroundProcesses() from pid=" + + callingPid + ", uid=" + callingUid + " is not allowed"; + Slog.w(TAG, msg); + // Silently return to avoid existing apps from crashing. + return; + } + final long callingId = Binder.clearCallingIdentity(); try { synchronized (this) { -- cgit v1.2.3 From fa94ce5c7738e449cb6bd68c77af4858018e49e0 Mon Sep 17 00:00:00 2001 From: Jing Ji Date: Tue, 25 Oct 2022 22:39:52 -0700 Subject: DO NOT MERGE: ActivityManager#killBackgroundProcesses can kill caller's own app only unless it's a system app. Bug: 239423414 Bug: 223376078 Test: atest CtsAppTestCases:ActivityManagerTest Merged-In: I35d20539ffac055a6d61260445620f45584bd9c5 Merged-In: Ieed6af77da1bc31cfecc5272b9f97971db7ae7b2 Merged-In: I8b8a427ee87339cc038e53adc0912283b05d2cfc Change-Id: Iac6baa889965b8ffecd9a43179a4c96632ad1d02 --- core/java/android/app/ActivityManager.java | 3 ++ core/res/AndroidManifest.xml | 6 +++- .../android/server/am/ActivityManagerService.java | 32 ++++++++++++++++++++-- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 5d1d225f4d2d..68a42d148109 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -3934,6 +3934,9 @@ public class ActivityManager { * processes to reclaim memory; the system will take care of restarting * these processes in the future as needed. * + *

Third party applications can only use this API to kill their own processes. + *

+ * * @param packageName The name of the package whose processes are to * be killed. */ diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 1a5ce3aeaeba..bfbc15986c60 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3151,7 +3151,11 @@ android:protectionLevel="normal" /> = FIRST_APPLICATION_UID + && (proc == null || !proc.info.isSystemApp())) { + final String msg = "Permission Denial: killAllBackgroundProcesses() from pid=" + + callingPid + ", uid=" + callingUid + " is not allowed"; + Slog.w(TAG, msg); + // Silently return to avoid existing apps from crashing. + return; + } + final long callingId = Binder.clearCallingIdentity(); try { synchronized (this) { -- cgit v1.2.3 From 8b382775b258220466a977453905797521e159de Mon Sep 17 00:00:00 2001 From: Jing Ji Date: Tue, 25 Oct 2022 22:39:52 -0700 Subject: DO NOT MERGE: ActivityManager#killBackgroundProcesses can kill caller's own app only unless it's a system app. Bug: 239423414 Bug: 223376078 Test: atest CtsAppTestCases:ActivityManagerTest Merged-In: I35d20539ffac055a6d61260445620f45584bd9c5 Merged-In: Ieed6af77da1bc31cfecc5272b9f97971db7ae7b2 Merged-In: I8b8a427ee87339cc038e53adc0912283b05d2cfc Change-Id: Iac6baa889965b8ffecd9a43179a4c96632ad1d02 --- core/java/android/app/ActivityManager.java | 3 ++ core/res/AndroidManifest.xml | 6 +++- .../android/server/am/ActivityManagerService.java | 32 ++++++++++++++++++++-- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 1059a6f2e868..811cd5b75275 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -3933,6 +3933,9 @@ public class ActivityManager { * processes to reclaim memory; the system will take care of restarting * these processes in the future as needed. * + *

Third party applications can only use this API to kill their own processes. + *

+ * * @param packageName The name of the package whose processes are to * be killed. */ diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index d2d60d2104a5..39de787a79af 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2528,7 +2528,11 @@ android:protectionLevel="normal" /> = FIRST_APPLICATION_UID + && (proc == null || !proc.info.isSystemApp())) { + final String msg = "Permission Denial: killAllBackgroundProcesses() from pid=" + + callingPid + ", uid=" + callingUid + " is not allowed"; + Slog.w(TAG, msg); + // Silently return to avoid existing apps from crashing. + return; + } + final long callingId = Binder.clearCallingIdentity(); try { synchronized (this) { -- cgit v1.2.3 From 37e748ab8a38b3e1ada63ee6321eb01d264229ec Mon Sep 17 00:00:00 2001 From: Ioana Alexandru Date: Thu, 5 Jan 2023 22:45:21 +0000 Subject: Trim strings added to persistent snoozed notification storage. This is a backport of ag/20581190 and includes the fix in ag/20778075. Note that on this branch, clearData doesn't seem to actually clear persistent storage. Fix: 258422365 Test: atest NotificationManagerServiceTest SnoozeHelperTest Change-Id: If7c7db6694330ffbac551d044efadb26219fe17f Merged-In: I5a2823f10053ea8c83c612a567d6d4f1b6af23e7 Merged-In: Ie809cb4d648a40622618e0fb374f36b6d8dc972a --- .../android/server/notification/SnoozeHelper.java | 29 ++++++++++++---- .../server/notification/SnoozeHelperTest.java | 40 +++++++++++++++++++++- 2 files changed, 61 insertions(+), 8 deletions(-) diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java index 15d7c1e7a210..ffce13f41f24 100644 --- a/services/core/java/com/android/server/notification/SnoozeHelper.java +++ b/services/core/java/com/android/server/notification/SnoozeHelper.java @@ -64,6 +64,9 @@ public class SnoozeHelper { static final int CONCURRENT_SNOOZE_LIMIT = 500; + // A safe size for strings to be put in persistent storage, to avoid breaking the XML write. + static final int MAX_STRING_LENGTH = 1000; + protected static final String XML_TAG_NAME = "snoozed-notifications"; private static final String XML_SNOOZED_NOTIFICATION = "notification"; @@ -153,7 +156,7 @@ public class SnoozeHelper { ArrayMap snoozed = mPersistedSnoozedNotifications.get(getPkgKey(userId, pkg)); if (snoozed != null) { - time = snoozed.get(key); + time = snoozed.get(getTrimmedString(key)); } } if (time == null) { @@ -167,7 +170,7 @@ public class SnoozeHelper { ArrayMap snoozed = mPersistedSnoozedNotificationsWithContext.get(getPkgKey(userId, pkg)); if (snoozed != null) { - return snoozed.get(key); + return snoozed.get(getTrimmedString(key)); } } return null; @@ -252,7 +255,8 @@ public class SnoozeHelper { scheduleRepost(pkg, key, userId, duration); Long activateAt = System.currentTimeMillis() + duration; synchronized (mLock) { - storeRecordLocked(pkg, key, userId, mPersistedSnoozedNotifications, activateAt); + storeRecordLocked(pkg, getTrimmedString(key), userId, mPersistedSnoozedNotifications, + activateAt); } } @@ -263,8 +267,10 @@ public class SnoozeHelper { int userId = record.getUser().getIdentifier(); if (contextId != null) { synchronized (mLock) { - storeRecordLocked(record.getSbn().getPackageName(), record.getKey(), - userId, mPersistedSnoozedNotificationsWithContext, contextId); + storeRecordLocked(record.getSbn().getPackageName(), + getTrimmedString(record.getKey()), + userId, mPersistedSnoozedNotificationsWithContext, + getTrimmedString(contextId)); } } snooze(record); @@ -281,6 +287,13 @@ public class SnoozeHelper { } } + private String getTrimmedString(String key) { + if (key != null && key.length() > MAX_STRING_LENGTH) { + return key.substring(0, MAX_STRING_LENGTH); + } + return key; + } + private void storeRecordLocked(String pkg, String key, Integer userId, ArrayMap> targets, T object) { @@ -385,12 +398,14 @@ public class SnoozeHelper { } protected void repost(String key, int userId, boolean muteOnReturn) { + final String trimmedKey = getTrimmedString(key); + NotificationRecord record; synchronized (mLock) { final String pkg = mPackages.remove(key); mUsers.remove(key); - removeRecordLocked(pkg, key, userId, mPersistedSnoozedNotifications); - removeRecordLocked(pkg, key, userId, mPersistedSnoozedNotificationsWithContext); + removeRecordLocked(pkg, trimmedKey, userId, mPersistedSnoozedNotifications); + removeRecordLocked(pkg, trimmedKey, userId, mPersistedSnoozedNotificationsWithContext); ArrayMap records = mSnoozedNotifications.get(getPkgKey(userId, pkg)); if (records == null) { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java index 8bead5774548..8d559fea3dc7 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java @@ -247,6 +247,37 @@ public class SnoozeHelperTest extends UiServiceTestCase { assertEquals("key2", captor2.getValue().getIntent().getStringExtra(EXTRA_KEY)); } + @Test + public void testLongTagPersistedNotification() throws Exception { + String longTag = "A".repeat(66000); + NotificationRecord r = getNotificationRecord("pkg", 1, longTag, UserHandle.SYSTEM); + mSnoozeHelper.snooze(r, 0); + + // We store the full key in temp storage. + ArgumentCaptor captor = ArgumentCaptor.forClass(PendingIntent.class); + verify(mAm).setExactAndAllowWhileIdle(anyInt(), anyLong(), captor.capture()); + assertEquals(66010, captor.getValue().getIntent().getStringExtra(EXTRA_KEY).length()); + + TypedXmlSerializer serializer = Xml.newFastSerializer(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); + serializer.startDocument(null, true); + mSnoozeHelper.writeXml(serializer); + serializer.endDocument(); + serializer.flush(); + + TypedXmlPullParser parser = Xml.newFastPullParser(); + parser.setInput(new BufferedInputStream( + new ByteArrayInputStream(baos.toByteArray())), "utf-8"); + mSnoozeHelper.readXml(parser, 4); + + mSnoozeHelper.scheduleRepostsForPersistedNotifications(5); + + // We trim the key in persistent storage. + verify(mAm, times(2)).setExactAndAllowWhileIdle(anyInt(), anyLong(), captor.capture()); + assertEquals(1000, captor.getValue().getIntent().getStringExtra(EXTRA_KEY).length()); + } + @Test public void testSnoozeForTime() throws Exception { NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM); @@ -595,13 +626,20 @@ public class SnoozeHelperTest extends UiServiceTestCase { public void testClearData() { // snooze 2 from same package NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM); - NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.SYSTEM); + NotificationRecord r2 = getNotificationRecord("pkg", 2, "two" + "2".repeat(66000), + UserHandle.SYSTEM); mSnoozeHelper.snooze(r, 1000); mSnoozeHelper.snooze(r2, 1000); assertTrue(mSnoozeHelper.isSnoozed( UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey())); assertTrue(mSnoozeHelper.isSnoozed( UserHandle.USER_SYSTEM, r2.getSbn().getPackageName(), r2.getKey())); + assertFalse(0L == mSnoozeHelper.getSnoozeTimeForUnpostedNotification( + r.getUser().getIdentifier(), r.getSbn().getPackageName(), + r.getSbn().getKey())); + assertFalse(0L == mSnoozeHelper.getSnoozeTimeForUnpostedNotification( + r2.getUser().getIdentifier(), r2.getSbn().getPackageName(), + r2.getSbn().getKey())); // clear data mSnoozeHelper.clearData(UserHandle.USER_SYSTEM, "pkg"); -- cgit v1.2.3 From 6503e5af84cd03434382b81d4839a7c050417fe8 Mon Sep 17 00:00:00 2001 From: Oli Lan Date: Fri, 2 Sep 2022 13:29:39 +0000 Subject: Validate package name passed to setApplicationRestrictions. (Reland) This adds validation that the package name passed to setApplicationRestrictions is in the correct format. This will avoid an issue where a path could be entered resulting in a file being written to an unexpected place. Bug: 239701237 Merged-In: I56c2fc14f906cdad80181ab577e2ebc276c151c1 Change-Id: I56c2fc14f906cdad80181ab577e2ebc276c151c1 (cherry picked from commit 1b9b59c63bffc675a042cba6cd666831abef2c3e) --- .../com/android/server/pm/UserManagerService.java | 41 ++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 9de485b28479..1e30fa0c2390 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -93,6 +93,7 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; +import android.util.EventLog; import android.util.IndentingPrintWriter; import android.util.IntArray; import android.util.Slog; @@ -4894,6 +4895,13 @@ public class UserManagerService extends IUserManager.Stub { public void setApplicationRestrictions(String packageName, Bundle restrictions, @UserIdInt int userId) { checkSystemOrRoot("set application restrictions"); + String validationResult = validateName(packageName); + if (validationResult != null) { + if (packageName.contains("../")) { + EventLog.writeEvent(0x534e4554, "239701237", -1, ""); + } + throw new IllegalArgumentException("Invalid package name: " + validationResult); + } if (restrictions != null) { restrictions.setDefusable(true); } @@ -4920,6 +4928,39 @@ public class UserManagerService extends IUserManager.Stub { mContext.sendBroadcastAsUser(changeIntent, UserHandle.of(userId)); } + /** + * Check if the given name is valid. + * + * Note: the logic is taken from FrameworkParsingPackageUtils in master, edited to remove + * unnecessary parts. Copied here for a security fix. + * + * @param name The name to check. + * @return null if it's valid, error message if not + */ + @VisibleForTesting + static String validateName(String name) { + final int n = name.length(); + boolean front = true; + for (int i = 0; i < n; i++) { + final char c = name.charAt(i); + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { + front = false; + continue; + } + if (!front) { + if ((c >= '0' && c <= '9') || c == '_') { + continue; + } + if (c == '.') { + front = true; + continue; + } + } + return "bad character '" + c + "'"; + } + return null; + } + private int getUidForPackage(String packageName) { final long ident = Binder.clearCallingIdentity(); try { -- cgit v1.2.3 From e936c7d37668f49b1c4671495c56b3242d7f5f24 Mon Sep 17 00:00:00 2001 From: Naomi Musgrave Date: Mon, 30 Jan 2023 18:29:14 +0000 Subject: Update OWNERS for MediaProjection & related classes Change-Id: I5af357405c2b4e2da87954612080fa85d08f991d Bug: 261563516 (cherry picked from commit abccffcc0abf85f4ac87179fc5235bfa3fdbfd8c) --- media/java/android/media/projection/OWNERS | 3 +++ services/core/java/com/android/server/media/projection/OWNERS | 2 +- .../tests/servicestests/src/com/android/server/media/projection/OWNERS | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 services/tests/servicestests/src/com/android/server/media/projection/OWNERS diff --git a/media/java/android/media/projection/OWNERS b/media/java/android/media/projection/OWNERS index 7e7335d68d3b..2273f816ac60 100644 --- a/media/java/android/media/projection/OWNERS +++ b/media/java/android/media/projection/OWNERS @@ -1 +1,4 @@ michaelwr@google.com +santoscordon@google.com +chaviw@google.com +nmusgrave@google.com diff --git a/services/core/java/com/android/server/media/projection/OWNERS b/services/core/java/com/android/server/media/projection/OWNERS index 7e7335d68d3b..832bcd9d70e6 100644 --- a/services/core/java/com/android/server/media/projection/OWNERS +++ b/services/core/java/com/android/server/media/projection/OWNERS @@ -1 +1 @@ -michaelwr@google.com +include /media/java/android/media/projection/OWNERS diff --git a/services/tests/servicestests/src/com/android/server/media/projection/OWNERS b/services/tests/servicestests/src/com/android/server/media/projection/OWNERS new file mode 100644 index 000000000000..832bcd9d70e6 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/media/projection/OWNERS @@ -0,0 +1 @@ +include /media/java/android/media/projection/OWNERS -- cgit v1.2.3 From 74184991b7af9edb2c8068476dc31cd77514fc87 Mon Sep 17 00:00:00 2001 From: Manjeet Rulhania Date: Thu, 23 Feb 2023 00:03:48 +0000 Subject: Rely on Display#getRealSize when dumping XML hierarchy * Previously, the height returned by Display#getSize was used to determine visible objects. Based on the API level and the presence of system decorations (e.g. notches), this can cause objects near the bottom of the screen to be considered out of bounds (i.e. only objects within y=0 and y=partial_height are considered even if the omitted decorations are at the top of the screen). cherry pick from aosp/2384832 Bug: 236669488 Test: manual Merged-In: Iba21245c947cec0d0054f6d86e3585ba73233223 Change-Id: I50f143528d1f5a5b677eb2628efbcd658f4cc1b0 (cherry picked from commit afebeb7b43f318a3ebd856f039d74475df92db9d) --- .../uiautomator/src/com/android/commands/uiautomator/DumpCommand.java | 2 +- .../com/android/uiautomator/core/AccessibilityNodeInfoDumper.java | 2 +- .../library/core-src/com/android/uiautomator/core/UiDevice.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmds/uiautomator/cmds/uiautomator/src/com/android/commands/uiautomator/DumpCommand.java b/cmds/uiautomator/cmds/uiautomator/src/com/android/commands/uiautomator/DumpCommand.java index 3b14be7327f7..24727c5f2448 100644 --- a/cmds/uiautomator/cmds/uiautomator/src/com/android/commands/uiautomator/DumpCommand.java +++ b/cmds/uiautomator/cmds/uiautomator/src/com/android/commands/uiautomator/DumpCommand.java @@ -107,7 +107,7 @@ public class DumpCommand extends Command { DisplayManagerGlobal.getInstance().getRealDisplay(Display.DEFAULT_DISPLAY); int rotation = display.getRotation(); Point size = new Point(); - display.getSize(size); + display.getRealSize(size); AccessibilityNodeInfoDumper.dumpWindowToFile(info, dumpFile, rotation, size.x, size.y); } diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java index ab198b319e27..488292d68620 100644 --- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java +++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java @@ -139,7 +139,7 @@ public class AccessibilityNodeInfoDumper { serializer.attribute("", "id", Integer.toString(displayId)); int rotation = display.getRotation(); Point size = new Point(); - display.getSize(size); + display.getRealSize(size); for (int i = 0, n = windows.size(); i < n; ++i) { dumpWindowRec(windows.get(i), serializer, i, size.x, size.y, rotation); } diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java index b1b432bf79ab..a31deb084575 100644 --- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java +++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/UiDevice.java @@ -767,7 +767,7 @@ public class UiDevice { if(root != null) { Display display = getAutomatorBridge().getDefaultDisplay(); Point size = new Point(); - display.getSize(size); + display.getRealSize(size); AccessibilityNodeInfoDumper.dumpWindowToFile(root, new File(new File(Environment.getDataDirectory(), "local/tmp"), fileName), display.getRotation(), size.x, size.y); -- cgit v1.2.3 From 4fad1456409b79d6e649a29d5116a4fe3160bd21 Mon Sep 17 00:00:00 2001 From: Hongwei Wang Date: Thu, 23 Feb 2023 13:23:37 -0800 Subject: Remove Activity if it enters PiP without window This is to prevent malicious app entering PiP without being visible first, like blocking onResume from completion. Which in turn leaves the PiP window in limbo and non-interactable. Bug: 265293293 Test: atest PinnedStackTests Change-Id: I458a9508662e72a1adb9d9818105f2e9d7096d44 --- services/core/java/com/android/server/wm/ActivityRecord.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 1a2eedceda88..a835aeae2387 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -1200,6 +1200,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } ensureActivityConfiguration(0 /* globalChanges */, PRESERVE_WINDOWS, true /* ignoreVisibility */); + if (inPictureInPictureMode && findMainWindow() == null) { + // Prevent malicious app entering PiP without valid WindowState, which can in turn + // result a non-touchable PiP window since the InputConsumer for PiP requires it. + EventLog.writeEvent(0x534e4554, "265293293", -1, ""); + removeImmediately(); + } } } -- cgit v1.2.3 From a2178650d678fafee22e8760de1d0f8e426d33f0 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Sun, 26 Feb 2023 22:38:35 -0800 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I5145b5ec019b10568e811764240b3519e4057b73 --- core/res/res/values-fa/strings.xml | 2 +- core/res/res/values-hy/strings.xml | 2 +- core/res/res/values-it/strings.xml | 2 +- core/res/res/values-ru/strings.xml | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index 257ed2f64d84..c02be6b4d9a7 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -296,7 +296,7 @@ "حالت ایمن" "‏سیستم Android" "جابه‌جا شدن به نمایه شخصی" - "جابه‌جا شدن به نمایه کاری" + "رفتن به نمایه کاری" "مخاطبین" "دسترسی به مخاطبین شما" "مکان" diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index f4acb07b6fba..f91b5fa69c94 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/strings.xml @@ -1936,7 +1936,7 @@ "%1$s՝ անհասանելի է" "Անհրաժեշտ է թույլտվություն" "Տեսախցիկն անհասանելի է" - "Շարունակեք հեռախոսով" + "Շարու­նակեք հեռախոսով" "Խոսափողն անհասանելի է" "Android TV-ի կարգավորումներն անհասանելի են" "Պլանշետի կարգավորումներն անհասանելի են" diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index a2b80cbc5b8b..ecbe3c24b744 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -1960,7 +1960,7 @@ "Tocca per sbloc. prof. di lav." "Connesso a %1$s" "Tocca per visualizzare i file" - "Blocca" + "Fissa" "Blocca %1$s" "Sgancia" "Sblocca %1$s" diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index d2244692105b..b4862a4ab8eb 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -1160,8 +1160,8 @@ "Отмена" "Внимание!" "Загрузка…" - "I" - "O" + "Включено" + "Выключено" "отмечено" "не отмечено" "выбрано" -- cgit v1.2.3 From 73d5730d1e19e3b60af6ebe5483d028b800728e0 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Mon, 27 Feb 2023 00:28:27 -0800 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: Ie19c62f2ce8e713eaa27e99fa6d28fe8d6379177 --- packages/SystemUI/res/values-ar/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index f0d0554ec75e..8d6feefd76cd 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -643,7 +643,7 @@ "رمز مفتاح اليمين" "رمز اليسار" "رمز اليمين" - "اضغط باستمرار مع السحب لإضافة الميزات." + "اضغط باستمرار مع السحب لإضافة المربّعات" "اضغط باستمرار مع السحب لإعادة ترتيب الميزات." "اسحب هنا للإزالة" "الحدّ الأدنى من عدد المربعات الذي تحتاج إليه هو %1$d" -- cgit v1.2.3 From 56e28c80a330a581ef667ab6daaa999454a56670 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Mon, 27 Feb 2023 03:54:55 -0800 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: Iebb6fd520113a841163acf38d473333ed1870709 --- packages/SettingsLib/res/values-af/strings.xml | 2 +- packages/SettingsLib/res/values-bs/strings.xml | 2 +- packages/SettingsLib/res/values-ca/strings.xml | 2 +- packages/SettingsLib/res/values-eu/strings.xml | 2 +- packages/SettingsLib/res/values-ne/strings.xml | 2 +- packages/SettingsLib/res/values-pl/strings.xml | 2 +- packages/SettingsLib/res/values-pt-rBR/strings.xml | 2 +- packages/SettingsLib/res/values-pt-rPT/strings.xml | 2 +- packages/SettingsLib/res/values-pt/strings.xml | 2 +- packages/SettingsLib/res/values-ro/strings.xml | 2 +- packages/SettingsLib/res/values-ru/strings.xml | 2 +- packages/SettingsLib/res/values-sk/strings.xml | 2 +- packages/SettingsLib/res/values-tr/strings.xml | 2 +- packages/SettingsLib/res/values-zh-rTW/strings.xml | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index 1992c9bb7dbf..c6d2974a5af3 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -529,7 +529,7 @@ "Wekkers en onthounotas" "Laat toe dat wekkers en onthounotas gestel word" "Wekkers en onthounotas" - "Laat hierdie program toe om wekkers te stel en tydsensitiewe handelinge te skeduleer. Dit laat die program op die agtergrond werk, wat meer batterykrag kan gebruik.\n\nAs hierdie toestemming af is, sal bestaande wekkers en tydgegronde geleenthede wat deur hierdie program geskeduleer is, nie werk nie." + "Laat hierdie app toe om wekkers te stel en tydsensitiewe handelinge te skeduleer. Dit laat die app op die agtergrond werk, wat meer batterykrag kan gebruik.\n\nAs hierdie toestemming af is, sal bestaande wekkers en tydgegronde geleenthede wat deur hierdie app geskeduleer is, nie werk nie." "skedule, wekker, onthounota, horlosie" "Skakel aan" "Skakel Moenie steur nie aan" diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index ae0e8357cc9e..cd9b86940d48 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -529,7 +529,7 @@ "Alarmi i podsjetnici" "Dozvoli postavljanje alarma i podsjetnika" "Alarmi i podsjetnici" - "Dozvolite ovoj aplikaciji da postavlja alarme i zakazuje vremenski osjetljive radnje. Ovim će se omogućiti aplikaciji da radi u pozadini, čime se može povećati potrošnja baterije.\n\nAko je ovo odobrenje isključeno, postojeći alarmi i događaji zasnovani na vremenu koje je ova aplikacija zakazala neće funkcionirati." + "Dozvolite ovoj aplikaciji da postavlja alarme i zakazuje vremenski osjetljive radnje. Ovim će se omogućiti aplikaciji da radi u pozadini, čime se može povećati potrošnja baterije.\n\nAko je ovo odobrenje isključeno, postojeći alarmi i događaji zasnovani na vremenu, a koje je ova aplikacija zakazala, neće funkcionirati." "raspored, alarm, podsjetnik, sat" "Uključi" "Uključi način rada Ne ometaj" diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index 7a21aec0c62c..946e6c7ded90 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -529,7 +529,7 @@ "Alarmes i recordatoris" "Permet la configuració d\'alarmes i recordatoris" "Alarmes i recordatoris" - "Permet que aquesta aplicació configuri alarmes i programi accions. Això permet a l\'aplicació executar-se en segon pla i, per tant, és possible que consumeixi més bateria.\n\nSi aquest permís està desactivat, les alarmes i els esdeveniments que ja hagi programat l\'aplicació no funcionaran." + "Permet que aquesta aplicació configuri alarmes i programi accions a una hora determinada. Això permet a l\'aplicació executar-se en segon pla i, per tant, és possible que consumeixi més bateria.\n\nSi aquest permís està desactivat, les alarmes i els esdeveniments que ja hagi programat l\'aplicació no funcionaran." "programació, alarma, recordatori, rellotge" "Activa" "Activa el mode No molestis" diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index f26b8200dff1..da6c34d6ace8 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -529,7 +529,7 @@ "Alarmak eta abisuak" "Eman alarmak eta abisuak ezartzeko baimena" "Alarmak eta abisuak" - "Eman alarmak ezartzeko eta denbora-muga duten ekintzak programatzeko baimena aplikazioari. Hala, aplikazioak atzeko planoan funtzionatuko du, eta litekeena da bateria gehiago kontsumitzea.\n\nEz baduzu ematen baimen hori, ez dute funtzionatuko aplikazio honen bidez programatutako alarmek eta denbora-muga duten ekintzek." + "Eman alarmak ezartzeko eta denbora-muga duten ekintzak programatzeko baimena aplikazioari. Hala, aplikazioak atzeko planoan funtzionatuko du, eta litekeena da bateria gehiago kontsumitzea.\n\nBaimen hori ematen ez baduzu, ez dute funtzionatuko aplikazio honen bidez programatutako alarmek eta denbora-muga duten ekintzek." "programazioa, alarma, abisua, erlojua" "Aktibatu" "Aktibatu ez molestatzeko modua" diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index cd8182d97118..f68a5e6b6677 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -527,7 +527,7 @@ "ठिक छ" "सम्पन्न भयो" "अलार्म र रिमाइन्डरहरू" - "अलार्म तथा रिमाइन्डर सेट गर्न दिइयोस्" + "अलार्म तथा रिमाइन्डर सेट गर्ने अनुमति दिनुहोस्" "अलार्म तथा रिमाइन्डर" "यो एपलाई अलार्म सेट गर्ने र समयमै पूरा गर्नु पर्ने कारबाहीहरूको रुटिन बनाउने अनुमति दिनुहोस्। यो अनुमति दिइएको छ भने यो एप ब्याकग्राउन्डमा चल्छ र धेरै ब्याट्री खपत हुन्छ।\n\nयो अनुमति दिइएको छैन भने सेट गरिएका अलार्म बज्दैनन् र यो एपले तय गरेका गतिविधि चल्दैनन्।" "समयतालिका, अलार्म, रिमाइन्डर, घडी" diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index a244e536a85a..3e0cdba148d7 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -529,7 +529,7 @@ "Alarmy i przypomnienia" "Zezwalaj na ustawianie alarmów i przypomnień" "Alarmy i przypomnienia" - "Zezwól na ustawianie alarmów i planowanie innych działań, w przypadku których czas jest istotny. Dzięki temu aplikacja będzie mogła działać w tle, co może zwiększyć wykorzystanie baterii.\n\nJeśli nie włączysz tych uprawnień, istniejące alarmy i zaplanowane wydarzenia z tej aplikacji nie będą działać." + "Zezwalaj tej aplikacji na ustawianie alarmów i planowanie działań, w przypadku których czas jest istotny. Aplikacja będzie mogła działać w tle, co może zwiększyć wykorzystanie baterii.\n\nJeśli nie włączysz tego uprawnienia, istniejące alarmy i zaplanowane wydarzenia z tej aplikacji nie będą działać." "harmonogram, alarm, przypomnienie, zegar" "Włącz" "Włącz tryb Nie przeszkadzać" diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index 947f1ee60cf5..73b2c0ccfbda 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -529,7 +529,7 @@ "Alarmes e lembretes" "Autorizar a definição de alarmes e lembretes" "Alarmes e lembretes" - "Permitir que o app defina alarmes e programe ações mais imediatas. Essa opção autoriza o app a ser executado em segundo plano, o que pode consumir mais bateria.\n\nSe a permissão for desativada, os alarmes e eventos programados pelo app não funcionarão." + "Permitir que o app defina alarmes e programe ações com hora marcada. Essa opção autoriza o app a ser executado em segundo plano, o que pode consumir mais bateria.\n\nSe a permissão for desativada, os alarmes e eventos programados pelo app não funcionarão." "programar, alarme, lembrete, relógio" "Ativar" "Ativar o Não perturbe" diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index 3365ade8cac4..6cb55e87dcb7 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml @@ -527,7 +527,7 @@ "OK" "Concluir" "Alarmes e lembretes" - "Permitir a definição de alarmes e lembretes" + "Permitir alarmes e lembretes" "Alarmes e lembretes" "Permita que esta app defina alarmes e agende outras ações com base no tempo. Esta ação permite que a app seja executada em segundo plano, o que pode utilizar mais bateria.\n\nSe esta autorização estiver desativada, os alarmes existentes e os eventos com base no tempo agendados por esta app não funcionam." "agendar, alarme, lembrete, relógio" diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index 947f1ee60cf5..73b2c0ccfbda 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -529,7 +529,7 @@ "Alarmes e lembretes" "Autorizar a definição de alarmes e lembretes" "Alarmes e lembretes" - "Permitir que o app defina alarmes e programe ações mais imediatas. Essa opção autoriza o app a ser executado em segundo plano, o que pode consumir mais bateria.\n\nSe a permissão for desativada, os alarmes e eventos programados pelo app não funcionarão." + "Permitir que o app defina alarmes e programe ações com hora marcada. Essa opção autoriza o app a ser executado em segundo plano, o que pode consumir mais bateria.\n\nSe a permissão for desativada, os alarmes e eventos programados pelo app não funcionarão." "programar, alarme, lembrete, relógio" "Ativar" "Ativar o Não perturbe" diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index d39f5360f34c..acf646594cd1 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -529,7 +529,7 @@ "Alarme și mementouri" "Permite setarea pentru alarme și mementouri" "Alarme și mementouri" - "Permite acestei aplicații să stabilească alarme și să planifice acțiuni dependente de timp. Astfel, aplicația poate să ruleze în fundal, fapt care ar putea consuma mai multă baterie.\n\nDacă permisiunea este dezactivată, alarmele și evenimentele dependente de timp planificate de aplicație nu vor funcționa." + "Permite acestei aplicații să seteze alarme și să planifice acțiuni care trebuie realizate în timp scurt. Astfel, aplicația poate să ruleze în fundal, ceea ce ar putea crește consumul de baterie.\n\nDacă permisiunea este dezactivată, alarmele și evenimentele dependente de timp planificate de aplicație nu vor funcționa." "programare, alarmă, memento, ceas" "Activează" "Activează Nu deranja" diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index 20014859c223..b9fd17152263 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -529,7 +529,7 @@ "Будильники и напоминания" "Разрешить установку будильников и напоминаний" "Будильники и напоминания" - "Если вы разрешите этому приложению устанавливать будильники и планировать на определенное время действия, оно будет работать в фоновом режиме. В таком случае заряд батареи может расходоваться быстрее.\n\nЕсли отключить эту настройку, текущие будильники и созданные приложением мероприятия перестанут запускаться." + "Вы можете разрешить этому приложению устанавливать будильники и планировать запуск действий в определенное время. В этом случае оно будет работать в фоновом режиме и быстрее расходовать заряд батареи.\n\nЕсли отключить это разрешение, текущие будильники и созданные приложением события перестанут запускаться." "установить, будильник, напоминание, часы" "Включить" "Включите режим \"Не беспокоить\"" diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index 51588e33a97e..a4dc08e9bcd9 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -529,7 +529,7 @@ "Budíky a pripomenutia" "Povoliť nastavovanie budíkov a pripomenutí" "Budíky a pripomenutia" - "Povoľte tejto aplikácii nastavovať budíky a plánovať akcie s časovým obmedzením. Aplikácii to umožní pracovať na pozadí, čo môže zvýšiť spotrebu batérie.\n\nAk je toto povolenie vypnuté, súčasné budíky a udalosti s časovým obmedzením naplánované touto aplikáciu nebudú fungovať." + "Povoľte tejto aplikácii nastavovať budíky a plánovať akcie s časovým obmedzením. Aplikácii to umožní pracovať na pozadí, čo môže zvýšiť spotrebu batérie.\n\nAk je toto povolenie vypnuté, existujúce budíky a udalosti s časovým obmedzením naplánované touto aplikáciu nebudú fungovať." "plán, budík, pripomenutie, hodiny" "Zapnúť" "Zapnite režim bez vyrušení" diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index c47e8eea521a..735faf6fcd4c 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -527,7 +527,7 @@ "Tamam" "Bitti" "Alarmlar ve hatırlatıcılar" - "Alarm ve hatırlatıcı ayarlanmasına izin ver" + "Alarm ve hatırlatıcı ayarlamasına izin ver" "Alarmlar ve hatırlatıcılar" "Bu uygulamanın alarm kurmasına ve zamana bağlı işlemler programlamasına izin verin. Bu izin, uygulamanın arka planda çalışmasına olanak sağlayarak daha fazla pil harcanmasına neden olabilir.\n\nBu izin verilmezse bu uygulama tarafından programlanmış mevcut alarmlar ve zamana bağlı etkinlikler çalışmaz." "program, alarm, hatırlatıcı, saat" diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index 27c4b382ffa8..d2c9b84b26df 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -240,7 +240,7 @@ "錯誤" "無線偵錯" "如要查看並使用可用的裝置,請開啟無線偵錯功能" - "使用 QR 圖碼配對裝置" + "使用 QR code 配對裝置" "使用 QR code 掃描器配對新裝置" "使用配對碼配對裝置" "使用六位數的配對碼配對新裝置" -- cgit v1.2.3 From e54d365603a3af914d18a7879432d7072c6c3ad7 Mon Sep 17 00:00:00 2001 From: Thomas Stuart Date: Mon, 21 Nov 2022 17:38:21 -0800 Subject: enforce stricter rules when registering phoneAccounts - include disable accounts when looking up accounts for a package to check if the limit is reached (10) - put a new limit of 10 supported schemes - put a new limit of 256 characters per scheme - put a new limit of 256 characters per address - ensure the Icon can write to memory w/o throwing an exception bug: 259064622 bug: 256819769 Test: cts + unit Change-Id: Ia7d8d00d9de0fb6694ded6a80c40bd55d7fdf7a7 Merged-In: Ia7d8d00d9de0fb6694ded6a80c40bd55d7fdf7a7 --- telecomm/java/android/telecom/PhoneAccount.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java index e332d3ff2b4d..808d032f5d66 100644 --- a/telecomm/java/android/telecom/PhoneAccount.java +++ b/telecomm/java/android/telecom/PhoneAccount.java @@ -517,6 +517,11 @@ public final class PhoneAccount implements Parcelable { /** * Sets the address. See {@link PhoneAccount#getAddress}. + *

+ * Note: The entire URI value is limited to 256 characters. This check is + * enforced when registering the PhoneAccount via + * {@link TelecomManager#registerPhoneAccount(PhoneAccount)} and will cause an + * {@link IllegalArgumentException} to be thrown if URI is over 256. * * @param value The address of the phone account. * @return The builder. @@ -550,6 +555,10 @@ public final class PhoneAccount implements Parcelable { /** * Sets the icon. See {@link PhoneAccount#getIcon}. + *

+ * Note: An {@link IllegalArgumentException} if the Icon cannot be written to memory. + * This check is enforced when registering the PhoneAccount via + * {@link TelecomManager#registerPhoneAccount(PhoneAccount)} * * @param icon The icon to set. */ @@ -583,6 +592,10 @@ public final class PhoneAccount implements Parcelable { /** * Specifies an additional URI scheme supported by the {@link PhoneAccount}. * + *

+ * Each URI scheme is limited to 256 characters. Adding a scheme over 256 characters will + * cause an {@link IllegalArgumentException} to be thrown when the account is registered. + * * @param uriScheme The URI scheme. * @return The builder. */ @@ -596,6 +609,12 @@ public final class PhoneAccount implements Parcelable { /** * Specifies the URI schemes supported by the {@link PhoneAccount}. * + *

+ * A max of 10 URI schemes can be added per account. Additionally, each URI scheme is + * limited to 256 characters. Adding more than 10 URI schemes or 256 characters on any + * scheme will cause an {@link IllegalArgumentException} to be thrown when the account + * is registered. + * * @param uriSchemes The URI schemes. * @return The builder. */ -- cgit v1.2.3 From a66a3156e03fbd1c3a29015db9193d66f2709f98 Mon Sep 17 00:00:00 2001 From: Thomas Stuart Date: Mon, 21 Nov 2022 17:38:21 -0800 Subject: enforce stricter rules when registering phoneAccounts - include disable accounts when looking up accounts for a package to check if the limit is reached (10) - put a new limit of 10 supported schemes - put a new limit of 256 characters per scheme - put a new limit of 256 characters per address - ensure the Icon can write to memory w/o throwing an exception bug: 259064622 bug: 256819769 Test: cts + unit Change-Id: Ia7d8d00d9de0fb6694ded6a80c40bd55d7fdf7a7 Merged-In: Ia7d8d00d9de0fb6694ded6a80c40bd55d7fdf7a7 --- telecomm/java/android/telecom/PhoneAccount.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java index 768c8eebf067..a85c85380fb7 100644 --- a/telecomm/java/android/telecom/PhoneAccount.java +++ b/telecomm/java/android/telecom/PhoneAccount.java @@ -483,6 +483,11 @@ public final class PhoneAccount implements Parcelable { /** * Sets the address. See {@link PhoneAccount#getAddress}. + *

+ * Note: The entire URI value is limited to 256 characters. This check is + * enforced when registering the PhoneAccount via + * {@link TelecomManager#registerPhoneAccount(PhoneAccount)} and will cause an + * {@link IllegalArgumentException} to be thrown if URI is over 256. * * @param value The address of the phone account. * @return The builder. @@ -516,6 +521,10 @@ public final class PhoneAccount implements Parcelable { /** * Sets the icon. See {@link PhoneAccount#getIcon}. + *

+ * Note: An {@link IllegalArgumentException} if the Icon cannot be written to memory. + * This check is enforced when registering the PhoneAccount via + * {@link TelecomManager#registerPhoneAccount(PhoneAccount)} * * @param icon The icon to set. */ @@ -549,6 +558,10 @@ public final class PhoneAccount implements Parcelable { /** * Specifies an additional URI scheme supported by the {@link PhoneAccount}. * + *

+ * Each URI scheme is limited to 256 characters. Adding a scheme over 256 characters will + * cause an {@link IllegalArgumentException} to be thrown when the account is registered. + * * @param uriScheme The URI scheme. * @return The builder. */ @@ -562,6 +575,12 @@ public final class PhoneAccount implements Parcelable { /** * Specifies the URI schemes supported by the {@link PhoneAccount}. * + *

+ * A max of 10 URI schemes can be added per account. Additionally, each URI scheme is + * limited to 256 characters. Adding more than 10 URI schemes or 256 characters on any + * scheme will cause an {@link IllegalArgumentException} to be thrown when the account + * is registered. + * * @param uriSchemes The URI schemes. * @return The builder. */ -- cgit v1.2.3 From 6a02885f90fa64d88bac31efbcdbc2bfe0a9328f Mon Sep 17 00:00:00 2001 From: Thomas Stuart Date: Mon, 21 Nov 2022 17:38:21 -0800 Subject: enforce stricter rules when registering phoneAccounts - include disable accounts when looking up accounts for a package to check if the limit is reached (10) - put a new limit of 10 supported schemes - put a new limit of 256 characters per scheme - put a new limit of 256 characters per address - ensure the Icon can write to memory w/o throwing an exception bug: 259064622 bug: 256819769 Test: cts + unit Change-Id: Ia7d8d00d9de0fb6694ded6a80c40bd55d7fdf7a7 Merged-In: Ia7d8d00d9de0fb6694ded6a80c40bd55d7fdf7a7 --- telecomm/java/android/telecom/PhoneAccount.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java index e332d3ff2b4d..808d032f5d66 100644 --- a/telecomm/java/android/telecom/PhoneAccount.java +++ b/telecomm/java/android/telecom/PhoneAccount.java @@ -517,6 +517,11 @@ public final class PhoneAccount implements Parcelable { /** * Sets the address. See {@link PhoneAccount#getAddress}. + *

+ * Note: The entire URI value is limited to 256 characters. This check is + * enforced when registering the PhoneAccount via + * {@link TelecomManager#registerPhoneAccount(PhoneAccount)} and will cause an + * {@link IllegalArgumentException} to be thrown if URI is over 256. * * @param value The address of the phone account. * @return The builder. @@ -550,6 +555,10 @@ public final class PhoneAccount implements Parcelable { /** * Sets the icon. See {@link PhoneAccount#getIcon}. + *

+ * Note: An {@link IllegalArgumentException} if the Icon cannot be written to memory. + * This check is enforced when registering the PhoneAccount via + * {@link TelecomManager#registerPhoneAccount(PhoneAccount)} * * @param icon The icon to set. */ @@ -583,6 +592,10 @@ public final class PhoneAccount implements Parcelable { /** * Specifies an additional URI scheme supported by the {@link PhoneAccount}. * + *

+ * Each URI scheme is limited to 256 characters. Adding a scheme over 256 characters will + * cause an {@link IllegalArgumentException} to be thrown when the account is registered. + * * @param uriScheme The URI scheme. * @return The builder. */ @@ -596,6 +609,12 @@ public final class PhoneAccount implements Parcelable { /** * Specifies the URI schemes supported by the {@link PhoneAccount}. * + *

+ * A max of 10 URI schemes can be added per account. Additionally, each URI scheme is + * limited to 256 characters. Adding more than 10 URI schemes or 256 characters on any + * scheme will cause an {@link IllegalArgumentException} to be thrown when the account + * is registered. + * * @param uriSchemes The URI schemes. * @return The builder. */ -- cgit v1.2.3 From a90f96a4c4e6b4b80a0556995751fdbe5e905aeb Mon Sep 17 00:00:00 2001 From: Brian Lee Date: Fri, 17 Feb 2023 16:05:17 -0800 Subject: Check key intent for selectors and prohibited flags Bug: 265015796 Test: atest FrameworksServicesTests: com.android.server.accounts.AccountManagerServiceTest Change-Id: Ie16f8654337bd75eaad3156817470674b4f0cee3 (cherry picked from commit e53a96304352e2965176c8d32ac1b504e52ef185) Merged-In: Ie16f8654337bd75eaad3156817470674b4f0cee3 --- .../server/accounts/AccountManagerService.java | 18 ++++++++--- .../server/accounts/AccountManagerServiceTest.java | 36 ++++++++++++++++++++++ .../AccountManagerServiceTestFixtures.java | 5 ++- .../accounts/TestAccountType1Authenticator.java | 5 +-- 4 files changed, 54 insertions(+), 10 deletions(-) diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 8c80dfb94d53..c0aa36a0fb77 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -4881,10 +4881,6 @@ public class AccountManagerService if (intent.getClipData() == null) { intent.setClipData(ClipData.newPlainText(null, null)); } - intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_READ_URI_PERMISSION - | Intent.FLAG_GRANT_WRITE_URI_PERMISSION - | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION - | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION)); final long bid = Binder.clearCallingIdentity(); try { PackageManager pm = mContext.getPackageManager(); @@ -4931,7 +4927,19 @@ public class AccountManagerService if (intent == null) { return (simulateIntent == null); } - return intent.filterEquals(simulateIntent); + if (!intent.filterEquals(simulateIntent)) { + return false; + } + + if (intent.getSelector() != simulateIntent.getSelector()) { + return false; + } + + int prohibitedFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION + | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION + | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION; + return (simulateIntent.getFlags() & prohibitedFlags) == 0; } private boolean isExportedSystemActivity(ActivityInfo activityInfo) { diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java index 491626281c69..5f93900aa2c3 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java @@ -18,6 +18,7 @@ package com.android.server.accounts; import static android.database.sqlite.SQLiteDatabase.deleteDatabase; +import static org.mockito.ArgumentMatchers.contains; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyInt; @@ -704,6 +705,41 @@ public class AccountManagerServiceTest extends AndroidTestCase { assertNotNull(intent.getParcelableExtra(AccountManagerServiceTestFixtures.KEY_CALLBACK)); } + @SmallTest + public void testStartAddAccountSessionWhereAuthenticatorReturnsIntentWithProhibitedFlags() + throws Exception { + unlockSystemUser(); + ResolveInfo resolveInfo = new ResolveInfo(); + resolveInfo.activityInfo = new ActivityInfo(); + resolveInfo.activityInfo.applicationInfo = new ApplicationInfo(); + when(mMockPackageManager.resolveActivityAsUser( + any(Intent.class), anyInt(), anyInt())).thenReturn(resolveInfo); + when(mMockPackageManager.checkSignatures( + anyInt(), anyInt())).thenReturn(PackageManager.SIGNATURE_MATCH); + + final CountDownLatch latch = new CountDownLatch(1); + Response response = new Response(latch, mMockAccountManagerResponse); + Bundle options = createOptionsWithAccountName( + AccountManagerServiceTestFixtures.ACCOUNT_NAME_INTERVENE); + int prohibitedFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION + | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION + | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION; + options.putInt(AccountManagerServiceTestFixtures.KEY_INTENT_FLAGS, prohibitedFlags); + + mAms.startAddAccountSession( + response, // response + AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, // accountType + "authTokenType", + null, // requiredFeatures + true, // expectActivityLaunch + options); // optionsIn + waitForLatch(latch); + + verify(mMockAccountManagerResponse).onError( + eq(AccountManager.ERROR_CODE_INVALID_RESPONSE), contains("invalid intent")); + } + @SmallTest public void testStartAddAccountSessionError() throws Exception { unlockSystemUser(); diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java index 73f30d9f9e79..b98a6a891d55 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java +++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java @@ -17,9 +17,6 @@ package com.android.server.accounts; import android.accounts.Account; -import java.util.ArrayList; -import java.util.List; - /** * Constants shared between test AccountAuthenticators and AccountManagerServiceTest. */ @@ -31,6 +28,8 @@ public final class AccountManagerServiceTestFixtures { "account_manager_service_test:account_status_token_key"; public static final String KEY_ACCOUNT_PASSWORD = "account_manager_service_test:account_password_key"; + public static final String KEY_INTENT_FLAGS = + "account_manager_service_test:intent_flags_key"; public static final String KEY_OPTIONS_BUNDLE = "account_manager_service_test:option_bundle_key"; public static final String ACCOUNT_NAME_SUCCESS = "success_on_return@fixture.com"; diff --git a/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java b/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java index 8106364477d9..924443e9d5cf 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java +++ b/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java @@ -24,8 +24,6 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; -import com.android.frameworks.servicestests.R; - import java.util.concurrent.atomic.AtomicInteger; /** @@ -270,11 +268,13 @@ public class TestAccountType1Authenticator extends AbstractAccountAuthenticator String accountName = null; Bundle sessionBundle = null; String password = null; + int intentFlags = 0; if (options != null) { accountName = options.getString(AccountManagerServiceTestFixtures.KEY_ACCOUNT_NAME); sessionBundle = options.getBundle( AccountManagerServiceTestFixtures.KEY_ACCOUNT_SESSION_BUNDLE); password = options.getString(AccountManagerServiceTestFixtures.KEY_ACCOUNT_PASSWORD); + intentFlags = options.getInt(AccountManagerServiceTestFixtures.KEY_INTENT_FLAGS, 0); } Bundle result = new Bundle(); @@ -302,6 +302,7 @@ public class TestAccountType1Authenticator extends AbstractAccountAuthenticator intent.putExtra(AccountManagerServiceTestFixtures.KEY_RESULT, eventualActivityResultData); intent.putExtra(AccountManagerServiceTestFixtures.KEY_CALLBACK, response); + intent.setFlags(intentFlags); result.putParcelable(AccountManager.KEY_INTENT, intent); } else { -- cgit v1.2.3 From 5e01f68bdabe8aa7154e1ed936235b5304f4c0cd Mon Sep 17 00:00:00 2001 From: Brian Lee Date: Fri, 17 Feb 2023 16:05:17 -0800 Subject: Check key intent for selectors and prohibited flags Bug: 265015796 Test: atest FrameworksServicesTests: com.android.server.accounts.AccountManagerServiceTest Change-Id: Ie16f8654337bd75eaad3156817470674b4f0cee3 (cherry picked from commit e53a96304352e2965176c8d32ac1b504e52ef185) Merged-In: Ie16f8654337bd75eaad3156817470674b4f0cee3 --- .../server/accounts/AccountManagerService.java | 18 ++++++++--- .../server/accounts/AccountManagerServiceTest.java | 36 ++++++++++++++++++++++ .../AccountManagerServiceTestFixtures.java | 5 ++- .../accounts/TestAccountType1Authenticator.java | 5 +-- 4 files changed, 54 insertions(+), 10 deletions(-) diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 8c80dfb94d53..c0aa36a0fb77 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -4881,10 +4881,6 @@ public class AccountManagerService if (intent.getClipData() == null) { intent.setClipData(ClipData.newPlainText(null, null)); } - intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_READ_URI_PERMISSION - | Intent.FLAG_GRANT_WRITE_URI_PERMISSION - | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION - | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION)); final long bid = Binder.clearCallingIdentity(); try { PackageManager pm = mContext.getPackageManager(); @@ -4931,7 +4927,19 @@ public class AccountManagerService if (intent == null) { return (simulateIntent == null); } - return intent.filterEquals(simulateIntent); + if (!intent.filterEquals(simulateIntent)) { + return false; + } + + if (intent.getSelector() != simulateIntent.getSelector()) { + return false; + } + + int prohibitedFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION + | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION + | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION; + return (simulateIntent.getFlags() & prohibitedFlags) == 0; } private boolean isExportedSystemActivity(ActivityInfo activityInfo) { diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java index 55619bc9c62a..b79e7873f20c 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java @@ -18,6 +18,7 @@ package com.android.server.accounts; import static android.database.sqlite.SQLiteDatabase.deleteDatabase; +import static org.mockito.ArgumentMatchers.contains; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyInt; @@ -705,6 +706,41 @@ public class AccountManagerServiceTest extends AndroidTestCase { assertNotNull(intent.getParcelableExtra(AccountManagerServiceTestFixtures.KEY_CALLBACK)); } + @SmallTest + public void testStartAddAccountSessionWhereAuthenticatorReturnsIntentWithProhibitedFlags() + throws Exception { + unlockSystemUser(); + ResolveInfo resolveInfo = new ResolveInfo(); + resolveInfo.activityInfo = new ActivityInfo(); + resolveInfo.activityInfo.applicationInfo = new ApplicationInfo(); + when(mMockPackageManager.resolveActivityAsUser( + any(Intent.class), anyInt(), anyInt())).thenReturn(resolveInfo); + when(mMockPackageManager.checkSignatures( + anyInt(), anyInt())).thenReturn(PackageManager.SIGNATURE_MATCH); + + final CountDownLatch latch = new CountDownLatch(1); + Response response = new Response(latch, mMockAccountManagerResponse); + Bundle options = createOptionsWithAccountName( + AccountManagerServiceTestFixtures.ACCOUNT_NAME_INTERVENE); + int prohibitedFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION + | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION + | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION; + options.putInt(AccountManagerServiceTestFixtures.KEY_INTENT_FLAGS, prohibitedFlags); + + mAms.startAddAccountSession( + response, // response + AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, // accountType + "authTokenType", + null, // requiredFeatures + true, // expectActivityLaunch + options); // optionsIn + waitForLatch(latch); + + verify(mMockAccountManagerResponse).onError( + eq(AccountManager.ERROR_CODE_INVALID_RESPONSE), contains("invalid intent")); + } + @SmallTest public void testStartAddAccountSessionError() throws Exception { unlockSystemUser(); diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java index 73f30d9f9e79..b98a6a891d55 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java +++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java @@ -17,9 +17,6 @@ package com.android.server.accounts; import android.accounts.Account; -import java.util.ArrayList; -import java.util.List; - /** * Constants shared between test AccountAuthenticators and AccountManagerServiceTest. */ @@ -31,6 +28,8 @@ public final class AccountManagerServiceTestFixtures { "account_manager_service_test:account_status_token_key"; public static final String KEY_ACCOUNT_PASSWORD = "account_manager_service_test:account_password_key"; + public static final String KEY_INTENT_FLAGS = + "account_manager_service_test:intent_flags_key"; public static final String KEY_OPTIONS_BUNDLE = "account_manager_service_test:option_bundle_key"; public static final String ACCOUNT_NAME_SUCCESS = "success_on_return@fixture.com"; diff --git a/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java b/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java index 8106364477d9..924443e9d5cf 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java +++ b/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java @@ -24,8 +24,6 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; -import com.android.frameworks.servicestests.R; - import java.util.concurrent.atomic.AtomicInteger; /** @@ -270,11 +268,13 @@ public class TestAccountType1Authenticator extends AbstractAccountAuthenticator String accountName = null; Bundle sessionBundle = null; String password = null; + int intentFlags = 0; if (options != null) { accountName = options.getString(AccountManagerServiceTestFixtures.KEY_ACCOUNT_NAME); sessionBundle = options.getBundle( AccountManagerServiceTestFixtures.KEY_ACCOUNT_SESSION_BUNDLE); password = options.getString(AccountManagerServiceTestFixtures.KEY_ACCOUNT_PASSWORD); + intentFlags = options.getInt(AccountManagerServiceTestFixtures.KEY_INTENT_FLAGS, 0); } Bundle result = new Bundle(); @@ -302,6 +302,7 @@ public class TestAccountType1Authenticator extends AbstractAccountAuthenticator intent.putExtra(AccountManagerServiceTestFixtures.KEY_RESULT, eventualActivityResultData); intent.putExtra(AccountManagerServiceTestFixtures.KEY_CALLBACK, response); + intent.setFlags(intentFlags); result.putParcelable(AccountManager.KEY_INTENT, intent); } else { -- cgit v1.2.3 From 1ef76e11daee3ca24acff6f3b488bce352581bf4 Mon Sep 17 00:00:00 2001 From: Brian Lee Date: Fri, 17 Feb 2023 16:05:17 -0800 Subject: Check key intent for selectors and prohibited flags Bug: 265015796 Test: atest FrameworksServicesTests: com.android.server.accounts.AccountManagerServiceTest Change-Id: Ie16f8654337bd75eaad3156817470674b4f0cee3 (cherry picked from commit e53a96304352e2965176c8d32ac1b504e52ef185) Merged-In: Ie16f8654337bd75eaad3156817470674b4f0cee3 --- .../server/accounts/AccountManagerService.java | 18 ++++++++--- .../server/accounts/AccountManagerServiceTest.java | 36 ++++++++++++++++++++++ .../AccountManagerServiceTestFixtures.java | 5 ++- .../accounts/TestAccountType1Authenticator.java | 5 +-- 4 files changed, 54 insertions(+), 10 deletions(-) diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 8c80dfb94d53..c0aa36a0fb77 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -4881,10 +4881,6 @@ public class AccountManagerService if (intent.getClipData() == null) { intent.setClipData(ClipData.newPlainText(null, null)); } - intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_READ_URI_PERMISSION - | Intent.FLAG_GRANT_WRITE_URI_PERMISSION - | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION - | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION)); final long bid = Binder.clearCallingIdentity(); try { PackageManager pm = mContext.getPackageManager(); @@ -4931,7 +4927,19 @@ public class AccountManagerService if (intent == null) { return (simulateIntent == null); } - return intent.filterEquals(simulateIntent); + if (!intent.filterEquals(simulateIntent)) { + return false; + } + + if (intent.getSelector() != simulateIntent.getSelector()) { + return false; + } + + int prohibitedFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION + | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION + | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION; + return (simulateIntent.getFlags() & prohibitedFlags) == 0; } private boolean isExportedSystemActivity(ActivityInfo activityInfo) { diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java index 491626281c69..5f93900aa2c3 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java @@ -18,6 +18,7 @@ package com.android.server.accounts; import static android.database.sqlite.SQLiteDatabase.deleteDatabase; +import static org.mockito.ArgumentMatchers.contains; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyInt; @@ -704,6 +705,41 @@ public class AccountManagerServiceTest extends AndroidTestCase { assertNotNull(intent.getParcelableExtra(AccountManagerServiceTestFixtures.KEY_CALLBACK)); } + @SmallTest + public void testStartAddAccountSessionWhereAuthenticatorReturnsIntentWithProhibitedFlags() + throws Exception { + unlockSystemUser(); + ResolveInfo resolveInfo = new ResolveInfo(); + resolveInfo.activityInfo = new ActivityInfo(); + resolveInfo.activityInfo.applicationInfo = new ApplicationInfo(); + when(mMockPackageManager.resolveActivityAsUser( + any(Intent.class), anyInt(), anyInt())).thenReturn(resolveInfo); + when(mMockPackageManager.checkSignatures( + anyInt(), anyInt())).thenReturn(PackageManager.SIGNATURE_MATCH); + + final CountDownLatch latch = new CountDownLatch(1); + Response response = new Response(latch, mMockAccountManagerResponse); + Bundle options = createOptionsWithAccountName( + AccountManagerServiceTestFixtures.ACCOUNT_NAME_INTERVENE); + int prohibitedFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION + | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION + | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION; + options.putInt(AccountManagerServiceTestFixtures.KEY_INTENT_FLAGS, prohibitedFlags); + + mAms.startAddAccountSession( + response, // response + AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, // accountType + "authTokenType", + null, // requiredFeatures + true, // expectActivityLaunch + options); // optionsIn + waitForLatch(latch); + + verify(mMockAccountManagerResponse).onError( + eq(AccountManager.ERROR_CODE_INVALID_RESPONSE), contains("invalid intent")); + } + @SmallTest public void testStartAddAccountSessionError() throws Exception { unlockSystemUser(); diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java index 73f30d9f9e79..b98a6a891d55 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java +++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java @@ -17,9 +17,6 @@ package com.android.server.accounts; import android.accounts.Account; -import java.util.ArrayList; -import java.util.List; - /** * Constants shared between test AccountAuthenticators and AccountManagerServiceTest. */ @@ -31,6 +28,8 @@ public final class AccountManagerServiceTestFixtures { "account_manager_service_test:account_status_token_key"; public static final String KEY_ACCOUNT_PASSWORD = "account_manager_service_test:account_password_key"; + public static final String KEY_INTENT_FLAGS = + "account_manager_service_test:intent_flags_key"; public static final String KEY_OPTIONS_BUNDLE = "account_manager_service_test:option_bundle_key"; public static final String ACCOUNT_NAME_SUCCESS = "success_on_return@fixture.com"; diff --git a/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java b/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java index 8106364477d9..924443e9d5cf 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java +++ b/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java @@ -24,8 +24,6 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; -import com.android.frameworks.servicestests.R; - import java.util.concurrent.atomic.AtomicInteger; /** @@ -270,11 +268,13 @@ public class TestAccountType1Authenticator extends AbstractAccountAuthenticator String accountName = null; Bundle sessionBundle = null; String password = null; + int intentFlags = 0; if (options != null) { accountName = options.getString(AccountManagerServiceTestFixtures.KEY_ACCOUNT_NAME); sessionBundle = options.getBundle( AccountManagerServiceTestFixtures.KEY_ACCOUNT_SESSION_BUNDLE); password = options.getString(AccountManagerServiceTestFixtures.KEY_ACCOUNT_PASSWORD); + intentFlags = options.getInt(AccountManagerServiceTestFixtures.KEY_INTENT_FLAGS, 0); } Bundle result = new Bundle(); @@ -302,6 +302,7 @@ public class TestAccountType1Authenticator extends AbstractAccountAuthenticator intent.putExtra(AccountManagerServiceTestFixtures.KEY_RESULT, eventualActivityResultData); intent.putExtra(AccountManagerServiceTestFixtures.KEY_CALLBACK, response); + intent.setFlags(intentFlags); result.putParcelable(AccountManager.KEY_INTENT, intent); } else { -- cgit v1.2.3 From 64f6c1e13588af3cf4d88a39d9d540c140982043 Mon Sep 17 00:00:00 2001 From: Brian Lee Date: Fri, 17 Feb 2023 16:05:17 -0800 Subject: Check key intent for selectors and prohibited flags Bug: 265015796 Test: atest FrameworksServicesTests: com.android.server.accounts.AccountManagerServiceTest Change-Id: Ie16f8654337bd75eaad3156817470674b4f0cee3 (cherry picked from commit e53a96304352e2965176c8d32ac1b504e52ef185) Merged-In: Ie16f8654337bd75eaad3156817470674b4f0cee3 --- .../server/accounts/AccountManagerService.java | 18 ++++++++--- .../server/accounts/AccountManagerServiceTest.java | 36 ++++++++++++++++++++++ .../AccountManagerServiceTestFixtures.java | 5 ++- .../accounts/TestAccountType1Authenticator.java | 5 +-- 4 files changed, 54 insertions(+), 10 deletions(-) diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 457cc819ebc4..639f35e1ae13 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -4884,10 +4884,6 @@ public class AccountManagerService if (intent.getClipData() == null) { intent.setClipData(ClipData.newPlainText(null, null)); } - intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_READ_URI_PERMISSION - | Intent.FLAG_GRANT_WRITE_URI_PERMISSION - | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION - | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION)); final long bid = Binder.clearCallingIdentity(); try { PackageManager pm = mContext.getPackageManager(); @@ -4933,7 +4929,19 @@ public class AccountManagerService if (intent == null) { return (simulateIntent == null); } - return intent.filterEquals(simulateIntent); + if (!intent.filterEquals(simulateIntent)) { + return false; + } + + if (intent.getSelector() != simulateIntent.getSelector()) { + return false; + } + + int prohibitedFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION + | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION + | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION; + return (simulateIntent.getFlags() & prohibitedFlags) == 0; } private boolean isExportedSystemActivity(ActivityInfo activityInfo) { diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java index 30ec1632a622..881d1b3d581c 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java @@ -18,6 +18,7 @@ package com.android.server.accounts; import static android.database.sqlite.SQLiteDatabase.deleteDatabase; +import static org.mockito.ArgumentMatchers.contains; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyInt; @@ -707,6 +708,41 @@ public class AccountManagerServiceTest extends AndroidTestCase { assertNotNull(intent.getParcelableExtra(AccountManagerServiceTestFixtures.KEY_CALLBACK)); } + @SmallTest + public void testStartAddAccountSessionWhereAuthenticatorReturnsIntentWithProhibitedFlags() + throws Exception { + unlockSystemUser(); + ResolveInfo resolveInfo = new ResolveInfo(); + resolveInfo.activityInfo = new ActivityInfo(); + resolveInfo.activityInfo.applicationInfo = new ApplicationInfo(); + when(mMockPackageManager.resolveActivityAsUser( + any(Intent.class), anyInt(), anyInt())).thenReturn(resolveInfo); + when(mMockPackageManager.checkSignatures( + anyInt(), anyInt())).thenReturn(PackageManager.SIGNATURE_MATCH); + + final CountDownLatch latch = new CountDownLatch(1); + Response response = new Response(latch, mMockAccountManagerResponse); + Bundle options = createOptionsWithAccountName( + AccountManagerServiceTestFixtures.ACCOUNT_NAME_INTERVENE); + int prohibitedFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION + | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION + | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION; + options.putInt(AccountManagerServiceTestFixtures.KEY_INTENT_FLAGS, prohibitedFlags); + + mAms.startAddAccountSession( + response, // response + AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, // accountType + "authTokenType", + null, // requiredFeatures + true, // expectActivityLaunch + options); // optionsIn + waitForLatch(latch); + + verify(mMockAccountManagerResponse).onError( + eq(AccountManager.ERROR_CODE_INVALID_RESPONSE), contains("invalid intent")); + } + @SmallTest public void testStartAddAccountSessionError() throws Exception { unlockSystemUser(); diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java index 73f30d9f9e79..b98a6a891d55 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java +++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java @@ -17,9 +17,6 @@ package com.android.server.accounts; import android.accounts.Account; -import java.util.ArrayList; -import java.util.List; - /** * Constants shared between test AccountAuthenticators and AccountManagerServiceTest. */ @@ -31,6 +28,8 @@ public final class AccountManagerServiceTestFixtures { "account_manager_service_test:account_status_token_key"; public static final String KEY_ACCOUNT_PASSWORD = "account_manager_service_test:account_password_key"; + public static final String KEY_INTENT_FLAGS = + "account_manager_service_test:intent_flags_key"; public static final String KEY_OPTIONS_BUNDLE = "account_manager_service_test:option_bundle_key"; public static final String ACCOUNT_NAME_SUCCESS = "success_on_return@fixture.com"; diff --git a/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java b/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java index 8106364477d9..924443e9d5cf 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java +++ b/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java @@ -24,8 +24,6 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; -import com.android.frameworks.servicestests.R; - import java.util.concurrent.atomic.AtomicInteger; /** @@ -270,11 +268,13 @@ public class TestAccountType1Authenticator extends AbstractAccountAuthenticator String accountName = null; Bundle sessionBundle = null; String password = null; + int intentFlags = 0; if (options != null) { accountName = options.getString(AccountManagerServiceTestFixtures.KEY_ACCOUNT_NAME); sessionBundle = options.getBundle( AccountManagerServiceTestFixtures.KEY_ACCOUNT_SESSION_BUNDLE); password = options.getString(AccountManagerServiceTestFixtures.KEY_ACCOUNT_PASSWORD); + intentFlags = options.getInt(AccountManagerServiceTestFixtures.KEY_INTENT_FLAGS, 0); } Bundle result = new Bundle(); @@ -302,6 +302,7 @@ public class TestAccountType1Authenticator extends AbstractAccountAuthenticator intent.putExtra(AccountManagerServiceTestFixtures.KEY_RESULT, eventualActivityResultData); intent.putExtra(AccountManagerServiceTestFixtures.KEY_CALLBACK, response); + intent.setFlags(intentFlags); result.putParcelable(AccountManager.KEY_INTENT, intent); } else { -- cgit v1.2.3 From b5095b2bc2cf192c5902ce0cbb90a9f0f70d4a7b Mon Sep 17 00:00:00 2001 From: Kweku Adams Date: Fri, 24 Feb 2023 18:45:51 +0000 Subject: Add new capability name. Rename PROCESS_CAPABILITY_NETWORK to PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK so it's clearer what the capability grants. Bug: 261999509 Test: CTS Change-Id: Ie0cc529c9154d6e3ac69d2d9e5a760a62bd7fe92 (cherry picked from commit c0f77b8cfa20b91cb5b14830bdef700ded1b661f) Merged-In: Ie0cc529c9154d6e3ac69d2d9e5a760a62bd7fe92 --- core/api/test-current.txt | 3 ++- core/java/android/app/ActivityManager.java | 12 ++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 1ca46428d708..d0bb1d0c4435 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -141,8 +141,9 @@ package android.app { field public static final int PROCESS_CAPABILITY_FOREGROUND_CAMERA = 2; // 0x2 field public static final int PROCESS_CAPABILITY_FOREGROUND_LOCATION = 1; // 0x1 field public static final int PROCESS_CAPABILITY_FOREGROUND_MICROPHONE = 4; // 0x4 - field public static final int PROCESS_CAPABILITY_NETWORK = 8; // 0x8 + field @Deprecated public static final int PROCESS_CAPABILITY_NETWORK = 8; // 0x8 field public static final int PROCESS_CAPABILITY_NONE = 0; // 0x0 + field public static final int PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK = 8; // 0x8 field public static final int PROCESS_STATE_FOREGROUND_SERVICE = 4; // 0x4 field public static final int PROCESS_STATE_TOP = 2; // 0x2 field public static final int STOP_USER_ON_SWITCH_DEFAULT = -1; // 0xffffffff diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 5d1d225f4d2d..b1f23282edbb 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -642,9 +642,17 @@ public class ActivityManager { @TestApi public static final int PROCESS_CAPABILITY_FOREGROUND_MICROPHONE = 1 << 2; - /** @hide Process can access network despite any power saving resrictions */ + /** @hide Process can access network despite any power saving restrictions */ @TestApi - public static final int PROCESS_CAPABILITY_NETWORK = 1 << 3; + public static final int PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK = 1 << 3; + /** + * @hide + * @deprecated Use {@link #PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK} instead. + */ + @TestApi + @Deprecated + public static final int PROCESS_CAPABILITY_NETWORK = + PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK; /** @hide all capabilities, the ORing of all flags in {@link ProcessCapability}*/ @TestApi -- cgit v1.2.3 From 2a2da8935ab70163044c34d7b0d9b9ed4cb91a76 Mon Sep 17 00:00:00 2001 From: Valentin Iftime Date: Wed, 15 Feb 2023 20:39:44 +0100 Subject: [DO NOT MERGE] Wait for preloading images to complete before inflating notifications NotificationContentInflater waits on SysUiBg thread for images to load, with a timeout of 1000ms. Test: 1. Build a test app that posts MessagingStyle notifications with a huge image (8k+) set as data Uri. 2. SystemUi should not ANR 3. adb logcat | grep NotificationInlineImageCache - shows timeout/cancellation logs Bug: 252766417 Bug: 223859644 Change-Id: I341db60223214cf2282b5c0270e343e1ce95fa01 --- .../row/NotificationContentInflater.java | 15 ++++++- .../row/NotificationInlineImageCache.java | 21 ++++++++-- .../row/NotificationInlineImageResolver.java | 49 ++++++++++++++++++++-- 3 files changed, 76 insertions(+), 9 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java index 4c693045bc88..e88d262d7db2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java @@ -439,6 +439,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder CancellationSignal cancellationSignal = new CancellationSignal(); cancellationSignal.setOnCancelListener( () -> runningInflations.values().forEach(CancellationSignal::cancel)); + return cancellationSignal; } @@ -711,6 +712,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder public static class AsyncInflationTask extends AsyncTask implements InflationCallback, InflationTask { + private static final long IMG_PRELOAD_TIMEOUT_MS = 1000L; private final NotificationEntry mEntry; private final Context mContext; private final boolean mInflateSynchronously; @@ -804,7 +806,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder recoveredBuilder, mIsLowPriority, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, packageContext); InflatedSmartReplyState previousSmartReplyState = mRow.getExistingSmartReplyState(); - return inflateSmartReplyViews( + InflationProgress result = inflateSmartReplyViews( inflationProgress, mReInflateFlags, mEntry, @@ -812,6 +814,11 @@ public class NotificationContentInflater implements NotificationRowContentBinder packageContext, previousSmartReplyState, mSmartRepliesInflater); + + // wait for image resolver to finish preloading + mRow.getImageResolver().waitForPreloadedImages(IMG_PRELOAD_TIMEOUT_MS); + + return result; } catch (Exception e) { mError = e; return null; @@ -846,6 +853,9 @@ public class NotificationContentInflater implements NotificationRowContentBinder mCallback.handleInflationException(mRow.getEntry(), new InflationException("Couldn't inflate contentViews" + e)); } + + // Cancel any image loading tasks, not useful any more + mRow.getImageResolver().cancelRunningTasks(); } @Override @@ -872,6 +882,9 @@ public class NotificationContentInflater implements NotificationRowContentBinder // Notify the resolver that the inflation task has finished, // try to purge unnecessary cached entries. mRow.getImageResolver().purgeCache(); + + // Cancel any image loading tasks that have not completed at this point + mRow.getImageResolver().cancelRunningTasks(); } private static class RtlEnabledContext extends ContextWrapper { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java index 41eeada0fcda..fe0b3123eb25 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java @@ -22,8 +22,11 @@ import android.os.AsyncTask; import android.util.Log; import java.util.Set; +import java.util.concurrent.CancellationException; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; /** * A cache for inline images of image messages. @@ -56,12 +59,13 @@ public class NotificationInlineImageCache implements NotificationInlineImageReso } @Override - public Drawable get(Uri uri) { + public Drawable get(Uri uri, long timeoutMs) { Drawable result = null; try { - result = mCache.get(uri).get(); - } catch (InterruptedException | ExecutionException ex) { - Log.d(TAG, "get: Failed get image from " + uri); + result = mCache.get(uri).get(timeoutMs, TimeUnit.MILLISECONDS); + } catch (InterruptedException | ExecutionException + | TimeoutException | CancellationException ex) { + Log.d(TAG, "get: Failed get image from " + uri + " " + ex); } return result; } @@ -72,6 +76,15 @@ public class NotificationInlineImageCache implements NotificationInlineImageReso mCache.entrySet().removeIf(entry -> !wantedSet.contains(entry.getKey())); } + @Override + public void cancelRunningTasks() { + mCache.forEach((key, value) -> { + if (value.getStatus() != AsyncTask.Status.FINISHED) { + value.cancel(true); + } + }); + } + private static class PreloadImageTask extends AsyncTask { private final NotificationInlineImageResolver mResolver; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java index b05e64ab1991..c620f448b3b7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java @@ -23,6 +23,7 @@ import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.os.Parcelable; +import android.os.SystemClock; import android.util.Log; import com.android.internal.R; @@ -45,6 +46,9 @@ import java.util.Set; public class NotificationInlineImageResolver implements ImageResolver { private static final String TAG = NotificationInlineImageResolver.class.getSimpleName(); + // Timeout for loading images from ImageCache when calling from UI thread + private static final long MAX_UI_THREAD_TIMEOUT_MS = 100L; + private final Context mContext; private final ImageCache mImageCache; private Set mWantedUriSet; @@ -123,17 +127,25 @@ public class NotificationInlineImageResolver implements ImageResolver { return null; } + /** + * Loads an image from the Uri. + * This method is synchronous and is usually called from the Main thread. + * It will time-out after MAX_UI_THREAD_TIMEOUT_MS. + * + * @param uri Uri of the target image. + * @return drawable of the image, null if loading failed/timeout + */ @Override public Drawable loadImage(Uri uri) { - return hasCache() ? loadImageFromCache(uri) : resolveImage(uri); + return hasCache() ? loadImageFromCache(uri, MAX_UI_THREAD_TIMEOUT_MS) : resolveImage(uri); } - private Drawable loadImageFromCache(Uri uri) { + private Drawable loadImageFromCache(Uri uri, long timeoutMs) { // if the uri isn't currently cached, try caching it first if (!mImageCache.hasEntry(uri)) { mImageCache.preload((uri)); } - return mImageCache.get(uri); + return mImageCache.get(uri, timeoutMs); } /** @@ -207,6 +219,30 @@ public class NotificationInlineImageResolver implements ImageResolver { return mWantedUriSet; } + /** + * Wait for a maximum timeout for images to finish preloading + * @param timeoutMs total timeout time + */ + void waitForPreloadedImages(long timeoutMs) { + if (!hasCache()) { + return; + } + Set preloadedUris = getWantedUriSet(); + if (preloadedUris != null) { + // Decrement remaining timeout after each image check + long endTimeMs = SystemClock.elapsedRealtime() + timeoutMs; + preloadedUris.forEach( + uri -> loadImageFromCache(uri, endTimeMs - SystemClock.elapsedRealtime())); + } + } + + void cancelRunningTasks() { + if (!hasCache()) { + return; + } + mImageCache.cancelRunningTasks(); + } + /** * A interface for internal cache implementation of this resolver. */ @@ -216,7 +252,7 @@ public class NotificationInlineImageResolver implements ImageResolver { * @param uri The uri of the image. * @return Drawable of the image. */ - Drawable get(Uri uri); + Drawable get(Uri uri, long timeoutMs); /** * Set the image resolver that actually resolves image from specified uri. @@ -241,6 +277,11 @@ public class NotificationInlineImageResolver implements ImageResolver { * Purge unnecessary entries in the cache. */ void purge(); + + /** + * Cancel all unfinished image loading tasks + */ + void cancelRunningTasks(); } } -- cgit v1.2.3 From b9cd15ad8a2f87893164ad2ab518039bb0b61424 Mon Sep 17 00:00:00 2001 From: Valentin Iftime Date: Wed, 15 Feb 2023 20:39:44 +0100 Subject: [DO NOT MERGE] Wait for preloading images to complete before inflating notifications NotificationContentInflater waits on SysUiBg thread for images to load, with a timeout of 1000ms. Test: 1. Build a test app that posts MessagingStyle notifications with a huge image (8k+) set as data Uri. 2. SystemUi should not ANR 3. adb logcat | grep NotificationInlineImageCache - shows timeout/cancellation logs Bug: 252766417 Bug: 223859644 Change-Id: I341db60223214cf2282b5c0270e343e1ce95fa01 (cherry picked from commit 195043f40e46ddcd2fe534a9dac344792d39d91c) Merged-In: I341db60223214cf2282b5c0270e343e1ce95fa01 --- .../row/NotificationContentInflater.java | 15 +++- .../row/NotificationInlineImageCache.java | 23 +++++-- .../row/NotificationInlineImageResolver.java | 79 ++++++++++++++++------ 3 files changed, 92 insertions(+), 25 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java index 73c4b054fd4e..88fb332f2079 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java @@ -439,6 +439,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder CancellationSignal cancellationSignal = new CancellationSignal(); cancellationSignal.setOnCancelListener( () -> runningInflations.values().forEach(CancellationSignal::cancel)); + return cancellationSignal; } @@ -711,6 +712,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder public static class AsyncInflationTask extends AsyncTask implements InflationCallback, InflationTask { + private static final long IMG_PRELOAD_TIMEOUT_MS = 1000L; private final NotificationEntry mEntry; private final Context mContext; private final boolean mInflateSynchronously; @@ -804,7 +806,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder recoveredBuilder, mIsLowPriority, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, packageContext); InflatedSmartReplyState previousSmartReplyState = mRow.getExistingSmartReplyState(); - return inflateSmartReplyViews( + InflationProgress result = inflateSmartReplyViews( inflationProgress, mReInflateFlags, mEntry, @@ -812,6 +814,11 @@ public class NotificationContentInflater implements NotificationRowContentBinder packageContext, previousSmartReplyState, mSmartRepliesInflater); + + // wait for image resolver to finish preloading + mRow.getImageResolver().waitForPreloadedImages(IMG_PRELOAD_TIMEOUT_MS); + + return result; } catch (Exception e) { mError = e; return null; @@ -846,6 +853,9 @@ public class NotificationContentInflater implements NotificationRowContentBinder mCallback.handleInflationException(mRow.getEntry(), new InflationException("Couldn't inflate contentViews" + e)); } + + // Cancel any image loading tasks, not useful any more + mRow.getImageResolver().cancelRunningTasks(); } @Override @@ -872,6 +882,9 @@ public class NotificationContentInflater implements NotificationRowContentBinder // Notify the resolver that the inflation task has finished, // try to purge unnecessary cached entries. mRow.getImageResolver().purgeCache(); + + // Cancel any image loading tasks that have not completed at this point + mRow.getImageResolver().cancelRunningTasks(); } private class RtlEnabledContext extends ContextWrapper { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java index 4b0e2ffd5d7f..75dfde89e14f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java @@ -23,8 +23,11 @@ import android.util.Log; import java.io.IOException; import java.util.Set; +import java.util.concurrent.CancellationException; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; /** * A cache for inline images of image messages. @@ -57,12 +60,13 @@ public class NotificationInlineImageCache implements NotificationInlineImageReso } @Override - public Drawable get(Uri uri) { + public Drawable get(Uri uri, long timeoutMs) { Drawable result = null; try { - result = mCache.get(uri).get(); - } catch (InterruptedException | ExecutionException ex) { - Log.d(TAG, "get: Failed get image from " + uri); + result = mCache.get(uri).get(timeoutMs, TimeUnit.MILLISECONDS); + } catch (InterruptedException | ExecutionException + | TimeoutException | CancellationException ex) { + Log.d(TAG, "get: Failed get image from " + uri + " " + ex); } return result; } @@ -73,6 +77,15 @@ public class NotificationInlineImageCache implements NotificationInlineImageReso mCache.entrySet().removeIf(entry -> !wantedSet.contains(entry.getKey())); } + @Override + public void cancelRunningTasks() { + mCache.forEach((key, value) -> { + if (value.getStatus() != AsyncTask.Status.FINISHED) { + value.cancel(true); + } + }); + } + private static class PreloadImageTask extends AsyncTask { private final NotificationInlineImageResolver mResolver; @@ -87,7 +100,7 @@ public class NotificationInlineImageCache implements NotificationInlineImageReso try { drawable = mResolver.resolveImage(target); - } catch (IOException | SecurityException ex) { + } catch (Exception ex) { Log.d(TAG, "PreloadImageTask: Resolve failed from " + target, ex); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java index 44ccb68cce4a..2caa434413f4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java @@ -23,6 +23,7 @@ import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.os.Parcelable; +import android.os.SystemClock; import android.util.Log; import com.android.internal.R; @@ -46,6 +47,9 @@ import java.util.Set; public class NotificationInlineImageResolver implements ImageResolver { private static final String TAG = NotificationInlineImageResolver.class.getSimpleName(); + // Timeout for loading images from ImageCache when calling from UI thread + private static final long MAX_UI_THREAD_TIMEOUT_MS = 100L; + private final Context mContext; private final ImageCache mImageCache; private Set mWantedUriSet; @@ -111,30 +115,38 @@ public class NotificationInlineImageResolver implements ImageResolver { * To resolve image from specified uri directly. If the resulting image is larger than the * maximum allowed size, scale it down. * @param uri Uri of the image. - * @return Drawable of the image. - * @throws IOException Throws if failed at resolving the image. + * @return Drawable of the image, or null if unable to load. */ - Drawable resolveImage(Uri uri) throws IOException { - return LocalImageResolver.resolveImage(uri, mContext, mMaxImageWidth, mMaxImageHeight); + Drawable resolveImage(Uri uri) { + try { + return LocalImageResolver.resolveImage(uri, mContext, mMaxImageWidth, mMaxImageHeight); + } catch (Exception ex) { + // Catch general Exception because ContentResolver can re-throw arbitrary Exception + // from remote process as a RuntimeException. See: Parcel#readException + Log.d(TAG, "resolveImage: Can't load image from " + uri, ex); + } + return null; } + /** + * Loads an image from the Uri. + * This method is synchronous and is usually called from the Main thread. + * It will time-out after MAX_UI_THREAD_TIMEOUT_MS. + * + * @param uri Uri of the target image. + * @return drawable of the image, null if loading failed/timeout + */ @Override public Drawable loadImage(Uri uri) { - Drawable result = null; - try { - if (hasCache()) { - // if the uri isn't currently cached, try caching it first - if (!mImageCache.hasEntry(uri)) { - mImageCache.preload((uri)); - } - result = mImageCache.get(uri); - } else { - result = resolveImage(uri); - } - } catch (IOException | SecurityException ex) { - Log.d(TAG, "loadImage: Can't load image from " + uri, ex); + return hasCache() ? loadImageFromCache(uri, MAX_UI_THREAD_TIMEOUT_MS) : resolveImage(uri); + } + + private Drawable loadImageFromCache(Uri uri, long timeoutMs) { + // if the uri isn't currently cached, try caching it first + if (!mImageCache.hasEntry(uri)) { + mImageCache.preload((uri)); } - return result; + return mImageCache.get(uri, timeoutMs); } /** @@ -208,6 +220,30 @@ public class NotificationInlineImageResolver implements ImageResolver { return mWantedUriSet; } + /** + * Wait for a maximum timeout for images to finish preloading + * @param timeoutMs total timeout time + */ + void waitForPreloadedImages(long timeoutMs) { + if (!hasCache()) { + return; + } + Set preloadedUris = getWantedUriSet(); + if (preloadedUris != null) { + // Decrement remaining timeout after each image check + long endTimeMs = SystemClock.elapsedRealtime() + timeoutMs; + preloadedUris.forEach( + uri -> loadImageFromCache(uri, endTimeMs - SystemClock.elapsedRealtime())); + } + } + + void cancelRunningTasks() { + if (!hasCache()) { + return; + } + mImageCache.cancelRunningTasks(); + } + /** * A interface for internal cache implementation of this resolver. */ @@ -217,7 +253,7 @@ public class NotificationInlineImageResolver implements ImageResolver { * @param uri The uri of the image. * @return Drawable of the image. */ - Drawable get(Uri uri); + Drawable get(Uri uri, long timeoutMs); /** * Set the image resolver that actually resolves image from specified uri. @@ -242,6 +278,11 @@ public class NotificationInlineImageResolver implements ImageResolver { * Purge unnecessary entries in the cache. */ void purge(); + + /** + * Cancel all unfinished image loading tasks + */ + void cancelRunningTasks(); } } -- cgit v1.2.3 From b07326f1fb3d0e42a6bab902c271974c42d93c06 Mon Sep 17 00:00:00 2001 From: Thomas Stuart Date: Mon, 21 Nov 2022 17:38:21 -0800 Subject: enforce stricter rules when registering phoneAccounts - include disable accounts when looking up accounts for a package to check if the limit is reached (10) - put a new limit of 10 supported schemes - put a new limit of 256 characters per scheme - put a new limit of 256 characters per address - ensure the Icon can write to memory w/o throwing an exception bug: 259064622 bug: 256819769 Test: cts + unit Change-Id: Ia7d8d00d9de0fb6694ded6a80c40bd55d7fdf7a7 Merged-In: Ia7d8d00d9de0fb6694ded6a80c40bd55d7fdf7a7 --- telecomm/java/android/telecom/PhoneAccount.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java index ec18c6a696b8..7a53447c1eee 100644 --- a/telecomm/java/android/telecom/PhoneAccount.java +++ b/telecomm/java/android/telecom/PhoneAccount.java @@ -555,6 +555,11 @@ public final class PhoneAccount implements Parcelable { /** * Sets the address. See {@link PhoneAccount#getAddress}. + *

+ * Note: The entire URI value is limited to 256 characters. This check is + * enforced when registering the PhoneAccount via + * {@link TelecomManager#registerPhoneAccount(PhoneAccount)} and will cause an + * {@link IllegalArgumentException} to be thrown if URI is over 256. * * @param value The address of the phone account. * @return The builder. @@ -588,6 +593,10 @@ public final class PhoneAccount implements Parcelable { /** * Sets the icon. See {@link PhoneAccount#getIcon}. + *

+ * Note: An {@link IllegalArgumentException} if the Icon cannot be written to memory. + * This check is enforced when registering the PhoneAccount via + * {@link TelecomManager#registerPhoneAccount(PhoneAccount)} * * @param icon The icon to set. */ @@ -621,6 +630,10 @@ public final class PhoneAccount implements Parcelable { /** * Specifies an additional URI scheme supported by the {@link PhoneAccount}. * + *

+ * Each URI scheme is limited to 256 characters. Adding a scheme over 256 characters will + * cause an {@link IllegalArgumentException} to be thrown when the account is registered. + * * @param uriScheme The URI scheme. * @return The builder. */ @@ -634,6 +647,12 @@ public final class PhoneAccount implements Parcelable { /** * Specifies the URI schemes supported by the {@link PhoneAccount}. * + *

+ * A max of 10 URI schemes can be added per account. Additionally, each URI scheme is + * limited to 256 characters. Adding more than 10 URI schemes or 256 characters on any + * scheme will cause an {@link IllegalArgumentException} to be thrown when the account + * is registered. + * * @param uriSchemes The URI schemes. * @return The builder. */ -- cgit v1.2.3 From f7ba4ef2f89f3850fd51144643ce13c49a8f4bd0 Mon Sep 17 00:00:00 2001 From: Brian Lee Date: Fri, 17 Feb 2023 16:05:17 -0800 Subject: Check key intent for selectors and prohibited flags Bug: 265015796 Test: atest FrameworksServicesTests: com.android.server.accounts.AccountManagerServiceTest Change-Id: Ie16f8654337bd75eaad3156817470674b4f0cee3 (cherry picked from commit e53a96304352e2965176c8d32ac1b504e52ef185) Merged-In: Ie16f8654337bd75eaad3156817470674b4f0cee3 --- .../server/accounts/AccountManagerService.java | 18 ++++++++--- .../server/accounts/AccountManagerServiceTest.java | 36 ++++++++++++++++++++++ .../AccountManagerServiceTestFixtures.java | 5 ++- .../accounts/TestAccountType1Authenticator.java | 5 +-- 4 files changed, 54 insertions(+), 10 deletions(-) diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 8d4337accc20..545373c289bc 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -4798,10 +4798,6 @@ public class AccountManagerService if (intent.getClipData() == null) { intent.setClipData(ClipData.newPlainText(null, null)); } - intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_READ_URI_PERMISSION - | Intent.FLAG_GRANT_WRITE_URI_PERMISSION - | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION - | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION)); long bid = Binder.clearCallingIdentity(); try { PackageManager pm = mContext.getPackageManager(); @@ -4848,7 +4844,19 @@ public class AccountManagerService if (intent == null) { return (simulateIntent == null); } - return intent.filterEquals(simulateIntent); + if (!intent.filterEquals(simulateIntent)) { + return false; + } + + if (intent.getSelector() != simulateIntent.getSelector()) { + return false; + } + + int prohibitedFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION + | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION + | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION; + return (simulateIntent.getFlags() & prohibitedFlags) == 0; } private boolean isExportedSystemActivity(ActivityInfo activityInfo) { diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java index 52670c7ffd18..3c54ca1f8b80 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java @@ -18,6 +18,7 @@ package com.android.server.accounts; import static android.database.sqlite.SQLiteDatabase.deleteDatabase; +import static org.mockito.ArgumentMatchers.contains; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyInt; @@ -691,6 +692,41 @@ public class AccountManagerServiceTest extends AndroidTestCase { assertNotNull(intent.getParcelableExtra(AccountManagerServiceTestFixtures.KEY_CALLBACK)); } + @SmallTest + public void testStartAddAccountSessionWhereAuthenticatorReturnsIntentWithProhibitedFlags() + throws Exception { + unlockSystemUser(); + ResolveInfo resolveInfo = new ResolveInfo(); + resolveInfo.activityInfo = new ActivityInfo(); + resolveInfo.activityInfo.applicationInfo = new ApplicationInfo(); + when(mMockPackageManager.resolveActivityAsUser( + any(Intent.class), anyInt(), anyInt())).thenReturn(resolveInfo); + when(mMockPackageManager.checkSignatures( + anyInt(), anyInt())).thenReturn(PackageManager.SIGNATURE_MATCH); + + final CountDownLatch latch = new CountDownLatch(1); + Response response = new Response(latch, mMockAccountManagerResponse); + Bundle options = createOptionsWithAccountName( + AccountManagerServiceTestFixtures.ACCOUNT_NAME_INTERVENE); + int prohibitedFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION + | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION + | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION; + options.putInt(AccountManagerServiceTestFixtures.KEY_INTENT_FLAGS, prohibitedFlags); + + mAms.startAddAccountSession( + response, // response + AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, // accountType + "authTokenType", + null, // requiredFeatures + true, // expectActivityLaunch + options); // optionsIn + waitForLatch(latch); + + verify(mMockAccountManagerResponse).onError( + eq(AccountManager.ERROR_CODE_INVALID_RESPONSE), contains("invalid intent")); + } + @SmallTest public void testStartAddAccountSessionError() throws Exception { unlockSystemUser(); diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java index 73f30d9f9e79..b98a6a891d55 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java +++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java @@ -17,9 +17,6 @@ package com.android.server.accounts; import android.accounts.Account; -import java.util.ArrayList; -import java.util.List; - /** * Constants shared between test AccountAuthenticators and AccountManagerServiceTest. */ @@ -31,6 +28,8 @@ public final class AccountManagerServiceTestFixtures { "account_manager_service_test:account_status_token_key"; public static final String KEY_ACCOUNT_PASSWORD = "account_manager_service_test:account_password_key"; + public static final String KEY_INTENT_FLAGS = + "account_manager_service_test:intent_flags_key"; public static final String KEY_OPTIONS_BUNDLE = "account_manager_service_test:option_bundle_key"; public static final String ACCOUNT_NAME_SUCCESS = "success_on_return@fixture.com"; diff --git a/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java b/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java index 8106364477d9..924443e9d5cf 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java +++ b/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java @@ -24,8 +24,6 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; -import com.android.frameworks.servicestests.R; - import java.util.concurrent.atomic.AtomicInteger; /** @@ -270,11 +268,13 @@ public class TestAccountType1Authenticator extends AbstractAccountAuthenticator String accountName = null; Bundle sessionBundle = null; String password = null; + int intentFlags = 0; if (options != null) { accountName = options.getString(AccountManagerServiceTestFixtures.KEY_ACCOUNT_NAME); sessionBundle = options.getBundle( AccountManagerServiceTestFixtures.KEY_ACCOUNT_SESSION_BUNDLE); password = options.getString(AccountManagerServiceTestFixtures.KEY_ACCOUNT_PASSWORD); + intentFlags = options.getInt(AccountManagerServiceTestFixtures.KEY_INTENT_FLAGS, 0); } Bundle result = new Bundle(); @@ -302,6 +302,7 @@ public class TestAccountType1Authenticator extends AbstractAccountAuthenticator intent.putExtra(AccountManagerServiceTestFixtures.KEY_RESULT, eventualActivityResultData); intent.putExtra(AccountManagerServiceTestFixtures.KEY_CALLBACK, response); + intent.setFlags(intentFlags); result.putParcelable(AccountManager.KEY_INTENT, intent); } else { -- cgit v1.2.3 From fa83e125d14e458545086d16f2e7d1051812dabc Mon Sep 17 00:00:00 2001 From: Mark Renouf Date: Wed, 22 Feb 2023 14:48:51 +0000 Subject: Prevent sharesheet from previewing unowned URIs Bug: 261036568 Test: manually via supplied tool (see bug) Change-Id: Ib3f5839d00c7cf09bca3b01fc0a8a6f0f4960993 Merged-In: Iee1a75ef6ecbf471badeb42d8ebea11e74d884c1 Merged-In: I83e93c373538460e38ec17f1fd8e39d7aea95c10 --- .../com/android/internal/app/ChooserActivity.java | 36 ++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 66abe30d0123..25ba36cc3240 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -16,6 +16,8 @@ package com.android.internal.app; +import static android.content.ContentProvider.getUserIdFromUri; + import static com.android.internal.util.LatencyTracker.ACTION_LOAD_SHARE_SHEET; import static java.lang.annotation.RetentionPolicy.SOURCE; @@ -143,6 +145,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Supplier; +import java.util.stream.Collectors; /** * The Chooser Activity handles intent resolution specifically for sharing intents - @@ -1292,7 +1295,7 @@ public class ChooserActivity extends ResolverActivity implements ImageView previewThumbnailView = contentPreviewLayout.findViewById( R.id.content_preview_thumbnail); - if (previewThumbnail == null) { + if (!validForContentPreview(previewThumbnail)) { previewThumbnailView.setVisibility(View.GONE); } else { mPreviewCoord = new ContentPreviewCoordinator(contentPreviewLayout, false); @@ -1322,6 +1325,10 @@ public class ChooserActivity extends ResolverActivity implements String action = targetIntent.getAction(); if (Intent.ACTION_SEND.equals(action)) { Uri uri = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM); + if (!validForContentPreview(uri)) { + contentPreviewLayout.setVisibility(View.GONE); + return contentPreviewLayout; + } imagePreview.findViewById(R.id.content_preview_image_1_large) .setTransitionName(ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME); mPreviewCoord.loadUriIntoView(R.id.content_preview_image_1_large, uri, 0); @@ -1331,7 +1338,7 @@ public class ChooserActivity extends ResolverActivity implements List uris = targetIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); List imageUris = new ArrayList<>(); for (Uri uri : uris) { - if (isImageType(resolver.getType(uri))) { + if (validForContentPreview(uri) && isImageType(resolver.getType(uri))) { imageUris.add(uri); } } @@ -1441,9 +1448,16 @@ public class ChooserActivity extends ResolverActivity implements String action = targetIntent.getAction(); if (Intent.ACTION_SEND.equals(action)) { Uri uri = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM); + if (!validForContentPreview(uri)) { + contentPreviewLayout.setVisibility(View.GONE); + return contentPreviewLayout; + } loadFileUriIntoView(uri, contentPreviewLayout); } else { List uris = targetIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); + uris = uris.stream() + .filter(ChooserActivity::validForContentPreview) + .collect(Collectors.toList()); int uriCount = uris.size(); if (uriCount == 0) { @@ -1502,6 +1516,24 @@ public class ChooserActivity extends ResolverActivity implements } } + /** + * Indicate if the incoming content URI should be allowed. + * + * @param uri the uri to test + * @return true if the URI is allowed for content preview + */ + private static boolean validForContentPreview(Uri uri) throws SecurityException { + if (uri == null) { + return false; + } + int userId = getUserIdFromUri(uri, UserHandle.USER_CURRENT); + if (userId != UserHandle.USER_CURRENT && userId != UserHandle.myUserId()) { + Log.e(TAG, "dropped invalid content URI belonging to user " + userId); + return false; + } + return true; + } + @VisibleForTesting protected boolean isImageType(String mimeType) { return mimeType != null && mimeType.startsWith("image/"); -- cgit v1.2.3 From 23bf0bda7d9b97a82ea04257318bb90677561476 Mon Sep 17 00:00:00 2001 From: Justin Dunlap Date: Wed, 1 Mar 2023 00:07:21 +0000 Subject: Revert "Make Activites touch opaque - DO NOT MERGE" This reverts commit 22261fa6649f6ec6441646743ad98132fcf47fe0. Reason for revert: Re-release due to functional regression Change-Id: I9ca1fa2f140d640159fabec1424c52867cf01a60 --- .../java/com/android/server/wm/ActivityRecord.java | 13 --- .../android/server/wm/ActivityRecordInputSink.java | 113 --------------------- 2 files changed, 126 deletions(-) delete mode 100644 services/core/java/com/android/server/wm/ActivityRecordInputSink.java diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 003d659e70ed..c2cfe0b7bd92 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -775,13 +775,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private AppSaturationInfo mLastAppSaturationInfo; - private final ActivityRecordInputSink mActivityRecordInputSink; - - // Activities with this uid are allowed to not create an input sink while being in the same - // task and directly above this ActivityRecord. This field is updated whenever a new activity - // is launched from this ActivityRecord. Touches are always allowed within the same uid. - int mAllowedTouchUid; - private final ColorDisplayService.ColorTransformController mColorTransformController = (matrix, translation) -> mWmService.mH.post(() -> { synchronized (mWmService.mGlobalLock) { @@ -1727,8 +1720,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A createTime = _createTime; } mAtmService.mPackageConfigPersister.updateConfigIfNeeded(this, mUserId, packageName); - - mActivityRecordInputSink = new ActivityRecordInputSink(this, sourceRecord); } /** @@ -3565,7 +3556,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A destroyImmediately("removeImmediately"); } onRemovedFromDisplay(); - mActivityRecordInputSink.releaseSurfaceControl(); super.removeImmediately(); } @@ -6700,9 +6690,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } else if (!show && mLastSurfaceShowing) { getSyncTransaction().hide(mSurfaceControl); } - if (show) { - mActivityRecordInputSink.applyChangesToSurfaceIfChanged(getSyncTransaction()); - } } if (mThumbnail != null) { mThumbnail.setShowing(getPendingTransaction(), show); diff --git a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java deleted file mode 100644 index 95b5cec9a144..000000000000 --- a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT 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.wm; - -import android.os.Process; -import android.view.InputWindowHandle; -import android.view.SurfaceControl; -import android.view.WindowManager; - -/** - * Creates a InputWindowHandle that catches all touches that would otherwise pass through an - * Activity. - */ -class ActivityRecordInputSink { - - private final ActivityRecord mActivityRecord; - private final String mName; - - private InputWindowHandle mInputWindowHandle; - private SurfaceControl mSurfaceControl; - - ActivityRecordInputSink(ActivityRecord activityRecord, ActivityRecord sourceRecord) { - mActivityRecord = activityRecord; - mName = Integer.toHexString(System.identityHashCode(this)) + " ActivityRecordInputSink " - + mActivityRecord.mActivityComponent.flattenToShortString(); - if (sourceRecord != null) { - sourceRecord.mAllowedTouchUid = mActivityRecord.getUid(); - } - } - - public void applyChangesToSurfaceIfChanged(SurfaceControl.Transaction transaction) { - boolean windowHandleChanged = updateInputWindowHandle(); - if (mSurfaceControl == null) { - mSurfaceControl = createSurface(transaction); - } - if (windowHandleChanged) { - transaction.setInputWindowInfo(mSurfaceControl, mInputWindowHandle); - } - } - - private SurfaceControl createSurface(SurfaceControl.Transaction t) { - SurfaceControl surfaceControl = mActivityRecord.makeChildSurface(null) - .setName(mName) - .setHidden(false) - .setCallsite("ActivityRecordInputSink.createSurface") - .build(); - // Put layer below all siblings (and the parent surface too) - t.setLayer(surfaceControl, Integer.MIN_VALUE); - return surfaceControl; - } - - private boolean updateInputWindowHandle() { - boolean changed = false; - if (mInputWindowHandle == null) { - mInputWindowHandle = createInputWindowHandle(); - changed = true; - } - // Don't block touches from passing through to an activity below us in the same task, if - // that activity is either from the same uid or if that activity has launched an activity - // in our uid. - final ActivityRecord activityBelowInTask = - mActivityRecord.getTask().getActivityBelow(mActivityRecord); - final boolean allowPassthrough = activityBelowInTask != null && ( - activityBelowInTask.mAllowedTouchUid == mActivityRecord.getUid() - || activityBelowInTask.isUid(mActivityRecord.getUid())); - boolean notTouchable = (mInputWindowHandle.layoutParamsFlags - & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0; - if (allowPassthrough || mActivityRecord.isAppTransitioning()) { - mInputWindowHandle.layoutParamsFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; - changed |= !notTouchable; - } else { - mInputWindowHandle.layoutParamsFlags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; - changed |= notTouchable; - } - return changed; - } - - private InputWindowHandle createInputWindowHandle() { - InputWindowHandle inputWindowHandle = new InputWindowHandle(null, - mActivityRecord.getDisplayId()); - inputWindowHandle.replaceTouchableRegionWithCrop = true; - inputWindowHandle.name = mName; - inputWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER; - inputWindowHandle.ownerUid = Process.myUid(); - inputWindowHandle.ownerPid = Process.myPid(); - inputWindowHandle.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; - inputWindowHandle.inputFeatures = - WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL; - return inputWindowHandle; - } - - void releaseSurfaceControl() { - if (mSurfaceControl != null) { - mSurfaceControl.release(); - mSurfaceControl = null; - } - } - -} -- cgit v1.2.3 From a418847bb8de788905aced4f59437de7cbfc5360 Mon Sep 17 00:00:00 2001 From: Linus Tufvesson Date: Wed, 1 Mar 2023 11:03:01 +0100 Subject: Make Activites touch opaque - DO NOT MERGE Block touches from passing through activities by adding a dedicated surface that consumes all touches that would otherwise pass through the bounds availble to the Activity. + Keep displayId in sync for ActivityRecord Bug: 194480991 Test: atest CtsWindowManagerDeviceTestCases:ActivityRecordInputSinkTests Test: atest CtsWindowManagerDeviceTestCases:CrossAppDragAndDropTests Test: atest CtsWindowManagerDeviceTestCases:PinnedStackTests Test: Used "System > Developer Options > Simulate secondary display" to test that moving activites between displays work as intended. Change-Id: Ie74674c87c81c571089463349ac6233717ed9f33 --- .../java/com/android/server/wm/ActivityRecord.java | 13 +++ .../android/server/wm/ActivityRecordInputSink.java | 117 +++++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 services/core/java/com/android/server/wm/ActivityRecordInputSink.java diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index c2cfe0b7bd92..003d659e70ed 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -775,6 +775,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private AppSaturationInfo mLastAppSaturationInfo; + private final ActivityRecordInputSink mActivityRecordInputSink; + + // Activities with this uid are allowed to not create an input sink while being in the same + // task and directly above this ActivityRecord. This field is updated whenever a new activity + // is launched from this ActivityRecord. Touches are always allowed within the same uid. + int mAllowedTouchUid; + private final ColorDisplayService.ColorTransformController mColorTransformController = (matrix, translation) -> mWmService.mH.post(() -> { synchronized (mWmService.mGlobalLock) { @@ -1720,6 +1727,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A createTime = _createTime; } mAtmService.mPackageConfigPersister.updateConfigIfNeeded(this, mUserId, packageName); + + mActivityRecordInputSink = new ActivityRecordInputSink(this, sourceRecord); } /** @@ -3556,6 +3565,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A destroyImmediately("removeImmediately"); } onRemovedFromDisplay(); + mActivityRecordInputSink.releaseSurfaceControl(); super.removeImmediately(); } @@ -6690,6 +6700,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } else if (!show && mLastSurfaceShowing) { getSyncTransaction().hide(mSurfaceControl); } + if (show) { + mActivityRecordInputSink.applyChangesToSurfaceIfChanged(getSyncTransaction()); + } } if (mThumbnail != null) { mThumbnail.setShowing(getPendingTransaction(), show); diff --git a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java new file mode 100644 index 000000000000..95a6e8b8b88f --- /dev/null +++ b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.wm; + +import android.os.Process; +import android.view.InputWindowHandle; +import android.view.SurfaceControl; +import android.view.WindowManager; + +/** + * Creates a InputWindowHandle that catches all touches that would otherwise pass through an + * Activity. + */ +class ActivityRecordInputSink { + + private final ActivityRecord mActivityRecord; + private final String mName; + + private InputWindowHandle mInputWindowHandle; + private SurfaceControl mSurfaceControl; + + ActivityRecordInputSink(ActivityRecord activityRecord, ActivityRecord sourceRecord) { + mActivityRecord = activityRecord; + mName = Integer.toHexString(System.identityHashCode(this)) + " ActivityRecordInputSink " + + mActivityRecord.mActivityComponent.flattenToShortString(); + if (sourceRecord != null) { + sourceRecord.mAllowedTouchUid = mActivityRecord.getUid(); + } + } + + public void applyChangesToSurfaceIfChanged(SurfaceControl.Transaction transaction) { + boolean windowHandleChanged = updateInputWindowHandle(); + if (mSurfaceControl == null) { + mSurfaceControl = createSurface(transaction); + } + if (windowHandleChanged) { + transaction.setInputWindowInfo(mSurfaceControl, mInputWindowHandle); + } + } + + private SurfaceControl createSurface(SurfaceControl.Transaction t) { + SurfaceControl surfaceControl = mActivityRecord.makeChildSurface(null) + .setName(mName) + .setHidden(false) + .setCallsite("ActivityRecordInputSink.createSurface") + .build(); + // Put layer below all siblings (and the parent surface too) + t.setLayer(surfaceControl, Integer.MIN_VALUE); + return surfaceControl; + } + + private boolean updateInputWindowHandle() { + boolean changed = false; + if (mInputWindowHandle == null) { + mInputWindowHandle = createInputWindowHandle(); + changed = true; + } + // Don't block touches from passing through to an activity below us in the same task, if + // that activity is either from the same uid or if that activity has launched an activity + // in our uid. + final ActivityRecord activityBelowInTask = + mActivityRecord.getTask().getActivityBelow(mActivityRecord); + final boolean allowPassthrough = activityBelowInTask != null && ( + activityBelowInTask.mAllowedTouchUid == mActivityRecord.getUid() + || activityBelowInTask.isUid(mActivityRecord.getUid())); + boolean notTouchable = (mInputWindowHandle.layoutParamsFlags + & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0; + if (allowPassthrough || mActivityRecord.isAppTransitioning()) { + mInputWindowHandle.layoutParamsFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; + changed |= !notTouchable; + } else { + mInputWindowHandle.layoutParamsFlags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; + changed |= notTouchable; + } + if (mInputWindowHandle.displayId != mActivityRecord.getDisplayId()) { + mInputWindowHandle.displayId = mActivityRecord.getDisplayId(); + changed = true; + } + return changed; + } + + private InputWindowHandle createInputWindowHandle() { + InputWindowHandle inputWindowHandle = new InputWindowHandle(null, + mActivityRecord.getDisplayId()); + inputWindowHandle.replaceTouchableRegionWithCrop = true; + inputWindowHandle.name = mName; + inputWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER; + inputWindowHandle.ownerUid = Process.myUid(); + inputWindowHandle.ownerPid = Process.myPid(); + inputWindowHandle.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; + inputWindowHandle.inputFeatures = + WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL; + return inputWindowHandle; + } + + void releaseSurfaceControl() { + if (mSurfaceControl != null) { + mSurfaceControl.release(); + mSurfaceControl = null; + } + } + +} -- cgit v1.2.3 From dd4ee68792b9f8683637f57af10544a22c8f5c27 Mon Sep 17 00:00:00 2001 From: Justin Dunlap Date: Wed, 1 Mar 2023 00:08:22 +0000 Subject: Revert "Make Activites touch opaque - DO NOT MERGE" This reverts commit 74ce78dfb4179cb317d6e2fc3cabe5f60af5d02d. Reason for revert: re-release due to functional regression Change-Id: I71e2e889d1f2d026f708bdb617b73a5c7c10467d --- .../java/com/android/server/wm/ActivityRecord.java | 13 --- .../android/server/wm/ActivityRecordInputSink.java | 113 --------------------- 2 files changed, 126 deletions(-) delete mode 100644 services/core/java/com/android/server/wm/ActivityRecordInputSink.java diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 1a2eedceda88..af921e201861 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -673,13 +673,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private AppSaturationInfo mLastAppSaturationInfo; - private final ActivityRecordInputSink mActivityRecordInputSink; - - // Activities with this uid are allowed to not create an input sink while being in the same - // task and directly above this ActivityRecord. This field is updated whenever a new activity - // is launched from this ActivityRecord. Touches are always allowed within the same uid. - int mAllowedTouchUid; - private final ColorDisplayService.ColorTransformController mColorTransformController = (matrix, translation) -> mWmService.mH.post(() -> { synchronized (mWmService.mGlobalLock) { @@ -1657,8 +1650,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A ? (TaskDisplayArea) WindowContainer.fromBinder(daToken.asBinder()) : null; mHandoverLaunchDisplayId = options.getLaunchDisplayId(); } - - mActivityRecordInputSink = new ActivityRecordInputSink(this, sourceRecord); } /** @@ -3179,7 +3170,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A @Override void removeImmediately() { onRemovedFromDisplay(); - mActivityRecordInputSink.releaseSurfaceControl(); super.removeImmediately(); } @@ -6046,9 +6036,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } else if (!show && mLastSurfaceShowing) { getSyncTransaction().hide(mSurfaceControl); } - if (show) { - mActivityRecordInputSink.applyChangesToSurfaceIfChanged(getSyncTransaction()); - } } if (mThumbnail != null) { mThumbnail.setShowing(getPendingTransaction(), show); diff --git a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java deleted file mode 100644 index 95b5cec9a144..000000000000 --- a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT 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.wm; - -import android.os.Process; -import android.view.InputWindowHandle; -import android.view.SurfaceControl; -import android.view.WindowManager; - -/** - * Creates a InputWindowHandle that catches all touches that would otherwise pass through an - * Activity. - */ -class ActivityRecordInputSink { - - private final ActivityRecord mActivityRecord; - private final String mName; - - private InputWindowHandle mInputWindowHandle; - private SurfaceControl mSurfaceControl; - - ActivityRecordInputSink(ActivityRecord activityRecord, ActivityRecord sourceRecord) { - mActivityRecord = activityRecord; - mName = Integer.toHexString(System.identityHashCode(this)) + " ActivityRecordInputSink " - + mActivityRecord.mActivityComponent.flattenToShortString(); - if (sourceRecord != null) { - sourceRecord.mAllowedTouchUid = mActivityRecord.getUid(); - } - } - - public void applyChangesToSurfaceIfChanged(SurfaceControl.Transaction transaction) { - boolean windowHandleChanged = updateInputWindowHandle(); - if (mSurfaceControl == null) { - mSurfaceControl = createSurface(transaction); - } - if (windowHandleChanged) { - transaction.setInputWindowInfo(mSurfaceControl, mInputWindowHandle); - } - } - - private SurfaceControl createSurface(SurfaceControl.Transaction t) { - SurfaceControl surfaceControl = mActivityRecord.makeChildSurface(null) - .setName(mName) - .setHidden(false) - .setCallsite("ActivityRecordInputSink.createSurface") - .build(); - // Put layer below all siblings (and the parent surface too) - t.setLayer(surfaceControl, Integer.MIN_VALUE); - return surfaceControl; - } - - private boolean updateInputWindowHandle() { - boolean changed = false; - if (mInputWindowHandle == null) { - mInputWindowHandle = createInputWindowHandle(); - changed = true; - } - // Don't block touches from passing through to an activity below us in the same task, if - // that activity is either from the same uid or if that activity has launched an activity - // in our uid. - final ActivityRecord activityBelowInTask = - mActivityRecord.getTask().getActivityBelow(mActivityRecord); - final boolean allowPassthrough = activityBelowInTask != null && ( - activityBelowInTask.mAllowedTouchUid == mActivityRecord.getUid() - || activityBelowInTask.isUid(mActivityRecord.getUid())); - boolean notTouchable = (mInputWindowHandle.layoutParamsFlags - & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0; - if (allowPassthrough || mActivityRecord.isAppTransitioning()) { - mInputWindowHandle.layoutParamsFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; - changed |= !notTouchable; - } else { - mInputWindowHandle.layoutParamsFlags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; - changed |= notTouchable; - } - return changed; - } - - private InputWindowHandle createInputWindowHandle() { - InputWindowHandle inputWindowHandle = new InputWindowHandle(null, - mActivityRecord.getDisplayId()); - inputWindowHandle.replaceTouchableRegionWithCrop = true; - inputWindowHandle.name = mName; - inputWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER; - inputWindowHandle.ownerUid = Process.myUid(); - inputWindowHandle.ownerPid = Process.myPid(); - inputWindowHandle.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; - inputWindowHandle.inputFeatures = - WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL; - return inputWindowHandle; - } - - void releaseSurfaceControl() { - if (mSurfaceControl != null) { - mSurfaceControl.release(); - mSurfaceControl = null; - } - } - -} -- cgit v1.2.3 From 73ee4cb9c4f108c2edf68443c339bc64a9240322 Mon Sep 17 00:00:00 2001 From: Linus Tufvesson Date: Wed, 1 Mar 2023 11:20:16 +0100 Subject: Make Activites touch opaque - DO NOT MERGE Block touches from passing through activities by adding a dedicated surface that consumes all touches that would otherwise pass through the bounds availble to the Activity. + Keep displayId in sync for ActivityRecord Bug: 194480991 Test: atest CtsWindowManagerDeviceTestCases:ActivityRecordInputSinkTests Test: atest CtsWindowManagerDeviceTestCases:CrossAppDragAndDropTests Test: atest CtsWindowManagerDeviceTestCases:PinnedStackTests Test: Used "System > Developer Options > Simulate secondary display" to test that moving activites between displays work as intended. Change-Id: Idace4104c2708ce99309fa54fd44dd000e7f8894 --- .../java/com/android/server/wm/ActivityRecord.java | 13 +++ .../android/server/wm/ActivityRecordInputSink.java | 117 +++++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 services/core/java/com/android/server/wm/ActivityRecordInputSink.java diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index af921e201861..1a2eedceda88 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -673,6 +673,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private AppSaturationInfo mLastAppSaturationInfo; + private final ActivityRecordInputSink mActivityRecordInputSink; + + // Activities with this uid are allowed to not create an input sink while being in the same + // task and directly above this ActivityRecord. This field is updated whenever a new activity + // is launched from this ActivityRecord. Touches are always allowed within the same uid. + int mAllowedTouchUid; + private final ColorDisplayService.ColorTransformController mColorTransformController = (matrix, translation) -> mWmService.mH.post(() -> { synchronized (mWmService.mGlobalLock) { @@ -1650,6 +1657,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A ? (TaskDisplayArea) WindowContainer.fromBinder(daToken.asBinder()) : null; mHandoverLaunchDisplayId = options.getLaunchDisplayId(); } + + mActivityRecordInputSink = new ActivityRecordInputSink(this, sourceRecord); } /** @@ -3170,6 +3179,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A @Override void removeImmediately() { onRemovedFromDisplay(); + mActivityRecordInputSink.releaseSurfaceControl(); super.removeImmediately(); } @@ -6036,6 +6046,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } else if (!show && mLastSurfaceShowing) { getSyncTransaction().hide(mSurfaceControl); } + if (show) { + mActivityRecordInputSink.applyChangesToSurfaceIfChanged(getSyncTransaction()); + } } if (mThumbnail != null) { mThumbnail.setShowing(getPendingTransaction(), show); diff --git a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java new file mode 100644 index 000000000000..95a6e8b8b88f --- /dev/null +++ b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.wm; + +import android.os.Process; +import android.view.InputWindowHandle; +import android.view.SurfaceControl; +import android.view.WindowManager; + +/** + * Creates a InputWindowHandle that catches all touches that would otherwise pass through an + * Activity. + */ +class ActivityRecordInputSink { + + private final ActivityRecord mActivityRecord; + private final String mName; + + private InputWindowHandle mInputWindowHandle; + private SurfaceControl mSurfaceControl; + + ActivityRecordInputSink(ActivityRecord activityRecord, ActivityRecord sourceRecord) { + mActivityRecord = activityRecord; + mName = Integer.toHexString(System.identityHashCode(this)) + " ActivityRecordInputSink " + + mActivityRecord.mActivityComponent.flattenToShortString(); + if (sourceRecord != null) { + sourceRecord.mAllowedTouchUid = mActivityRecord.getUid(); + } + } + + public void applyChangesToSurfaceIfChanged(SurfaceControl.Transaction transaction) { + boolean windowHandleChanged = updateInputWindowHandle(); + if (mSurfaceControl == null) { + mSurfaceControl = createSurface(transaction); + } + if (windowHandleChanged) { + transaction.setInputWindowInfo(mSurfaceControl, mInputWindowHandle); + } + } + + private SurfaceControl createSurface(SurfaceControl.Transaction t) { + SurfaceControl surfaceControl = mActivityRecord.makeChildSurface(null) + .setName(mName) + .setHidden(false) + .setCallsite("ActivityRecordInputSink.createSurface") + .build(); + // Put layer below all siblings (and the parent surface too) + t.setLayer(surfaceControl, Integer.MIN_VALUE); + return surfaceControl; + } + + private boolean updateInputWindowHandle() { + boolean changed = false; + if (mInputWindowHandle == null) { + mInputWindowHandle = createInputWindowHandle(); + changed = true; + } + // Don't block touches from passing through to an activity below us in the same task, if + // that activity is either from the same uid or if that activity has launched an activity + // in our uid. + final ActivityRecord activityBelowInTask = + mActivityRecord.getTask().getActivityBelow(mActivityRecord); + final boolean allowPassthrough = activityBelowInTask != null && ( + activityBelowInTask.mAllowedTouchUid == mActivityRecord.getUid() + || activityBelowInTask.isUid(mActivityRecord.getUid())); + boolean notTouchable = (mInputWindowHandle.layoutParamsFlags + & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0; + if (allowPassthrough || mActivityRecord.isAppTransitioning()) { + mInputWindowHandle.layoutParamsFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; + changed |= !notTouchable; + } else { + mInputWindowHandle.layoutParamsFlags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; + changed |= notTouchable; + } + if (mInputWindowHandle.displayId != mActivityRecord.getDisplayId()) { + mInputWindowHandle.displayId = mActivityRecord.getDisplayId(); + changed = true; + } + return changed; + } + + private InputWindowHandle createInputWindowHandle() { + InputWindowHandle inputWindowHandle = new InputWindowHandle(null, + mActivityRecord.getDisplayId()); + inputWindowHandle.replaceTouchableRegionWithCrop = true; + inputWindowHandle.name = mName; + inputWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER; + inputWindowHandle.ownerUid = Process.myUid(); + inputWindowHandle.ownerPid = Process.myPid(); + inputWindowHandle.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; + inputWindowHandle.inputFeatures = + WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL; + return inputWindowHandle; + } + + void releaseSurfaceControl() { + if (mSurfaceControl != null) { + mSurfaceControl.release(); + mSurfaceControl = null; + } + } + +} -- cgit v1.2.3 From e0d45afddfbd943b348c4767ec25369300f1a985 Mon Sep 17 00:00:00 2001 From: Valentin Iftime Date: Wed, 15 Feb 2023 20:39:44 +0100 Subject: [DO NOT MERGE] Wait for preloading images to complete before inflating notifications NotificationContentInflater waits on SysUiBg thread for images to load, with a timeout of 1000ms. Test: 1. Build a test app that posts MessagingStyle notifications with a huge image (8k+) set as data Uri. 2. SystemUi should not ANR 3. adb logcat | grep NotificationInlineImageCache - shows timeout/cancellation logs Bug: 252766417 Bug: 223859644 Change-Id: I341db60223214cf2282b5c0270e343e1ce95fa01 (cherry picked from commit 195043f40e46ddcd2fe534a9dac344792d39d91c) Merged-In: I341db60223214cf2282b5c0270e343e1ce95fa01 --- .../row/NotificationContentInflater.java | 15 +++- .../row/NotificationInlineImageCache.java | 23 +++++-- .../row/NotificationInlineImageResolver.java | 79 ++++++++++++++++------ 3 files changed, 92 insertions(+), 25 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java index 1530e5238c67..dd71f581790a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java @@ -439,6 +439,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder CancellationSignal cancellationSignal = new CancellationSignal(); cancellationSignal.setOnCancelListener( () -> runningInflations.values().forEach(CancellationSignal::cancel)); + return cancellationSignal; } @@ -711,6 +712,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder public static class AsyncInflationTask extends AsyncTask implements InflationCallback, InflationTask { + private static final long IMG_PRELOAD_TIMEOUT_MS = 1000L; private final NotificationEntry mEntry; private final Context mContext; private final boolean mInflateSynchronously; @@ -804,7 +806,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder recoveredBuilder, mIsLowPriority, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, packageContext); InflatedSmartReplyState previousSmartReplyState = mRow.getExistingSmartReplyState(); - return inflateSmartReplyViews( + InflationProgress result = inflateSmartReplyViews( inflationProgress, mReInflateFlags, mEntry, @@ -812,6 +814,11 @@ public class NotificationContentInflater implements NotificationRowContentBinder packageContext, previousSmartReplyState, mSmartRepliesInflater); + + // wait for image resolver to finish preloading + mRow.getImageResolver().waitForPreloadedImages(IMG_PRELOAD_TIMEOUT_MS); + + return result; } catch (Exception e) { mError = e; return null; @@ -846,6 +853,9 @@ public class NotificationContentInflater implements NotificationRowContentBinder mCallback.handleInflationException(mRow.getEntry(), new InflationException("Couldn't inflate contentViews" + e)); } + + // Cancel any image loading tasks, not useful any more + mRow.getImageResolver().cancelRunningTasks(); } @Override @@ -872,6 +882,9 @@ public class NotificationContentInflater implements NotificationRowContentBinder // Notify the resolver that the inflation task has finished, // try to purge unnecessary cached entries. mRow.getImageResolver().purgeCache(); + + // Cancel any image loading tasks that have not completed at this point + mRow.getImageResolver().cancelRunningTasks(); } private static class RtlEnabledContext extends ContextWrapper { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java index 4b0e2ffd5d7f..75dfde89e14f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java @@ -23,8 +23,11 @@ import android.util.Log; import java.io.IOException; import java.util.Set; +import java.util.concurrent.CancellationException; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; /** * A cache for inline images of image messages. @@ -57,12 +60,13 @@ public class NotificationInlineImageCache implements NotificationInlineImageReso } @Override - public Drawable get(Uri uri) { + public Drawable get(Uri uri, long timeoutMs) { Drawable result = null; try { - result = mCache.get(uri).get(); - } catch (InterruptedException | ExecutionException ex) { - Log.d(TAG, "get: Failed get image from " + uri); + result = mCache.get(uri).get(timeoutMs, TimeUnit.MILLISECONDS); + } catch (InterruptedException | ExecutionException + | TimeoutException | CancellationException ex) { + Log.d(TAG, "get: Failed get image from " + uri + " " + ex); } return result; } @@ -73,6 +77,15 @@ public class NotificationInlineImageCache implements NotificationInlineImageReso mCache.entrySet().removeIf(entry -> !wantedSet.contains(entry.getKey())); } + @Override + public void cancelRunningTasks() { + mCache.forEach((key, value) -> { + if (value.getStatus() != AsyncTask.Status.FINISHED) { + value.cancel(true); + } + }); + } + private static class PreloadImageTask extends AsyncTask { private final NotificationInlineImageResolver mResolver; @@ -87,7 +100,7 @@ public class NotificationInlineImageCache implements NotificationInlineImageReso try { drawable = mResolver.resolveImage(target); - } catch (IOException | SecurityException ex) { + } catch (Exception ex) { Log.d(TAG, "PreloadImageTask: Resolve failed from " + target, ex); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java index 44ccb68cce4a..2caa434413f4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java @@ -23,6 +23,7 @@ import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.os.Parcelable; +import android.os.SystemClock; import android.util.Log; import com.android.internal.R; @@ -46,6 +47,9 @@ import java.util.Set; public class NotificationInlineImageResolver implements ImageResolver { private static final String TAG = NotificationInlineImageResolver.class.getSimpleName(); + // Timeout for loading images from ImageCache when calling from UI thread + private static final long MAX_UI_THREAD_TIMEOUT_MS = 100L; + private final Context mContext; private final ImageCache mImageCache; private Set mWantedUriSet; @@ -111,30 +115,38 @@ public class NotificationInlineImageResolver implements ImageResolver { * To resolve image from specified uri directly. If the resulting image is larger than the * maximum allowed size, scale it down. * @param uri Uri of the image. - * @return Drawable of the image. - * @throws IOException Throws if failed at resolving the image. + * @return Drawable of the image, or null if unable to load. */ - Drawable resolveImage(Uri uri) throws IOException { - return LocalImageResolver.resolveImage(uri, mContext, mMaxImageWidth, mMaxImageHeight); + Drawable resolveImage(Uri uri) { + try { + return LocalImageResolver.resolveImage(uri, mContext, mMaxImageWidth, mMaxImageHeight); + } catch (Exception ex) { + // Catch general Exception because ContentResolver can re-throw arbitrary Exception + // from remote process as a RuntimeException. See: Parcel#readException + Log.d(TAG, "resolveImage: Can't load image from " + uri, ex); + } + return null; } + /** + * Loads an image from the Uri. + * This method is synchronous and is usually called from the Main thread. + * It will time-out after MAX_UI_THREAD_TIMEOUT_MS. + * + * @param uri Uri of the target image. + * @return drawable of the image, null if loading failed/timeout + */ @Override public Drawable loadImage(Uri uri) { - Drawable result = null; - try { - if (hasCache()) { - // if the uri isn't currently cached, try caching it first - if (!mImageCache.hasEntry(uri)) { - mImageCache.preload((uri)); - } - result = mImageCache.get(uri); - } else { - result = resolveImage(uri); - } - } catch (IOException | SecurityException ex) { - Log.d(TAG, "loadImage: Can't load image from " + uri, ex); + return hasCache() ? loadImageFromCache(uri, MAX_UI_THREAD_TIMEOUT_MS) : resolveImage(uri); + } + + private Drawable loadImageFromCache(Uri uri, long timeoutMs) { + // if the uri isn't currently cached, try caching it first + if (!mImageCache.hasEntry(uri)) { + mImageCache.preload((uri)); } - return result; + return mImageCache.get(uri, timeoutMs); } /** @@ -208,6 +220,30 @@ public class NotificationInlineImageResolver implements ImageResolver { return mWantedUriSet; } + /** + * Wait for a maximum timeout for images to finish preloading + * @param timeoutMs total timeout time + */ + void waitForPreloadedImages(long timeoutMs) { + if (!hasCache()) { + return; + } + Set preloadedUris = getWantedUriSet(); + if (preloadedUris != null) { + // Decrement remaining timeout after each image check + long endTimeMs = SystemClock.elapsedRealtime() + timeoutMs; + preloadedUris.forEach( + uri -> loadImageFromCache(uri, endTimeMs - SystemClock.elapsedRealtime())); + } + } + + void cancelRunningTasks() { + if (!hasCache()) { + return; + } + mImageCache.cancelRunningTasks(); + } + /** * A interface for internal cache implementation of this resolver. */ @@ -217,7 +253,7 @@ public class NotificationInlineImageResolver implements ImageResolver { * @param uri The uri of the image. * @return Drawable of the image. */ - Drawable get(Uri uri); + Drawable get(Uri uri, long timeoutMs); /** * Set the image resolver that actually resolves image from specified uri. @@ -242,6 +278,11 @@ public class NotificationInlineImageResolver implements ImageResolver { * Purge unnecessary entries in the cache. */ void purge(); + + /** + * Cancel all unfinished image loading tasks + */ + void cancelRunningTasks(); } } -- cgit v1.2.3 From ca49ddc03fc161e11e4ea99a3e70ef766715410f Mon Sep 17 00:00:00 2001 From: Mugdha Lakhani Date: Thu, 29 Dec 2022 15:18:07 +0000 Subject: DO NOT MERGE Isolated processes must fail registering BRs. Broadcast Receivers should not be allowed to be registered by isolated processes. Bug: b/263358101 Test: atest SdkSandboxRestrictionsHostTest Change-Id: I5bb2ee3ce8a447105a18851fdffa5a769cc3fe49 (cherry picked from commit 43b8a91b0584dd1c6a136702e68e1f0cd519cb51) --- .../java/com/android/server/am/ActivityManagerService.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 9c7d9447a1a6..40c0057fb3ed 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -13053,12 +13053,17 @@ public class ActivityManagerService extends IActivityManager.Stub public Intent registerReceiverWithFeature(IApplicationThread caller, String callerPackage, String callerFeatureId, String receiverId, IIntentReceiver receiver, IntentFilter filter, String permission, int userId, int flags) { + enforceNotIsolatedCaller("registerReceiver"); + // Allow Sandbox process to register only unexported receivers. - if ((flags & Context.RECEIVER_NOT_EXPORTED) != 0) { - enforceNotIsolatedCaller("registerReceiver"); - } else if (mSdkSandboxSettings.isBroadcastReceiverRestrictionsEnforced()) { - enforceNotIsolatedOrSdkSandboxCaller("registerReceiver"); + boolean unexported = (flags & Context.RECEIVER_NOT_EXPORTED) != 0; + if (mSdkSandboxSettings.isBroadcastReceiverRestrictionsEnforced() + && Process.isSdkSandboxUid(Binder.getCallingUid()) + && !unexported) { + throw new SecurityException("SDK sandbox process not allowed to call " + + "registerReceiver"); } + ArrayList stickyIntents = null; ProcessRecord callerApp = null; final boolean visibleToInstantApps -- cgit v1.2.3 From 2a01a489c10c80f96a4291b0c901ce1e65cd4c42 Mon Sep 17 00:00:00 2001 From: Ioana Alexandru Date: Thu, 5 Jan 2023 22:45:21 +0000 Subject: Trim strings added to persistent snoozed notification storage. This is a backport of ag/20581190 and includes the fix in ag/20778075. Note that on this branch, clearData doesn't seem to actually clear persistent storage. Fix: 258422365 Test: atest NotificationManagerServiceTest SnoozeHelperTest Change-Id: If7c7db6694330ffbac551d044efadb26219fe17f Merged-In: I5a2823f10053ea8c83c612a567d6d4f1b6af23e7 Merged-In: Ie809cb4d648a40622618e0fb374f36b6d8dc972a --- .../android/server/notification/SnoozeHelper.java | 29 +++++++++++---- .../server/notification/SnoozeHelperTest.java | 41 +++++++++++++++++++++- 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java index 2e08a9cbf844..f3b92eaf6473 100644 --- a/services/core/java/com/android/server/notification/SnoozeHelper.java +++ b/services/core/java/com/android/server/notification/SnoozeHelper.java @@ -64,6 +64,9 @@ public class SnoozeHelper { static final int CONCURRENT_SNOOZE_LIMIT = 500; + // A safe size for strings to be put in persistent storage, to avoid breaking the XML write. + static final int MAX_STRING_LENGTH = 1000; + protected static final String XML_TAG_NAME = "snoozed-notifications"; private static final String XML_SNOOZED_NOTIFICATION = "notification"; @@ -152,7 +155,7 @@ public class SnoozeHelper { ArrayMap snoozed = mPersistedSnoozedNotifications.get(getPkgKey(userId, pkg)); if (snoozed != null) { - time = snoozed.get(key); + time = snoozed.get(getTrimmedString(key)); } } if (time == null) { @@ -166,7 +169,7 @@ public class SnoozeHelper { ArrayMap snoozed = mPersistedSnoozedNotificationsWithContext.get(getPkgKey(userId, pkg)); if (snoozed != null) { - return snoozed.get(key); + return snoozed.get(getTrimmedString(key)); } } return null; @@ -251,7 +254,8 @@ public class SnoozeHelper { scheduleRepost(pkg, key, userId, duration); Long activateAt = System.currentTimeMillis() + duration; synchronized (mLock) { - storeRecordLocked(pkg, key, userId, mPersistedSnoozedNotifications, activateAt); + storeRecordLocked(pkg, getTrimmedString(key), userId, mPersistedSnoozedNotifications, + activateAt); } } @@ -262,8 +266,10 @@ public class SnoozeHelper { int userId = record.getUser().getIdentifier(); if (contextId != null) { synchronized (mLock) { - storeRecordLocked(record.getSbn().getPackageName(), record.getKey(), - userId, mPersistedSnoozedNotificationsWithContext, contextId); + storeRecordLocked(record.getSbn().getPackageName(), + getTrimmedString(record.getKey()), + userId, mPersistedSnoozedNotificationsWithContext, + getTrimmedString(contextId)); } } snooze(record); @@ -280,6 +286,13 @@ public class SnoozeHelper { } } + private String getTrimmedString(String key) { + if (key != null && key.length() > MAX_STRING_LENGTH) { + return key.substring(0, MAX_STRING_LENGTH); + } + return key; + } + private void storeRecordLocked(String pkg, String key, Integer userId, ArrayMap> targets, T object) { @@ -384,12 +397,14 @@ public class SnoozeHelper { } protected void repost(String key, int userId, boolean muteOnReturn) { + final String trimmedKey = getTrimmedString(key); + NotificationRecord record; synchronized (mLock) { final String pkg = mPackages.remove(key); mUsers.remove(key); - removeRecordLocked(pkg, key, userId, mPersistedSnoozedNotifications); - removeRecordLocked(pkg, key, userId, mPersistedSnoozedNotificationsWithContext); + removeRecordLocked(pkg, trimmedKey, userId, mPersistedSnoozedNotifications); + removeRecordLocked(pkg, trimmedKey, userId, mPersistedSnoozedNotificationsWithContext); ArrayMap records = mSnoozedNotifications.get(getPkgKey(userId, pkg)); if (records == null) { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java index 8bead5774548..883613f6a2b6 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java @@ -69,6 +69,7 @@ import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.util.Collections; @SmallTest @RunWith(AndroidJUnit4.class) @@ -247,6 +248,37 @@ public class SnoozeHelperTest extends UiServiceTestCase { assertEquals("key2", captor2.getValue().getIntent().getStringExtra(EXTRA_KEY)); } + @Test + public void testLongTagPersistedNotification() throws Exception { + String longTag = String.join("", Collections.nCopies(66000, "A")); + NotificationRecord r = getNotificationRecord("pkg", 1, longTag, UserHandle.SYSTEM); + mSnoozeHelper.snooze(r, 0); + + // We store the full key in temp storage. + ArgumentCaptor captor = ArgumentCaptor.forClass(PendingIntent.class); + verify(mAm).setExactAndAllowWhileIdle(anyInt(), anyLong(), captor.capture()); + assertEquals(66010, captor.getValue().getIntent().getStringExtra(EXTRA_KEY).length()); + + TypedXmlSerializer serializer = Xml.newFastSerializer(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); + serializer.startDocument(null, true); + mSnoozeHelper.writeXml(serializer); + serializer.endDocument(); + serializer.flush(); + + TypedXmlPullParser parser = Xml.newFastPullParser(); + parser.setInput(new BufferedInputStream( + new ByteArrayInputStream(baos.toByteArray())), "utf-8"); + mSnoozeHelper.readXml(parser, 4); + + mSnoozeHelper.scheduleRepostsForPersistedNotifications(5); + + // We trim the key in persistent storage. + verify(mAm, times(2)).setExactAndAllowWhileIdle(anyInt(), anyLong(), captor.capture()); + assertEquals(1000, captor.getValue().getIntent().getStringExtra(EXTRA_KEY).length()); + } + @Test public void testSnoozeForTime() throws Exception { NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM); @@ -595,13 +627,20 @@ public class SnoozeHelperTest extends UiServiceTestCase { public void testClearData() { // snooze 2 from same package NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM); - NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.SYSTEM); + NotificationRecord r2 = getNotificationRecord("pkg", 2, + "two" + String.join("", Collections.nCopies(66000, "2")), UserHandle.SYSTEM); mSnoozeHelper.snooze(r, 1000); mSnoozeHelper.snooze(r2, 1000); assertTrue(mSnoozeHelper.isSnoozed( UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey())); assertTrue(mSnoozeHelper.isSnoozed( UserHandle.USER_SYSTEM, r2.getSbn().getPackageName(), r2.getKey())); + assertFalse(0L == mSnoozeHelper.getSnoozeTimeForUnpostedNotification( + r.getUser().getIdentifier(), r.getSbn().getPackageName(), + r.getSbn().getKey())); + assertFalse(0L == mSnoozeHelper.getSnoozeTimeForUnpostedNotification( + r2.getUser().getIdentifier(), r2.getSbn().getPackageName(), + r2.getSbn().getKey())); // clear data mSnoozeHelper.clearData(UserHandle.USER_SYSTEM, "pkg"); -- cgit v1.2.3 From ad0b1553852db13b30b7b24e82eb8d25e3edacc2 Mon Sep 17 00:00:00 2001 From: Ioana Alexandru Date: Thu, 5 Jan 2023 22:45:21 +0000 Subject: Trim strings added to persistent snoozed notification storage. This is a backport of ag/20581190 and includes the fix in ag/20778075. Note that on this branch, clearData doesn't seem to actually clear persistent storage. Fix: 258422365 Test: atest NotificationManagerServiceTest SnoozeHelperTest Change-Id: If7c7db6694330ffbac551d044efadb26219fe17f Merged-In: I5a2823f10053ea8c83c612a567d6d4f1b6af23e7 Merged-In: Ie809cb4d648a40622618e0fb374f36b6d8dc972a --- .../android/server/notification/SnoozeHelper.java | 29 +++++++++++---- .../server/notification/SnoozeHelperTest.java | 41 +++++++++++++++++++++- 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java index 2e08a9cbf844..f3b92eaf6473 100644 --- a/services/core/java/com/android/server/notification/SnoozeHelper.java +++ b/services/core/java/com/android/server/notification/SnoozeHelper.java @@ -64,6 +64,9 @@ public class SnoozeHelper { static final int CONCURRENT_SNOOZE_LIMIT = 500; + // A safe size for strings to be put in persistent storage, to avoid breaking the XML write. + static final int MAX_STRING_LENGTH = 1000; + protected static final String XML_TAG_NAME = "snoozed-notifications"; private static final String XML_SNOOZED_NOTIFICATION = "notification"; @@ -152,7 +155,7 @@ public class SnoozeHelper { ArrayMap snoozed = mPersistedSnoozedNotifications.get(getPkgKey(userId, pkg)); if (snoozed != null) { - time = snoozed.get(key); + time = snoozed.get(getTrimmedString(key)); } } if (time == null) { @@ -166,7 +169,7 @@ public class SnoozeHelper { ArrayMap snoozed = mPersistedSnoozedNotificationsWithContext.get(getPkgKey(userId, pkg)); if (snoozed != null) { - return snoozed.get(key); + return snoozed.get(getTrimmedString(key)); } } return null; @@ -251,7 +254,8 @@ public class SnoozeHelper { scheduleRepost(pkg, key, userId, duration); Long activateAt = System.currentTimeMillis() + duration; synchronized (mLock) { - storeRecordLocked(pkg, key, userId, mPersistedSnoozedNotifications, activateAt); + storeRecordLocked(pkg, getTrimmedString(key), userId, mPersistedSnoozedNotifications, + activateAt); } } @@ -262,8 +266,10 @@ public class SnoozeHelper { int userId = record.getUser().getIdentifier(); if (contextId != null) { synchronized (mLock) { - storeRecordLocked(record.getSbn().getPackageName(), record.getKey(), - userId, mPersistedSnoozedNotificationsWithContext, contextId); + storeRecordLocked(record.getSbn().getPackageName(), + getTrimmedString(record.getKey()), + userId, mPersistedSnoozedNotificationsWithContext, + getTrimmedString(contextId)); } } snooze(record); @@ -280,6 +286,13 @@ public class SnoozeHelper { } } + private String getTrimmedString(String key) { + if (key != null && key.length() > MAX_STRING_LENGTH) { + return key.substring(0, MAX_STRING_LENGTH); + } + return key; + } + private void storeRecordLocked(String pkg, String key, Integer userId, ArrayMap> targets, T object) { @@ -384,12 +397,14 @@ public class SnoozeHelper { } protected void repost(String key, int userId, boolean muteOnReturn) { + final String trimmedKey = getTrimmedString(key); + NotificationRecord record; synchronized (mLock) { final String pkg = mPackages.remove(key); mUsers.remove(key); - removeRecordLocked(pkg, key, userId, mPersistedSnoozedNotifications); - removeRecordLocked(pkg, key, userId, mPersistedSnoozedNotificationsWithContext); + removeRecordLocked(pkg, trimmedKey, userId, mPersistedSnoozedNotifications); + removeRecordLocked(pkg, trimmedKey, userId, mPersistedSnoozedNotificationsWithContext); ArrayMap records = mSnoozedNotifications.get(getPkgKey(userId, pkg)); if (records == null) { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java index 8bead5774548..883613f6a2b6 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java @@ -69,6 +69,7 @@ import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.util.Collections; @SmallTest @RunWith(AndroidJUnit4.class) @@ -247,6 +248,37 @@ public class SnoozeHelperTest extends UiServiceTestCase { assertEquals("key2", captor2.getValue().getIntent().getStringExtra(EXTRA_KEY)); } + @Test + public void testLongTagPersistedNotification() throws Exception { + String longTag = String.join("", Collections.nCopies(66000, "A")); + NotificationRecord r = getNotificationRecord("pkg", 1, longTag, UserHandle.SYSTEM); + mSnoozeHelper.snooze(r, 0); + + // We store the full key in temp storage. + ArgumentCaptor captor = ArgumentCaptor.forClass(PendingIntent.class); + verify(mAm).setExactAndAllowWhileIdle(anyInt(), anyLong(), captor.capture()); + assertEquals(66010, captor.getValue().getIntent().getStringExtra(EXTRA_KEY).length()); + + TypedXmlSerializer serializer = Xml.newFastSerializer(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); + serializer.startDocument(null, true); + mSnoozeHelper.writeXml(serializer); + serializer.endDocument(); + serializer.flush(); + + TypedXmlPullParser parser = Xml.newFastPullParser(); + parser.setInput(new BufferedInputStream( + new ByteArrayInputStream(baos.toByteArray())), "utf-8"); + mSnoozeHelper.readXml(parser, 4); + + mSnoozeHelper.scheduleRepostsForPersistedNotifications(5); + + // We trim the key in persistent storage. + verify(mAm, times(2)).setExactAndAllowWhileIdle(anyInt(), anyLong(), captor.capture()); + assertEquals(1000, captor.getValue().getIntent().getStringExtra(EXTRA_KEY).length()); + } + @Test public void testSnoozeForTime() throws Exception { NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM); @@ -595,13 +627,20 @@ public class SnoozeHelperTest extends UiServiceTestCase { public void testClearData() { // snooze 2 from same package NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM); - NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.SYSTEM); + NotificationRecord r2 = getNotificationRecord("pkg", 2, + "two" + String.join("", Collections.nCopies(66000, "2")), UserHandle.SYSTEM); mSnoozeHelper.snooze(r, 1000); mSnoozeHelper.snooze(r2, 1000); assertTrue(mSnoozeHelper.isSnoozed( UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey())); assertTrue(mSnoozeHelper.isSnoozed( UserHandle.USER_SYSTEM, r2.getSbn().getPackageName(), r2.getKey())); + assertFalse(0L == mSnoozeHelper.getSnoozeTimeForUnpostedNotification( + r.getUser().getIdentifier(), r.getSbn().getPackageName(), + r.getSbn().getKey())); + assertFalse(0L == mSnoozeHelper.getSnoozeTimeForUnpostedNotification( + r2.getUser().getIdentifier(), r2.getSbn().getPackageName(), + r2.getSbn().getKey())); // clear data mSnoozeHelper.clearData(UserHandle.USER_SYSTEM, "pkg"); -- cgit v1.2.3 From 4df0bbaf93e8ba9ca1bac66bdd6c96d619fcb27d Mon Sep 17 00:00:00 2001 From: "gang.huang" Date: Thu, 23 Feb 2023 15:18:50 +0800 Subject: Change the wait time of setDefaultLauncher from 20s to 60s On some low-performance devices, the method setDefaultLauncher can not be completed whithin 20s Bug: 270519786 Change-Id: Iab78a57d4329095358cad6e79088909b48a22264 Merged-In: Iab78a57d4329095358cad6e79088909b48a22264 --- .../android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java index b921838e0bfc..4c0361d30d67 100644 --- a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java +++ b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java @@ -263,7 +263,7 @@ public class ShortcutManagerTestUtils { + instrumentation.getContext().getUserId() + " " + RoleManager.ROLE_HOME + " " + packageName + " 0"); waitUntil("Failed to get shortcut access", - () -> hasShortcutAccess(instrumentation, packageName), 20); + () -> hasShortcutAccess(instrumentation, packageName), 60); } public static void setDefaultLauncher(Instrumentation instrumentation, Context packageContext) { -- cgit v1.2.3 From 931093dfb41fc41659c9f2d6f76bd74e85cf1da8 Mon Sep 17 00:00:00 2001 From: Ioana Alexandru Date: Thu, 5 Jan 2023 22:45:21 +0000 Subject: Trim strings added to persistent snoozed notification storage. This is a backport of ag/20581190 and includes the fix in ag/20778075. Note that on this branch, clearData doesn't seem to actually clear persistent storage. Bug: 258422365 Test: atest NotificationManagerServiceTest SnoozeHelperTest Change-Id: If7c7db6694330ffbac551d044efadb26219fe17f Merged-In: I5a2823f10053ea8c83c612a567d6d4f1b6af23e7 Merged-In: Ie809cb4d648a40622618e0fb374f36b6d8dc972a Merged-In: If7c7db6694330ffbac551d044efadb26219fe17f --- .../android/server/notification/SnoozeHelper.java | 29 +++++++++++---- .../server/notification/SnoozeHelperTest.java | 41 +++++++++++++++++++++- 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java index a29ac131f94c..a6cae49c4fd6 100644 --- a/services/core/java/com/android/server/notification/SnoozeHelper.java +++ b/services/core/java/com/android/server/notification/SnoozeHelper.java @@ -62,6 +62,9 @@ public class SnoozeHelper { static final int CONCURRENT_SNOOZE_LIMIT = 500; + // A safe size for strings to be put in persistent storage, to avoid breaking the XML write. + static final int MAX_STRING_LENGTH = 1000; + protected static final String XML_TAG_NAME = "snoozed-notifications"; private static final String XML_SNOOZED_NOTIFICATION = "notification"; @@ -150,7 +153,7 @@ public class SnoozeHelper { ArrayMap snoozed = mPersistedSnoozedNotifications.get(getPkgKey(userId, pkg)); if (snoozed != null) { - time = snoozed.get(key); + time = snoozed.get(getTrimmedString(key)); } } if (time == null) { @@ -164,7 +167,7 @@ public class SnoozeHelper { ArrayMap snoozed = mPersistedSnoozedNotificationsWithContext.get(getPkgKey(userId, pkg)); if (snoozed != null) { - return snoozed.get(key); + return snoozed.get(getTrimmedString(key)); } } return null; @@ -249,7 +252,8 @@ public class SnoozeHelper { scheduleRepost(pkg, key, userId, duration); Long activateAt = System.currentTimeMillis() + duration; synchronized (mLock) { - storeRecordLocked(pkg, key, userId, mPersistedSnoozedNotifications, activateAt); + storeRecordLocked(pkg, getTrimmedString(key), userId, mPersistedSnoozedNotifications, + activateAt); } } @@ -260,8 +264,10 @@ public class SnoozeHelper { int userId = record.getUser().getIdentifier(); if (contextId != null) { synchronized (mLock) { - storeRecordLocked(record.getSbn().getPackageName(), record.getKey(), - userId, mPersistedSnoozedNotificationsWithContext, contextId); + storeRecordLocked(record.getSbn().getPackageName(), + getTrimmedString(record.getKey()), + userId, mPersistedSnoozedNotificationsWithContext, + getTrimmedString(contextId)); } } snooze(record); @@ -278,6 +284,13 @@ public class SnoozeHelper { } } + private String getTrimmedString(String key) { + if (key != null && key.length() > MAX_STRING_LENGTH) { + return key.substring(0, MAX_STRING_LENGTH); + } + return key; + } + private void storeRecordLocked(String pkg, String key, Integer userId, ArrayMap> targets, T object) { @@ -382,12 +395,14 @@ public class SnoozeHelper { } protected void repost(String key, int userId, boolean muteOnReturn) { + final String trimmedKey = getTrimmedString(key); + NotificationRecord record; synchronized (mLock) { final String pkg = mPackages.remove(key); mUsers.remove(key); - removeRecordLocked(pkg, key, userId, mPersistedSnoozedNotifications); - removeRecordLocked(pkg, key, userId, mPersistedSnoozedNotificationsWithContext); + removeRecordLocked(pkg, trimmedKey, userId, mPersistedSnoozedNotifications); + removeRecordLocked(pkg, trimmedKey, userId, mPersistedSnoozedNotificationsWithContext); ArrayMap records = mSnoozedNotifications.get(getPkgKey(userId, pkg)); if (records == null) { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java index 83ba7108af7f..2b6fbea5130a 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java @@ -67,6 +67,7 @@ import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.util.Collections; @SmallTest @RunWith(AndroidJUnit4.class) @@ -245,6 +246,37 @@ public class SnoozeHelperTest extends UiServiceTestCase { assertEquals("key2", captor2.getValue().getIntent().getStringExtra(EXTRA_KEY)); } + @Test + public void testLongTagPersistedNotification() throws Exception { + String longTag = String.join("", Collections.nCopies(66000, "A")); + NotificationRecord r = getNotificationRecord("pkg", 1, longTag, UserHandle.SYSTEM); + mSnoozeHelper.snooze(r, 0); + + // We store the full key in temp storage. + ArgumentCaptor captor = ArgumentCaptor.forClass(PendingIntent.class); + verify(mAm).setExactAndAllowWhileIdle(anyInt(), anyLong(), captor.capture()); + assertEquals(66010, captor.getValue().getIntent().getStringExtra(EXTRA_KEY).length()); + + XmlSerializer serializer = Xml.newSerializer(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); + serializer.startDocument(null, true); + mSnoozeHelper.writeXml(serializer); + serializer.endDocument(); + serializer.flush(); + + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(new BufferedInputStream( + new ByteArrayInputStream(baos.toByteArray())), "utf-8"); + mSnoozeHelper.readXml(parser, 4); + + mSnoozeHelper.scheduleRepostsForPersistedNotifications(5); + + // We trim the key in persistent storage. + verify(mAm, times(2)).setExactAndAllowWhileIdle(anyInt(), anyLong(), captor.capture()); + assertEquals(1000, captor.getValue().getIntent().getStringExtra(EXTRA_KEY).length()); + } + @Test public void testSnoozeForTime() throws Exception { NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM); @@ -593,13 +625,20 @@ public class SnoozeHelperTest extends UiServiceTestCase { public void testClearData() { // snooze 2 from same package NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM); - NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.SYSTEM); + NotificationRecord r2 = getNotificationRecord("pkg", 2, + "two" + String.join("", Collections.nCopies(66000, "2")), UserHandle.SYSTEM); mSnoozeHelper.snooze(r, 1000); mSnoozeHelper.snooze(r2, 1000); assertTrue(mSnoozeHelper.isSnoozed( UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey())); assertTrue(mSnoozeHelper.isSnoozed( UserHandle.USER_SYSTEM, r2.getSbn().getPackageName(), r2.getKey())); + assertFalse(0L == mSnoozeHelper.getSnoozeTimeForUnpostedNotification( + r.getUser().getIdentifier(), r.getSbn().getPackageName(), + r.getSbn().getKey())); + assertFalse(0L == mSnoozeHelper.getSnoozeTimeForUnpostedNotification( + r2.getUser().getIdentifier(), r2.getSbn().getPackageName(), + r2.getSbn().getKey())); // clear data mSnoozeHelper.clearData(UserHandle.USER_SYSTEM, "pkg"); -- cgit v1.2.3 From b8a07871459ed895fc814730e198df4a0b5860dc Mon Sep 17 00:00:00 2001 From: Ioana Alexandru Date: Thu, 5 Jan 2023 22:45:21 +0000 Subject: Trim strings added to persistent snoozed notification storage. This is a backport of ag/20581190 and includes the fix in ag/20778075. Note that on this branch, clearData doesn't seem to actually clear persistent storage. Bug: 258422365 Test: atest NotificationManagerServiceTest SnoozeHelperTest Change-Id: If7c7db6694330ffbac551d044efadb26219fe17f Merged-In: I5a2823f10053ea8c83c612a567d6d4f1b6af23e7 Merged-In: Ie809cb4d648a40622618e0fb374f36b6d8dc972a --- .../android/server/notification/SnoozeHelper.java | 29 +++++++++++---- .../server/notification/SnoozeHelperTest.java | 41 +++++++++++++++++++++- 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java index 2e08a9cbf844..f3b92eaf6473 100644 --- a/services/core/java/com/android/server/notification/SnoozeHelper.java +++ b/services/core/java/com/android/server/notification/SnoozeHelper.java @@ -64,6 +64,9 @@ public class SnoozeHelper { static final int CONCURRENT_SNOOZE_LIMIT = 500; + // A safe size for strings to be put in persistent storage, to avoid breaking the XML write. + static final int MAX_STRING_LENGTH = 1000; + protected static final String XML_TAG_NAME = "snoozed-notifications"; private static final String XML_SNOOZED_NOTIFICATION = "notification"; @@ -152,7 +155,7 @@ public class SnoozeHelper { ArrayMap snoozed = mPersistedSnoozedNotifications.get(getPkgKey(userId, pkg)); if (snoozed != null) { - time = snoozed.get(key); + time = snoozed.get(getTrimmedString(key)); } } if (time == null) { @@ -166,7 +169,7 @@ public class SnoozeHelper { ArrayMap snoozed = mPersistedSnoozedNotificationsWithContext.get(getPkgKey(userId, pkg)); if (snoozed != null) { - return snoozed.get(key); + return snoozed.get(getTrimmedString(key)); } } return null; @@ -251,7 +254,8 @@ public class SnoozeHelper { scheduleRepost(pkg, key, userId, duration); Long activateAt = System.currentTimeMillis() + duration; synchronized (mLock) { - storeRecordLocked(pkg, key, userId, mPersistedSnoozedNotifications, activateAt); + storeRecordLocked(pkg, getTrimmedString(key), userId, mPersistedSnoozedNotifications, + activateAt); } } @@ -262,8 +266,10 @@ public class SnoozeHelper { int userId = record.getUser().getIdentifier(); if (contextId != null) { synchronized (mLock) { - storeRecordLocked(record.getSbn().getPackageName(), record.getKey(), - userId, mPersistedSnoozedNotificationsWithContext, contextId); + storeRecordLocked(record.getSbn().getPackageName(), + getTrimmedString(record.getKey()), + userId, mPersistedSnoozedNotificationsWithContext, + getTrimmedString(contextId)); } } snooze(record); @@ -280,6 +286,13 @@ public class SnoozeHelper { } } + private String getTrimmedString(String key) { + if (key != null && key.length() > MAX_STRING_LENGTH) { + return key.substring(0, MAX_STRING_LENGTH); + } + return key; + } + private void storeRecordLocked(String pkg, String key, Integer userId, ArrayMap> targets, T object) { @@ -384,12 +397,14 @@ public class SnoozeHelper { } protected void repost(String key, int userId, boolean muteOnReturn) { + final String trimmedKey = getTrimmedString(key); + NotificationRecord record; synchronized (mLock) { final String pkg = mPackages.remove(key); mUsers.remove(key); - removeRecordLocked(pkg, key, userId, mPersistedSnoozedNotifications); - removeRecordLocked(pkg, key, userId, mPersistedSnoozedNotificationsWithContext); + removeRecordLocked(pkg, trimmedKey, userId, mPersistedSnoozedNotifications); + removeRecordLocked(pkg, trimmedKey, userId, mPersistedSnoozedNotificationsWithContext); ArrayMap records = mSnoozedNotifications.get(getPkgKey(userId, pkg)); if (records == null) { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java index 8bead5774548..883613f6a2b6 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java @@ -69,6 +69,7 @@ import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.util.Collections; @SmallTest @RunWith(AndroidJUnit4.class) @@ -247,6 +248,37 @@ public class SnoozeHelperTest extends UiServiceTestCase { assertEquals("key2", captor2.getValue().getIntent().getStringExtra(EXTRA_KEY)); } + @Test + public void testLongTagPersistedNotification() throws Exception { + String longTag = String.join("", Collections.nCopies(66000, "A")); + NotificationRecord r = getNotificationRecord("pkg", 1, longTag, UserHandle.SYSTEM); + mSnoozeHelper.snooze(r, 0); + + // We store the full key in temp storage. + ArgumentCaptor captor = ArgumentCaptor.forClass(PendingIntent.class); + verify(mAm).setExactAndAllowWhileIdle(anyInt(), anyLong(), captor.capture()); + assertEquals(66010, captor.getValue().getIntent().getStringExtra(EXTRA_KEY).length()); + + TypedXmlSerializer serializer = Xml.newFastSerializer(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); + serializer.startDocument(null, true); + mSnoozeHelper.writeXml(serializer); + serializer.endDocument(); + serializer.flush(); + + TypedXmlPullParser parser = Xml.newFastPullParser(); + parser.setInput(new BufferedInputStream( + new ByteArrayInputStream(baos.toByteArray())), "utf-8"); + mSnoozeHelper.readXml(parser, 4); + + mSnoozeHelper.scheduleRepostsForPersistedNotifications(5); + + // We trim the key in persistent storage. + verify(mAm, times(2)).setExactAndAllowWhileIdle(anyInt(), anyLong(), captor.capture()); + assertEquals(1000, captor.getValue().getIntent().getStringExtra(EXTRA_KEY).length()); + } + @Test public void testSnoozeForTime() throws Exception { NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM); @@ -595,13 +627,20 @@ public class SnoozeHelperTest extends UiServiceTestCase { public void testClearData() { // snooze 2 from same package NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM); - NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.SYSTEM); + NotificationRecord r2 = getNotificationRecord("pkg", 2, + "two" + String.join("", Collections.nCopies(66000, "2")), UserHandle.SYSTEM); mSnoozeHelper.snooze(r, 1000); mSnoozeHelper.snooze(r2, 1000); assertTrue(mSnoozeHelper.isSnoozed( UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey())); assertTrue(mSnoozeHelper.isSnoozed( UserHandle.USER_SYSTEM, r2.getSbn().getPackageName(), r2.getKey())); + assertFalse(0L == mSnoozeHelper.getSnoozeTimeForUnpostedNotification( + r.getUser().getIdentifier(), r.getSbn().getPackageName(), + r.getSbn().getKey())); + assertFalse(0L == mSnoozeHelper.getSnoozeTimeForUnpostedNotification( + r2.getUser().getIdentifier(), r2.getSbn().getPackageName(), + r2.getSbn().getKey())); // clear data mSnoozeHelper.clearData(UserHandle.USER_SYSTEM, "pkg"); -- cgit v1.2.3 From 6b587717ce31b073baedc8aa963eb7f47a93b53f Mon Sep 17 00:00:00 2001 From: Ioana Alexandru Date: Thu, 5 Jan 2023 22:45:21 +0000 Subject: Trim strings added to persistent snoozed notification storage. This is a backport of ag/20581190 and includes the fix in ag/20778075. Note that on this branch, clearData doesn't seem to actually clear persistent storage. Bug: 258422365 Test: atest NotificationManagerServiceTest SnoozeHelperTest Change-Id: If7c7db6694330ffbac551d044efadb26219fe17f Merged-In: I5a2823f10053ea8c83c612a567d6d4f1b6af23e7 Merged-In: Ie809cb4d648a40622618e0fb374f36b6d8dc972a Merged-In: If7c7db6694330ffbac551d044efadb26219fe17f --- .../android/server/notification/SnoozeHelper.java | 29 +++++++++++---- .../server/notification/SnoozeHelperTest.java | 41 +++++++++++++++++++++- 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java index a29ac131f94c..a6cae49c4fd6 100644 --- a/services/core/java/com/android/server/notification/SnoozeHelper.java +++ b/services/core/java/com/android/server/notification/SnoozeHelper.java @@ -62,6 +62,9 @@ public class SnoozeHelper { static final int CONCURRENT_SNOOZE_LIMIT = 500; + // A safe size for strings to be put in persistent storage, to avoid breaking the XML write. + static final int MAX_STRING_LENGTH = 1000; + protected static final String XML_TAG_NAME = "snoozed-notifications"; private static final String XML_SNOOZED_NOTIFICATION = "notification"; @@ -150,7 +153,7 @@ public class SnoozeHelper { ArrayMap snoozed = mPersistedSnoozedNotifications.get(getPkgKey(userId, pkg)); if (snoozed != null) { - time = snoozed.get(key); + time = snoozed.get(getTrimmedString(key)); } } if (time == null) { @@ -164,7 +167,7 @@ public class SnoozeHelper { ArrayMap snoozed = mPersistedSnoozedNotificationsWithContext.get(getPkgKey(userId, pkg)); if (snoozed != null) { - return snoozed.get(key); + return snoozed.get(getTrimmedString(key)); } } return null; @@ -249,7 +252,8 @@ public class SnoozeHelper { scheduleRepost(pkg, key, userId, duration); Long activateAt = System.currentTimeMillis() + duration; synchronized (mLock) { - storeRecordLocked(pkg, key, userId, mPersistedSnoozedNotifications, activateAt); + storeRecordLocked(pkg, getTrimmedString(key), userId, mPersistedSnoozedNotifications, + activateAt); } } @@ -260,8 +264,10 @@ public class SnoozeHelper { int userId = record.getUser().getIdentifier(); if (contextId != null) { synchronized (mLock) { - storeRecordLocked(record.getSbn().getPackageName(), record.getKey(), - userId, mPersistedSnoozedNotificationsWithContext, contextId); + storeRecordLocked(record.getSbn().getPackageName(), + getTrimmedString(record.getKey()), + userId, mPersistedSnoozedNotificationsWithContext, + getTrimmedString(contextId)); } } snooze(record); @@ -278,6 +284,13 @@ public class SnoozeHelper { } } + private String getTrimmedString(String key) { + if (key != null && key.length() > MAX_STRING_LENGTH) { + return key.substring(0, MAX_STRING_LENGTH); + } + return key; + } + private void storeRecordLocked(String pkg, String key, Integer userId, ArrayMap> targets, T object) { @@ -382,12 +395,14 @@ public class SnoozeHelper { } protected void repost(String key, int userId, boolean muteOnReturn) { + final String trimmedKey = getTrimmedString(key); + NotificationRecord record; synchronized (mLock) { final String pkg = mPackages.remove(key); mUsers.remove(key); - removeRecordLocked(pkg, key, userId, mPersistedSnoozedNotifications); - removeRecordLocked(pkg, key, userId, mPersistedSnoozedNotificationsWithContext); + removeRecordLocked(pkg, trimmedKey, userId, mPersistedSnoozedNotifications); + removeRecordLocked(pkg, trimmedKey, userId, mPersistedSnoozedNotificationsWithContext); ArrayMap records = mSnoozedNotifications.get(getPkgKey(userId, pkg)); if (records == null) { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java index 83ba7108af7f..2b6fbea5130a 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java @@ -67,6 +67,7 @@ import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.util.Collections; @SmallTest @RunWith(AndroidJUnit4.class) @@ -245,6 +246,37 @@ public class SnoozeHelperTest extends UiServiceTestCase { assertEquals("key2", captor2.getValue().getIntent().getStringExtra(EXTRA_KEY)); } + @Test + public void testLongTagPersistedNotification() throws Exception { + String longTag = String.join("", Collections.nCopies(66000, "A")); + NotificationRecord r = getNotificationRecord("pkg", 1, longTag, UserHandle.SYSTEM); + mSnoozeHelper.snooze(r, 0); + + // We store the full key in temp storage. + ArgumentCaptor captor = ArgumentCaptor.forClass(PendingIntent.class); + verify(mAm).setExactAndAllowWhileIdle(anyInt(), anyLong(), captor.capture()); + assertEquals(66010, captor.getValue().getIntent().getStringExtra(EXTRA_KEY).length()); + + XmlSerializer serializer = Xml.newSerializer(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); + serializer.startDocument(null, true); + mSnoozeHelper.writeXml(serializer); + serializer.endDocument(); + serializer.flush(); + + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(new BufferedInputStream( + new ByteArrayInputStream(baos.toByteArray())), "utf-8"); + mSnoozeHelper.readXml(parser, 4); + + mSnoozeHelper.scheduleRepostsForPersistedNotifications(5); + + // We trim the key in persistent storage. + verify(mAm, times(2)).setExactAndAllowWhileIdle(anyInt(), anyLong(), captor.capture()); + assertEquals(1000, captor.getValue().getIntent().getStringExtra(EXTRA_KEY).length()); + } + @Test public void testSnoozeForTime() throws Exception { NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM); @@ -593,13 +625,20 @@ public class SnoozeHelperTest extends UiServiceTestCase { public void testClearData() { // snooze 2 from same package NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM); - NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.SYSTEM); + NotificationRecord r2 = getNotificationRecord("pkg", 2, + "two" + String.join("", Collections.nCopies(66000, "2")), UserHandle.SYSTEM); mSnoozeHelper.snooze(r, 1000); mSnoozeHelper.snooze(r2, 1000); assertTrue(mSnoozeHelper.isSnoozed( UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey())); assertTrue(mSnoozeHelper.isSnoozed( UserHandle.USER_SYSTEM, r2.getSbn().getPackageName(), r2.getKey())); + assertFalse(0L == mSnoozeHelper.getSnoozeTimeForUnpostedNotification( + r.getUser().getIdentifier(), r.getSbn().getPackageName(), + r.getSbn().getKey())); + assertFalse(0L == mSnoozeHelper.getSnoozeTimeForUnpostedNotification( + r2.getUser().getIdentifier(), r2.getSbn().getPackageName(), + r2.getSbn().getKey())); // clear data mSnoozeHelper.clearData(UserHandle.USER_SYSTEM, "pkg"); -- cgit v1.2.3 From be9d9c04db77c2ccd22ec98d257524102f2f16a5 Mon Sep 17 00:00:00 2001 From: Pinyao Ting Date: Wed, 15 Feb 2023 15:20:25 -0800 Subject: Limit the number of shortcuts per app that can be retained by system This is a second attempt at fixing the issue, the previous CL ag/20642213 was reverted because it simply throws an exception when the limit is reached, which causes apps to crash since chat apps tends to be sending large amount of conversation shortcuts and they have no way to know how many of these shortcuts are still cached by the system. Instead of throwing an exception, this CL simply removes excessive shortcuts to avoid crashes. Currently there is a limit on the number of shortcuts an app can publish in respect to each launcher activity. This CL further implements a global maximum of total number of shortcuts that can be retained for an app to mitigate from any potential system health issue. When the global maximum is reached, ShortcutService will proactively removes shortcuts from system memory. Cached shortcuts are removed first, followed by dynamic shortcuts, using last updated time as tie-breaker. This CL additionally addresses an unexpected flow where re-publishing previously removed shortcuts that are still retained by the system could cause the total number of shortcuts to exceed previously set limit. Bug: 250576066 233155034 Test: manual Change-Id: I001c7a87b62aefa9487bf8efaf3cd02d7cb21521 Merged-In: I001c7a87b62aefa9487bf8efaf3cd02d7cb21521 --- .../com/android/server/pm/ShortcutPackage.java | 99 +++++++++++++++++++--- .../com/android/server/pm/ShortcutService.java | 25 +++++- 2 files changed, 112 insertions(+), 12 deletions(-) diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index 890c89152a7c..8507ad028a19 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -430,6 +430,7 @@ class ShortcutPackage extends ShortcutPackageItem { @NonNull List changedShortcuts) { Preconditions.checkArgument(newShortcut.isEnabled(), "pushDynamicShortcuts() cannot publish disabled shortcuts"); + ensureShortcutCountBeforePush(); newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC); @@ -437,7 +438,7 @@ class ShortcutPackage extends ShortcutPackageItem { final ShortcutInfo oldShortcut = findShortcutById(newShortcut.getId()); boolean deleted = false; - if (oldShortcut == null) { + if (oldShortcut == null || !oldShortcut.isDynamic()) { final ShortcutService service = mShortcutUser.mService; final int maxShortcuts = service.getMaxActivityShortcuts(); @@ -446,18 +447,12 @@ class ShortcutPackage extends ShortcutPackageItem { final ArrayList activityShortcuts = all.get(newShortcut.getActivity()); if (activityShortcuts != null && activityShortcuts.size() > maxShortcuts) { - Slog.e(TAG, "Error pushing shortcut. There are already " - + activityShortcuts.size() + " shortcuts, exceeding the " + maxShortcuts - + " shortcuts limit when pushing the new shortcut " + newShortcut - + ". Id of shortcuts currently available in system memory are " - + activityShortcuts.stream().map(ShortcutInfo::getId) - .collect(Collectors.joining(",", "[", "]"))); - // TODO: This should not have happened. If it does, identify the root cause where - // possible, otherwise bail-out early to prevent memory issue. + // Root cause was discovered in b/233155034, so this should not be happening. + service.wtf("Error pushing shortcut. There are already " + + activityShortcuts.size() + " shortcuts."); } if (activityShortcuts != null && activityShortcuts.size() == maxShortcuts) { // Max has reached. Delete the shortcut with lowest rank. - // Sort by isManifestShortcut() and getRank(). Collections.sort(activityShortcuts, mShortcutTypeAndRankComparator); @@ -473,7 +468,8 @@ class ShortcutPackage extends ShortcutPackageItem { deleted = deleteDynamicWithId(shortcut.getId(), /* ignoreInvisible =*/ true, /*ignorePersistedShortcuts=*/ true) != null; } - } else { + } + if (oldShortcut != null) { // It's an update case. // Make sure the target is updatable. (i.e. should be mutable.) oldShortcut.ensureUpdatableWith(newShortcut, /*isUpdating=*/ false); @@ -505,6 +501,32 @@ class ShortcutPackage extends ShortcutPackageItem { return deleted; } + private void ensureShortcutCountBeforePush() { + final ShortcutService service = mShortcutUser.mService; + // Ensure the total number of shortcuts doesn't exceed the hard limit per app. + final int maxShortcutPerApp = service.getMaxAppShortcuts(); + synchronized (mLock) { + final List appShortcuts = mShortcuts.values().stream().filter(si -> + !si.isPinned()).collect(Collectors.toList()); + if (appShortcuts.size() >= maxShortcutPerApp) { + // Max has reached. Removes shortcuts until they fall within the hard cap. + // Sort by isManifestShortcut(), isDynamic() and getLastChangedTimestamp(). + Collections.sort(appShortcuts, mShortcutTypeRankAndTimeComparator); + + while (appShortcuts.size() >= maxShortcutPerApp) { + final ShortcutInfo shortcut = appShortcuts.remove(appShortcuts.size() - 1); + if (shortcut.isDeclaredInManifest()) { + // All shortcuts are manifest shortcuts and cannot be removed. + throw new IllegalArgumentException(getPackageName() + " has published " + + appShortcuts.size() + " manifest shortcuts across different" + + " activities."); + } + forceDeleteShortcutInner(shortcut.getId()); + } + } + } + } + /** * Remove all shortcuts that aren't pinned, cached nor dynamic. * @@ -1366,6 +1388,61 @@ class ShortcutPackage extends ShortcutPackageItem { return Integer.compare(a.getRank(), b.getRank()); }; + /** + * To sort by isManifestShortcut(), isDynamic(), getRank() and + * getLastChangedTimestamp(). i.e. manifest shortcuts come before non-manifest shortcuts, + * dynamic shortcuts come before floating shortcuts, then sort by last changed timestamp. + * + * This is used to decide which shortcuts to remove when the total number of shortcuts retained + * for the app exceeds the limit defined in {@link ShortcutService#getMaxAppShortcuts()}. + * + * (Note the number of manifest shortcuts is always <= the max number, because if there are + * more, ShortcutParser would ignore the rest.) + */ + final Comparator mShortcutTypeRankAndTimeComparator = (ShortcutInfo a, + ShortcutInfo b) -> { + if (a.isDeclaredInManifest() && !b.isDeclaredInManifest()) { + return -1; + } + if (!a.isDeclaredInManifest() && b.isDeclaredInManifest()) { + return 1; + } + if (a.isDynamic() && b.isDynamic()) { + return Integer.compare(a.getRank(), b.getRank()); + } + if (a.isDynamic()) { + return -1; + } + if (b.isDynamic()) { + return 1; + } + if (a.isCached() && b.isCached()) { + // if both shortcuts are cached, prioritize shortcuts cached by people tile, + if (a.hasFlags(ShortcutInfo.FLAG_CACHED_PEOPLE_TILE) + && !b.hasFlags(ShortcutInfo.FLAG_CACHED_PEOPLE_TILE)) { + return -1; + } else if (!a.hasFlags(ShortcutInfo.FLAG_CACHED_PEOPLE_TILE) + && b.hasFlags(ShortcutInfo.FLAG_CACHED_PEOPLE_TILE)) { + return 1; + } + // followed by bubbles. + if (a.hasFlags(ShortcutInfo.FLAG_CACHED_BUBBLES) + && !b.hasFlags(ShortcutInfo.FLAG_CACHED_BUBBLES)) { + return -1; + } else if (!a.hasFlags(ShortcutInfo.FLAG_CACHED_BUBBLES) + && b.hasFlags(ShortcutInfo.FLAG_CACHED_BUBBLES)) { + return 1; + } + } + if (a.isCached()) { + return -1; + } + if (b.isCached()) { + return 1; + } + return Long.compare(b.getLastChangedTimestamp(), a.getLastChangedTimestamp()); + }; + /** * Build a list of shortcuts for each target activity and return as a map. The result won't * contain "floating" shortcuts because they don't belong on any activities. diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index f2bcf5e461a7..2f3056e89614 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -180,6 +180,9 @@ public class ShortcutService extends IShortcutService.Stub { @VisibleForTesting static final int DEFAULT_MAX_SHORTCUTS_PER_ACTIVITY = 15; + @VisibleForTesting + static final int DEFAULT_MAX_SHORTCUTS_PER_APP = 100; + @VisibleForTesting static final int DEFAULT_MAX_ICON_DIMENSION_DP = 96; @@ -256,6 +259,11 @@ public class ShortcutService extends IShortcutService.Stub { */ String KEY_MAX_SHORTCUTS = "max_shortcuts"; + /** + * Key name for the max shortcuts can be retained in system ram per app. (int) + */ + String KEY_MAX_SHORTCUTS_PER_APP = "max_shortcuts_per_app"; + /** * Key name for icon compression quality, 0-100. */ @@ -328,10 +336,15 @@ public class ShortcutService extends IShortcutService.Stub { new SparseArray<>(); /** - * Max number of dynamic + manifest shortcuts that each application can have at a time. + * Max number of dynamic + manifest shortcuts that each activity can have at a time. */ private int mMaxShortcuts; + /** + * Max number of shortcuts that can exists in system ram for each application. + */ + private int mMaxShortcutsPerApp; + /** * Max number of updating API calls that each application can make during the interval. */ @@ -806,6 +819,9 @@ public class ShortcutService extends IShortcutService.Stub { mMaxShortcuts = Math.max(0, (int) parser.getLong( ConfigConstants.KEY_MAX_SHORTCUTS, DEFAULT_MAX_SHORTCUTS_PER_ACTIVITY)); + mMaxShortcutsPerApp = Math.max(0, (int) parser.getLong( + ConfigConstants.KEY_MAX_SHORTCUTS_PER_APP, DEFAULT_MAX_SHORTCUTS_PER_APP)); + final int iconDimensionDp = Math.max(1, injectIsLowRamDevice() ? (int) parser.getLong( ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM, @@ -1757,6 +1773,13 @@ public class ShortcutService extends IShortcutService.Stub { return mMaxShortcuts; } + /** + * Return the max number of shortcuts can be retaiend in system ram for each application. + */ + int getMaxAppShortcuts() { + return mMaxShortcutsPerApp; + } + /** * - Sends a notification to LauncherApps * - Write to file -- cgit v1.2.3 From 94437e989c0391b2dbf28d33120fdc28a4ce8d4d Mon Sep 17 00:00:00 2001 From: Pinyao Ting Date: Wed, 15 Feb 2023 15:20:25 -0800 Subject: Limit the number of shortcuts per app that can be retained by system This is a second attempt at fixing the issue, the previous CL ag/20642213 was reverted because it simply throws an exception when the limit is reached, which causes apps to crash since chat apps tends to be sending large amount of conversation shortcuts and they have no way to know how many of these shortcuts are still cached by the system. Instead of throwing an exception, this CL simply removes excessive shortcuts to avoid crashes. Currently there is a limit on the number of shortcuts an app can publish in respect to each launcher activity. This CL further implements a global maximum of total number of shortcuts that can be retained for an app to mitigate from any potential system health issue. When the global maximum is reached, ShortcutService will proactively removes shortcuts from system memory. Cached shortcuts are removed first, followed by dynamic shortcuts, using last updated time as tie-breaker. This CL additionally addresses an unexpected flow where re-publishing previously removed shortcuts that are still retained by the system could cause the total number of shortcuts to exceed previously set limit. Bug: 250576066 233155034 Test: manual Change-Id: I001c7a87b62aefa9487bf8efaf3cd02d7cb21521 Merged-In: I001c7a87b62aefa9487bf8efaf3cd02d7cb21521 --- .../com/android/server/pm/ShortcutPackage.java | 88 +++++++++++++++++++++- .../com/android/server/pm/ShortcutService.java | 25 +++++- 2 files changed, 109 insertions(+), 4 deletions(-) diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index 3506fd9be15d..e40075371910 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -405,6 +405,7 @@ class ShortcutPackage extends ShortcutPackageItem { @NonNull List changedShortcuts) { Preconditions.checkArgument(newShortcut.isEnabled(), "pushDynamicShortcuts() cannot publish disabled shortcuts"); + ensureShortcutCountBeforePush(); newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC); @@ -412,7 +413,7 @@ class ShortcutPackage extends ShortcutPackageItem { final ShortcutInfo oldShortcut = findShortcutById(newShortcut.getId()); boolean deleted = false; - if (oldShortcut == null) { + if (oldShortcut == null || !oldShortcut.isDynamic()) { final ShortcutService service = mShortcutUser.mService; final int maxShortcuts = service.getMaxActivityShortcuts(); @@ -422,7 +423,6 @@ class ShortcutPackage extends ShortcutPackageItem { if (activityShortcuts != null && activityShortcuts.size() == maxShortcuts) { // Max has reached. Delete the shortcut with lowest rank. - // Sort by isManifestShortcut() and getRank(). Collections.sort(activityShortcuts, mShortcutTypeAndRankComparator); @@ -437,7 +437,8 @@ class ShortcutPackage extends ShortcutPackageItem { changedShortcuts.add(shortcut); deleted = deleteDynamicWithId(shortcut.getId(), /*ignoreInvisible=*/ true) != null; } - } else { + } + if (oldShortcut != null) { // It's an update case. // Make sure the target is updatable. (i.e. should be mutable.) oldShortcut.ensureUpdatableWith(newShortcut, /*isUpdating=*/ false); @@ -463,6 +464,32 @@ class ShortcutPackage extends ShortcutPackageItem { return deleted; } + private void ensureShortcutCountBeforePush() { + final ShortcutService service = mShortcutUser.mService; + // Ensure the total number of shortcuts doesn't exceed the hard limit per app. + final int maxShortcutPerApp = service.getMaxAppShortcuts(); + synchronized (mLock) { + final List appShortcuts = mShortcuts.values().stream().filter(si -> + !si.isPinned()).collect(Collectors.toList()); + if (appShortcuts.size() >= maxShortcutPerApp) { + // Max has reached. Removes shortcuts until they fall within the hard cap. + // Sort by isManifestShortcut(), isDynamic() and getLastChangedTimestamp(). + Collections.sort(appShortcuts, mShortcutTypeRankAndTimeComparator); + + while (appShortcuts.size() >= maxShortcutPerApp) { + final ShortcutInfo shortcut = appShortcuts.remove(appShortcuts.size() - 1); + if (shortcut.isDeclaredInManifest()) { + // All shortcuts are manifest shortcuts and cannot be removed. + throw new IllegalArgumentException(getPackageName() + " has published " + + appShortcuts.size() + " manifest shortcuts across different" + + " activities."); + } + forceDeleteShortcutInner(shortcut.getId()); + } + } + } + } + /** * Remove all shortcuts that aren't pinned, cached nor dynamic. * @@ -1368,6 +1395,61 @@ class ShortcutPackage extends ShortcutPackageItem { return Integer.compare(a.getRank(), b.getRank()); }; + /** + * To sort by isManifestShortcut(), isDynamic(), getRank() and + * getLastChangedTimestamp(). i.e. manifest shortcuts come before non-manifest shortcuts, + * dynamic shortcuts come before floating shortcuts, then sort by last changed timestamp. + * + * This is used to decide which shortcuts to remove when the total number of shortcuts retained + * for the app exceeds the limit defined in {@link ShortcutService#getMaxAppShortcuts()}. + * + * (Note the number of manifest shortcuts is always <= the max number, because if there are + * more, ShortcutParser would ignore the rest.) + */ + final Comparator mShortcutTypeRankAndTimeComparator = (ShortcutInfo a, + ShortcutInfo b) -> { + if (a.isDeclaredInManifest() && !b.isDeclaredInManifest()) { + return -1; + } + if (!a.isDeclaredInManifest() && b.isDeclaredInManifest()) { + return 1; + } + if (a.isDynamic() && b.isDynamic()) { + return Integer.compare(a.getRank(), b.getRank()); + } + if (a.isDynamic()) { + return -1; + } + if (b.isDynamic()) { + return 1; + } + if (a.isCached() && b.isCached()) { + // if both shortcuts are cached, prioritize shortcuts cached by people tile, + if (a.hasFlags(ShortcutInfo.FLAG_CACHED_PEOPLE_TILE) + && !b.hasFlags(ShortcutInfo.FLAG_CACHED_PEOPLE_TILE)) { + return -1; + } else if (!a.hasFlags(ShortcutInfo.FLAG_CACHED_PEOPLE_TILE) + && b.hasFlags(ShortcutInfo.FLAG_CACHED_PEOPLE_TILE)) { + return 1; + } + // followed by bubbles. + if (a.hasFlags(ShortcutInfo.FLAG_CACHED_BUBBLES) + && !b.hasFlags(ShortcutInfo.FLAG_CACHED_BUBBLES)) { + return -1; + } else if (!a.hasFlags(ShortcutInfo.FLAG_CACHED_BUBBLES) + && b.hasFlags(ShortcutInfo.FLAG_CACHED_BUBBLES)) { + return 1; + } + } + if (a.isCached()) { + return -1; + } + if (b.isCached()) { + return 1; + } + return Long.compare(b.getLastChangedTimestamp(), a.getLastChangedTimestamp()); + }; + /** * Build a list of shortcuts for each target activity and return as a map. The result won't * contain "floating" shortcuts because they don't belong on any activities. diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 62d6717e847a..940eb345c06d 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -179,6 +179,9 @@ public class ShortcutService extends IShortcutService.Stub { @VisibleForTesting static final int DEFAULT_MAX_SHORTCUTS_PER_ACTIVITY = 15; + @VisibleForTesting + static final int DEFAULT_MAX_SHORTCUTS_PER_APP = 100; + @VisibleForTesting static final int DEFAULT_MAX_ICON_DIMENSION_DP = 96; @@ -253,6 +256,11 @@ public class ShortcutService extends IShortcutService.Stub { */ String KEY_MAX_SHORTCUTS = "max_shortcuts"; + /** + * Key name for the max shortcuts can be retained in system ram per app. (int) + */ + String KEY_MAX_SHORTCUTS_PER_APP = "max_shortcuts_per_app"; + /** * Key name for icon compression quality, 0-100. */ @@ -325,10 +333,15 @@ public class ShortcutService extends IShortcutService.Stub { new SparseArray<>(); /** - * Max number of dynamic + manifest shortcuts that each application can have at a time. + * Max number of dynamic + manifest shortcuts that each activity can have at a time. */ private int mMaxShortcuts; + /** + * Max number of shortcuts that can exists in system ram for each application. + */ + private int mMaxShortcutsPerApp; + /** * Max number of updating API calls that each application can make during the interval. */ @@ -790,6 +803,9 @@ public class ShortcutService extends IShortcutService.Stub { mMaxShortcuts = Math.max(0, (int) parser.getLong( ConfigConstants.KEY_MAX_SHORTCUTS, DEFAULT_MAX_SHORTCUTS_PER_ACTIVITY)); + mMaxShortcutsPerApp = Math.max(0, (int) parser.getLong( + ConfigConstants.KEY_MAX_SHORTCUTS_PER_APP, DEFAULT_MAX_SHORTCUTS_PER_APP)); + final int iconDimensionDp = Math.max(1, injectIsLowRamDevice() ? (int) parser.getLong( ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM, @@ -1757,6 +1773,13 @@ public class ShortcutService extends IShortcutService.Stub { return mMaxShortcuts; } + /** + * Return the max number of shortcuts can be retaiend in system ram for each application. + */ + int getMaxAppShortcuts() { + return mMaxShortcutsPerApp; + } + /** * - Sends a notification to LauncherApps * - Write to file -- cgit v1.2.3 From e66544a48fca830dc9a12efcf8ce828c3df4cba2 Mon Sep 17 00:00:00 2001 From: Mark Renouf Date: Wed, 22 Feb 2023 14:55:38 +0000 Subject: Prevent sharesheet from previewing unowned URIs Bug: 261036568 Test: manually via supplied tool (see bug) Change-Id: I582bacca197d814204b48b917a550f72dbde87d6 Merged-In: Ib3f5839d00c7cf09bca3b01fc0a8a6f0f4960993 Merged-In: Iee1a75ef6ecbf471badeb42d8ebea11e74d884c1 Merged-In: I83e93c373538460e38ec17f1fd8e39d7aea95c10 --- .../com/android/internal/app/ChooserActivity.java | 36 ++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 7bb1ed8abc0a..a204dc0112c9 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -16,6 +16,8 @@ package com.android.internal.app; +import static android.content.ContentProvider.getUserIdFromUri; + import static java.lang.annotation.RetentionPolicy.SOURCE; import android.animation.Animator; @@ -149,6 +151,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; /** * The Chooser Activity handles intent resolution specifically for sharing intents - @@ -1375,7 +1378,7 @@ public class ChooserActivity extends ResolverActivity implements ImageView previewThumbnailView = contentPreviewLayout.findViewById( R.id.content_preview_thumbnail); - if (previewThumbnail == null) { + if (!validForContentPreview(previewThumbnail)) { previewThumbnailView.setVisibility(View.GONE); } else { mPreviewCoord = new ContentPreviewCoordinator(contentPreviewLayout, false); @@ -1403,6 +1406,10 @@ public class ChooserActivity extends ResolverActivity implements String action = targetIntent.getAction(); if (Intent.ACTION_SEND.equals(action)) { Uri uri = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM); + if (!validForContentPreview(uri)) { + contentPreviewLayout.setVisibility(View.GONE); + return contentPreviewLayout; + } imagePreview.findViewById(R.id.content_preview_image_1_large) .setTransitionName(ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME); mPreviewCoord.loadUriIntoView(R.id.content_preview_image_1_large, uri, 0); @@ -1412,7 +1419,7 @@ public class ChooserActivity extends ResolverActivity implements List uris = targetIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); List imageUris = new ArrayList<>(); for (Uri uri : uris) { - if (isImageType(resolver.getType(uri))) { + if (validForContentPreview(uri) && isImageType(resolver.getType(uri))) { imageUris.add(uri); } } @@ -1521,9 +1528,16 @@ public class ChooserActivity extends ResolverActivity implements String action = targetIntent.getAction(); if (Intent.ACTION_SEND.equals(action)) { Uri uri = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM); + if (!validForContentPreview(uri)) { + contentPreviewLayout.setVisibility(View.GONE); + return contentPreviewLayout; + } loadFileUriIntoView(uri, contentPreviewLayout); } else { List uris = targetIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); + uris = uris.stream() + .filter(ChooserActivity::validForContentPreview) + .collect(Collectors.toList()); int uriCount = uris.size(); if (uriCount == 0) { @@ -1577,6 +1591,24 @@ public class ChooserActivity extends ResolverActivity implements } } + /** + * Indicate if the incoming content URI should be allowed. + * + * @param uri the uri to test + * @return true if the URI is allowed for content preview + */ + private static boolean validForContentPreview(Uri uri) throws SecurityException { + if (uri == null) { + return false; + } + int userId = getUserIdFromUri(uri, UserHandle.USER_CURRENT); + if (userId != UserHandle.USER_CURRENT && userId != UserHandle.myUserId()) { + Log.e(TAG, "dropped invalid content URI belonging to user " + userId); + return false; + } + return true; + } + @VisibleForTesting protected boolean isImageType(String mimeType) { return mimeType != null && mimeType.startsWith("image/"); -- cgit v1.2.3 From 3062b80fb28014a7482d5fa8b2a5c852134a5845 Mon Sep 17 00:00:00 2001 From: Mark Renouf Date: Wed, 22 Feb 2023 15:14:08 +0000 Subject: Prevent sharesheet from previewing unowned URIs Bug: 261036568 Test: manually via supplied tool (see bug) Change-Id: I21accf6f753d2f676f1602d6e1ce829c5ef29e9a Merged-In: I582bacca197d814204b48b917a550f72dbde87d6 Merged-In: Ib3f5839d00c7cf09bca3b01fc0a8a6f0f4960993 Merged-In: Iee1a75ef6ecbf471badeb42d8ebea11e74d884c1 Merged-In: I83e93c373538460e38ec17f1fd8e39d7aea95c10 --- .../com/android/internal/app/ChooserActivity.java | 36 ++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 14cf258f18ab..5048185565c7 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -16,6 +16,8 @@ package com.android.internal.app; +import static android.content.ContentProvider.getUserIdFromUri; + import static java.lang.annotation.RetentionPolicy.SOURCE; import android.animation.Animator; @@ -148,6 +150,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; /** * The Chooser Activity handles intent resolution specifically for sharing intents - @@ -1301,7 +1304,7 @@ public class ChooserActivity extends ResolverActivity implements ImageView previewThumbnailView = contentPreviewLayout.findViewById( R.id.content_preview_thumbnail); - if (previewThumbnail == null) { + if (!validForContentPreview(previewThumbnail)) { previewThumbnailView.setVisibility(View.GONE); } else { mPreviewCoord = new ContentPreviewCoordinator(contentPreviewLayout, false); @@ -1327,6 +1330,10 @@ public class ChooserActivity extends ResolverActivity implements String action = targetIntent.getAction(); if (Intent.ACTION_SEND.equals(action)) { Uri uri = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM); + if (!validForContentPreview(uri)) { + contentPreviewLayout.setVisibility(View.GONE); + return contentPreviewLayout; + } mPreviewCoord.loadUriIntoView(R.id.content_preview_image_1_large, uri, 0); } else { ContentResolver resolver = getContentResolver(); @@ -1334,7 +1341,7 @@ public class ChooserActivity extends ResolverActivity implements List uris = targetIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); List imageUris = new ArrayList<>(); for (Uri uri : uris) { - if (isImageType(resolver.getType(uri))) { + if (validForContentPreview(uri) && isImageType(resolver.getType(uri))) { imageUris.add(uri); } } @@ -1441,9 +1448,16 @@ public class ChooserActivity extends ResolverActivity implements String action = targetIntent.getAction(); if (Intent.ACTION_SEND.equals(action)) { Uri uri = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM); + if (!validForContentPreview(uri)) { + contentPreviewLayout.setVisibility(View.GONE); + return contentPreviewLayout; + } loadFileUriIntoView(uri, contentPreviewLayout); } else { List uris = targetIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); + uris = uris.stream() + .filter(ChooserActivity::validForContentPreview) + .collect(Collectors.toList()); int uriCount = uris.size(); if (uriCount == 0) { @@ -1497,6 +1511,24 @@ public class ChooserActivity extends ResolverActivity implements } } + /** + * Indicate if the incoming content URI should be allowed. + * + * @param uri the uri to test + * @return true if the URI is allowed for content preview + */ + private static boolean validForContentPreview(Uri uri) throws SecurityException { + if (uri == null) { + return false; + } + int userId = getUserIdFromUri(uri, UserHandle.USER_CURRENT); + if (userId != UserHandle.USER_CURRENT && userId != UserHandle.myUserId()) { + Log.e(TAG, "dropped invalid content URI belonging to user " + userId); + return false; + } + return true; + } + @VisibleForTesting protected boolean isImageType(String mimeType) { return mimeType != null && mimeType.startsWith("image/"); -- cgit v1.2.3 From 657c2a411593ffb95c7f91a972bbe84f1a2df790 Mon Sep 17 00:00:00 2001 From: arthurhung Date: Thu, 23 Jul 2020 17:15:04 +0800 Subject: Fix WindowInputTests#testOverlapWindow failing (1/2) The 'InputWindowInfo.isTrustedOverlay' function had been removed, we have to indicate the trustedOverlay flag when fill input info to overlay window. Bug: 161789401 Bug: 158717144 Test: atest WindowInputTests Change-Id: Idf17fd64c6a1399d04c51e3f0bd600dc4a84239a Merged-In: Idf17fd64c6a1399d04c51e3f0bd600dc4a84239a --- .../com/android/server/wm/InputConsumerImpl.java | 1 + .../java/com/android/server/wm/InputMonitor.java | 32 +++++++++++++++++----- .../java/com/android/server/wm/WindowState.java | 14 +--------- 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java index 19185736fc89..6bbaa8f27f2d 100644 --- a/services/core/java/com/android/server/wm/InputConsumerImpl.java +++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java @@ -86,6 +86,7 @@ class InputConsumerImpl implements IBinder.DeathRecipient { mWindowHandle.ownerUid = Process.myUid(); mWindowHandle.inputFeatures = 0; mWindowHandle.scaleFactor = 1.0f; + mWindowHandle.trustedOverlay = true; mInputSurface = mService.makeSurfaceBuilder(mService.mRoot.getDisplayContent(displayId).getSession()) .setContainerLayer() diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index fe9bf12ec96a..f0c027375c33 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -27,7 +27,18 @@ import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS; +import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_CONSUMER; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; +import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; +import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; +import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE; import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT; @@ -66,9 +77,6 @@ final class InputMonitor { private boolean mUpdateInputWindowsPending; private boolean mUpdateInputWindowsImmediately; - // Currently focused input window handle. - private InputWindowHandle mFocusedInputWindowHandle; - private boolean mDisableWallpaperTouchEvents; private final Rect mTmpRect = new Rect(); private final UpdateInputForAllWindowsConsumer mUpdateInputForAllWindowsConsumer; @@ -310,10 +318,6 @@ final class InputMonitor { Slog.d(TAG_WM, "addInputWindowHandle: " + child + ", " + inputWindowHandle); } - - if (hasFocus) { - mFocusedInputWindowHandle = inputWindowHandle; - } } void setUpdateInputWindowsNeededLw() { @@ -572,6 +576,7 @@ final class InputMonitor { inputWindowHandle.portalToDisplayId = INVALID_DISPLAY; inputWindowHandle.touchableRegion.setEmpty(); inputWindowHandle.setTouchableRegionCrop(null); + inputWindowHandle.trustedOverlay = isTrustedOverlay(type); } /** @@ -586,4 +591,17 @@ final class InputMonitor { populateOverlayInputInfo(inputWindowHandle, name, TYPE_SECURE_SYSTEM_OVERLAY, true); t.setInputWindowInfo(sc, inputWindowHandle); } + + static boolean isTrustedOverlay(int type) { + return type == TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY + || type == TYPE_INPUT_METHOD || type == TYPE_INPUT_METHOD_DIALOG + || type == TYPE_MAGNIFICATION_OVERLAY || type == TYPE_STATUS_BAR + || type == TYPE_NOTIFICATION_SHADE + || type == TYPE_NAVIGATION_BAR + || type == TYPE_NAVIGATION_BAR_PANEL + || type == TYPE_SECURE_SYSTEM_OVERLAY + || type == TYPE_DOCK_DIVIDER + || type == TYPE_ACCESSIBILITY_OVERLAY + || type == TYPE_INPUT_CONSUMER; + } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 6e1b17c4c51f..cf23c206f250 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -62,7 +62,6 @@ import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY; -import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY; @@ -87,7 +86,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_PRIORITY_PHONE; import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION; import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT; import static android.view.WindowManager.LayoutParams.TYPE_SEARCH_BAR; -import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL; @@ -955,17 +953,7 @@ class WindowState extends WindowContainer implements WindowManagerP mInputWindowHandle.trustedOverlay = (mAttrs.privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0 && mOwnerCanAddInternalSystemWindow; - mInputWindowHandle.trustedOverlay |= - mAttrs.type == TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY - || mAttrs.type == TYPE_INPUT_METHOD || mAttrs.type == TYPE_INPUT_METHOD_DIALOG - || mAttrs.type == TYPE_MAGNIFICATION_OVERLAY || mAttrs.type == TYPE_STATUS_BAR - || mAttrs.type == TYPE_NOTIFICATION_SHADE - || mAttrs.type == TYPE_NAVIGATION_BAR - || mAttrs.type == TYPE_NAVIGATION_BAR_PANEL - || mAttrs.type == TYPE_SECURE_SYSTEM_OVERLAY - || mAttrs.type == TYPE_DOCK_DIVIDER - || mAttrs.type == TYPE_ACCESSIBILITY_OVERLAY - || mAttrs.type == TYPE_INPUT_CONSUMER; + mInputWindowHandle.trustedOverlay |= InputMonitor.isTrustedOverlay(mAttrs.type); // Make sure we initial all fields before adding to parentWindow, to prevent exception // during onDisplayChanged. -- cgit v1.2.3 From 2296ccfde0678b86f22e1da7bd57518f3bfafbba Mon Sep 17 00:00:00 2001 From: Pinyao Ting Date: Wed, 15 Feb 2023 15:20:25 -0800 Subject: Limit the number of shortcuts per app that can be retained by system This is a second attempt at fixing the issue, the previous CL ag/20642213 was reverted because it simply throws an exception when the limit is reached, which causes apps to crash since chat apps tends to be sending large amount of conversation shortcuts and they have no way to know how many of these shortcuts are still cached by the system. Instead of throwing an exception, this CL simply removes excessive shortcuts to avoid crashes. Currently there is a limit on the number of shortcuts an app can publish in respect to each launcher activity. This CL further implements a global maximum of total number of shortcuts that can be retained for an app to mitigate from any potential system health issue. When the global maximum is reached, ShortcutService will proactively removes shortcuts from system memory. Cached shortcuts are removed first, followed by dynamic shortcuts, using last updated time as tie-breaker. This CL additionally addresses an unexpected flow where re-publishing previously removed shortcuts that are still retained by the system could cause the total number of shortcuts to exceed previously set limit. Bug: 250576066 233155034 Test: manual Change-Id: I001c7a87b62aefa9487bf8efaf3cd02d7cb21521 Merged-In: I001c7a87b62aefa9487bf8efaf3cd02d7cb21521 --- .../com/android/server/pm/ShortcutPackage.java | 79 +++++++++++++++++++++- .../com/android/server/pm/ShortcutService.java | 25 ++++++- 2 files changed, 100 insertions(+), 4 deletions(-) diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index f2bfb2aa1405..b2d78ec7d95d 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -68,6 +68,7 @@ import java.util.List; import java.util.Objects; import java.util.Set; import java.util.function.Predicate; +import java.util.stream.Collectors; /** * Package information used by {@link ShortcutService}. @@ -350,6 +351,7 @@ class ShortcutPackage extends ShortcutPackageItem { @NonNull List changedShortcuts) { Preconditions.checkArgument(newShortcut.isEnabled(), "pushDynamicShortcuts() cannot publish disabled shortcuts"); + ensureShortcutCountBeforePush(); newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC); @@ -357,7 +359,7 @@ class ShortcutPackage extends ShortcutPackageItem { final ShortcutInfo oldShortcut = mShortcuts.get(newShortcut.getId()); boolean deleted = false; - if (oldShortcut == null) { + if (oldShortcut == null || !oldShortcut.isDynamic()) { final ShortcutService service = mShortcutUser.mService; final int maxShortcuts = service.getMaxActivityShortcuts(); @@ -367,7 +369,6 @@ class ShortcutPackage extends ShortcutPackageItem { if (activityShortcuts != null && activityShortcuts.size() == maxShortcuts) { // Max has reached. Delete the shortcut with lowest rank. - // Sort by isManifestShortcut() and getRank(). Collections.sort(activityShortcuts, mShortcutTypeAndRankComparator); @@ -382,7 +383,8 @@ class ShortcutPackage extends ShortcutPackageItem { changedShortcuts.add(shortcut); deleted = deleteDynamicWithId(shortcut.getId(), /*ignoreInvisible=*/ true) != null; } - } else { + } + if (oldShortcut != null) { // It's an update case. // Make sure the target is updatable. (i.e. should be mutable.) oldShortcut.ensureUpdatableWith(newShortcut, /*isUpdating=*/ false); @@ -396,6 +398,30 @@ class ShortcutPackage extends ShortcutPackageItem { return deleted; } + private void ensureShortcutCountBeforePush() { + final ShortcutService service = mShortcutUser.mService; + // Ensure the total number of shortcuts doesn't exceed the hard limit per app. + final int maxShortcutPerApp = service.getMaxAppShortcuts(); + final List appShortcuts = mShortcuts.values().stream().filter(si -> + !si.isPinned()).collect(Collectors.toList()); + if (appShortcuts.size() >= maxShortcutPerApp) { + // Max has reached. Removes shortcuts until they fall within the hard cap. + // Sort by isManifestShortcut(), isDynamic() and getLastChangedTimestamp(). + Collections.sort(appShortcuts, mShortcutTypeRankAndTimeComparator); + + while (appShortcuts.size() >= maxShortcutPerApp) { + final ShortcutInfo shortcut = appShortcuts.remove(appShortcuts.size() - 1); + if (shortcut.isDeclaredInManifest()) { + // All shortcuts are manifest shortcuts and cannot be removed. + throw new IllegalArgumentException(getPackageName() + " has published " + + appShortcuts.size() + " manifest shortcuts across different" + + " activities."); + } + forceDeleteShortcutInner(shortcut.getId()); + } + } + } + /** * Remove all shortcuts that aren't pinned, cached nor dynamic. * @@ -1222,6 +1248,53 @@ class ShortcutPackage extends ShortcutPackageItem { return Integer.compare(a.getRank(), b.getRank()); }; + /** + * To sort by isManifestShortcut(), isDynamic(), getRank() and + * getLastChangedTimestamp(). i.e. manifest shortcuts come before non-manifest shortcuts, + * dynamic shortcuts come before floating shortcuts, then sort by last changed timestamp. + * + * This is used to decide which shortcuts to remove when the total number of shortcuts retained + * for the app exceeds the limit defined in {@link ShortcutService#getMaxAppShortcuts()}. + * + * (Note the number of manifest shortcuts is always <= the max number, because if there are + * more, ShortcutParser would ignore the rest.) + */ + final Comparator mShortcutTypeRankAndTimeComparator = (ShortcutInfo a, + ShortcutInfo b) -> { + if (a.isDeclaredInManifest() && !b.isDeclaredInManifest()) { + return -1; + } + if (!a.isDeclaredInManifest() && b.isDeclaredInManifest()) { + return 1; + } + if (a.isDynamic() && b.isDynamic()) { + return Integer.compare(a.getRank(), b.getRank()); + } + if (a.isDynamic()) { + return -1; + } + if (b.isDynamic()) { + return 1; + } + if (a.isCached() && b.isCached()) { + // if both shortcuts are cached, prioritize shortcuts cached by bubbles. + if (a.hasFlags(ShortcutInfo.FLAG_CACHED_BUBBLES) + && !b.hasFlags(ShortcutInfo.FLAG_CACHED_BUBBLES)) { + return -1; + } else if (!a.hasFlags(ShortcutInfo.FLAG_CACHED_BUBBLES) + && b.hasFlags(ShortcutInfo.FLAG_CACHED_BUBBLES)) { + return 1; + } + } + if (a.isCached()) { + return -1; + } + if (b.isCached()) { + return 1; + } + return Long.compare(b.getLastChangedTimestamp(), a.getLastChangedTimestamp()); + }; + /** * Build a list of shortcuts for each target activity and return as a map. The result won't * contain "floating" shortcuts because they don't belong on any activities. diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 0c42ff6be520..c3375d9346e5 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -171,6 +171,9 @@ public class ShortcutService extends IShortcutService.Stub { @VisibleForTesting static final int DEFAULT_MAX_SHORTCUTS_PER_ACTIVITY = 15; + @VisibleForTesting + static final int DEFAULT_MAX_SHORTCUTS_PER_APP = 100; + @VisibleForTesting static final int DEFAULT_MAX_ICON_DIMENSION_DP = 96; @@ -245,6 +248,11 @@ public class ShortcutService extends IShortcutService.Stub { */ String KEY_MAX_SHORTCUTS = "max_shortcuts"; + /** + * Key name for the max shortcuts can be retained in system ram per app. (int) + */ + String KEY_MAX_SHORTCUTS_PER_APP = "max_shortcuts_per_app"; + /** * Key name for icon compression quality, 0-100. */ @@ -302,10 +310,15 @@ public class ShortcutService extends IShortcutService.Stub { new SparseArray<>(); /** - * Max number of dynamic + manifest shortcuts that each application can have at a time. + * Max number of dynamic + manifest shortcuts that each activity can have at a time. */ private int mMaxShortcuts; + /** + * Max number of shortcuts that can exists in system ram for each application. + */ + private int mMaxShortcutsPerApp; + /** * Max number of updating API calls that each application can make during the interval. */ @@ -732,6 +745,9 @@ public class ShortcutService extends IShortcutService.Stub { mMaxShortcuts = Math.max(0, (int) parser.getLong( ConfigConstants.KEY_MAX_SHORTCUTS, DEFAULT_MAX_SHORTCUTS_PER_ACTIVITY)); + mMaxShortcutsPerApp = Math.max(0, (int) parser.getLong( + ConfigConstants.KEY_MAX_SHORTCUTS_PER_APP, DEFAULT_MAX_SHORTCUTS_PER_APP)); + final int iconDimensionDp = Math.max(1, injectIsLowRamDevice() ? (int) parser.getLong( ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM, @@ -1661,6 +1677,13 @@ public class ShortcutService extends IShortcutService.Stub { return mMaxShortcuts; } + /** + * Return the max number of shortcuts can be retaiend in system ram for each application. + */ + int getMaxAppShortcuts() { + return mMaxShortcutsPerApp; + } + /** * - Sends a notification to LauncherApps * - Write to file -- cgit v1.2.3 From 7cb165f1c02e8c5a3093bb331e084b0fa6a32e90 Mon Sep 17 00:00:00 2001 From: Austin Borger Date: Fri, 3 Mar 2023 15:44:28 -0800 Subject: Fix vulnerability in AttributionSource due to incorrect Binder call AttributionSource uses Binder.getCallingUid to verify the UID of the caller from another process. However, getCallingUid does not always behave as expected. If the AttributionSource is unparceled outside a transaction thread, which is quite possible, getCallingUid will return the UID of the current process instead. If this is a system process, the UID check gets bypassed entirely, meaning any uid can be provided. This patch fixes the vulnerability by emptying out the state of the AttributionSource, so that the service checking its credentials will fail to give permission to the app. Bug: 267231571 Test: v2/android-virtual-infra/test_mapping/presubmit-avd Merged-In: Ic301a8518b8e57e1c9a2c9f2f845e51dca145257 Change-Id: Ic985afdb5e4495f2e051e6d0d62d39c6660c585e --- core/java/android/content/AttributionSource.java | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java index 3f2fa2188d24..16b18c85e790 100644 --- a/core/java/android/content/AttributionSource.java +++ b/core/java/android/content/AttributionSource.java @@ -31,6 +31,7 @@ import android.os.Parcelable; import android.os.Process; import android.permission.PermissionManager; import android.util.ArraySet; +import android.util.Log; import com.android.internal.annotations.Immutable; @@ -87,6 +88,8 @@ import java.util.Set; */ @Immutable public final class AttributionSource implements Parcelable { + private static final String TAG = "AttributionSource"; + private static final String DESCRIPTOR = "android.content.AttributionSource"; private static final Binder sDefaultToken = new Binder(DESCRIPTOR); @@ -154,9 +157,20 @@ public final class AttributionSource implements Parcelable { AttributionSource(@NonNull Parcel in) { this(AttributionSourceState.CREATOR.createFromParcel(in)); - // Since we just unpacked this object as part of it transiting a Binder - // call, this is the perfect time to enforce that its UID and PID can be trusted - enforceCallingUidAndPid(); + if (!Binder.isDirectlyHandlingTransaction()) { + Log.e(TAG, "Unable to verify calling UID #" + mAttributionSourceState.uid + " PID #" + + mAttributionSourceState.pid + " when not handling Binder transaction; " + + "clearing."); + mAttributionSourceState.pid = -1; + mAttributionSourceState.uid = -1; + mAttributionSourceState.packageName = null; + mAttributionSourceState.attributionTag = null; + mAttributionSourceState.next = null; + } else { + // Since we just unpacked this object as part of it transiting a Binder + // call, this is the perfect time to enforce that its UID and PID can be trusted + enforceCallingUidAndPid(); + } } /** @hide */ -- cgit v1.2.3 From 2fc07deafd257dcc934bb802887ef4297f49548f Mon Sep 17 00:00:00 2001 From: Austin Borger Date: Thu, 9 Feb 2023 16:00:02 -0800 Subject: Fix vulnerability in AttributionSource due to incorrect Binder call AttributionSource uses Binder.getCallingUid to verify the UID of the caller from another process. However, getCallingUid does not always behave as expected. If the AttributionSource is unparceled outside a transaction thread, which is quite possible, getCallingUid will return the UID of the current process instead. If this is a system process, the UID check gets bypassed entirely, meaning any uid can be provided. This patch fixes the vulnerability by emptying out the state of the AttributionSource, so that the service checking its credentials will fail to give permission to the app. Bug: 267231571 Test: v2/android-virtual-infra/test_mapping/presubmit-avd Merged-In: Ic301a8518b8e57e1c9a2c9f2f845e51dca145257 Change-Id: Ifdc53f87b7ef53b69cc01ec1955b4cb1dfd3345b Merged-In: Ic985afdb5e4495f2e051e6d0d62d39c6660c585e --- core/java/android/content/AttributionSource.java | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java index 2f61fee88e9f..ec56f9a7cf0c 100644 --- a/core/java/android/content/AttributionSource.java +++ b/core/java/android/content/AttributionSource.java @@ -30,6 +30,7 @@ import android.os.Parcelable; import android.os.Process; import android.permission.PermissionManager; import android.util.ArraySet; +import android.util.Log; import com.android.internal.annotations.Immutable; @@ -86,6 +87,8 @@ import java.util.Set; */ @Immutable public final class AttributionSource implements Parcelable { + private static final String TAG = "AttributionSource"; + private static final String DESCRIPTOR = "android.content.AttributionSource"; private static final Binder sDefaultToken = new Binder(DESCRIPTOR); @@ -153,9 +156,20 @@ public final class AttributionSource implements Parcelable { AttributionSource(@NonNull Parcel in) { this(AttributionSourceState.CREATOR.createFromParcel(in)); - // Since we just unpacked this object as part of it transiting a Binder - // call, this is the perfect time to enforce that its UID and PID can be trusted - enforceCallingUidAndPid(); + if (!Binder.isHandlingTransaction()) { + Log.e(TAG, "Unable to verify calling UID #" + mAttributionSourceState.uid + " PID #" + + mAttributionSourceState.pid + " when not handling Binder transaction; " + + "clearing."); + mAttributionSourceState.pid = -1; + mAttributionSourceState.uid = -1; + mAttributionSourceState.packageName = null; + mAttributionSourceState.attributionTag = null; + mAttributionSourceState.next = null; + } else { + // Since we just unpacked this object as part of it transiting a Binder + // call, this is the perfect time to enforce that its UID and PID can be trusted + enforceCallingUidAndPid(); + } } /** @hide */ -- cgit v1.2.3 From 5f3a3f2a4bff1344333599f208a9118e22dcde4b Mon Sep 17 00:00:00 2001 From: Diego Vela Date: Tue, 7 Mar 2023 22:41:53 +0000 Subject: Revert "Fix vulnerability in AttributionSource due to incorrect ..." Revert submission 21778925-tm-dev-2-attribution-source Reason for revert: b/272115855 Reverted changes: /q/submissionid:21778925-tm-dev-2-attribution-source Bug: 272115855 Change-Id: Ia3d101bab6613da6515acc87af9295da664827cf --- core/java/android/content/AttributionSource.java | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java index 16b18c85e790..3f2fa2188d24 100644 --- a/core/java/android/content/AttributionSource.java +++ b/core/java/android/content/AttributionSource.java @@ -31,7 +31,6 @@ import android.os.Parcelable; import android.os.Process; import android.permission.PermissionManager; import android.util.ArraySet; -import android.util.Log; import com.android.internal.annotations.Immutable; @@ -88,8 +87,6 @@ import java.util.Set; */ @Immutable public final class AttributionSource implements Parcelable { - private static final String TAG = "AttributionSource"; - private static final String DESCRIPTOR = "android.content.AttributionSource"; private static final Binder sDefaultToken = new Binder(DESCRIPTOR); @@ -157,20 +154,9 @@ public final class AttributionSource implements Parcelable { AttributionSource(@NonNull Parcel in) { this(AttributionSourceState.CREATOR.createFromParcel(in)); - if (!Binder.isDirectlyHandlingTransaction()) { - Log.e(TAG, "Unable to verify calling UID #" + mAttributionSourceState.uid + " PID #" - + mAttributionSourceState.pid + " when not handling Binder transaction; " - + "clearing."); - mAttributionSourceState.pid = -1; - mAttributionSourceState.uid = -1; - mAttributionSourceState.packageName = null; - mAttributionSourceState.attributionTag = null; - mAttributionSourceState.next = null; - } else { - // Since we just unpacked this object as part of it transiting a Binder - // call, this is the perfect time to enforce that its UID and PID can be trusted - enforceCallingUidAndPid(); - } + // Since we just unpacked this object as part of it transiting a Binder + // call, this is the perfect time to enforce that its UID and PID can be trusted + enforceCallingUidAndPid(); } /** @hide */ -- cgit v1.2.3 From 0759f0134c8d38598e49b976f131821b91544737 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Wed, 8 Mar 2023 22:04:23 -0800 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: Ieafc8af6af510c015a4059fcba756be9e82d3fbf --- core/res/res/values-ca/strings.xml | 6 +++--- core/res/res/values-es-rUS/strings.xml | 2 +- core/res/res/values-es/strings.xml | 6 +++--- core/res/res/values-eu/strings.xml | 4 ++-- core/res/res/values-fa/strings.xml | 4 ++-- core/res/res/values-gl/strings.xml | 4 ++-- core/res/res/values-it/strings.xml | 2 +- core/res/res/values-or/strings.xml | 2 +- core/res/res/values-pt-rPT/strings.xml | 28 ++++++++++++++-------------- core/res/res/values-ru/strings.xml | 4 ++-- core/res/res/values-sk/strings.xml | 2 +- core/res/res/values-sv/strings.xml | 2 +- 12 files changed, 33 insertions(+), 33 deletions(-) diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index 8ea725a94f82..94d9686f8816 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -1318,8 +1318,8 @@ "Permet" "Denega" "<b>%1$s</b> vol enviar un missatge a <b>%2$s</b>." - "Aquesta acció ""pot produir càrrecs"" al teu compte per a mòbils." - "Aquesta acció produirà càrrecs al teu compte per a mòbils." + "Aquesta acció ""pot produir càrrecs"" al teu compte mòbil." + "Aquesta acció produirà càrrecs al teu compte mòbil." "Envia" "Cancel·la" "Recorda la meva selecció" @@ -1358,7 +1358,7 @@ "S\'està carregant el dispositiu connectat. Toca per veure més opcions." "S\'ha detectat un accessori d\'àudio analògic" "El dispositiu connectat no és compatible amb aquest telèfon. Toca per obtenir més informació." - "Depuració per USB activada" + "Depuració per USB connectada" "Toca per desactivar la depuració per USB" "Selecciona per desactivar la depuració per USB" "S\'ha connectat la depuració sense fil" diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index c0c7ef1894d2..cc8c1ebe1ce5 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -1358,7 +1358,7 @@ "Cargando el dispositivo conectado. Presiona para ver más opciones." "Se detectó un accesorio de audio analógico" "El dispositivo adjunto no es compatible con este teléfono. Presiona para obtener más información." - "Depuración por USB conectada" + "Depuración por USB activada" "Presiona para desactivar" "Seleccionar para desactivar la depuración por USB" "Se conectó la depuración inalámbrica" diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index bc516d50fe60..662ce96d0ebe 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -1358,9 +1358,9 @@ "Cargando el dispositivo conectado. Toca para ver más opciones." "Se ha detectado un accesorio de audio analógico" "El dispositivo adjunto no es compatible con este teléfono. Toca para obtener más información." - "Depuración USB habilitada" - "Toca para desactivar la depuración USB" - "Seleccionar para inhabilitar la depuración USB" + "Depuración por USB activa" + "Toca para desactivar la depuración por USB" + "Seleccionar para inhabilitar la depuración por USB" "Depuración inalámbrica conectada" "Toca para desactivar la depuración inalámbrica" "Toca para desactivar la depuración inalámbrica." diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index 3632f2d8e76d..dd43d854b296 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -1851,8 +1851,8 @@ "Administratzaileak eguneratu du" "Administratzaileak ezabatu du" "Ados" - "Bateria-aurreztaileak gai iluna aktibatzen du, eta murriztu edo desaktibatu egiten ditu atzeko planoko jarduerak, zenbait efektu bisual, eta eginbide jakin eta sareko konexio batzuk." - "Bateria-aurreztaileak gai iluna aktibatzen du, eta atzeko planoko jarduerak, zenbait efektu bisual, eta eginbide jakin eta sareko konexio batzuk murrizten edo desaktibatzen ditu." + "Bateria-aurreztaileak gai iluna aktibatzen du, eta atzeko planoko jarduerak, zenbait efektu bisual, eta eginbide jakin eta sareko konexio batzuk mugatzen edo desaktibatzen ditu." + "Bateria-aurreztaileak gai iluna aktibatzen du, eta atzeko planoko jarduerak, zenbait efektu bisual, eta eginbide jakin eta sareko konexio batzuk mugatzen edo desaktibatzen ditu." "Datu-erabilera murrizteko, atzeko planoan datuak bidaltzea eta jasotzea galarazten die datu-aurreztaileak aplikazio batzuei. Erabiltzen ari zaren aplikazioek datuak atzitu ahalko dituzte, baina baliteke maiztasun txikiagoarekin atzitzea. Ondorioz, adibidez, baliteke irudiak ez erakustea haiek sakatu arte." "Datu-aurreztailea aktibatu nahi duzu?" "Aktibatu" diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index c02be6b4d9a7..6a223ea71823 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -1244,11 +1244,11 @@ "درحال آغاز کردن برنامه‌ها." "درحال اتمام راه‌اندازی." "ادامه راه‌اندازی؟" - "دکمه روشن/ خاموش را فشار دادید — این کار معمولاً صفحه‌نمایش را خاموش می‌کند.\n\nهنگام راه‌اندازی اثر انگشت، آرام ضربه بزنید." + "دکمه روشن/خاموش را فشار دادید — این کار معمولاً صفحه‌نمایش را خاموش می‌کند.\n\nهنگام راه‌اندازی اثر انگشت، آرام ضربه بزنید." "خاموش کردن صفحه" "ادامه راه‌اندازی" "تأیید اثر انگشت را ادامه می‌دهید؟" - "دکمه روشن/ خاموش را فشار دادید — این کار معمولاً صفحه‌نمایش را خاموش می‌کند.\n\nبرای تأیید اثر انگشتتان، آرام ضربه بزنید." + "دکمه روشن/خاموش را فشار دادید — این کار معمولاً صفحه‌نمایش را خاموش می‌کند.\n\nبرای تأیید اثر انگشتتان، آرام ضربه بزنید." "خاموش کردن صفحه" "ادامه" "%1$s در حال اجرا" diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml index f48443e94b4c..4d3deddbebf8 100644 --- a/core/res/res/values-gl/strings.xml +++ b/core/res/res/values-gl/strings.xml @@ -1317,8 +1317,8 @@ "Permitir" "Rexeitar" "<b>%1$s</b> quere enviar unha mensaxe a <b>%2$s</b>." - "Esta acción ""pode supoñer custos"" na túa conta de teléfono móbil." - "Esta acción suporá custos na túa conta de teléfono móbil." + "Esta acción ""pode supoñer custos"" na conta do teu operador móbil." + "Esta acción suporá custos na conta do teu operador móbil." "Enviar" "Cancelar" "Lembrar a miña opción" diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index ecbe3c24b744..495e8ebb8945 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -297,7 +297,7 @@ "Modalità provvisoria" "Sistema Android" "Passa al profilo personale" - "Passa a profilo di lavoro" + "Passa al profilo di lavoro" "Contatti" "Possono accedere ai contatti" "Posizione" diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml index fbc4024e4e23..971e3e41e7ca 100644 --- a/core/res/res/values-or/strings.xml +++ b/core/res/res/values-or/strings.xml @@ -1851,7 +1851,7 @@ "ଆପଣଙ୍କ ଆଡମିନ୍‌‌ ଅପଡେଟ୍‍ କରିଛନ୍ତି" "ଆପଣଙ୍କ ଆଡମିନ୍‌‌ ଡିଲିଟ୍‍ କରିଛନ୍ତି" "ଠିକ୍ ଅଛି" - "ବ୍ୟାଟେରୀ ସେଭର୍ ଗାଢ଼ା ଥିମକୁ ଚାଲୁ କରେ ଏବଂ ପୃଷ୍ଠପଟ କାର୍ଯ୍ୟକଳାପ, କିଛି ଭିଜୁଆଲ୍ ଇଫେକ୍ଟ, କିଛି ଫିଚର୍ ଏବଂ କିଛି ନେଟୱାର୍କ ସଂଯୋଗକୁ ସୀମିତ କିମ୍ବା ବନ୍ଦ କରେ।" + "ବେଟେରୀ ସେଭର ଗାଢ଼ା ଥିମକୁ ଚାଲୁ କରେ ଏବଂ ପୃଷ୍ଠପଟ କାର୍ଯ୍ୟକଳାପ, କିଛି ଭିଜୁଆଲ ଇଫେକ୍ଟ, କିଛି ଫିଚର ଏବଂ କିଛି ନେଟୱାର୍କ ସଂଯୋଗକୁ ସୀମିତ କିମ୍ବା ବନ୍ଦ କରେ।" "ବ୍ୟାଟେରୀ ସେଭର୍ ଗାଢ଼ା ଥିମକୁ ଚାଲୁ କରେ ଏବଂ ପୃଷ୍ଠପଟ କାର୍ଯ୍ୟକଳାପ, କିଛି ଭିଜୁଆଲ୍ ଇଫେକ୍ଟ, କିଛି ଫିଚର୍ ଏବଂ କିଛି ନେଟୱାର୍କ ସଂଯୋଗକୁ ସୀମିତ କିମ୍ବା ବନ୍ଦ କରେ।" "ଡାଟା ବ୍ୟବହାର କମ୍‍ କରିବାରେ ସାହାଯ୍ୟ କରିବାକୁ, ଡାଟା ସେଭର୍‍ ବ୍ୟାକ୍‌ଗ୍ରାଉଣ୍ଡରେ ଡାଟା ପଠାଇବା କିମ୍ବା ପ୍ରାପ୍ତ କରିବାକୁ କିଛି ଆପ୍‍କୁ ବାରଣ କରେ। ଆପଣ ବର୍ତ୍ତମାନ ବ୍ୟବହାର କରୁଥିବା ଆପ୍‍, ଡାଟା ଆକ୍ସେସ୍‍ କରିପାରେ, କିନ୍ତୁ ଏହା କମ୍‍ ଥର କରିପାରେ। ଏହାର ଅର୍ଥ ହୋଇପାରେ ଯେମିତି ଆପଣ ଇମେଜଗୁଡ଼ିକୁ ଟାପ୍‍ ନକରିବା ପର୍ଯ୍ୟନ୍ତ ସେଗୁଡ଼ିକ ଡିସପ୍ଲେ ହୁଏ ନାହିଁ।" "ଡାଟା ସେଭର୍‌ ଚାଲୁ କରିବେ?" diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index 22e4ed2501e3..cda521808f06 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -559,7 +559,7 @@ "Permite que a app aprenda o nível de complexidade do bloqueio de ecrã (elevado, médio, baixo ou nenhum), que indica o intervalo de comprimento e o tipo de bloqueio de ecrã possíveis. A app também pode sugerir aos utilizadores que atualizem o bloqueio de ecrã para um determinado nível, mas estes podem ignorar livremente a sugestão e continuar a navegação. Tenha em atenção que o bloqueio de ecrã não é armazenado em texto simples, pelo que a app desconhece a palavra-passe exata." "mostrar notificações" "Permite à app mostrar notificações" - "Utilizar hardware biométrico" + "Usar hardware biométrico" "Permite que a app utilize hardware biométrico para autenticação." "gerir o hardware de impressão digital" "Permite que a app invoque métodos para adicionar e eliminar modelos de impressão digital para utilização." @@ -573,8 +573,8 @@ "Permite que a app modifique a sua coleção de fotos." "ler as localizações a partir da sua coleção de multimédia" "Permite que a app leia as localizações a partir da sua coleção de multimédia." - "Utilizar a biometria" - "Utilizar a biometria ou o bloqueio de ecrã" + "Usar a biometria" + "Usar a biometria ou o bloqueio de ecrã" "Confirme a sua identidade" "Utilize a biometria para continuar." "Utilize a biometria ou o bloqueio de ecrã para continuar" @@ -584,7 +584,7 @@ "Autenticação cancelada" "Nenhum PIN, padrão ou palavra-passe definidos." "Erro ao autenticar." - "Utilizar o bloqueio de ecrã" + "Usar o bloqueio de ecrã" "Introduza o bloqueio de ecrã para continuar" "Prima firmemente o sensor" "Não foi possível processar a impressão digital. Tente novamente." @@ -616,8 +616,8 @@ "Sensor temporariamente desativado." "Não é possível usar o sensor de impressões digitais. Visite um fornecedor de serviços de reparação" "Dedo %d" - "Utilizar a impressão digital" - "Utilizar o bloqueio de ecrã ou a impressão digital" + "Usar a impressão digital" + "Usar o bloqueio de ecrã ou a impressão digital" "Utilize a sua impressão digital para continuar." "Utilize a impressão digital ou o bloqueio de ecrã para continuar" @@ -670,8 +670,8 @@ "O Desbloqueio facial não é suportado neste dispositivo" "Sensor temporariamente desativado." "Rosto %d" - "Utilizar o Desbloqueio facial" - "Utilizar o bloqueio através do rosto ou de ecrã" + "Usar o Desbloqueio facial" + "Usar o bloqueio através do rosto ou de ecrã" "Utilize o rosto para continuar" "Utilize o rosto ou o bloqueio de ecrã para continuar" @@ -1187,13 +1187,13 @@ "Enviar com %1$s" "Enviar" "Selecione uma app Página inicial" - "Utilizar %1$s como Página inicial" + "Usar %1$s como Página inicial" "Capturar imagem" "Capturar imagem com" "Capturar imagem com o %1$s" "Capturar imagem" - "Utilizar por predefinição para esta ação." - "Utilizar outra app" + "Usar por predefinição para esta ação." + "Usar outra app" "Limpar a predefinição nas Definições do Sistema > Apps > Transferidas." "Escolha uma ação" "Escolher uma app para o dispositivo USB" @@ -1696,7 +1696,7 @@ "Editar atalhos" "Concluído" "Desativar atalho" - "Utilizar atalho" + "Usar atalho" "Inversão de cores" "Correção da cor" "Modo para uma mão" @@ -2147,8 +2147,8 @@ "Sem apps pessoais" "Abrir a app %s no seu perfil pessoal?" "Abrir a app %s no seu perfil de trabalho?" - "Utilizar navegador pessoal" - "Utilizar navegador de trabalho" + "Usar navegador pessoal" + "Usar navegador de trabalho" "PIN para desbloqueio de rede do cartão SIM" "PIN para desbloqueio do subconjunto da rede do cartão SIM" "PIN para desbloqueio empresarial do cartão SIM" diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index b4862a4ab8eb..baac4a3153f9 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -1853,8 +1853,8 @@ "Обновлено администратором" "Удалено администратором" "ОК" - "В режиме энергосбережения включается тёмная тема, ограничиваются или отключаются фоновые процессы, а также некоторые визуальные эффекты, функции и сетевые подключения." - "В режиме энергосбережения включается тёмная тема, ограничиваются или отключаются фоновые процессы, а также некоторые визуальные эффекты, функции и сетевые подключения." + "В режиме энергосбережения включается тёмная тема, ограничиваются или отключаются фоновые процессы, а также некоторые визуальные эффекты, часть функций и сетевых подключений." + "В режиме энергосбережения включается тёмная тема, ограничиваются или отключаются фоновые процессы, а также некоторые визуальные эффекты, часть функций и сетевых подключений." "В режиме экономии трафика фоновая передача данных для некоторых приложений отключена. Приложение, которым вы пользуетесь, может получать и отправлять данные, но реже, чем обычно. Например, изображения могут не загружаться, пока вы не нажмете на них." "Включить экономию трафика?" "Включить" diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index 5400d1a575ec..b4a954a7fecc 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -1359,7 +1359,7 @@ "Pripojené zariadenie sa nabíja. Ďalšie možností získate klepnutím." "Bolo zistené analógové zvukové príslušenstvo" "Pripojené zariadenie nie je kompatibilné s týmto telefónom. Ďalšie informácie zobrazíte klepnutím." - "Ladenie cez USB pripojené" + "Ladenie cez USB je pripojené" "Klepnutím vypnite ladenie cez USB" "Vyberte, ak chcete zakázať ladenie cez USB." "Bezdrôtové ladenie je pripojené" diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index ae410a316cbb..68aab13daa0f 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -299,7 +299,7 @@ "Byt till jobbprofilen" "Kontakter" "få tillgång till dina kontakter" - "Plats" + "plats" "komma åt enhetens platsuppgifter" "Kalender" "få tillgång till din kalender" -- cgit v1.2.3 From caa4b3d670ba6d7854dfae55afb6f0f093cad200 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Wed, 8 Mar 2023 22:14:08 -0800 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: If7f17d8c4bad33dd7ab1e7b96b75b5b5ac032120 --- core/res/res/values-mcc334-mnc020-th/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/res/res/values-mcc334-mnc020-th/strings.xml b/core/res/res/values-mcc334-mnc020-th/strings.xml index e4e62f0a0417..dfd73abd82e4 100644 --- a/core/res/res/values-mcc334-mnc020-th/strings.xml +++ b/core/res/res/values-mcc334-mnc020-th/strings.xml @@ -20,7 +20,7 @@ - "การตรวจสอบสิทธิ์ล้มเหลว -29-" + "การตรวจสอบสิทธิ์ไม่สำเร็จ -29-" "ไม่ได้สมัครใช้บริการ -33-" "ไม่อนุญาตการเชื่อมต่อ PDN หลายรายการสำหรับ APN ที่กำหนด -55-" -- cgit v1.2.3 From 115e12b243a9f6449718c1d83e90ddbaede1afd7 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Thu, 9 Mar 2023 00:23:07 -0800 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: Idb82453b48b4dc59816723591985428340502972 --- packages/SystemUI/res/values-ar/strings.xml | 4 ++-- packages/SystemUI/res/values-ca/strings.xml | 2 +- packages/SystemUI/res/values-es/strings.xml | 4 ++-- packages/SystemUI/res/values-fr/strings.xml | 2 +- packages/SystemUI/res/values-hr/strings.xml | 2 +- packages/SystemUI/res/values-it/strings.xml | 2 +- packages/SystemUI/res/values-iw/strings.xml | 2 +- packages/SystemUI/res/values-ko/strings.xml | 2 +- packages/SystemUI/res/values-pt-rPT/strings.xml | 6 +++--- packages/SystemUI/res/values-te/strings.xml | 2 +- packages/SystemUI/res/values-uk/strings.xml | 2 +- 11 files changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index 8d6feefd76cd..cbaaac67695f 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -227,8 +227,8 @@ "التدوير التلقائي" "التدوير التلقائي للشاشة" "الموقع الجغرافي" - "الوصول إلى الكاميرا" - "الوصول إلى الميكروفون" + "الكاميرا" + "الميكروفون" "متاح" "محظور" "جهاز الوسائط" diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index 89f6a1a194b1..46c8c2a01897 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -649,7 +649,7 @@ "suprimir el mosaic" "afegir un mosaic al final" "Mou el mosaic" - "Afegeix un mosaic" + "Afegeix una icona" "Mou a la posició %1$d" "Afegeix a la posició %1$d" "Posició %1$d" diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 9e77bd05f24f..a4acbb6f86d6 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -51,8 +51,8 @@ "La huella digital de tu clave RSA es:\n%1$s" "Permitir siempre desde este ordenador" "Permitir" - "Depuración USB no permitida" - "El usuario con el que se ha iniciado sesión en este dispositivo no puede activar la depuración USB. Para utilizar esta función, inicia sesión con la cuenta de usuario principal." + "Depuración por USB no permitida" + "El usuario con el que se ha iniciado sesión en este dispositivo no puede activar la depuración por USB. Para utilizar esta función, inicia sesión con la cuenta de usuario principal." "¿Quieres cambiar el idioma del sistema a %1$s?" "Otro dispositivo ha solicitado un cambio en el idioma del sistema" "Cambiar idioma" diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index d86888fe5771..3165c70b960f 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -835,7 +835,7 @@ "Introuvable" "Commande indisponible" "Impossible d\'accéder à \"%1$s\". Vérifiez l\'application %2$s pour vous assurer que la commande est toujours disponible et que les paramètres de l\'application n\'ont pas changé." - "Ouvrir l\'application" + "Ouvrir l\'appli" "Impossible de charger l\'état" "Erreur. Veuillez réessayer." "Ajouter des commandes" diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index e34a08e624af..e5660c59cdc2 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -835,7 +835,7 @@ "Nije pronađeno" "Kontrola nije dostupna" "Nije moguće pristupiti uređaju: %1$s. U aplikaciji %2$s provjerite je li kontrola i dalje dostupna te potvrdite da se postavke aplikacije nisu promijenile." - "Otvori apl." + "Otvori aplikaciju" "Status se ne može učitati" "Pogreška, pokušajte ponovo" "Dodaj kontrole" diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index 03ea51f43065..2308b4216ce5 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -912,7 +912,7 @@ "Telefonata in corso" "Dati mobili" "%1$s/%2$s" - "Connessione attiva" + "Connessa" "Nessuna connessione dati mobili automatica" "Nessuna connessione" "Nessun\'altra rete disponibile" diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index 1ac0604821b8..53d61fe845f3 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -835,7 +835,7 @@ "לא נמצא" "הפקד לא זמין" "לא ניתן להתחבר אל %1$s. יש לבדוק את האפליקציה %2$s כדי לוודא שהפקד עדיין זמין ושהגדרות האפליקציה לא השתנו." - "לפתיחת האפליקציה" + "פתיחת האפליקציה" "לא ניתן לטעון את הסטטוס" "שגיאה, יש לנסות שוב" "הוספת פקדים" diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index b73ab22730b8..078d2f50eee5 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -833,7 +833,7 @@ "통계를 로드할 수 없음" "오류. 다시 시도하세요." "컨트롤 추가" - "컨트롤 수정" + "제어 설정 수정" "출력 추가" "그룹" "기기 1대 선택됨" diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index 80cde7501ca5..4520f0a26088 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -139,8 +139,8 @@ "Desbloqueio com a face. Prima o ícone de desb. p/ continuar." "Autenticado" "Usar PIN" - "Utilizar padrão" - "Utilizar palavra-passe" + "Usar padrão" + "Usar palavra-passe" "PIN incorreto." "Padrão incorreto." "Palavra-passe incorreta." @@ -670,7 +670,7 @@ "Ecrã de bloqueio" "Telem. deslig. devido ao calor" "O seu telemóvel já está a funcionar normalmente.\nToque para obter mais informações." - "O telemóvel estava muito quente, por isso desligou-se para arrefecer. Agora funciona normalmente.\n\nO telemóvel pode sobreaquecer se:\n • Utilizar aplicações que utilizam mais recursos (jogos, vídeo ou aplicações de navegação)\n • Transferir ou carregar ficheiros grandes\n • Utilizar em altas temperaturas" + "O telemóvel estava muito quente, por isso desligou-se para arrefecer. Agora funciona normalmente.\n\nO telemóvel pode sobreaquecer se:\n • Usar aplicações que utilizam mais recursos (jogos, vídeo ou aplicações de navegação)\n • Transferir ou carregar ficheiros grandes\n • Usar em altas temperaturas" "Veja os passos de manutenção" "O telemóvel está a aquecer" "Algumas funcionalidades são limitadas enquanto o telemóvel arrefece.\nToque para obter mais informações." diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index cdd0d518e199..a3fa1664d157 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -829,7 +829,7 @@ "కనుగొనబడలేదు" "కంట్రోల్ అందుబాటులో లేదు" "%1$sను యాక్సెస్ చేయడం సాధ్యపడలేదు. %2$s యాప్‌ను చెక్ చేసి, కంట్రోల్ ఇప్పటికీ అందుబాటులో ఉందని, యాప్ సెట్టింగ్‌లు మారలేదని నిర్ధారించుకోండి." - "యాప్‌ను తెరువు" + "యాప్‌‌ను తెరవండి" "స్టేటస్ లోడ్ చేయడం సాధ్యపడలేదు" "ఎర్రర్, మళ్లీ ప్రయత్నించండి" "కంట్రోల్స్‌ను జోడించండి" diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index 53b09091a5ce..6dfccc0d3888 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -222,7 +222,7 @@ "Джерело сигналу" "Слухові апарати" "Увімкнення…" - "Автоматичне обертання" + "Автообертання" "Автоматично обертати екран" "Геодані" "Доступ до камери" -- cgit v1.2.3 From 2178216b98bf9865edee198f45192f0b883624ab Mon Sep 17 00:00:00 2001 From: Lucas Lin Date: Fri, 3 Mar 2023 08:13:50 +0000 Subject: Sanitize VPN label to prevent HTML injection This commit will try to sanitize the content of VpnDialog. This commit creates a function which will try to sanitize the VPN label, if the sanitized VPN label is different from the original one, which means the VPN label might contain HTML tag or the VPN label violates the words restriction(may contain some wording which will mislead the user). For this kind of case, show the package name instead of the VPN label to prevent misleading the user. The malicious VPN app might be able to add a large number of line breaks with HTML in order to hide the system-displayed text from the user in the connection request dialog. Thus, sanitizing the content of the dialog is needed. Bug: 204554636 Test: N/A Change-Id: I8eb890fd2e5797d8d6ab5b12f9c628bc9616081d Merged-In: I8eb890fd2e5797d8d6ab5b12f9c628bc9616081d --- packages/VpnDialogs/res/values/strings.xml | 28 ++++++++++++ .../src/com/android/vpndialogs/ConfirmDialog.java | 53 ++++++++++++++++++++-- 2 files changed, 76 insertions(+), 5 deletions(-) diff --git a/packages/VpnDialogs/res/values/strings.xml b/packages/VpnDialogs/res/values/strings.xml index 443a9bc33b90..b4166f0bedfd 100644 --- a/packages/VpnDialogs/res/values/strings.xml +++ b/packages/VpnDialogs/res/values/strings.xml @@ -89,4 +89,32 @@ without any consequences. [CHAR LIMIT=20] --> Dismiss + + + %1$s… ( + %2$s) + + + + + %1$s ( + %2$s) + diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java index e66f2cc17a7f..a1fbffe50af7 100644 --- a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java +++ b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java @@ -44,12 +44,18 @@ public class ConfirmDialog extends AlertActivity implements DialogInterface.OnClickListener, ImageGetter { private static final String TAG = "VpnConfirm"; + // Usually the label represents the app name, 150 code points might be enough to display the app + // name, and 150 code points won't cover the warning message from VpnDialog. + static final int MAX_VPN_LABEL_LENGTH = 150; + @VpnManager.VpnType private final int mVpnType; private String mPackage; private IConnectivityManager mService; + private View mView; + public ConfirmDialog() { this(VpnManager.TYPE_VPN_SERVICE); } @@ -58,6 +64,42 @@ public class ConfirmDialog extends AlertActivity mVpnType = vpnType; } + /** + * This function will use the string resource to combine the VPN label and the package name. + * + * If the VPN label violates the length restriction, the first 30 code points of VPN label and + * the package name will be returned. Or return the VPN label and the package name directly if + * the VPN label doesn't violate the length restriction. + * + * The result will be something like, + * - ThisIsAVeryLongVpnAppNameWhich... (com.vpn.app) + * if the VPN label violates the length restriction. + * or + * - VpnLabelWith<br>HtmlTag (com.vpn.app) + * if the VPN label doesn't violate the length restriction. + * + */ + private String getSimplifiedLabel(String vpnLabel, String packageName) { + if (vpnLabel.codePointCount(0, vpnLabel.length()) > 30) { + return getString(R.string.sanitized_vpn_label_with_ellipsis, + vpnLabel.substring(0, vpnLabel.offsetByCodePoints(0, 30)), + packageName); + } + + return getString(R.string.sanitized_vpn_label, vpnLabel, packageName); + } + + protected String getSanitizedVpnLabel(String vpnLabel, String packageName) { + final String sanitizedVpnLabel = Html.escapeHtml(vpnLabel); + final boolean exceedMaxVpnLabelLength = sanitizedVpnLabel.codePointCount(0, + sanitizedVpnLabel.length()) > MAX_VPN_LABEL_LENGTH; + if (exceedMaxVpnLabelLength || !vpnLabel.equals(sanitizedVpnLabel)) { + return getSimplifiedLabel(sanitizedVpnLabel, packageName); + } + + return sanitizedVpnLabel; + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -80,15 +122,16 @@ public class ConfirmDialog extends AlertActivity finish(); return; } - View view = View.inflate(this, R.layout.confirm, null); - ((TextView) view.findViewById(R.id.warning)).setText( - Html.fromHtml(getString(R.string.warning, getVpnLabel()), - this, null /* tagHandler */)); + mView = View.inflate(this, R.layout.confirm, null); + ((TextView) mView.findViewById(R.id.warning)).setText( + Html.fromHtml(getString(R.string.warning, getSanitizedVpnLabel( + getVpnLabel().toString(), mPackage)), + this /* imageGetter */, null /* tagHandler */)); mAlertParams.mTitle = getText(R.string.prompt); mAlertParams.mPositiveButtonText = getText(android.R.string.ok); mAlertParams.mPositiveButtonListener = this; mAlertParams.mNegativeButtonText = getText(android.R.string.cancel); - mAlertParams.mView = view; + mAlertParams.mView = mView; setupAlert(); getWindow().setCloseOnTouchOutside(false); -- cgit v1.2.3 From 733089e71ca4b98417586e593a1fb0e50f3a5c61 Mon Sep 17 00:00:00 2001 From: Valentin Iftime Date: Wed, 15 Feb 2023 20:39:44 +0100 Subject: [DO NOT MERGE] Wait for preloading images to complete before inflating notifications NotificationContentInflater waits on SysUiBg thread for images to load, with a timeout of 1000ms. Test: 1. Build a test app that posts MessagingStyle notifications with a huge image (8k+) set as data Uri. 2. SystemUi should not ANR 3. adb logcat | grep NotificationInlineImageCache - shows timeout/cancellation logs Bug: 252766417 Bug: 223859644 Change-Id: I341db60223214cf2282b5c0270e343e1ce95fa01 (cherry picked from commit 195043f40e46ddcd2fe534a9dac344792d39d91c) Merged-In: I341db60223214cf2282b5c0270e343e1ce95fa01 --- .../row/NotificationContentInflater.java | 16 +++++- .../row/NotificationInlineImageCache.java | 24 ++++++--- .../row/NotificationInlineImageResolver.java | 57 +++++++++++++++++++--- 3 files changed, 84 insertions(+), 13 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java index 582e3e5b6c34..208cf466d0dc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java @@ -429,6 +429,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder CancellationSignal cancellationSignal = new CancellationSignal(); cancellationSignal.setOnCancelListener( () -> runningInflations.values().forEach(CancellationSignal::cancel)); + return cancellationSignal; } @@ -694,6 +695,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder public static class AsyncInflationTask extends AsyncTask implements InflationCallback, InflationTask { + private static final long IMG_PRELOAD_TIMEOUT_MS = 1000L; private final NotificationEntry mEntry; private final Context mContext; private final boolean mInflateSynchronously; @@ -776,10 +778,16 @@ public class NotificationContentInflater implements NotificationRowContentBinder InflationProgress inflationProgress = createRemoteViews(mReInflateFlags, recoveredBuilder, mIsLowPriority, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, packageContext); - return inflateSmartReplyViews(inflationProgress, mReInflateFlags, mEntry, + + InflationProgress result = inflateSmartReplyViews(inflationProgress, mReInflateFlags, mEntry, mRow.getContext(), packageContext, mRow.getHeadsUpManager(), mSmartReplyConstants, mSmartReplyController, mRow.getExistingSmartRepliesAndActions()); + + // wait for image resolver to finish preloading + mRow.getImageResolver().waitForPreloadedImages(IMG_PRELOAD_TIMEOUT_MS); + + return result; } catch (Exception e) { mError = e; return null; @@ -814,6 +822,9 @@ public class NotificationContentInflater implements NotificationRowContentBinder mCallback.handleInflationException(mRow.getEntry(), new InflationException("Couldn't inflate contentViews" + e)); } + + // Cancel any image loading tasks, not useful any more + mRow.getImageResolver().cancelRunningTasks(); } @Override @@ -840,6 +851,9 @@ public class NotificationContentInflater implements NotificationRowContentBinder // Notify the resolver that the inflation task has finished, // try to purge unnecessary cached entries. mRow.getImageResolver().purgeCache(); + + // Cancel any image loading tasks that have not completed at this point + mRow.getImageResolver().cancelRunningTasks(); } private class RtlEnabledContext extends ContextWrapper { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java index 4b0e2ffd5d7f..6fdc8a3dce0b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java @@ -21,10 +21,12 @@ import android.net.Uri; import android.os.AsyncTask; import android.util.Log; -import java.io.IOException; import java.util.Set; +import java.util.concurrent.CancellationException; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; /** * A cache for inline images of image messages. @@ -57,12 +59,13 @@ public class NotificationInlineImageCache implements NotificationInlineImageReso } @Override - public Drawable get(Uri uri) { + public Drawable get(Uri uri, long timeoutMs) { Drawable result = null; try { - result = mCache.get(uri).get(); - } catch (InterruptedException | ExecutionException ex) { - Log.d(TAG, "get: Failed get image from " + uri); + result = mCache.get(uri).get(timeoutMs, TimeUnit.MILLISECONDS); + } catch (InterruptedException | ExecutionException + | TimeoutException | CancellationException ex) { + Log.d(TAG, "get: Failed get image from " + uri + " " + ex); } return result; } @@ -73,6 +76,15 @@ public class NotificationInlineImageCache implements NotificationInlineImageReso mCache.entrySet().removeIf(entry -> !wantedSet.contains(entry.getKey())); } + @Override + public void cancelRunningTasks() { + mCache.forEach((key, value) -> { + if (value.getStatus() != AsyncTask.Status.FINISHED) { + value.cancel(true); + } + }); + } + private static class PreloadImageTask extends AsyncTask { private final NotificationInlineImageResolver mResolver; @@ -87,7 +99,7 @@ public class NotificationInlineImageCache implements NotificationInlineImageReso try { drawable = mResolver.resolveImage(target); - } catch (IOException | SecurityException ex) { + } catch (Exception ex) { Log.d(TAG, "PreloadImageTask: Resolve failed from " + target, ex); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java index 7bd192d850c1..b240855fe6e9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java @@ -26,6 +26,7 @@ import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Bundle; import android.os.Parcelable; +import android.os.SystemClock; import android.util.Log; import com.android.internal.R; @@ -49,6 +50,9 @@ import java.util.Set; public class NotificationInlineImageResolver implements ImageResolver { private static final String TAG = NotificationInlineImageResolver.class.getSimpleName(); + // Timeout for loading images from ImageCache when calling from UI thread + private static final long MAX_UI_THREAD_TIMEOUT_MS = 100L; + private final Context mContext; private final ImageCache mImageCache; private Set mWantedUriSet; @@ -132,16 +136,20 @@ public class NotificationInlineImageResolver implements ImageResolver { return image; } + /** + * Loads an image from the Uri. + * This method is synchronous and is usually called from the Main thread. + * It will time-out after MAX_UI_THREAD_TIMEOUT_MS. + * + * @param uri Uri of the target image. + * @return drawable of the image, null if loading failed/timeout + */ @Override public Drawable loadImage(Uri uri) { Drawable result = null; try { if (hasCache()) { - // if the uri isn't currently cached, try caching it first - if (!mImageCache.hasEntry(uri)) { - mImageCache.preload((uri)); - } - result = mImageCache.get(uri); + result = loadImageFromCache(uri, MAX_UI_THREAD_TIMEOUT_MS); } else { result = resolveImage(uri); } @@ -151,6 +159,14 @@ public class NotificationInlineImageResolver implements ImageResolver { return result; } + private Drawable loadImageFromCache(Uri uri, long timeoutMs) { + // if the uri isn't currently cached, try caching it first + if (!mImageCache.hasEntry(uri)) { + mImageCache.preload((uri)); + } + return mImageCache.get(uri, timeoutMs); + } + /** * Resolve the message list from specified notification and * refresh internal cache according to the result. @@ -222,6 +238,30 @@ public class NotificationInlineImageResolver implements ImageResolver { return mWantedUriSet; } + /** + * Wait for a maximum timeout for images to finish preloading + * @param timeoutMs total timeout time + */ + void waitForPreloadedImages(long timeoutMs) { + if (!hasCache()) { + return; + } + Set preloadedUris = getWantedUriSet(); + if (preloadedUris != null) { + // Decrement remaining timeout after each image check + long endTimeMs = SystemClock.elapsedRealtime() + timeoutMs; + preloadedUris.forEach( + uri -> loadImageFromCache(uri, endTimeMs - SystemClock.elapsedRealtime())); + } + } + + void cancelRunningTasks() { + if (!hasCache()) { + return; + } + mImageCache.cancelRunningTasks(); + } + /** * A interface for internal cache implementation of this resolver. */ @@ -231,7 +271,7 @@ public class NotificationInlineImageResolver implements ImageResolver { * @param uri The uri of the image. * @return Drawable of the image. */ - Drawable get(Uri uri); + Drawable get(Uri uri, long timeoutMs); /** * Set the image resolver that actually resolves image from specified uri. @@ -256,6 +296,11 @@ public class NotificationInlineImageResolver implements ImageResolver { * Purge unnecessary entries in the cache. */ void purge(); + + /** + * Cancel all unfinished image loading tasks + */ + void cancelRunningTasks(); } } -- cgit v1.2.3 From 33f83bf7a59b0421d8f9ba16b2dac7ded23fe854 Mon Sep 17 00:00:00 2001 From: Azhara Assanova Date: Thu, 9 Mar 2023 13:34:31 +0000 Subject: Add Toast#show() specific javadoc The added javadoc comment already exists in Toast's class level javadoc on line 75. Bug: 265306493 Test: None Change-Id: I5692db36461dae1f79a72eb2bb15d8379bd24a84 --- core/java/android/widget/Toast.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java index dbf3570b1d24..3c8288cad52f 100644 --- a/core/java/android/widget/Toast.java +++ b/core/java/android/widget/Toast.java @@ -188,6 +188,9 @@ public class Toast { /** * Show the view for the specified duration. + * + *

Note that toasts being sent from the background are rate limited, so avoid sending such + * toasts in quick succession. */ public void show() { if (Compatibility.isChangeEnabled(CHANGE_TEXT_TOASTS_IN_THE_SYSTEM)) { -- cgit v1.2.3 From 80096c5dc09a36de3661b8f33ac7b64dcd19aace Mon Sep 17 00:00:00 2001 From: Kohsuke Yatoh Date: Fri, 24 Feb 2023 04:11:22 +0000 Subject: Ignore testShowHideInSameFrame on T ImeOpenCloseStressTest#testShowHideInSameFrame() is very flaky on T. It's passing on T QPR, and rewritten in U (also passing). This could be a real bug on Android side as the same test is passing on T QPR. However, let's just ignore this test as T (not QPR) does not accept general fix now. I7a66fd84e4094be249714c2597706ee25938adbe is an unrelated change that's in T QPR to avoid merging this CL there. Bug: 268556567 Test: m InputMethodStressTest Change-Id: Ibcd01e0d722f5ebb846e75f72ba4e770f7030d32 Merged-In: I7a66fd84e4094be249714c2597706ee25938adbe --- .../src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java index 8419276f4406..14c5cb4bb1a9 100644 --- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java +++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java @@ -42,6 +42,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.platform.app.InstrumentationRegistry; import org.junit.Before; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -123,6 +124,7 @@ public final class ImeOpenCloseStressTest { } } + @Ignore("b/268556567") @Test public void testShowHideInSameFrame() { TestActivity activity = TestActivity.start(); -- cgit v1.2.3 From c551c33b3fe0c70378381c3321217aab703e49a4 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Tue, 14 Mar 2023 18:43:00 -0700 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: Ibabc182f32b6e49365086836cb50828ebe9223f2 --- packages/SettingsLib/res/values-ar/arrays.xml | 2 +- packages/SettingsLib/res/values-es/strings.xml | 2 +- packages/SettingsLib/res/values-pt-rPT/arrays.xml | 18 +++++++------- packages/SettingsLib/res/values-pt-rPT/strings.xml | 28 +++++++++++----------- packages/SettingsLib/res/values-ro/strings.xml | 2 +- packages/SettingsLib/res/values-sw/strings.xml | 2 +- 6 files changed, 27 insertions(+), 27 deletions(-) diff --git a/packages/SettingsLib/res/values-ar/arrays.xml b/packages/SettingsLib/res/values-ar/arrays.xml index eb4be38da93e..09808f6d1caf 100644 --- a/packages/SettingsLib/res/values-ar/arrays.xml +++ b/packages/SettingsLib/res/values-ar/arrays.xml @@ -60,7 +60,7 @@ "غير مفعّل" - "تمّ تفعيل التصفية" + "تمّ تفعيل الفلترة" "مفعّل" diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index eecfe2b5fb66..c101758e49db 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -475,7 +475,7 @@ "Es posible que el dispositivo se apague pronto (%1$s)" "%1$s - %2$s" "%1$s hasta la carga completa" - "%1$s: %2$s hasta la carga completa" + "%1$s - %2$s hasta la carga completa" "%1$s - Carga limitada temporalmente" "Desconocido" "Cargando" diff --git a/packages/SettingsLib/res/values-pt-rPT/arrays.xml b/packages/SettingsLib/res/values-pt-rPT/arrays.xml index e1623e38eaa6..a72ec79a0997 100644 --- a/packages/SettingsLib/res/values-pt-rPT/arrays.xml +++ b/packages/SettingsLib/res/values-pt-rPT/arrays.xml @@ -55,7 +55,7 @@ "Nunca utilizar a verificação HDCP" - "Utilizar a verificação HDCP para conteúdo DRM apenas" + "Usar a verificação HDCP para conteúdo DRM apenas" "Usar sempre a verificação HDCP" @@ -86,7 +86,7 @@ "map14" - "Utilizar seleção do sistema (predefinido)" + "Usar seleção do sistema (predefinido)" "SBC" "AAC" "Áudio Qualcomm® aptX™" @@ -94,7 +94,7 @@ "LDAC" - "Utilizar seleção do sistema (predefinido)" + "Usar seleção do sistema (predefinido)" "SBC" "AAC" "Áudio Qualcomm® aptX™" @@ -102,38 +102,38 @@ "LDAC" - "Utilizar seleção do sistema (predefinido)" + "Usar seleção do sistema (predefinido)" "44,1 kHz" "48,0 kHz" "88,2 kHz" "96,0 kHz" - "Utilizar seleção do sistema (predefinido)" + "Usar seleção do sistema (predefinido)" "44,1 kHz" "48,0 kHz" "88,2 kHz" "96,0 kHz" - "Utilizar seleção do sistema (predefinido)" + "Usar seleção do sistema (predefinido)" "16 bits/amostra" "24 bits/amostra" "32 bits/amostra" - "Utilizar seleção do sistema (predefinido)" + "Usar seleção do sistema (predefinido)" "16 bits/amostra" "24 bits/amostra" "32 bits/amostra" - "Utilizar seleção do sistema (predefinido)" + "Usar seleção do sistema (predefinido)" "Mono" "Estéreo" - "Utilizar seleção do sistema (predefinido)" + "Usar seleção do sistema (predefinido)" "Mono" "Estéreo" diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index 6cb55e87dcb7..5068b844c58b 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml @@ -118,7 +118,7 @@ "Dispositivo de entrada" "Acesso à internet" "Partilha de contactos" - "Utilizar para a partilha de contactos" + "Usar para a partilha de contactos" "Partilha da ligação à internet" "Mensagens de texto" "Acesso ao SIM" @@ -137,15 +137,15 @@ "Ligado a um dispositivo de entrada" "Lig. ao disposit. p/ acesso à Internet" "A partilhar lig. à Internet local c/ dispos." - "Utilizar para acesso à Internet" - "Utilizar para o mapa" - "Utilizar para acesso ao SIM" - "Utilizar para áudio de multimédia" - "Utilizar para áudio do telefone" - "Utilizar para transferência de ficheiros" - "Utilizar para entrada" - "Utilizar para aparelhos auditivos" - "Utilizar para LE_AUDIO" + "Usar para acesso à Internet" + "Usar para o mapa" + "Usar para acesso ao SIM" + "Usar para áudio de multimédia" + "Usar para áudio do telefone" + "Usar para transferência de ficheiros" + "Usar para entrada" + "Usar para aparelhos auditivos" + "Usar para LE_AUDIO" "Sincr." "SINCRONIZAR" "Cancelar" @@ -441,7 +441,7 @@ "Definir implementação WebView" "Esta opção já não é válida. Tente novamente." "Modo de cor da imagem" - "Utilizar sRGB" + "Usar sRGB" "Desativado" "Monocromacia" "Deuteranomalia (vermelho-verde)" @@ -511,9 +511,9 @@ "Próximo" "Palavra-passe obrigatória" "Métodos de introdução activos" - "Utilizar idiomas do sistema" + "Usar idiomas do sistema" "Falha ao abrir as definições para %1$s" - "Este método de introdução pode permitir a recolha de todo o texto que digitar, incluindo dados pessoais como, por exemplo, palavras-passe e números de cartões de crédito. Decorre da aplicação %1$s. Utilizar este método de introdução?" + "Este método de introdução pode permitir a recolha de todo o texto que digitar, incluindo dados pessoais como, por exemplo, palavras-passe e números de cartões de crédito. Decorre da aplicação %1$s. Usar este método de introdução?" "Nota: após reiniciar, só é possível iniciar esta aplicação quando o telemóvel for desbloqueado." "Estado do registo IMS" "Registado" @@ -649,7 +649,7 @@ "Teclado físico" "Escolha um esquema de teclado" "Predefinição" - "Ative o ecrã" + "Ativação do ecrã" "Permitir a ativação do ecrã" "Permita que uma app ative o ecrã. Se a autorização for concedida, a app pode ativar o ecrã em qualquer altura sem a sua intenção explícita." "Interromper a transmissão da app %1$s?" diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index acf646594cd1..b3ef9434c2e7 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -523,7 +523,7 @@ "{count,plural, =0{Niciun dispozitiv conectat}=1{Un dispozitiv conectat}few{# dispozitive conectate}other{# de dispozitive conectate}}" "Mai mult timp." "Mai puțin timp." - "Anulați" + "Anulează" "OK" "Gata" "Alarme și mementouri" diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index 9e79c0f78927..9096ce518455 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -525,7 +525,7 @@ "Muda kidogo." "Ghairi" "Sawa" - "Imemaliza" + "Nimemaliza" "Ving\'ora na vikumbusho" "Ruhusu iweke kengele na vikumbusho" "Kengele na vikumbusho" -- cgit v1.2.3 From 3ed3eb209f764360873882dc27946a90aa9d4ddb Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Tue, 14 Mar 2023 20:06:52 -0700 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I25060cc85d1b1bd33de0c0ae84b8b43a5479a03b --- core/res/res/values-ca/strings.xml | 2 +- core/res/res/values-da/strings.xml | 2 +- core/res/res/values-fa/strings.xml | 2 +- core/res/res/values-it/strings.xml | 2 +- core/res/res/values-ko/strings.xml | 4 ++-- core/res/res/values-uk/strings.xml | 4 ++-- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index 94d9686f8816..353bff0cbaef 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -1900,7 +1900,7 @@ "Android" "Port perifèric USB" "Més opcions" - "Tanca el menú addicional" + "Tanca el menú de desbordament" "Maximitza" "Tanca" "%1$s: %2$s" diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index 537072ca0a22..e41d3bf09cd9 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -1139,7 +1139,7 @@ "Markér tekst" "Fortryd" "Annuller fortryd" - "AutoFyld" + "Autofyld" "Tekstmarkering" "Føj til ordbog" "Slet" diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index 6a223ea71823..f1a387d6b7a4 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -2029,7 +2029,7 @@ "سیم‌کارت %d مجوز لازم را ندارد" "سیم‌کارت %d مجاز نیست" "سیم‌کارت %d مجاز نیست" - "پنجره بازشو" + "پنجره بالاپر" "‎+ %1$d‎" "نسخه برنامه تنزل داده شده است یا با این میان‌بر سازگار نیست" "نمی‌توان میان‌بر را بازیابی کرد زیرا برنامه از پشتیبان‌گیری و بازیابی پشتیبانی نمی‌کند" diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index 495e8ebb8945..ec78dfde6dc0 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -1967,7 +1967,7 @@ "Informazioni app" "−%1$s" "Avvio della demo…" - "Ripristino del dispositivo…" + "Reset del dispositivo…" "Widget %1$s disattivato" "Audioconferenza" "Descrizione comando" diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index 4718ee73ef80..98b5e6f28ed1 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -1346,7 +1346,7 @@ "권한 필요 없음" "비용이 부과될 수 있습니다." "확인" - "이 기기를 USB로 충전 중" + "이 기기를 USB로 충전 중." "USB를 통해 연결된 기기 충전" "USB 파일 전송 사용 설정됨" "USB를 통해 PTP 사용 설정됨" @@ -1357,7 +1357,7 @@ "연결된 기기를 충전합니다. 옵션을 더 보려면 탭하세요." "아날로그 오디오 액세서리가 감지됨" "연결된 기기가 이 휴대전화와 호환되지 않습니다. 자세히 알아보려면 탭하세요." - "USB 디버깅 연결됨" + "USB 디버깅 연결됨." "USB 디버깅을 사용 중지하려면 탭하세요." "USB 디버깅을 사용하지 않으려면 선택합니다." "무선 디버깅 연결됨" diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index 2258ab728418..33abd638bf45 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -226,9 +226,9 @@ "Оновлення системи Android" "Підготовка до оновлення…" "Обробка пакета оновлення…" - "Перезавантаження…" + "Перезапуск…" "Скидання налаштувань" - "Перезавантаження…" + "Перезапуск…" "Вимкнення..." "Ваш пристрій буде вимкнено." "Пристрій Android TV буде вимкнено." -- cgit v1.2.3 From d4e8230bd53d3d03bf8fce6fe6550d8e396f17a7 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Tue, 14 Mar 2023 21:56:36 -0700 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: Ie1b2d13e02cd52c8830303597a7e67d7d3b7792d --- packages/SystemUI/res/values-bg/strings.xml | 2 +- packages/SystemUI/res/values-ca/strings.xml | 2 +- packages/SystemUI/res/values-fa/strings.xml | 2 +- packages/SystemUI/res/values-pt-rPT/strings.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index 3837fe48ada7..2927ddf4c295 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -223,7 +223,7 @@ "Авт. ориентация" "Автоматично завъртане на екрана" "Местоположение" - "Камера: достъп" + "Достъп до камерата" "Достъп до микрофона" "Налице" "Блокирано" diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index 46c8c2a01897..b4b4bde1e3cc 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -225,7 +225,7 @@ "Gira la pantalla automàticament" "Ubicació" "Accés a la càmera" - "Accés al micròfon" + "Accés al micro" "Disponible" "Bloquejat" "Dispositiu multimèdia" diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index 270954ed3a6f..c4c87bed0ab3 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -937,7 +937,7 @@ "تمام" "کپی شد" "از %1$s" - "رد شدن نوشتار کپی‌شده" + "بستن نوشتار کپی‌شده" "ویرایش نوشتار کپی‌شده" "ویرایش تصویر کپی‌شده" "ارسال به دستگاهی در اطراف" diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index 4520f0a26088..11b5a1dba28e 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -340,7 +340,7 @@ "%2$s • A carregar • Carga completa em %1$s" "%2$s • A carregar rapidamente • Carga completa em %1$s" "%2$s • A carregar lentamente • Carga completa em %1$s" - "%2$s • A carregar na estação de ancoragem • Carga completa em %1$s" + "%2$s • A carregar na estação de carregamento • Carga completa em %1$s" "Mudar utilizador" "Todas as apps e dados desta sessão serão eliminados." "Bem-vindo de volta, convidado!" -- cgit v1.2.3 From a753fefb7f41c813333d2acc2c781eca76554064 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Tue, 14 Mar 2023 22:00:37 -0700 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: Ife7ca68eb84fff16becb90d1c19082b886303d13 --- packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml index b6327e2a48fc..26042408af24 100644 --- a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml @@ -26,7 +26,7 @@ "Cartão inválido." "Carregada" "%s • A carregar sem fios" - "%s • A carregar na estação de ancoragem" + "%s • A carregar na estação de carregamento" "%s • A carregar…" "%s • A carregar rapidamente…" "%s • A carregar lentamente…" -- cgit v1.2.3 From c3db1e4451490ddc7f6033a6ab7d54e71ebda9d8 Mon Sep 17 00:00:00 2001 From: Valentin Iftime Date: Wed, 22 Feb 2023 09:38:55 +0100 Subject: [DO NOT MERGE] Prevent RemoteViews crashing SystemUi Catch canvas drawing exceptions caused by unsuported image sizes. Test: 1. Post a custom view notification with a layout containing an ImageView that references a 5k x 5k image 2. Add an App Widget to the home screen with that has the layout mentioned above as preview/initial layout. Bug: 268193777 Change-Id: Ib3bda769c499b4069b49c566b1b227f98f707a8a Merged-In: Ib3bda769c499b4069b49c566b1b227f98f707a8a --- core/java/android/appwidget/AppWidgetHostView.java | 39 ++++++++++++++++------ .../row/ExpandableNotificationRow.java | 4 ++- .../row/ExpandableNotificationRowController.java | 9 +++-- .../notification/row/NotificationContentView.java | 39 ++++++++++++++++++++++ .../row/NotificationEntryManagerInflationTest.java | 3 +- .../notification/row/NotificationTestHelper.java | 4 ++- 6 files changed, 83 insertions(+), 15 deletions(-) diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java index 8aa27853b462..129dbc1fd355 100644 --- a/core/java/android/appwidget/AppWidgetHostView.java +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -30,6 +30,7 @@ import android.content.pm.LauncherActivityInfo; import android.content.pm.LauncherApps; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; +import android.graphics.Canvas; import android.graphics.Color; import android.graphics.PointF; import android.graphics.Rect; @@ -310,19 +311,26 @@ public class AppWidgetHostView extends FrameLayout { super.onLayout(changed, left, top, right, bottom); } catch (final RuntimeException e) { Log.e(TAG, "Remote provider threw runtime exception, using error view instead.", e); - removeViewInLayout(mView); - View child = getErrorView(); - prepareView(child); - addViewInLayout(child, 0, child.getLayoutParams()); - measureChild(child, MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY)); - child.layout(0, 0, child.getMeasuredWidth() + mPaddingLeft + mPaddingRight, - child.getMeasuredHeight() + mPaddingTop + mPaddingBottom); - mView = child; - mViewMode = VIEW_MODE_ERROR; + handleViewError(); } } + /** + * Remove bad view and replace with error message view + */ + private void handleViewError() { + removeViewInLayout(mView); + View child = getErrorView(); + prepareView(child); + addViewInLayout(child, 0, child.getLayoutParams()); + measureChild(child, MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY)); + child.layout(0, 0, child.getMeasuredWidth() + mPaddingLeft + mPaddingRight, + child.getMeasuredHeight() + mPaddingTop + mPaddingBottom); + mView = child; + mViewMode = VIEW_MODE_ERROR; + } + /** * Provide guidance about the size of this widget to the AppWidgetManager. The widths and * heights should correspond to the full area the AppWidgetHostView is given. Padding added by @@ -932,4 +940,15 @@ public class AppWidgetHostView extends FrameLayout { reapplyLastRemoteViews(); } } + + @Override + protected void dispatchDraw(@NonNull Canvas canvas) { + try { + super.dispatchDraw(canvas); + } catch (Exception e) { + // Catch draw exceptions that may be caused by RemoteViews + Log.e(TAG, "Drawing view failed: " + e); + post(this::handleViewError); + } + } } 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 73bb6cd9ba1c..0007bbde94a2 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 @@ -69,6 +69,7 @@ import android.widget.ImageView; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.ContrastColorUtil; import com.android.internal.widget.CachingIconView; import com.android.internal.widget.CallLayout; @@ -1554,7 +1555,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView PeopleNotificationIdentifier peopleNotificationIdentifier, OnUserInteractionCallback onUserInteractionCallback, Optional bubblesManagerOptional, - NotificationGutsManager gutsManager) { + NotificationGutsManager gutsManager, + IStatusBarService statusBarService) { mEntry = entry; mAppName = appName; if (mMenuRow == null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java index c9fcdac8e45f..b09ea1c6282a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java @@ -25,6 +25,7 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; +import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; @@ -84,6 +85,7 @@ public class ExpandableNotificationRowController implements NodeController { private final boolean mAllowLongPress; private final PeopleNotificationIdentifier mPeopleNotificationIdentifier; private final Optional mBubblesManagerOptional; + private final IStatusBarService mStatusBarService; @Inject public ExpandableNotificationRowController( @@ -109,7 +111,8 @@ public class ExpandableNotificationRowController implements NodeController { FalsingManager falsingManager, FalsingCollector falsingCollector, PeopleNotificationIdentifier peopleNotificationIdentifier, - Optional bubblesManagerOptional) { + Optional bubblesManagerOptional, + IStatusBarService statusBarService) { mView = view; mListContainer = listContainer; mActivatableNotificationViewController = activatableNotificationViewController; @@ -134,6 +137,7 @@ public class ExpandableNotificationRowController implements NodeController { mFalsingCollector = falsingCollector; mPeopleNotificationIdentifier = peopleNotificationIdentifier; mBubblesManagerOptional = bubblesManagerOptional; + mStatusBarService = statusBarService; } /** @@ -160,7 +164,8 @@ public class ExpandableNotificationRowController implements NodeController { mPeopleNotificationIdentifier, mOnUserInteractionCallback, mBubblesManagerOptional, - mNotificationGutsManager + mNotificationGutsManager, + mStatusBarService ); mView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); if (mAllowLongPress) { 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 4f54e4feb21d..8e3e488f53f2 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 @@ -21,10 +21,13 @@ import android.annotation.Nullable; import android.app.Notification; import android.app.PendingIntent; import android.content.Context; +import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Build; +import android.os.RemoteException; import android.provider.Settings; +import android.service.notification.StatusBarNotification; import android.util.ArrayMap; import android.util.AttributeSet; import android.util.Log; @@ -40,6 +43,7 @@ import android.widget.LinearLayout; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dependency; +import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.R; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.statusbar.RemoteInputController; @@ -125,6 +129,8 @@ public class NotificationContentView extends FrameLayout { private RemoteInputController mRemoteInputController; private Runnable mExpandedVisibleListener; private PeopleNotificationIdentifier mPeopleIdentifier; + private IStatusBarService mStatusBarService; + /** * List of listeners for when content views become inactive (i.e. not the showing view). */ @@ -178,6 +184,7 @@ public class NotificationContentView extends FrameLayout { mHybridGroupManager = new HybridGroupManager(getContext()); mSmartReplyConstants = Dependency.get(SmartReplyConstants.class); mSmartReplyController = Dependency.get(SmartReplyController.class); + mStatusBarService = Dependency.get(IStatusBarService.class); initView(); } @@ -1978,4 +1985,36 @@ public class NotificationContentView extends FrameLayout { } return Notification.COLOR_INVALID; } + + @Override + protected void dispatchDraw(Canvas canvas) { + try { + super.dispatchDraw(canvas); + } catch (Exception e) { + // Catch draw exceptions that may be caused by RemoteViews + Log.e(TAG, "Drawing view failed: " + e); + cancelNotification(e); + } + } + + private void cancelNotification(Exception exception) { + try { + setVisibility(GONE); + final StatusBarNotification sbn = mNotificationEntry.getSbn(); + if (mStatusBarService != null) { + // report notification inflation errors back up + // to notification delegates + mStatusBarService.onNotificationError( + sbn.getPackageName(), + sbn.getTag(), + sbn.getId(), + sbn.getUid(), + sbn.getInitialPid(), + exception.getMessage(), + sbn.getUser().getIdentifier()); + } + } catch (RemoteException ex) { + Log.e(TAG, "cancelNotification failed: " + ex); + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java index cea49b71f009..41f83d141e35 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java @@ -265,7 +265,8 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { new FalsingManagerFake(), new FalsingCollectorFake(), mPeopleNotificationIdentifier, - Optional.of(mock(BubblesManager.class)) + Optional.of(mock(BubblesManager.class)), + mock(IStatusBarService.class) )); when(mNotificationRowComponentBuilder.activatableNotificationView(any())) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index 0bb66fc14553..d6fc41aa7fd3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -43,6 +43,7 @@ import android.text.TextUtils; import android.view.LayoutInflater; import android.widget.RemoteViews; +import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.TestableDependency; import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.classifier.FalsingManagerFake; @@ -472,7 +473,8 @@ public class NotificationTestHelper { mPeopleNotificationIdentifier, mock(OnUserInteractionCallback.class), Optional.of(mock(BubblesManager.class)), - mock(NotificationGutsManager.class)); + mock(NotificationGutsManager.class), + mock(IStatusBarService.class)); row.setAboveShelfChangedListener(aboveShelf -> { }); mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags); -- cgit v1.2.3 From 7505c570ff7646894692158dc6f7bda7a1281507 Mon Sep 17 00:00:00 2001 From: Valentin Iftime Date: Wed, 22 Feb 2023 09:38:55 +0100 Subject: [DO NOT MERGE] Prevent RemoteViews crashing SystemUi Catch canvas drawing exceptions caused by unsuported image sizes. Test: 1. Post a custom view notification with a layout containing an ImageView that references a 5k x 5k image 2. Add an App Widget to the home screen with that has the layout mentioned above as preview/initial layout. Bug: 268193777 Change-Id: Ib3bda769c499b4069b49c566b1b227f98f707a8a Merged-In: Ib3bda769c499b4069b49c566b1b227f98f707a8a --- core/java/android/appwidget/AppWidgetHostView.java | 39 ++++++++++++++++------ .../row/ExpandableNotificationRow.java | 4 ++- .../row/ExpandableNotificationRowController.java | 9 +++-- .../notification/row/NotificationContentView.java | 39 ++++++++++++++++++++++ .../row/NotificationEntryManagerInflationTest.java | 3 +- .../notification/row/NotificationTestHelper.java | 4 ++- 6 files changed, 83 insertions(+), 15 deletions(-) diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java index 8be2b4873c67..b0ffd7690377 100644 --- a/core/java/android/appwidget/AppWidgetHostView.java +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -31,6 +31,7 @@ import android.content.pm.LauncherActivityInfo; import android.content.pm.LauncherApps; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; +import android.graphics.Canvas; import android.graphics.Color; import android.graphics.PointF; import android.graphics.Rect; @@ -312,19 +313,26 @@ public class AppWidgetHostView extends FrameLayout { super.onLayout(changed, left, top, right, bottom); } catch (final RuntimeException e) { Log.e(TAG, "Remote provider threw runtime exception, using error view instead.", e); - removeViewInLayout(mView); - View child = getErrorView(); - prepareView(child); - addViewInLayout(child, 0, child.getLayoutParams()); - measureChild(child, MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY)); - child.layout(0, 0, child.getMeasuredWidth() + mPaddingLeft + mPaddingRight, - child.getMeasuredHeight() + mPaddingTop + mPaddingBottom); - mView = child; - mViewMode = VIEW_MODE_ERROR; + handleViewError(); } } + /** + * Remove bad view and replace with error message view + */ + private void handleViewError() { + removeViewInLayout(mView); + View child = getErrorView(); + prepareView(child); + addViewInLayout(child, 0, child.getLayoutParams()); + measureChild(child, MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY)); + child.layout(0, 0, child.getMeasuredWidth() + mPaddingLeft + mPaddingRight, + child.getMeasuredHeight() + mPaddingTop + mPaddingBottom); + mView = child; + mViewMode = VIEW_MODE_ERROR; + } + /** * Provide guidance about the size of this widget to the AppWidgetManager. The widths and * heights should correspond to the full area the AppWidgetHostView is given. Padding added by @@ -940,4 +948,15 @@ public class AppWidgetHostView extends FrameLayout { reapplyLastRemoteViews(); } } + + @Override + protected void dispatchDraw(@NonNull Canvas canvas) { + try { + super.dispatchDraw(canvas); + } catch (Exception e) { + // Catch draw exceptions that may be caused by RemoteViews + Log.e(TAG, "Drawing view failed: " + e); + post(this::handleViewError); + } + } } 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 819f5a3f6dc2..e1b44122b225 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 @@ -70,6 +70,7 @@ import android.widget.ImageView; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.ContrastColorUtil; import com.android.internal.widget.CachingIconView; import com.android.internal.widget.CallLayout; @@ -1568,7 +1569,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView PeopleNotificationIdentifier peopleNotificationIdentifier, OnUserInteractionCallback onUserInteractionCallback, Optional bubblesManagerOptional, - NotificationGutsManager gutsManager) { + NotificationGutsManager gutsManager, + IStatusBarService statusBarService) { mEntry = entry; mAppName = appName; if (mMenuRow == null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java index 0662a1eba8b6..539838e7d32c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java @@ -26,6 +26,7 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; import com.android.systemui.R; +import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; @@ -85,6 +86,7 @@ public class ExpandableNotificationRowController implements NodeController { private final boolean mAllowLongPress; private final PeopleNotificationIdentifier mPeopleNotificationIdentifier; private final Optional mBubblesManagerOptional; + private final IStatusBarService mStatusBarService; private final ExpandableNotificationRowDragController mDragController; @@ -113,7 +115,8 @@ public class ExpandableNotificationRowController implements NodeController { FalsingCollector falsingCollector, PeopleNotificationIdentifier peopleNotificationIdentifier, Optional bubblesManagerOptional, - ExpandableNotificationRowDragController dragController) { + ExpandableNotificationRowDragController dragController, + IStatusBarService statusBarService) { mView = view; mListContainer = listContainer; mActivatableNotificationViewController = activatableNotificationViewController; @@ -139,6 +142,7 @@ public class ExpandableNotificationRowController implements NodeController { mPeopleNotificationIdentifier = peopleNotificationIdentifier; mBubblesManagerOptional = bubblesManagerOptional; mDragController = dragController; + mStatusBarService = statusBarService; } /** @@ -165,7 +169,8 @@ public class ExpandableNotificationRowController implements NodeController { mPeopleNotificationIdentifier, mOnUserInteractionCallback, mBubblesManagerOptional, - mNotificationGutsManager + mNotificationGutsManager, + mStatusBarService ); mView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); if (mAllowLongPress) { 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 9cc484c02802..ac4f1563ce00 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 @@ -21,10 +21,13 @@ import android.annotation.Nullable; import android.app.Notification; import android.app.PendingIntent; import android.content.Context; +import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Build; +import android.os.RemoteException; import android.provider.Settings; +import android.service.notification.StatusBarNotification; import android.util.ArrayMap; import android.util.AttributeSet; import android.util.IndentingPrintWriter; @@ -41,6 +44,7 @@ import android.widget.LinearLayout; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dependency; +import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.R; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.statusbar.RemoteInputController; @@ -127,6 +131,8 @@ public class NotificationContentView extends FrameLayout implements Notification private RemoteInputController mRemoteInputController; private Runnable mExpandedVisibleListener; private PeopleNotificationIdentifier mPeopleIdentifier; + private IStatusBarService mStatusBarService; + /** * List of listeners for when content views become inactive (i.e. not the showing view). */ @@ -180,6 +186,7 @@ public class NotificationContentView extends FrameLayout implements Notification mHybridGroupManager = new HybridGroupManager(getContext()); mSmartReplyConstants = Dependency.get(SmartReplyConstants.class); mSmartReplyController = Dependency.get(SmartReplyController.class); + mStatusBarService = Dependency.get(IStatusBarService.class); initView(); } @@ -2030,4 +2037,36 @@ public class NotificationContentView extends FrameLayout implements Notification } return false; } + + @Override + protected void dispatchDraw(Canvas canvas) { + try { + super.dispatchDraw(canvas); + } catch (Exception e) { + // Catch draw exceptions that may be caused by RemoteViews + Log.e(TAG, "Drawing view failed: " + e); + cancelNotification(e); + } + } + + private void cancelNotification(Exception exception) { + try { + setVisibility(GONE); + final StatusBarNotification sbn = mNotificationEntry.getSbn(); + if (mStatusBarService != null) { + // report notification inflation errors back up + // to notification delegates + mStatusBarService.onNotificationError( + sbn.getPackageName(), + sbn.getTag(), + sbn.getId(), + sbn.getUid(), + sbn.getInitialPid(), + exception.getMessage(), + sbn.getUser().getIdentifier()); + } + } catch (RemoteException ex) { + Log.e(TAG, "cancelNotification failed: " + ex); + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java index d3738f42e020..b00e80788521 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java @@ -271,7 +271,8 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { new FalsingCollectorFake(), mPeopleNotificationIdentifier, Optional.of(mock(BubblesManager.class)), - mock(ExpandableNotificationRowDragController.class) + mock(ExpandableNotificationRowDragController.class), + mock(IStatusBarService.class) )); when(mNotificationRowComponentBuilder.activatableNotificationView(any())) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index c5d1e3acb2b9..883f8e9bd7b2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -43,6 +43,7 @@ import android.text.TextUtils; import android.view.LayoutInflater; import android.widget.RemoteViews; +import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.TestableDependency; import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.classifier.FalsingManagerFake; @@ -490,7 +491,8 @@ public class NotificationTestHelper { mPeopleNotificationIdentifier, mock(OnUserInteractionCallback.class), Optional.of(mock(BubblesManager.class)), - mock(NotificationGutsManager.class)); + mock(NotificationGutsManager.class), + mock(IStatusBarService.class)); row.setAboveShelfChangedListener(aboveShelf -> { }); mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags); -- cgit v1.2.3 From cfc0b34432ab54e3fa472db5c43e620293f64a5d Mon Sep 17 00:00:00 2001 From: Valentin Iftime Date: Wed, 22 Feb 2023 09:38:55 +0100 Subject: [DO NOT MERGE] Prevent RemoteViews crashing SystemUi Catch canvas drawing exceptions caused by unsuported image sizes. Test: 1. Post a custom view notification with a layout containing an ImageView that references a 5k x 5k image 2. Add an App Widget to the home screen with that has the layout mentioned above as preview/initial layout. Bug: 268193777 Change-Id: Ib3bda769c499b4069b49c566b1b227f98f707a8a Merged-In: Ib3bda769c499b4069b49c566b1b227f98f707a8a --- core/java/android/appwidget/AppWidgetHostView.java | 39 ++++++++++++++++------ .../row/ExpandableNotificationRow.java | 4 ++- .../row/ExpandableNotificationRowController.java | 10 ++++-- .../notification/row/NotificationContentView.java | 37 ++++++++++++++++++++ .../row/NotificationEntryManagerInflationTest.java | 4 ++- .../notification/row/NotificationTestHelper.java | 5 ++- 6 files changed, 84 insertions(+), 15 deletions(-) diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java index 3fef92b203b6..e2351ee89c42 100644 --- a/core/java/android/appwidget/AppWidgetHostView.java +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -28,6 +28,7 @@ import android.content.pm.LauncherActivityInfo; import android.content.pm.LauncherApps; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; +import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Rect; import android.os.Bundle; @@ -250,19 +251,26 @@ public class AppWidgetHostView extends FrameLayout { super.onLayout(changed, left, top, right, bottom); } catch (final RuntimeException e) { Log.e(TAG, "Remote provider threw runtime exception, using error view instead.", e); - removeViewInLayout(mView); - View child = getErrorView(); - prepareView(child); - addViewInLayout(child, 0, child.getLayoutParams()); - measureChild(child, MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY)); - child.layout(0, 0, child.getMeasuredWidth() + mPaddingLeft + mPaddingRight, - child.getMeasuredHeight() + mPaddingTop + mPaddingBottom); - mView = child; - mViewMode = VIEW_MODE_ERROR; + handleViewError(); } } + /** + * Remove bad view and replace with error message view + */ + private void handleViewError() { + removeViewInLayout(mView); + View child = getErrorView(); + prepareView(child); + addViewInLayout(child, 0, child.getLayoutParams()); + measureChild(child, MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY)); + child.layout(0, 0, child.getMeasuredWidth() + mPaddingLeft + mPaddingRight, + child.getMeasuredHeight() + mPaddingTop + mPaddingBottom); + mView = child; + mViewMode = VIEW_MODE_ERROR; + } + /** * Provide guidance about the size of this widget to the AppWidgetManager. The widths and * heights should correspond to the full area the AppWidgetHostView is given. Padding added by @@ -725,4 +733,15 @@ public class AppWidgetHostView extends FrameLayout { } }; } + + @Override + protected void dispatchDraw(Canvas canvas) { + try { + super.dispatchDraw(canvas); + } catch (Exception e) { + // Catch draw exceptions that may be caused by RemoteViews + Log.e(TAG, "Drawing view failed: " + e); + post(this::handleViewError); + } + } } 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 94e12e82f850..5809dc4b9c70 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 @@ -65,6 +65,7 @@ import android.widget.ImageView; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.ContrastColorUtil; import com.android.internal.widget.CachingIconView; import com.android.systemui.Dependency; @@ -1612,7 +1613,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView OnAppOpsClickListener onAppOpsClickListener, FalsingManager falsingManager, StatusBarStateController statusBarStateController, - PeopleNotificationIdentifier peopleNotificationIdentifier) { + PeopleNotificationIdentifier peopleNotificationIdentifier, + IStatusBarService statusBarService) { mAppName = appName; if (mMenuRow == null) { mMenuRow = new NotificationMenuRow(mContext, peopleNotificationIdentifier); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java index 7a6109d2ce78..8c92e730cd66 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java @@ -22,6 +22,8 @@ import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENAB import android.view.View; import android.view.ViewGroup; +import androidx.annotation.NonNull; +import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -69,6 +71,7 @@ public class ExpandableNotificationRowController { private final FalsingManager mFalsingManager; private final boolean mAllowLongPress; private final PeopleNotificationIdentifier mPeopleNotificationIdentifier; + private final IStatusBarService mStatusBarService; @Inject public ExpandableNotificationRowController(ExpandableNotificationRow view, @@ -84,7 +87,8 @@ public class ExpandableNotificationRowController { NotificationGutsManager notificationGutsManager, @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress, @DismissRunnable Runnable onDismissRunnable, FalsingManager falsingManager, - PeopleNotificationIdentifier peopleNotificationIdentifier) { + PeopleNotificationIdentifier peopleNotificationIdentifier, + IStatusBarService statusBarService) { mView = view; mActivatableNotificationViewController = activatableNotificationViewController; mMediaManager = mediaManager; @@ -105,6 +109,7 @@ public class ExpandableNotificationRowController { mAllowLongPress = allowLongPress; mFalsingManager = falsingManager; mPeopleNotificationIdentifier = peopleNotificationIdentifier; + mStatusBarService = statusBarService; } /** @@ -125,7 +130,8 @@ public class ExpandableNotificationRowController { mOnAppOpsClickListener, mFalsingManager, mStatusBarStateController, - mPeopleNotificationIdentifier + mPeopleNotificationIdentifier, + mStatusBarService ); mView.setOnDismissRunnable(mOnDismissRunnable); mView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); 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 1f5b063b0aa2..411c02e3be1d 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 @@ -24,9 +24,11 @@ import android.annotation.Nullable; import android.app.Notification; import android.app.PendingIntent; import android.content.Context; +import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Build; +import android.os.RemoteException; import android.provider.Settings; import android.service.notification.StatusBarNotification; import android.util.ArrayMap; @@ -45,6 +47,7 @@ import android.widget.LinearLayout; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ContrastColorUtil; import com.android.systemui.Dependency; +import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.R; import com.android.systemui.statusbar.MediaTransferManager; import com.android.systemui.statusbar.RemoteInputController; @@ -126,6 +129,8 @@ public class NotificationContentView extends FrameLayout { private RemoteInputController mRemoteInputController; private Runnable mExpandedVisibleListener; private PeopleNotificationIdentifier mPeopleIdentifier; + private IStatusBarService mStatusBarService; + /** * List of listeners for when content views become inactive (i.e. not the showing view). */ @@ -182,6 +187,7 @@ public class NotificationContentView extends FrameLayout { mMediaTransferManager = new MediaTransferManager(getContext()); mSmartReplyConstants = Dependency.get(SmartReplyConstants.class); mSmartReplyController = Dependency.get(SmartReplyController.class); + mStatusBarService = Dependency.get(IStatusBarService.class); initView(); } @@ -1965,4 +1971,35 @@ public class NotificationContentView extends FrameLayout { } return Notification.COLOR_INVALID; } + + @Override + protected void dispatchDraw(Canvas canvas) { + try { + super.dispatchDraw(canvas); + } catch (Exception e) { + // Catch draw exceptions that may be caused by RemoteViews + Log.e(TAG, "Drawing view failed: " + e); + cancelNotification(e); + } + } + + private void cancelNotification(Exception exception) { + try { + setVisibility(GONE); + if (mStatusBarService != null) { + // report notification inflation errors back up + // to notification delegates + mStatusBarService.onNotificationError( + mStatusBarNotification.getPackageName(), + mStatusBarNotification.getTag(), + mStatusBarNotification.getId(), + mStatusBarNotification.getUid(), + mStatusBarNotification.getInitialPid(), + exception.getMessage(), + mStatusBarNotification.getUser().getIdentifier()); + } + } catch (RemoteException ex) { + Log.e(TAG, "cancelNotification failed: " + ex); + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java index 7dfead7575a9..fada48d8735c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java @@ -40,6 +40,7 @@ import android.testing.TestableLooper; import androidx.asynclayoutinflater.view.AsyncLayoutInflater; import androidx.test.filters.SmallTest; +import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.NotificationMessagingUtil; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; @@ -244,7 +245,8 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { true, null, mFalsingManager, - mPeopleNotificationIdentifier + mPeopleNotificationIdentifier, + mock(IStatusBarService.class) )); when(mNotificationRowComponentBuilder.activatableNotificationView(any())) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index b9eb4d1e29c2..de2e50727039 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -43,6 +43,7 @@ import android.text.TextUtils; import android.view.LayoutInflater; import android.widget.RemoteViews; +import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.TestableDependency; import com.android.systemui.bubbles.BubbleController; import com.android.systemui.bubbles.BubblesTestActivity; @@ -422,7 +423,9 @@ public class NotificationTestHelper { mock(ExpandableNotificationRow.OnAppOpsClickListener.class), mock(FalsingManager.class), mStatusBarStateController, - mPeopleNotificationIdentifier); + mPeopleNotificationIdentifier, + mock(IStatusBarService.class)); + row.setAboveShelfChangedListener(aboveShelf -> { }); mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags); inflateAndWait(entry); -- cgit v1.2.3 From 10752edb540a053e304139894f941fcaef60949b Mon Sep 17 00:00:00 2001 From: Valentin Iftime Date: Wed, 22 Feb 2023 09:38:55 +0100 Subject: [DO NOT MERGE] Prevent RemoteViews crashing SystemUi Catch canvas drawing exceptions caused by unsuported image sizes. Test: 1. Post a custom view notification with a layout containing an ImageView that references a 5k x 5k image 2. Add an App Widget to the home screen with that has the layout mentioned above as preview/initial layout. Bug: 268193777 Change-Id: Ib3bda769c499b4069b49c566b1b227f98f707a8a Merged-In: Ib3bda769c499b4069b49c566b1b227f98f707a8a --- core/java/android/appwidget/AppWidgetHostView.java | 39 ++++++++++++---- .../row/ExpandableNotificationRow.java | 7 ++- .../row/ExpandableNotificationRowController.java | 10 ++-- .../notification/row/NotificationContentView.java | 54 +++++++++++++++++++++- .../row/NotificationEntryManagerInflationTest.java | 3 +- .../notification/row/NotificationTestHelper.java | 4 +- 6 files changed, 99 insertions(+), 18 deletions(-) diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java index 8be2b4873c67..b0ffd7690377 100644 --- a/core/java/android/appwidget/AppWidgetHostView.java +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -31,6 +31,7 @@ import android.content.pm.LauncherActivityInfo; import android.content.pm.LauncherApps; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; +import android.graphics.Canvas; import android.graphics.Color; import android.graphics.PointF; import android.graphics.Rect; @@ -312,19 +313,26 @@ public class AppWidgetHostView extends FrameLayout { super.onLayout(changed, left, top, right, bottom); } catch (final RuntimeException e) { Log.e(TAG, "Remote provider threw runtime exception, using error view instead.", e); - removeViewInLayout(mView); - View child = getErrorView(); - prepareView(child); - addViewInLayout(child, 0, child.getLayoutParams()); - measureChild(child, MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY)); - child.layout(0, 0, child.getMeasuredWidth() + mPaddingLeft + mPaddingRight, - child.getMeasuredHeight() + mPaddingTop + mPaddingBottom); - mView = child; - mViewMode = VIEW_MODE_ERROR; + handleViewError(); } } + /** + * Remove bad view and replace with error message view + */ + private void handleViewError() { + removeViewInLayout(mView); + View child = getErrorView(); + prepareView(child); + addViewInLayout(child, 0, child.getLayoutParams()); + measureChild(child, MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY)); + child.layout(0, 0, child.getMeasuredWidth() + mPaddingLeft + mPaddingRight, + child.getMeasuredHeight() + mPaddingTop + mPaddingBottom); + mView = child; + mViewMode = VIEW_MODE_ERROR; + } + /** * Provide guidance about the size of this widget to the AppWidgetManager. The widths and * heights should correspond to the full area the AppWidgetHostView is given. Padding added by @@ -940,4 +948,15 @@ public class AppWidgetHostView extends FrameLayout { reapplyLastRemoteViews(); } } + + @Override + protected void dispatchDraw(@NonNull Canvas canvas) { + try { + super.dispatchDraw(canvas); + } catch (Exception e) { + // Catch draw exceptions that may be caused by RemoteViews + Log.e(TAG, "Drawing view failed: " + e); + post(this::handleViewError); + } + } } 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 eb496abad460..84c8f1c934b0 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 @@ -72,6 +72,7 @@ import android.widget.ImageView; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.ContrastColorUtil; import com.android.internal.widget.CachingIconView; import com.android.internal.widget.CallLayout; @@ -1551,7 +1552,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView NotificationGutsManager gutsManager, MetricsLogger metricsLogger, SmartReplyConstants smartReplyConstants, - SmartReplyController smartReplyController) { + SmartReplyController smartReplyController, + IStatusBarService statusBarService) { mEntry = entry; mAppName = appName; if (mMenuRow == null) { @@ -1580,7 +1582,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mPeopleNotificationIdentifier, rivSubcomponentFactory, smartReplyConstants, - smartReplyController); + smartReplyController, + statusBarService); } mOnUserInteractionCallback = onUserInteractionCallback; mBubblesManagerOptional = bubblesManagerOptional; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java index 599039d46556..2d8f2ca98031 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java @@ -28,6 +28,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.logging.MetricsLogger; +import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; @@ -100,8 +101,8 @@ public class ExpandableNotificationRowController implements NotifViewController private final Optional mBubblesManagerOptional; private final SmartReplyConstants mSmartReplyConstants; private final SmartReplyController mSmartReplyController; - private final ExpandableNotificationRowDragController mDragController; + private final IStatusBarService mStatusBarService; @Inject public ExpandableNotificationRowController( @@ -133,7 +134,8 @@ public class ExpandableNotificationRowController implements NotifViewController FeatureFlags featureFlags, PeopleNotificationIdentifier peopleNotificationIdentifier, Optional bubblesManagerOptional, - ExpandableNotificationRowDragController dragController) { + ExpandableNotificationRowDragController dragController, + IStatusBarService statusBarService) { mView = view; mListContainer = listContainer; mRemoteInputViewSubcomponentFactory = rivSubcomponentFactory; @@ -164,6 +166,7 @@ public class ExpandableNotificationRowController implements NotifViewController mMetricsLogger = metricsLogger; mSmartReplyConstants = smartReplyConstants; mSmartReplyController = smartReplyController; + mStatusBarService = statusBarService; } /** @@ -194,7 +197,8 @@ public class ExpandableNotificationRowController implements NotifViewController mNotificationGutsManager, mMetricsLogger, mSmartReplyConstants, - mSmartReplyController + mSmartReplyController, + mStatusBarService ); mView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); if (mAllowLongPress) { 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 ba26cfaa30b4..f647365755bc 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 @@ -21,10 +21,13 @@ import android.annotation.Nullable; import android.app.Notification; import android.app.PendingIntent; import android.content.Context; +import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Build; +import android.os.RemoteException; import android.provider.Settings; +import android.service.notification.StatusBarNotification; import android.util.ArrayMap; import android.util.AttributeSet; import android.util.IndentingPrintWriter; @@ -39,6 +42,7 @@ import android.widget.ImageView; import android.widget.LinearLayout; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.R; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.statusbar.RemoteInputController; @@ -129,6 +133,7 @@ public class NotificationContentView extends FrameLayout implements Notification private Runnable mExpandedVisibleListener; private PeopleNotificationIdentifier mPeopleIdentifier; private RemoteInputViewSubcomponent.Factory mRemoteInputSubcomponentFactory; + private IStatusBarService mStatusBarService; /** * List of listeners for when content views become inactive (i.e. not the showing view). @@ -194,11 +199,13 @@ public class NotificationContentView extends FrameLayout implements Notification PeopleNotificationIdentifier peopleNotificationIdentifier, RemoteInputViewSubcomponent.Factory rivSubcomponentFactory, SmartReplyConstants smartReplyConstants, - SmartReplyController smartReplyController) { + SmartReplyController smartReplyController, + IStatusBarService statusBarService) { mPeopleIdentifier = peopleNotificationIdentifier; mRemoteInputSubcomponentFactory = rivSubcomponentFactory; mSmartReplyConstants = smartReplyConstants; mSmartReplyController = smartReplyController; + mStatusBarService = statusBarService; } public void reinflate() { @@ -2090,4 +2097,49 @@ public class NotificationContentView extends FrameLayout implements Notification @Nullable RemoteInputView mView; @Nullable RemoteInputViewController mController; } + + @VisibleForTesting + protected void setContractedWrapper(NotificationViewWrapper contractedWrapper) { + mContractedWrapper = contractedWrapper; + } + @VisibleForTesting + protected void setExpandedWrapper(NotificationViewWrapper expandedWrapper) { + mExpandedWrapper = expandedWrapper; + } + @VisibleForTesting + protected void setHeadsUpWrapper(NotificationViewWrapper headsUpWrapper) { + mHeadsUpWrapper = headsUpWrapper; + } + + @Override + protected void dispatchDraw(Canvas canvas) { + try { + super.dispatchDraw(canvas); + } catch (Exception e) { + // Catch draw exceptions that may be caused by RemoteViews + Log.e(TAG, "Drawing view failed: " + e); + cancelNotification(e); + } + } + + private void cancelNotification(Exception exception) { + try { + setVisibility(GONE); + final StatusBarNotification sbn = mNotificationEntry.getSbn(); + if (mStatusBarService != null) { + // report notification inflation errors back up + // to notification delegates + mStatusBarService.onNotificationError( + sbn.getPackageName(), + sbn.getTag(), + sbn.getId(), + sbn.getUid(), + sbn.getInitialPid(), + exception.getMessage(), + sbn.getUser().getIdentifier()); + } + } catch (RemoteException ex) { + Log.e(TAG, "cancelNotification failed: " + ex); + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java index 251ac7d250fe..2d9397e2dd7e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java @@ -281,7 +281,8 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { mock(FeatureFlags.class), mPeopleNotificationIdentifier, Optional.of(mock(BubblesManager.class)), - mock(ExpandableNotificationRowDragController.class))); + mock(ExpandableNotificationRowDragController.class), + mock(IStatusBarService.class))); when(mNotificationRowComponentBuilder.activatableNotificationView(any())) .thenReturn(mNotificationRowComponentBuilder); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index 38bd078589dc..49d1c5d86a16 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -48,6 +48,7 @@ import android.view.LayoutInflater; import android.widget.RemoteViews; import com.android.internal.logging.MetricsLogger; +import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.TestableDependency; import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.classifier.FalsingManagerFake; @@ -523,7 +524,8 @@ public class NotificationTestHelper { mock(NotificationGutsManager.class), mock(MetricsLogger.class), mock(SmartReplyConstants.class), - mock(SmartReplyController.class)); + mock(SmartReplyController.class), + mock(IStatusBarService.class)); row.setAboveShelfChangedListener(aboveShelf -> { }); mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags); -- cgit v1.2.3 From 441e13525343d16197b0868b4b3d5af72cc6a013 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Thu, 16 Mar 2023 15:26:22 -0700 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I99c241ff88ca25097bad056bf5aba9350c0f26ce --- core/res/res/values-ca/strings.xml | 2 +- core/res/res/values-gu/strings.xml | 2 +- core/res/res/values-ru/strings.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index 353bff0cbaef..be378eaf3da6 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -1503,7 +1503,7 @@ "Següent" "Omet" "No s\'ha trobat cap coincidència" - "Troba-ho a la pàgina" + "Cerca a la pàgina" "{count,plural, =1{# coincidència}many{# de {total}}other{# de {total}}}" "Fet" "S\'està esborrant l\'emmagatzematge compartit…" diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml index 5af104e8a784..faca6c71122c 100644 --- a/core/res/res/values-gu/strings.xml +++ b/core/res/res/values-gu/strings.xml @@ -1346,7 +1346,7 @@ "કોઈ પરવાનગીઓ જરૂરી નથી" "આનાથી તમારા પૈસા ખર્ચ થઈ શકે છે" "ઓકે" - "આ ડિવાઇસને USB મારફતે ચાર્જ કરી રહ્યાં છીએ" + "આ ડિવાઇસને USB મારફતે ચાર્જ કરી રહ્યાં છીએ." "કનેક્ટેડ ઉપકરણને USB મારફતે ચાર્જ કરી રહ્યાં છીએ" "USB ફાઇલ ટ્રાન્સફર ચાલુ છે" "USB મારફતે PTP ચાલુ કર્યું" diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index baac4a3153f9..1d96ccf2bc13 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -1348,7 +1348,7 @@ "Не требуется разрешений" "это может стоить вам денег!" "ОК" - "Зарядка устройства через USB…" + "Зарядка устройства через USB" "Зарядка устройства через USB…" "Передача файлов через USB включена" "Режим PTP включен" -- cgit v1.2.3 From 92114886bdce8467c52c655c186f3e7ab1e134d8 Mon Sep 17 00:00:00 2001 From: Brian Lee Date: Fri, 17 Feb 2023 16:05:17 -0800 Subject: Check key intent for selectors and prohibited flags Bug: 265015796 Test: atest FrameworksServicesTests: com.android.server.accounts.AccountManagerServiceTest Change-Id: Ie16f8654337bd75eaad3156817470674b4f0cee3 (cherry picked from commit e53a96304352e2965176c8d32ac1b504e52ef185) Merged-In: Ie16f8654337bd75eaad3156817470674b4f0cee3 --- .../server/accounts/AccountManagerService.java | 18 ++++++++--- .../server/accounts/AccountManagerServiceTest.java | 36 ++++++++++++++++++++++ .../AccountManagerServiceTestFixtures.java | 5 ++- .../accounts/TestAccountType1Authenticator.java | 5 +-- 4 files changed, 54 insertions(+), 10 deletions(-) diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 7255afbbfbba..ef8670209c44 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -4797,10 +4797,6 @@ public class AccountManagerService if (intent.getClipData() == null) { intent.setClipData(ClipData.newPlainText(null, null)); } - intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_READ_URI_PERMISSION - | Intent.FLAG_GRANT_WRITE_URI_PERMISSION - | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION - | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION)); long bid = Binder.clearCallingIdentity(); try { PackageManager pm = mContext.getPackageManager(); @@ -4847,7 +4843,19 @@ public class AccountManagerService if (intent == null) { return (simulateIntent == null); } - return intent.filterEquals(simulateIntent); + if (!intent.filterEquals(simulateIntent)) { + return false; + } + + if (intent.getSelector() != simulateIntent.getSelector()) { + return false; + } + + int prohibitedFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION + | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION + | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION; + return (simulateIntent.getFlags() & prohibitedFlags) == 0; } private boolean isExportedSystemActivity(ActivityInfo activityInfo) { diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java index ac305a93bfb8..f7b7a5801b42 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java @@ -18,6 +18,7 @@ package com.android.server.accounts; import static android.database.sqlite.SQLiteDatabase.deleteDatabase; +import static org.mockito.ArgumentMatchers.contains; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyInt; @@ -691,6 +692,41 @@ public class AccountManagerServiceTest extends AndroidTestCase { assertNotNull(intent.getParcelableExtra(AccountManagerServiceTestFixtures.KEY_CALLBACK)); } + @SmallTest + public void testStartAddAccountSessionWhereAuthenticatorReturnsIntentWithProhibitedFlags() + throws Exception { + unlockSystemUser(); + ResolveInfo resolveInfo = new ResolveInfo(); + resolveInfo.activityInfo = new ActivityInfo(); + resolveInfo.activityInfo.applicationInfo = new ApplicationInfo(); + when(mMockPackageManager.resolveActivityAsUser( + any(Intent.class), anyInt(), anyInt())).thenReturn(resolveInfo); + when(mMockPackageManager.checkSignatures( + anyInt(), anyInt())).thenReturn(PackageManager.SIGNATURE_MATCH); + + final CountDownLatch latch = new CountDownLatch(1); + Response response = new Response(latch, mMockAccountManagerResponse); + Bundle options = createOptionsWithAccountName( + AccountManagerServiceTestFixtures.ACCOUNT_NAME_INTERVENE); + int prohibitedFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION + | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION + | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION; + options.putInt(AccountManagerServiceTestFixtures.KEY_INTENT_FLAGS, prohibitedFlags); + + mAms.startAddAccountSession( + response, // response + AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1, // accountType + "authTokenType", + null, // requiredFeatures + true, // expectActivityLaunch + options); // optionsIn + waitForLatch(latch); + + verify(mMockAccountManagerResponse).onError( + eq(AccountManager.ERROR_CODE_INVALID_RESPONSE), contains("invalid intent")); + } + @SmallTest public void testStartAddAccountSessionError() throws Exception { unlockSystemUser(); diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java index 73f30d9f9e79..b98a6a891d55 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java +++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTestFixtures.java @@ -17,9 +17,6 @@ package com.android.server.accounts; import android.accounts.Account; -import java.util.ArrayList; -import java.util.List; - /** * Constants shared between test AccountAuthenticators and AccountManagerServiceTest. */ @@ -31,6 +28,8 @@ public final class AccountManagerServiceTestFixtures { "account_manager_service_test:account_status_token_key"; public static final String KEY_ACCOUNT_PASSWORD = "account_manager_service_test:account_password_key"; + public static final String KEY_INTENT_FLAGS = + "account_manager_service_test:intent_flags_key"; public static final String KEY_OPTIONS_BUNDLE = "account_manager_service_test:option_bundle_key"; public static final String ACCOUNT_NAME_SUCCESS = "success_on_return@fixture.com"; diff --git a/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java b/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java index 8106364477d9..924443e9d5cf 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java +++ b/services/tests/servicestests/src/com/android/server/accounts/TestAccountType1Authenticator.java @@ -24,8 +24,6 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; -import com.android.frameworks.servicestests.R; - import java.util.concurrent.atomic.AtomicInteger; /** @@ -270,11 +268,13 @@ public class TestAccountType1Authenticator extends AbstractAccountAuthenticator String accountName = null; Bundle sessionBundle = null; String password = null; + int intentFlags = 0; if (options != null) { accountName = options.getString(AccountManagerServiceTestFixtures.KEY_ACCOUNT_NAME); sessionBundle = options.getBundle( AccountManagerServiceTestFixtures.KEY_ACCOUNT_SESSION_BUNDLE); password = options.getString(AccountManagerServiceTestFixtures.KEY_ACCOUNT_PASSWORD); + intentFlags = options.getInt(AccountManagerServiceTestFixtures.KEY_INTENT_FLAGS, 0); } Bundle result = new Bundle(); @@ -302,6 +302,7 @@ public class TestAccountType1Authenticator extends AbstractAccountAuthenticator intent.putExtra(AccountManagerServiceTestFixtures.KEY_RESULT, eventualActivityResultData); intent.putExtra(AccountManagerServiceTestFixtures.KEY_CALLBACK, response); + intent.setFlags(intentFlags); result.putParcelable(AccountManager.KEY_INTENT, intent); } else { -- cgit v1.2.3 From 31632d82e90e1810ef8f21f12312fd6a77af6863 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Thu, 16 Mar 2023 17:14:06 -0700 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I2b07c57dac71563d75dfbcc6f6b87208a499d16e --- packages/SystemUI/res/values-th/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index b0c947f7ef10..992e9091a52d 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -19,7 +19,7 @@ - "อินเทอร์เฟซผู้ใช้ของระบบ" + "UI ของระบบ" "เปิดโหมดประหยัดแบตเตอรี่ใช่ไหม" "คุณมีแบตเตอรี่เหลืออยู่ %s โหมดประหยัดแบตเตอรี่จะเปิดธีมมืด จำกัดกิจกรรมในเบื้องหลัง และหน่วงเวลาการแจ้งเตือน" "โหมดประหยัดแบตเตอรี่จะเปิดธีมมืด จำกัดกิจกรรมในเบื้องหลัง และหน่วงเวลาการแจ้งเตือน" -- cgit v1.2.3 From c87692080cc694a8c2ac5c4ea20df7aba15ef0f6 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Thu, 16 Mar 2023 20:09:01 -0700 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I91a9137345db6d1ac90e3862644208bd951f2205 --- packages/SettingsLib/res/values-fa/strings.xml | 4 ++-- packages/SettingsLib/res/values-th/strings.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index 0e0915f71ec5..615ab3342935 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -278,7 +278,7 @@ "برنامه موقعیت مکانی ساختگی: %1$s" "شبکه" "گواهینامه نمایش بی‌سیم" - "‏فعال کردن گزارش‌گیری طولانی Wi‑Fi" + "‏فعال کردن گزارش‌گیری مفصل Wi‑Fi" "‏محدود کردن اسکن کردن Wi‑Fi" "‏تصادفی‌سازی MAC غیرپایای Wi-Fi" "داده تلفن همراه همیشه فعال باشد" @@ -388,7 +388,7 @@ "‏پرداز زدن HWUI نمایه" "‏فعال کردن لایه‌های اشکال‌زدایی GPU" "‏مجاز کردن بارگیری لایه‌های اشکال‌زدایی GPU برای برنامه‌های اشکا‌ل‌زدایی" - "فعال کردن گزارش طولانی فروشنده" + "فعال کردن گزارش‌گیری مفصل فروشنده" "شامل گزارشات اشکال تکمیلی ورود به سیستم فروشنده ویژه دستگاه می‌شود که ممکن است دربرگیرنده اطلاعات خصوصی، استفاده بیشتر از باتری، و/یا استفاده بیشتر از فضای ذخیره‌سازی باشد." "مقیاس پویانمایی پنجره" "مقیاس پویانمایی انتقالی" diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index cf8f76efbc2b..de7defa3ec21 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -299,7 +299,7 @@ "ทริกเกอร์การเลือกตัวแปลงรหัส\nเสียงบลูทูธ: บิตต่อตัวอย่าง" "โหมดช่องสัญญาณเสียงบลูทูธ" "ทริกเกอร์การเลือกตัวแปลงรหัส\nเสียงบลูทูธ: โหมดช่องสัญญาณ" - "ตัวแปลงรหัสเสียงบลูทูธที่ใช้ LDAC: คุณภาพการเล่น" + "ตัวแปลงสัญญาณเสียงบลูทูธที่ใช้ LDAC: คุณภาพการเล่น" "ทริกเกอร์การเลือกตัวแปลงรหัส LDAC\nเสียงบลูทูธ: คุณภาพการเล่น" "สตรีมมิง: %1$s" "DNS ส่วนตัว" -- cgit v1.2.3 From 7167e7fec72fc79e8ebb2aa880ca094acf8b438c Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Wed, 22 Mar 2023 08:15:02 -0700 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I373bb5ba3820e8dff9d8959bd7ae6b988760dedb --- core/res/res/values-ca/strings.xml | 4 ++-- core/res/res/values-da/strings.xml | 2 +- core/res/res/values-en-rCA/strings.xml | 2 +- core/res/res/values-hi/strings.xml | 2 +- core/res/res/values-it/strings.xml | 2 +- core/res/res/values-mr/strings.xml | 4 ++-- core/res/res/values-ms/strings.xml | 2 +- core/res/res/values-nl/strings.xml | 4 ++-- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index be378eaf3da6..9f2b95e9bc76 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -575,7 +575,7 @@ "Permet que l\'aplicació llegeixi les ubicacions de les teves col·leccions multimèdia." "Utilitza la biometria" "Fes servir la biometria o el bloqueig de pantalla" - "Verifica que ets tu" + "Verifica la teva identitat" "Utilitza la teva biometria per continuar" "Utilitza la biometria o el bloqueig de pantalla per continuar" "Maquinari biomètric no disponible" @@ -1223,7 +1223,7 @@ "Escala" "Mostra sempre" "Torna a activar-ho a Configuració del sistema > Aplicacions > Baixades." - "%1$s no admet la mida de pantalla actual i és possible que funcioni de manera inesperada." + "%1$s no admet la mida de visualització actual i és possible que funcioni de manera inesperada." "Mostra sempre" "%1$s es va crear per a una versió incompatible del sistema operatiu Android i pot funcionar de manera inesperada. És possible que hi hagi disponible una versió actualitzada de l\'aplicació." "Mostra sempre" diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index e41d3bf09cd9..931e8a55a5a1 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -1247,7 +1247,7 @@ "Du har trykket på afbryderknappen, hvilket som regel slukker skærmen.\n\nPrøv at trykke let på knappen, mens du konfigurerer dit fingeraftryk." "Sluk skærm" "Fortsæt" - "Vil du bekræfte dit fingeraftryk?" + "Vil du verificere dit fingeraftryk?" "Du har trykket på afbryderknappen, hvilket som regel slukker skærmen.\n\nPrøv at trykke let på knappen for at bekræfte dit fingeraftryk." "Sluk skærm" "Fortsæt" diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml index 9b3891a81b0f..89aa468117c5 100644 --- a/core/res/res/values-en-rCA/strings.xml +++ b/core/res/res/values-en-rCA/strings.xml @@ -1685,7 +1685,7 @@ "View and control screen" "It can read all content on the screen and display content over other apps." "View and perform actions" - "It can track your interactions with an app or a hardware sensor, and interact with apps on your behalf." + "It can track your interactions with an app or a hardware sensor and interact with apps on your behalf." "Allow" "Deny" "Tap a feature to start using it:" diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index 2cc0797147ee..47c942aff585 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -1683,7 +1683,7 @@ "%1$s को अपना डिवाइस पूरी तरह कंट्रोल करने की मंज़ूरी दें?" "पूरी तरह कंट्रोल करने की अनुमति उन ऐप्लिकेशन के लिए ठीक है जो सुलभता से जुड़ी ज़रूरतों के लिए बने हैं, लेकिन ज़्यादातर ऐप्लिकेशन के लिए यह ठीक नहीं है." "स्क्रीन को देखें और कंट्रोल करें" - "यह स्क्रीन पर दिखने वाली हर तरह के कॉन्टेंट को पढ़ सकता है और उसे दूसरे ऐप्लिकेशन पर दिखा सकता है." + "यह स्क्रीन पर दिखने वाले कॉन्टेंट को पढ़ सकता है और उसे दूसरे ऐप्लिकेशन के ऊपर दिखा सकता है." "देखें और कार्रवाई करें" "यह आपके और किसी ऐप्लिकेशन या हार्डवेयर सेंसर के बीच होने वाले इंटरैक्शन को ट्रैक कर सकता है और आपकी तरफ़ से ऐप्लिकेशन के साथ इंटरैक्ट कर सकता है." "अनुमति दें" diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index ec78dfde6dc0..1f363c4fa22c 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -1684,7 +1684,7 @@ "Vuoi consentire a %1$s di avere il controllo totale del tuo dispositivo?" "Il controllo totale è appropriato per le app che rispondono alle tue esigenze di accessibilità, ma non per gran parte delle app." "Visualizzare e controllare lo schermo" - "Può leggere i contenuti presenti sullo schermo e mostrare i contenuti su altre app." + "Può leggere tutti i contenuti presenti sullo schermo e mostrare i contenuti sopra altre app." "Visualizzare ed eseguire azioni" "Può tenere traccia delle tue interazioni con un\'app o un sensore hardware e interagire con app per tuo conto." "Consenti" diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index 628a6ba9142c..33bf4a408c81 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/strings.xml @@ -585,11 +585,11 @@ "एरर ऑथेंटिकेट करत आहे" "स्क्रीन लॉक वापरा" "पुढे सुरू ठेवण्यासाठी तुमचे स्क्रीन लॉक एंटर करा" - "सेन्सरवर जोरात दाबा" + "सेन्सरवर जोरात प्रेस करा" "फिंगरप्रिंटवर प्रक्रिया करणे शक्य झाले नाही. कृपया पुन्हा प्रयत्न करा." "फिंगरप्रिंट सेन्सर स्वच्छ करा आणि पुन्हा प्रयत्न करा" "सेन्सर स्वच्छ करा आणि पुन्हा प्रयत्न करा" - "सेन्सरवर जोरात दाबा" + "सेन्सरवर जोरात प्रेस करा" "बोट खूप सावकाश हलविले. कृपया पुन्हा प्रयत्न करा." "दुसरी फिंगरप्रिंट वापरून पहा" "खूप प्रखर" diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml index d594bd0bcf13..73b7a5e0bbc4 100644 --- a/core/res/res/values-ms/strings.xml +++ b/core/res/res/values-ms/strings.xml @@ -2002,7 +2002,7 @@ "Kemas kini %1$s dan %2$s dalam ""%3$s""?" "Kemas kini item ini dalam ""%4$s"": %1$s, %2$s dan %3$s ?" "Simpan" - "Tidak, terima kasih" + "Tidak perlu" "Bukan sekarang" "Jangan" "Kemas kini" diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index 17e7231dfa4c..ae9c84a58a25 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -1683,9 +1683,9 @@ "Toestaan dat %1$s volledige controle over je apparaat heeft?" "Volledige controle is gepast voor apps die je helpen met toegankelijkheid, maar niet voor de meeste apps." "Scherm bekijken en bedienen" - "De functie kan alle content op het scherm lezen en content bovenop andere apps weergeven." + "Deze functie kan alle content op het scherm lezen en content bovenop andere apps weergeven." "Acties bekijken en uitvoeren" - "De functie kan je interacties met een app of een hardwaresensor bijhouden en namens jou met apps communiceren." + "Deze functie kan je interacties met een app of een hardwaresensor bijhouden en namens jou met apps communiceren." "Toestaan" "Weigeren" "Tik op een functie om deze te gebruiken:" -- cgit v1.2.3 From 6625f0b8c68f57549784040d22a69b93e7049e6b Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Wed, 22 Mar 2023 09:51:24 -0700 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I6b5bfecd7961bbc3415f7e2f68dd81cd7973fa49 --- packages/SystemUI/res/values-af/strings.xml | 2 +- packages/SystemUI/res/values-et/strings.xml | 6 +++--- packages/SystemUI/res/values-it/strings.xml | 2 +- packages/SystemUI/res/values-mr/strings.xml | 8 ++++---- packages/SystemUI/res/values-pt-rPT/strings.xml | 2 +- packages/SystemUI/res/values-th/strings.xml | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index cbfcfd20d760..2eb90a97a3d4 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -792,7 +792,7 @@ "Wys en beheer toestelle van sluitskerm af?" "Jy kan vir jou eksterne toestelle kontroles op die sluitskerm byvoeg.\n\nJou toestelprogram kan jou dalk toelaat om sommige toestelle te beheer sonder om jou foon of tablet te ontsluit.\n\nJy kan enige tyd in Instellings veranderings maak." "Beheer toestelle van sluitskerm af?" - "Jy kan sommige toestelle beheer sonder om jou foon of tablet te ontsluit.\n\nJou toestelprogram bepaal watter toestelle op dié manier beheer kan word." + "Jy kan sommige toestelle beheer sonder om jou foon of tablet te ontsluit.\n\nJou toestelapp bepaal watter toestelle op dié manier beheer kan word." "Nee, dankie" "Ja" "PIN bevat letters of simbole" diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index 697478244ea7..48cb7dcf958a 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -790,9 +790,9 @@ "Soovitas %s" "Seade on lukustatud" "Kas soovite seadmete juhtelemente lukustuskuval kuvada ja kasutada?" - "Võite lukustuskuvale oma väliste seadmete juhtelemendid lisada.\n\nTeie seadmerakendus võib võimaldada teil teatud seadmeid ilma telefoni või tahvelarvutit avamata hallata.\n\nSaate igal ajal seadetes muudatusi teha." - "Kas soovite seadmeid lukustuskuva kaudu hallata?" - "Võite teatud seadmeid ilma telefoni või tahvelarvutit avamata hallata.\n\nTeie seadmerakendus määrab, milliseid seadmeid saab sel viisil hallata." + "Võite lukustuskuvale oma väliste seadmete juhtelemendid lisada.\n\nTeie seadmerakendus võib võimaldada teil teatud seadmeid ilma telefoni või tahvelarvutit avamata juhtida.\n\nSaate igal ajal seadetes muudatusi teha." + "Kas soovite seadmeid lukustuskuva kaudu juhtida?" + "Võite teatud seadmeid ilma telefoni või tahvelarvutit avamata juhtida.\n\nTeie seadmerakendus määrab, milliseid seadmeid saab sel viisil juhtida." "Tänan, ei" "Jah" "PIN-kood sisaldab tähti või sümboleid" diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index 2308b4216ce5..3e62a8adf3aa 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -728,7 +728,7 @@ "Rifiuta" "Tocca per programmare il Risparmio energetico" "Attiva questa funzionalità se è probabile che la batteria si scarichi" - "No grazie" + "No, grazie" "Dump heap SysUI" "In uso" "Le app stanno usando %s." diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index 7862ee2d34f0..c30c1dc2e40a 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -136,7 +136,7 @@ "चेहरा ऑथेंटिकेशन केलेला आहे" "निश्चित केले" "पूर्ण करण्यासाठी खात्री करा वर टॅप करा" - "चेहऱ्याने अनलॉक केले. सुरू ठेवण्यासाठी अनलॉक करा आयकन दाबा." + "चेहऱ्याने अनलॉक केले. सुरू ठेवण्यासाठी अनलॉक करा आयकन प्रेस करा." "ऑथेंटिकेशन केलेले" "पिन वापरा" "पॅटर्न वापरा" @@ -311,8 +311,8 @@ "उघडण्यासाठी पुन्हा टॅप करा" "पुन्हा टॅप करा" "उघडण्यासाठी वर स्वाइप करा" - "उघडण्यासाठी अनलॉक करा आयकन दाबा" - "चेहऱ्याने अनलॉक केले. उघडण्यासाठी अनलॉक करा आयकन दाबा." + "उघडण्यासाठी अनलॉक करा आयकन प्रेस करा" + "चेहऱ्याने अनलॉक केले. उघडण्यासाठी अनलॉक करा आयकन प्रेस करा." "डावीकडे हलवा" "खाली हलवा" @@ -792,7 +792,7 @@ "लॉक स्क्रीनवरून डिव्हाइस दाखवायचे आणि नियंत्रित करायचे का?" "तुम्ही तुमच्या बाह्य डिव्हाइससाठी लॉक स्क्रीनवर नियंत्रणे जोडू शकता.\n\nतुमचे डिव्हाइस अ‍ॅप तुम्हाला तुमचा फोन किंवा टॅबलेट अनलॉक न करता काही डिव्हाइस नियंत्रित करण्याची अनुमती देऊ शकते.\n\nतुम्ही सेटिंग्ज मध्ये कधीही बदल करू शकता." "लॉक स्क्रीनवरून डिव्हाइस नियंत्रित करायची का?" - "तुमचा फोन किंवा टॅबलेट अनलॉक न करता तुम्ही काही डिव्हाइस नियंत्रित करू शकता.\n\nतुमचे डिव्हाइस अ‍ॅप अशा प्रकारे कोणते डिव्हाइस नियंत्रित केले जाऊ शकतात हे निर्धारित करते." + "तुमचा फोन किंवा टॅबलेट अनलॉक न करता तुम्ही काही डिव्हाइस नियंत्रित करू शकता.\n\nअशाप्रकारे कोणती डिव्हाइस नियंत्रित केली जाऊ शकतात, हे तुमचे डिव्हाइस अ‍ॅप निर्धारित करते." "नाही, नको" "होय" "पिनमध्ये अक्षरे किंवा चिन्हे आहेत" diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index 11b5a1dba28e..4520f0a26088 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -340,7 +340,7 @@ "%2$s • A carregar • Carga completa em %1$s" "%2$s • A carregar rapidamente • Carga completa em %1$s" "%2$s • A carregar lentamente • Carga completa em %1$s" - "%2$s • A carregar na estação de carregamento • Carga completa em %1$s" + "%2$s • A carregar na estação de ancoragem • Carga completa em %1$s" "Mudar utilizador" "Todas as apps e dados desta sessão serão eliminados." "Bem-vindo de volta, convidado!" diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index 992e9091a52d..858787fc328d 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -794,7 +794,7 @@ "ควบคุมอุปกรณ์จากหน้าจอล็อกไหม" "คุณควบคุมอุปกรณ์บางอย่างได้โดยไม่ต้องปลดล็อกโทรศัพท์หรือแท็บเล็ต\n\nแอปของอุปกรณ์จะระบุอุปกรณ์ที่สามารถควบคุมด้วยวิธีนี้ได้" "ไม่เป็นไร" - "มี" + "ใช่" "PIN ประกอบด้วยตัวอักษรหรือสัญลักษณ์" "ยืนยัน %s" "PIN ไม่ถูกต้อง" -- cgit v1.2.3 From a5d6654a7a3bded41c0e71eab4b414561c03653b Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Wed, 22 Mar 2023 09:55:02 -0700 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I6ec165509839787d3bafd0019beedcdcc642412b --- packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml index 26042408af24..b6327e2a48fc 100644 --- a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml @@ -26,7 +26,7 @@ "Cartão inválido." "Carregada" "%s • A carregar sem fios" - "%s • A carregar na estação de carregamento" + "%s • A carregar na estação de ancoragem" "%s • A carregar…" "%s • A carregar rapidamente…" "%s • A carregar lentamente…" -- cgit v1.2.3 From d89446a90769e496b6ef102698955ea241643a9b Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Wed, 22 Mar 2023 12:07:24 -0700 Subject: Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I7f1dd239ae7180b6e70d6e59df005190ed2871b5 --- packages/SettingsLib/res/values-nb/strings.xml | 4 ++-- packages/SettingsLib/res/values-sw/strings.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index 4f8c76c5652f..91ee5d6bd74e 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -255,12 +255,12 @@ "Wi‑Fi-tilkoblingskode" "Tilkoblingen mislyktes" "Sørg for at enheten er koblet til samme nettverk." - "Koble til enheten via Wifi ved å skanne en QR-kode" + "Koble til enheten via wifi ved å skanne en QR-kode" "Kobler til enheten …" "Kunne ikke koble til enheten. Enten var QR-koden feil, eller enheten er ikke koblet til samme nettverk." "IP-adresse og port" "Skann QR-koden" - "Koble til enheten via Wifi ved å skanne en QR-kode" + "Koble til enheten via wifi ved å skanne en QR-kode" "Koble til et Wifi-nettverk" "adb, feilsøking, utvikler" "Snarvei til feilrapport" diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index 9096ce518455..21bd1c82b1b9 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -223,7 +223,7 @@ "Kasi zaidi" "Chagua wasifu" - "Ya Binafsi" + "Binafsi" "Ya Kazini" "Chaguo za wasanidi" "Washa chaguo za wasanidi programu" -- cgit v1.2.3 From 1009c5341112e062efa8703516467a7cde72ffa7 Mon Sep 17 00:00:00 2001 From: Kevin Jeon Date: Fri, 17 Mar 2023 17:47:22 +0000 Subject: DO NOT MERGE: Grant MANAGE_USERS access to Traceur This change updates the privapp allowlist to grant the MANAGE_USERS permission to Traceur. This permission is needed to query admin user status, as Traceur shouldn't be able to start if the current user is not an admin. Test: Using ABTD, apply this change with ag/22140600 to verify that Traceur still works as intended (opening app, tracing, etc.). Bug: 262243665 Bug: 262244249 Change-Id: I481c95fed4a364239f0671ed1b4d9bfaa0b07bd6 --- data/etc/privapp-permissions-platform.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 813b7995fe89..3dd16721e529 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -516,6 +516,8 @@ applications that come with the platform + + -- cgit v1.2.3 From f42db15239663604eb5d36edb04a0f9a04576568 Mon Sep 17 00:00:00 2001 From: Kevin Jeon Date: Fri, 17 Mar 2023 19:26:17 +0000 Subject: Grant MANAGE_USERS access to Traceur This change updates the privapp allowlist to grant the MANAGE_USERS permission to Traceur. This permission is needed to query admin user status, as Traceur shouldn't be able to start if the current user is not an admin. Test: Using ABTD, apply this change with ag/22119816 to verify that Traceur still works as intended (opening app, tracing, etc.). Bug: 262243665 Bug: 262244249 Change-Id: I8e2174065b686c052cb080b3590ea4d89e7a7783 Merged-In: I481c95fed4a364239f0671ed1b4d9bfaa0b07bd6 --- data/etc/privapp-permissions-platform.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 813b7995fe89..3dd16721e529 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -516,6 +516,8 @@ applications that come with the platform + + -- cgit v1.2.3 From 6c4a347bd225f6427b50978ae02ad6f1af15f538 Mon Sep 17 00:00:00 2001 From: John Wu Date: Thu, 14 Jul 2022 00:44:58 +0000 Subject: Proper PendingIntent.queryIntentComponents implementation PendingIntent.queryIntentComponents currently does not take the pending intent creator UID into account when performing component resolution. This will return incorrect results, since explicit intents with non-matching intent filter can only be resolved if the sender is the same UID as the target component. Bug: 238415222 Bug: 271845008 Test: atest CtsContentTestCases:PackageManagerTest Change-Id: Ib4e1c04c8de4e9bcee35d584dfb213954ec65449 Merged-In: Ib4e1c04c8de4e9bcee35d584dfb213954ec65449 --- .../com/android/server/am/ActivityManagerService.java | 17 ++++++++++------- services/core/java/com/android/server/pm/Computer.java | 2 ++ .../core/java/com/android/server/pm/ComputerEngine.java | 9 +++++++++ .../android/server/pm/PackageManagerInternalBase.java | 3 ++- 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index ecfa1f8bde27..cd221a8feb18 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -450,6 +450,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.UUID; import java.util.concurrent.CopyOnWriteArrayList; @@ -5535,7 +5536,7 @@ public class ActivityManagerService extends IActivityManager.Stub IIntentSender pendingResult, int matchFlags) { enforceCallingPermission(Manifest.permission.GET_INTENT_SENDER_INTENT, "queryIntentComponentsForIntentSender()"); - Preconditions.checkNotNull(pendingResult); + Objects.requireNonNull(pendingResult); final PendingIntentRecord res; try { res = (PendingIntentRecord) pendingResult; @@ -5547,17 +5548,19 @@ public class ActivityManagerService extends IActivityManager.Stub return null; } final int userId = res.key.userId; + final int uid = res.uid; + final String resolvedType = res.key.requestResolvedType; switch (res.key.type) { case ActivityManager.INTENT_SENDER_ACTIVITY: - return new ParceledListSlice<>(mContext.getPackageManager() - .queryIntentActivitiesAsUser(intent, matchFlags, userId)); + return new ParceledListSlice<>(mPackageManagerInt.queryIntentActivities( + intent, resolvedType, matchFlags, uid, userId)); case ActivityManager.INTENT_SENDER_SERVICE: case ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE: - return new ParceledListSlice<>(mContext.getPackageManager() - .queryIntentServicesAsUser(intent, matchFlags, userId)); + return new ParceledListSlice<>(mPackageManagerInt.queryIntentServices( + intent, matchFlags, uid, userId)); case ActivityManager.INTENT_SENDER_BROADCAST: - return new ParceledListSlice<>(mContext.getPackageManager() - .queryBroadcastReceiversAsUser(intent, matchFlags, userId)); + return new ParceledListSlice<>(mPackageManagerInt.queryIntentReceivers( + intent, resolvedType, matchFlags, uid, userId, false)); default: // ActivityManager.INTENT_SENDER_ACTIVITY_RESULT throw new IllegalStateException("Unsupported intent sender type: " + res.key.type); } diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java index eb635500580a..3d9e89aa1846 100644 --- a/services/core/java/com/android/server/pm/Computer.java +++ b/services/core/java/com/android/server/pm/Computer.java @@ -112,6 +112,8 @@ public interface Computer extends PackageDataSnapshot { @PackageManager.ResolveInfoFlagsBits long flags, @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags, int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits); + @NonNull List queryIntentActivitiesInternal(Intent intent, String resolvedType, + long flags, int filterCallingUid, int userId); @NonNull List queryIntentActivitiesInternal(Intent intent, String resolvedType, long flags, int userId); @NonNull List queryIntentServicesInternal(Intent intent, String resolvedType, diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java index 259ca655d2b9..4a640ce6274c 100644 --- a/services/core/java/com/android/server/pm/ComputerEngine.java +++ b/services/core/java/com/android/server/pm/ComputerEngine.java @@ -599,6 +599,15 @@ public class ComputerEngine implements Computer { resolveForStart, userId, intent); } + @NonNull + @Override + public final List queryIntentActivitiesInternal(Intent intent, String resolvedType, + @PackageManager.ResolveInfoFlagsBits long flags, int filterCallingUid, int userId) { + return queryIntentActivitiesInternal( + intent, resolvedType, flags, 0 /*privateResolveFlags*/, filterCallingUid, + userId, false /*resolveForStart*/, true /*allowDynamicSplits*/); + } + public final @NonNull List queryIntentActivitiesInternal(Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) { return queryIntentActivitiesInternal( diff --git a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java index 652847ad1647..96f37424ea4a 100644 --- a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java +++ b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java @@ -308,7 +308,8 @@ abstract class PackageManagerInternalBase extends PackageManagerInternal { public final List queryIntentActivities( Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int filterCallingUid, int userId) { - return snapshot().queryIntentActivitiesInternal(intent, resolvedType, flags, userId); + return snapshot().queryIntentActivitiesInternal(intent, resolvedType, flags, + filterCallingUid, userId); } @Override -- cgit v1.2.3 From 3ee3b7a20a109063cdc2233a1429f78ad8c5ab79 Mon Sep 17 00:00:00 2001 From: John Wu Date: Thu, 21 Jul 2022 22:29:01 +0000 Subject: Update ActivityIntentHelper to use PendingIntents directly Use PendingIntent.queryIntentComponents to query components instead of going through the intent contained. This allows package manager to properly determine the identity to perform the intent resolution. Bug: 238415222 Bug: 271845008 Test: manual Change-Id: I0dd32765755732a7a9141e3be2b04b7eed696928 Merged-In: I0dd32765755732a7a9141e3be2b04b7eed696928 --- .../com/android/systemui/ActivityIntentHelper.java | 61 +++++++++++++++++++--- .../statusbar/phone/CentralSurfacesImpl.java | 2 +- .../StatusBarNotificationActivityStarter.java | 4 +- .../phone/StatusBarRemoteInputCallback.java | 5 +- .../StatusBarNotificationActivityStarterTest.java | 5 -- 5 files changed, 60 insertions(+), 17 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/ActivityIntentHelper.java b/packages/SystemUI/src/com/android/systemui/ActivityIntentHelper.java index 43b3929808b3..df65bcf9c10d 100644 --- a/packages/SystemUI/src/com/android/systemui/ActivityIntentHelper.java +++ b/packages/SystemUI/src/com/android/systemui/ActivityIntentHelper.java @@ -16,6 +16,7 @@ package com.android.systemui; +import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; @@ -34,12 +35,12 @@ import javax.inject.Inject; @SysUISingleton public class ActivityIntentHelper { - private final Context mContext; + private final PackageManager mPm; @Inject public ActivityIntentHelper(Context context) { // TODO: inject a package manager, not a context. - mContext = context; + mPm = context.getPackageManager(); } /** @@ -56,6 +57,15 @@ public class ActivityIntentHelper { return targetActivityInfo == null; } + /** + * @see #wouldLaunchResolverActivity(Intent, int) + */ + public boolean wouldPendingLaunchResolverActivity(PendingIntent intent, int currentUserId) { + ActivityInfo targetActivityInfo = getPendingTargetActivityInfo(intent, currentUserId, + false /* onlyDirectBootAware */); + return targetActivityInfo == null; + } + /** * Returns info about the target Activity of a given intent, or null if the intent does not * resolve to a specific component meeting the requirements. @@ -68,19 +78,45 @@ public class ActivityIntentHelper { */ public ActivityInfo getTargetActivityInfo(Intent intent, int currentUserId, boolean onlyDirectBootAware) { - PackageManager packageManager = mContext.getPackageManager(); - int flags = PackageManager.MATCH_DEFAULT_ONLY; + int flags = PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA; if (!onlyDirectBootAware) { flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE; } - final List appList = packageManager.queryIntentActivitiesAsUser( + final List appList = mPm.queryIntentActivitiesAsUser( intent, flags, currentUserId); if (appList.size() == 0) { return null; } - ResolveInfo resolved = packageManager.resolveActivityAsUser(intent, - flags | PackageManager.GET_META_DATA, currentUserId); + if (appList.size() == 1) { + return appList.get(0).activityInfo; + } + ResolveInfo resolved = mPm.resolveActivityAsUser(intent, flags, currentUserId); + if (resolved == null || wouldLaunchResolverActivity(resolved, appList)) { + return null; + } else { + return resolved.activityInfo; + } + } + + /** + * @see #getTargetActivityInfo(Intent, int, boolean) + */ + public ActivityInfo getPendingTargetActivityInfo(PendingIntent intent, int currentUserId, + boolean onlyDirectBootAware) { + int flags = PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA; + if (!onlyDirectBootAware) { + flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE; + } + final List appList = intent.queryIntentComponents(flags); + if (appList.size() == 0) { + return null; + } + if (appList.size() == 1) { + return appList.get(0).activityInfo; + } + ResolveInfo resolved = mPm.resolveActivityAsUser(intent.getIntent(), flags, currentUserId); if (resolved == null || wouldLaunchResolverActivity(resolved, appList)) { return null; } else { @@ -103,6 +139,17 @@ public class ActivityIntentHelper { | ActivityInfo.FLAG_SHOW_FOR_ALL_USERS)) > 0; } + /** + * @see #wouldShowOverLockscreen(Intent, int) + */ + public boolean wouldPendingShowOverLockscreen(PendingIntent intent, int currentUserId) { + ActivityInfo targetActivityInfo = getPendingTargetActivityInfo(intent, + currentUserId, false /* onlyDirectBootAware */); + return targetActivityInfo != null + && (targetActivityInfo.flags & (ActivityInfo.FLAG_SHOW_WHEN_LOCKED + | ActivityInfo.FLAG_SHOW_FOR_ALL_USERS)) > 0; + } + /** * Determines if sending the given intent would result in starting an Intent resolver activity, * instead of resolving to a specific component. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java index 0e35cbc1aaf5..54da45a4e907 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -4117,7 +4117,7 @@ public class CentralSurfacesImpl extends CoreStartable implements final PendingIntent intent, @Nullable final Runnable intentSentUiThreadCallback, @Nullable ActivityLaunchAnimator.Controller animationController) { final boolean willLaunchResolverActivity = intent.isActivity() - && mActivityIntentHelper.wouldLaunchResolverActivity(intent.getIntent(), + && mActivityIntentHelper.wouldPendingLaunchResolverActivity(intent, mLockscreenUserManager.getCurrentUserId()); boolean animate = !willLaunchResolverActivity diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index cf776e3b60d1..0d1e3d48cca7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -253,12 +253,12 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte boolean isActivityIntent = intent != null && intent.isActivity() && !isBubble; final boolean willLaunchResolverActivity = isActivityIntent - && mActivityIntentHelper.wouldLaunchResolverActivity(intent.getIntent(), + && mActivityIntentHelper.wouldPendingLaunchResolverActivity(intent, mLockscreenUserManager.getCurrentUserId()); final boolean animate = !willLaunchResolverActivity && mCentralSurfaces.shouldAnimateLaunch(isActivityIntent); boolean showOverLockscreen = mKeyguardStateController.isShowing() && intent != null - && mActivityIntentHelper.wouldShowOverLockscreen(intent.getIntent(), + && mActivityIntentHelper.wouldPendingShowOverLockscreen(intent, mLockscreenUserManager.getCurrentUserId()); ActivityStarter.OnDismissAction postKeyguardAction = new ActivityStarter.OnDismissAction() { @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java index 262dc837f22c..c53830d810b4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java @@ -258,8 +258,9 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks, final boolean isActivity = pendingIntent.isActivity(); if (isActivity || appRequestedAuth) { mActionClickLogger.logWaitingToCloseKeyguard(pendingIntent); - final boolean afterKeyguardGone = mActivityIntentHelper.wouldLaunchResolverActivity( - pendingIntent.getIntent(), mLockscreenUserManager.getCurrentUserId()); + final boolean afterKeyguardGone = mActivityIntentHelper + .wouldPendingLaunchResolverActivity(pendingIntent, + mLockscreenUserManager.getCurrentUserId()); mActivityStarter.dismissKeyguardThenExecute(() -> { mActionClickLogger.logKeyguardGone(pendingIntent); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java index ecea14c6a522..53692d974d73 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java @@ -37,7 +37,6 @@ import android.app.KeyguardManager; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; -import android.content.Intent; import android.os.Handler; import android.os.RemoteException; import android.os.UserHandle; @@ -137,8 +136,6 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { @Mock private PendingIntent mContentIntent; @Mock - private Intent mContentIntentInner; - @Mock private OnUserInteractionCallback mOnUserInteractionCallback; @Mock private Runnable mFutureDismissalRunnable; @@ -163,7 +160,6 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); when(mContentIntent.isActivity()).thenReturn(true); when(mContentIntent.getCreatorUserHandle()).thenReturn(UserHandle.of(1)); - when(mContentIntent.getIntent()).thenReturn(mContentIntentInner); mNotificationTestHelper = new NotificationTestHelper( mContext, @@ -387,7 +383,6 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { eq(entry.getKey()), any(NotificationVisibility.class)); // The content intent should NOT be sent on click. - verify(mContentIntent).getIntent(); verify(mContentIntent).isActivity(); verifyNoMoreInteractions(mContentIntent); -- cgit v1.2.3 From 223e9c5839308d8cd2e14242315a0e27a5154258 Mon Sep 17 00:00:00 2001 From: Beth Thibodeau Date: Mon, 13 Mar 2023 16:59:33 -0500 Subject: Use PendingIntent for media click action over lockscreen The clickIntent is provided by apps as the notification's contentIntent, and it should be sent as is. This fixes the case where the intent called an activity that could show over lockscreen. Bug: 271845008 Test: atest MediaControlPanelTest Test: manually with test app Change-Id: I09d64452c46c4d21b9d958570020b2f5e6c2b23f Merged-In: I09d64452c46c4d21b9d958570020b2f5e6c2b23f (cherry picked from commit cb2904c7ff653a87cc98904bcb3bcb9c3b6e06ea) --- .../src/com/android/systemui/media/MediaControlPanel.java | 12 ++++++------ .../com/android/systemui/media/MediaControlPanelTest.kt | 14 ++++++++------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java index 37be0b382c91..e6c4226e1e2f 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java @@ -428,16 +428,16 @@ public class MediaControlPanel { mLogger.logTapContentView(mUid, mPackageName, mInstanceId); logSmartspaceCardReported(SMARTSPACE_CARD_CLICK_EVENT); - // See StatusBarNotificationActivityStarter#onNotificationClicked boolean showOverLockscreen = mKeyguardStateController.isShowing() - && mActivityIntentHelper.wouldShowOverLockscreen(clickIntent.getIntent(), + && mActivityIntentHelper.wouldPendingShowOverLockscreen(clickIntent, mLockscreenUserManager.getCurrentUserId()); if (showOverLockscreen) { - mActivityStarter.startActivity(clickIntent.getIntent(), - /* dismissShade */ true, - /* animationController */ null, - /* showOverLockscreenWhenLocked */ true); + try { + clickIntent.send(); + } catch (PendingIntent.CanceledException e) { + Log.e(TAG, "Pending intent for " + key + " was cancelled"); + } } else { mActivityStarter.postStartActivityDismissingKeyguard(clickIntent, buildLaunchAnimatorController(mMediaViewHolder.getPlayer())); diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt index 6cf2e6606fe8..14d34e4284f1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt @@ -71,7 +71,6 @@ import com.android.systemui.util.mockito.KotlinArgumentCaptor import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.eq -import com.android.systemui.util.mockito.nullable import com.android.systemui.util.mockito.withArgCaptor import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat @@ -1548,7 +1547,7 @@ public class MediaControlPanelTest : SysuiTestCase() { fun tapContentView_showOverLockscreen_openActivity() { // WHEN we are on lockscreen and this activity can show over lockscreen whenever(keyguardStateController.isShowing).thenReturn(true) - whenever(activityIntentHelper.wouldShowOverLockscreen(any(), any())).thenReturn(true) + whenever(activityIntentHelper.wouldPendingShowOverLockscreen(any(), any())).thenReturn(true) val clickIntent = mock(Intent::class.java) val pendingIntent = mock(PendingIntent::class.java) @@ -1559,17 +1558,20 @@ public class MediaControlPanelTest : SysuiTestCase() { player.bindPlayer(data, KEY) verify(viewHolder.player).setOnClickListener(captor.capture()) - // THEN it shows without dismissing keyguard first + // THEN it sends the PendingIntent without dismissing keyguard first, + // and does not use the Intent directly (see b/271845008) captor.value.onClick(viewHolder.player) - verify(activityStarter).startActivity(eq(clickIntent), eq(true), - nullable(), eq(true)) + verify(pendingIntent).send() + verify(pendingIntent, never()).getIntent() + verify(activityStarter, never()).postStartActivityDismissingKeyguard(eq(clickIntent), any()) } @Test fun tapContentView_noShowOverLockscreen_dismissKeyguard() { // WHEN we are on lockscreen and the activity cannot show over lockscreen whenever(keyguardStateController.isShowing).thenReturn(true) - whenever(activityIntentHelper.wouldShowOverLockscreen(any(), any())).thenReturn(false) + whenever(activityIntentHelper.wouldPendingShowOverLockscreen(any(), any())) + .thenReturn(false) val clickIntent = mock(Intent::class.java) val pendingIntent = mock(PendingIntent::class.java) -- cgit v1.2.3