From 49419d93e57e0592aabc2a61d2a9a3f56d38ef77 Mon Sep 17 00:00:00 2001 From: Louis Chang Date: Wed, 28 Sep 2022 06:46:29 +0000 Subject: [PATCH 01/14] Trim the activity info of another uid if no privilege The activity info could be from another uid which is different from the app that hosts the task. The information should be trimmed if the caller app doesn't have the privilege. Bug: 243130512 Test: verified locally Test: atest RecentTasksTest Change-Id: Ia343ac70e5bb9aeae718fca6674e1ca491a14512 (cherry picked from commit 401e782b244bf84fd5aab371f60c2e52d6226fb3) Merged-In: Ia343ac70e5bb9aeae718fca6674e1ca491a14512 --- .../com/android/server/wm/AppTaskImpl.java | 2 +- .../com/android/server/wm/RecentTasks.java | 8 ++++-- .../com/android/server/wm/RunningTasks.java | 4 +++ .../core/java/com/android/server/wm/Task.java | 21 +++++++++++++++ .../android/server/wm/RecentTasksTest.java | 27 +++++++++++++++---- 5 files changed, 54 insertions(+), 8 deletions(-) diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java index e80c2607a0ade..0bfc48b4b54c5 100644 --- a/services/core/java/com/android/server/wm/AppTaskImpl.java +++ b/services/core/java/com/android/server/wm/AppTaskImpl.java @@ -98,7 +98,7 @@ class AppTaskImpl extends IAppTask.Stub { throw new IllegalArgumentException("Unable to find task ID " + mTaskId); } return mService.getRecentTasks().createRecentTaskInfo(task, - false /* stripExtras */); + false /* stripExtras */, true /* getTasksAllowed */); } finally { Binder.restoreCallingIdentity(origId); } diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java index 4860762a5f7f7..1fc061b2ca787 100644 --- a/services/core/java/com/android/server/wm/RecentTasks.java +++ b/services/core/java/com/android/server/wm/RecentTasks.java @@ -976,7 +976,7 @@ class RecentTasks { continue; } - res.add(createRecentTaskInfo(task, true /* stripExtras */)); + res.add(createRecentTaskInfo(task, true /* stripExtras */, getTasksAllowed)); } return res; } @@ -1895,7 +1895,8 @@ class RecentTasks { /** * Creates a new RecentTaskInfo from a Task. */ - ActivityManager.RecentTaskInfo createRecentTaskInfo(Task tr, boolean stripExtras) { + ActivityManager.RecentTaskInfo createRecentTaskInfo(Task tr, boolean stripExtras, + boolean getTasksAllowed) { final ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo(); // If the recent Task is detached, we consider it will be re-attached to the default // TaskDisplayArea because we currently only support recent overview in the default TDA. @@ -1907,6 +1908,9 @@ class RecentTasks { rti.id = rti.isRunning ? rti.taskId : INVALID_TASK_ID; rti.persistentId = rti.taskId; rti.lastSnapshotData.set(tr.mLastTaskSnapshotData); + if (!getTasksAllowed) { + Task.trimIneffectiveInfo(tr, rti); + } // Fill in organized child task info for the task created by organizer. if (tr.mCreatedByOrganizer) { diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java index 120fec0fe0e63..0e60274ba3815 100644 --- a/services/core/java/com/android/server/wm/RunningTasks.java +++ b/services/core/java/com/android/server/wm/RunningTasks.java @@ -142,6 +142,10 @@ class RunningTasks { task.fillTaskInfo(rti, !mKeepIntentExtra); // Fill in some deprecated values rti.id = rti.taskId; + + if (!mAllowed) { + Task.trimIneffectiveInfo(task, rti); + } return rti; } } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index aede32c453f23..7dc6f95212511 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -3446,6 +3446,27 @@ class Task extends TaskFragment { info.isSleeping = shouldSleepActivities(); } + /** + * Removes the activity info if the activity belongs to a different uid, which is + * different from the app that hosts the task. + */ + static void trimIneffectiveInfo(Task task, TaskInfo info) { + final ActivityRecord baseActivity = task.getActivity(r -> !r.finishing, + false /* traverseTopToBottom */); + final int baseActivityUid = + baseActivity != null ? baseActivity.getUid() : task.effectiveUid; + + if (info.topActivityInfo != null + && task.effectiveUid != info.topActivityInfo.applicationInfo.uid) { + info.topActivity = null; + info.topActivityInfo = null; + } + + if (task.effectiveUid != baseActivityUid) { + info.baseActivity = null; + } + } + @Nullable PictureInPictureParams getPictureInPictureParams() { final Task topTask = getTopMostTask(); if (topTask == null) return null; diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java index adf694c2a88df..0462e1be7a5f3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java @@ -30,6 +30,7 @@ import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE; import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; +import static android.os.Process.NOBODY_UID; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; @@ -1220,20 +1221,34 @@ public class RecentTasksTest extends WindowTestsBase { @Test public void testCreateRecentTaskInfo_detachedTask() { - final Task task = createTaskBuilder(".Task").setCreateActivity(true).build(); + final Task task = createTaskBuilder(".Task").build(); + new ActivityBuilder(mSupervisor.mService) + .setTask(task) + .setUid(NOBODY_UID) + .setComponent(getUniqueComponentName()) + .build(); final TaskDisplayArea tda = task.getDisplayArea(); assertTrue(task.isAttached()); assertTrue(task.supportsMultiWindow()); - RecentTaskInfo info = mRecentTasks.createRecentTaskInfo(task, true); + RecentTaskInfo info = mRecentTasks.createRecentTaskInfo(task, true /* stripExtras */, + true /* getTasksAllowed */); assertTrue(info.supportsMultiWindow); + info = mRecentTasks.createRecentTaskInfo(task, true /* stripExtras */, + false /* getTasksAllowed */); + + assertTrue(info.topActivity == null); + assertTrue(info.topActivityInfo == null); + assertTrue(info.baseActivity == null); + // The task can be put in split screen even if it is not attached now. task.removeImmediately(); - info = mRecentTasks.createRecentTaskInfo(task, true); + info = mRecentTasks.createRecentTaskInfo(task, true /* stripExtras */, + true /* getTasksAllowed */); assertTrue(info.supportsMultiWindow); @@ -1242,7 +1257,8 @@ public class RecentTasksTest extends WindowTestsBase { doReturn(false).when(tda).supportsNonResizableMultiWindow(); doReturn(false).when(task).isResizeable(); - info = mRecentTasks.createRecentTaskInfo(task, true); + info = mRecentTasks.createRecentTaskInfo(task, true /* stripExtras */, + true /* getTasksAllowed */); assertFalse(info.supportsMultiWindow); @@ -1250,7 +1266,8 @@ public class RecentTasksTest extends WindowTestsBase { // the device supports it. doReturn(true).when(tda).supportsNonResizableMultiWindow(); - info = mRecentTasks.createRecentTaskInfo(task, true); + info = mRecentTasks.createRecentTaskInfo(task, true /* stripExtras */, + true /* getTasksAllowed */); assertTrue(info.supportsMultiWindow); } From d5ac0ac93bd25f8a74ff35f1c89917f70ac5e99e Mon Sep 17 00:00:00 2001 From: Nicolo' Mazzucato Date: Wed, 5 Oct 2022 13:49:21 +0000 Subject: [PATCH 02/14] Fix QSPanel horizontal layout issue after unfolding QS (Quick Settings) layout could be horizontal or not. When it is horizontal, the media player is displayed on the side of QS. This is based on orientation, split shade state, and media player visibility. However the configuration changed lister was triggering the update only after orientation changed, neglecting split shade state. When a foldable device that is in the state "folded and landscape" is unfolded, the orientation stays the same, despite the split shade state changes. QSPanelControllerBase was updating the QS layout only if the orientation changed, neglecting other condition changes. Fixes: 216244185 Test: CorrectQSAfterUnfold QSPanelControllerBaseTest Change-Id: I7f1444adf477f75c267a48bf8f420d3507f82709 (cherry picked from commit 3e6a8bcf0d78f2ed6fc4c298c5d25ace21c11ac8) Merged-In: I7f1444adf477f75c267a48bf8f420d3507f82709 --- .../systemui/qs/QSPanelControllerBase.java | 30 +++++++++---------- .../qs/QSPanelControllerBaseTest.java | 22 +++++++++++++- 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java index ded466a0cb25b..2727c83ad8770 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java @@ -23,8 +23,8 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; import android.content.res.Configuration; +import android.content.res.Configuration.Orientation; import android.metrics.LogMaker; -import android.util.Log; import android.view.View; import com.android.internal.annotations.VisibleForTesting; @@ -75,6 +75,7 @@ public abstract class QSPanelControllerBase extends ViewContr @Nullable private Consumer mMediaVisibilityChangedListener; + @Orientation private int mLastOrientation; private String mCachedSpecs = ""; @Nullable @@ -88,21 +89,16 @@ public abstract class QSPanelControllerBase extends ViewContr new QSPanel.OnConfigurationChangedListener() { @Override public void onConfigurationChange(Configuration newConfig) { + mQSLogger.logOnConfigurationChanged( + /* lastOrientation= */ mLastOrientation, + /* newOrientation= */ newConfig.orientation, + /* containerName= */ mView.getDumpableTag()); + mShouldUseSplitNotificationShade = - LargeScreenUtils.shouldUseSplitNotificationShade(getResources()); - // Logging to aid the investigation of b/216244185. - Log.d(TAG, - "onConfigurationChange: " - + "mShouldUseSplitNotificationShade=" - + mShouldUseSplitNotificationShade + ", " - + "newConfig.windowConfiguration=" - + newConfig.windowConfiguration); - mQSLogger.logOnConfigurationChanged(mLastOrientation, newConfig.orientation, - mView.getDumpableTag()); - if (newConfig.orientation != mLastOrientation) { - mLastOrientation = newConfig.orientation; - switchTileLayout(false); - } + LargeScreenUtils.shouldUseSplitNotificationShade(getResources()); + mLastOrientation = newConfig.orientation; + + switchTileLayoutIfNeeded(); onConfigurationChanged(); } }; @@ -334,6 +330,10 @@ public abstract class QSPanelControllerBase extends ViewContr } } + private void switchTileLayoutIfNeeded() { + switchTileLayout(/* force= */ false); + } + boolean switchTileLayout(boolean force) { /* Whether or not the panel currently contains a media player. */ boolean horizontal = shouldUseHorizontalLayout(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java index 3cad2a005882b..b847ad07cd725 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java @@ -277,7 +277,7 @@ public class QSPanelControllerBaseTest extends SysuiTestCase { // Then the layout changes assertThat(mController.shouldUseHorizontalLayout()).isTrue(); - verify(mHorizontalLayoutListener).run(); // not invoked + verify(mHorizontalLayoutListener).run(); // When it is rotated back to portrait mConfiguration.orientation = Configuration.ORIENTATION_PORTRAIT; @@ -300,4 +300,24 @@ public class QSPanelControllerBaseTest extends SysuiTestCase { verify(mQSTile).refreshState(); verify(mOtherTile, never()).refreshState(); } + + @Test + public void configurationChange_onlySplitShadeConfigChanges_horizontalLayoutStatusUpdated() { + // Preconditions for horizontal layout + when(mMediaHost.getVisible()).thenReturn(true); + when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(false); + mConfiguration.orientation = Configuration.ORIENTATION_LANDSCAPE; + mController.setUsingHorizontalLayoutChangeListener(mHorizontalLayoutListener); + mController.mOnConfigurationChangedListener.onConfigurationChange(mConfiguration); + assertThat(mController.shouldUseHorizontalLayout()).isTrue(); + reset(mHorizontalLayoutListener); + + // Only split shade status changes + when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(true); + mController.mOnConfigurationChangedListener.onConfigurationChange(mConfiguration); + + // Horizontal layout is updated accordingly. + assertThat(mController.shouldUseHorizontalLayout()).isFalse(); + verify(mHorizontalLayoutListener).run(); + } } From 621c982330ca957f8e9f52583d5da6f7a015d06c Mon Sep 17 00:00:00 2001 From: Liahav Eitan Date: Wed, 28 Sep 2022 14:50:56 +0000 Subject: [PATCH 03/14] [2/2] Fix sharing to another profile where an app has multiple targets Complete the change in ag/19657256 to also apply in SelectableTargetInfo. That would ensure that in all cases of cross-profile targets the URIs are fixed to include the user-id prefix. Bug:244876518 Bug:242605257 Test: manually share image from work Photos app to personal WhatsApp's frequent contact target. Change-Id: Ib41c8a3c46afcc2d62a4c1a924212bcd98bcfbe4 (cherry picked from commit a4fe4bd9a23bccbe29d72bebd6a028ce83383838) Merged-In: Ib41c8a3c46afcc2d62a4c1a924212bcd98bcfbe4 --- .../internal/app/chooser/DisplayResolveInfo.java | 11 ++--------- .../internal/app/chooser/SelectableTargetInfo.java | 1 + .../com/android/internal/app/chooser/TargetInfo.java | 11 +++++++++++ 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java index 5f4a9cd5141e6..473134ea46f3f 100644 --- a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java +++ b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java @@ -172,14 +172,14 @@ public class DisplayResolveInfo implements TargetInfo, Parcelable { @Override public boolean startAsCaller(ResolverActivity activity, Bundle options, int userId) { - prepareIntentForCrossProfileLaunch(mResolvedIntent, userId); + TargetInfo.prepareIntentForCrossProfileLaunch(mResolvedIntent, userId); activity.startActivityAsCaller(mResolvedIntent, options, false, userId); return true; } @Override public boolean startAsUser(Activity activity, Bundle options, UserHandle user) { - prepareIntentForCrossProfileLaunch(mResolvedIntent, user.getIdentifier()); + TargetInfo.prepareIntentForCrossProfileLaunch(mResolvedIntent, user.getIdentifier()); activity.startActivityAsUser(mResolvedIntent, options, user); return false; } @@ -224,13 +224,6 @@ public class DisplayResolveInfo implements TargetInfo, Parcelable { } }; - private static void prepareIntentForCrossProfileLaunch(Intent intent, int targetUserId) { - final int currentUserId = UserHandle.myUserId(); - if (targetUserId != currentUserId) { - intent.fixUris(currentUserId); - } - } - private DisplayResolveInfo(Parcel in) { mDisplayLabel = in.readCharSequence(); mExtendedInfo = in.readCharSequence(); diff --git a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java index 264e4f76d35df..4b9b7cb98dac3 100644 --- a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java +++ b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java @@ -232,6 +232,7 @@ public final class SelectableTargetInfo implements ChooserTargetInfo { } intent.setComponent(mChooserTarget.getComponentName()); intent.putExtras(mChooserTarget.getIntentExtras()); + TargetInfo.prepareIntentForCrossProfileLaunch(intent, userId); // Important: we will ignore the target security checks in ActivityManager // if and only if the ChooserTarget's target package is the same package diff --git a/core/java/com/android/internal/app/chooser/TargetInfo.java b/core/java/com/android/internal/app/chooser/TargetInfo.java index f56ab17cb0597..7bb7ddc65c6db 100644 --- a/core/java/com/android/internal/app/chooser/TargetInfo.java +++ b/core/java/com/android/internal/app/chooser/TargetInfo.java @@ -130,4 +130,15 @@ public interface TargetInfo { * @return true if this target should be pinned to the front by the request of the user */ boolean isPinned(); + + /** + * Fix the URIs in {@code intent} if cross-profile sharing is required. This should be called + * before launching the intent as another user. + */ + static void prepareIntentForCrossProfileLaunch(Intent intent, int targetUserId) { + final int currentUserId = UserHandle.myUserId(); + if (targetUserId != currentUserId) { + intent.fixUris(currentUserId); + } + } } From 7cad088a533f967d94c8d436b609e4ed2b184897 Mon Sep 17 00:00:00 2001 From: Daniel Norman Date: Wed, 5 Oct 2022 16:28:20 -0700 Subject: [PATCH 04/14] Disable all A11yServices from an uninstalled package. Previous logic would exit the loop after removing the first service matching the uninstalled package. Bug: 243378132 Test: atest AccessibilityEndToEndTest Test: m sts; sts-tradefed run sts-dynamic-develop -m \ CtsAccessibilityServiceTestCases Change-Id: I4ba30345d8600674ee8a9ea3ff411aecbf3655a3 (cherry picked from commit e1f343acdeeddd9a08c9f6c832faf788ce101763) Merged-In: I4ba30345d8600674ee8a9ea3ff411aecbf3655a3 --- .../AccessibilityManagerService.java | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 6417db0eb0504..29194c58bd0c0 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -657,25 +657,27 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub userState.mBindingServices.removeIf(filter); userState.mCrashedServices.removeIf(filter); final Iterator it = userState.mEnabledServices.iterator(); + boolean anyServiceRemoved = false; while (it.hasNext()) { final ComponentName comp = it.next(); final String compPkg = comp.getPackageName(); if (compPkg.equals(packageName)) { it.remove(); - // Update the enabled services setting. - persistComponentNamesToSettingLocked( - Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, - userState.mEnabledServices, userId); - // Update the touch exploration granted services setting. userState.mTouchExplorationGrantedServices.remove(comp); - persistComponentNamesToSettingLocked( - Settings.Secure. - TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES, - userState.mTouchExplorationGrantedServices, userId); - onUserStateChangedLocked(userState); - return; + anyServiceRemoved = true; } } + if (anyServiceRemoved) { + // Update the enabled services setting. + persistComponentNamesToSettingLocked( + Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, + userState.mEnabledServices, userId); + // Update the touch exploration granted services setting. + persistComponentNamesToSettingLocked( + Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES, + userState.mTouchExplorationGrantedServices, userId); + onUserStateChangedLocked(userState); + } } } From e144b7802e04841a22afcb5100ac46be5e595d82 Mon Sep 17 00:00:00 2001 From: Songchun Fan Date: Wed, 17 Aug 2022 09:37:18 -0700 Subject: [PATCH 05/14] [RESTRICT AUTOMERGE] [SettingsProvider] mem limit should be checked before settings are updated Previously, a setting is updated before the memory usage limit check, which can be exploited by malicious apps and cause OoM DoS. This CL changes the logic to checkMemLimit -> update -> updateMemUsage. BUG: 239415861 Test: atest com.android.providers.settings.SettingsStateTest (cherry picked from commit 8eeb92950f4a7012d4cf282106a1418fd211f475) Merged-In: I20551a2dba9aa79efa0c064824f349f551c2c2e4 Change-Id: I20551a2dba9aa79efa0c064824f349f551c2c2e4 (cherry picked from commit d7a84dc64d2e19059ec742e0f644c3dc6095e15f) Merged-In: I20551a2dba9aa79efa0c064824f349f551c2c2e4 --- .../providers/settings/SettingsState.java | 79 ++++++++++++------- .../providers/settings/SettingsStateTest.java | 38 +++++++++ 2 files changed, 88 insertions(+), 29 deletions(-) diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java index fd7554f118730..528af2ec25289 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java @@ -376,9 +376,11 @@ final class SettingsState { Setting newSetting = new Setting(name, oldSetting.getValue(), null, oldSetting.getPackageName(), oldSetting.getTag(), false, oldSetting.getId()); - mSettings.put(name, newSetting); - updateMemoryUsagePerPackageLocked(newSetting.getPackageName(), oldValue, + int newSize = getNewMemoryUsagePerPackageLocked(newSetting.getPackageName(), oldValue, newSetting.getValue(), oldDefaultValue, newSetting.getDefaultValue()); + checkNewMemoryUsagePerPackageLocked(newSetting.getPackageName(), newSize); + mSettings.put(name, newSetting); + updateMemoryUsagePerPackageLocked(newSetting.getPackageName(), newSize); scheduleWriteIfNeededLocked(); } } @@ -410,6 +412,12 @@ final class SettingsState { Setting oldState = mSettings.get(name); String oldValue = (oldState != null) ? oldState.value : null; String oldDefaultValue = (oldState != null) ? oldState.defaultValue : null; + String newDefaultValue = makeDefault ? value : oldDefaultValue; + + int newSize = getNewMemoryUsagePerPackageLocked(packageName, oldValue, value, + oldDefaultValue, newDefaultValue); + checkNewMemoryUsagePerPackageLocked(packageName, newSize); + Setting newState; if (oldState != null) { @@ -430,8 +438,7 @@ final class SettingsState { addHistoricalOperationLocked(HISTORICAL_OPERATION_UPDATE, newState); - updateMemoryUsagePerPackageLocked(packageName, oldValue, value, - oldDefaultValue, newState.getDefaultValue()); + updateMemoryUsagePerPackageLocked(packageName, newSize); scheduleWriteIfNeededLocked(); @@ -552,13 +559,14 @@ final class SettingsState { } Setting oldState = mSettings.remove(name); + int newSize = getNewMemoryUsagePerPackageLocked(oldState.packageName, oldState.value, + null, oldState.defaultValue, null); FrameworkStatsLog.write(FrameworkStatsLog.SETTING_CHANGED, name, /* value= */ "", /* newValue= */ "", oldState.value, /* tag */ "", false, getUserIdFromKey(mKey), FrameworkStatsLog.SETTING_CHANGED__REASON__DELETED); - updateMemoryUsagePerPackageLocked(oldState.packageName, oldState.value, - null, oldState.defaultValue, null); + updateMemoryUsagePerPackageLocked(oldState.packageName, newSize); addHistoricalOperationLocked(HISTORICAL_OPERATION_DELETE, oldState); @@ -579,16 +587,18 @@ final class SettingsState { Setting oldSetting = new Setting(setting); String oldValue = setting.getValue(); String oldDefaultValue = setting.getDefaultValue(); + String newValue = oldDefaultValue; + String newDefaultValue = oldDefaultValue; + + int newSize = getNewMemoryUsagePerPackageLocked(setting.packageName, oldValue, + newValue, oldDefaultValue, newDefaultValue); + checkNewMemoryUsagePerPackageLocked(setting.packageName, newSize); if (!setting.reset()) { return false; } - String newValue = setting.getValue(); - String newDefaultValue = setting.getDefaultValue(); - - updateMemoryUsagePerPackageLocked(setting.packageName, oldValue, - newValue, oldDefaultValue, newDefaultValue); + updateMemoryUsagePerPackageLocked(setting.packageName, newSize); addHistoricalOperationLocked(HISTORICAL_OPERATION_RESET, oldSetting); @@ -696,38 +706,49 @@ final class SettingsState { } @GuardedBy("mLock") - private void updateMemoryUsagePerPackageLocked(String packageName, String oldValue, + private boolean isExemptFromMemoryUsageCap(String packageName) { + return mMaxBytesPerAppPackage == MAX_BYTES_PER_APP_PACKAGE_UNLIMITED + || SYSTEM_PACKAGE_NAME.equals(packageName); + } + + @GuardedBy("mLock") + private void checkNewMemoryUsagePerPackageLocked(String packageName, int newSize) + throws IllegalStateException { + if (isExemptFromMemoryUsageCap(packageName)) { + return; + } + if (newSize > mMaxBytesPerAppPackage) { + throw new IllegalStateException("You are adding too many system settings. " + + "You should stop using system settings for app specific data" + + " package: " + packageName); + } + } + + @GuardedBy("mLock") + private int getNewMemoryUsagePerPackageLocked(String packageName, String oldValue, String newValue, String oldDefaultValue, String newDefaultValue) { - if (mMaxBytesPerAppPackage == MAX_BYTES_PER_APP_PACKAGE_UNLIMITED) { - return; + if (isExemptFromMemoryUsageCap(packageName)) { + return 0; } - - if (SYSTEM_PACKAGE_NAME.equals(packageName)) { - return; - } - + final Integer currentSize = mPackageToMemoryUsage.get(packageName); final int oldValueSize = (oldValue != null) ? oldValue.length() : 0; final int newValueSize = (newValue != null) ? newValue.length() : 0; final int oldDefaultValueSize = (oldDefaultValue != null) ? oldDefaultValue.length() : 0; final int newDefaultValueSize = (newDefaultValue != null) ? newDefaultValue.length() : 0; final int deltaSize = newValueSize + newDefaultValueSize - oldValueSize - oldDefaultValueSize; + return Math.max((currentSize != null) ? currentSize + deltaSize : deltaSize, 0); + } - Integer currentSize = mPackageToMemoryUsage.get(packageName); - final int newSize = Math.max((currentSize != null) - ? currentSize + deltaSize : deltaSize, 0); - - if (newSize > mMaxBytesPerAppPackage) { - throw new IllegalStateException("You are adding too many system settings. " - + "You should stop using system settings for app specific data" - + " package: " + packageName); + @GuardedBy("mLock") + private void updateMemoryUsagePerPackageLocked(String packageName, int newSize) { + if (isExemptFromMemoryUsageCap(packageName)) { + return; } - if (DEBUG) { Slog.i(LOG_TAG, "Settings for package: " + packageName + " size: " + newSize + " bytes."); } - mPackageToMemoryUsage.put(packageName, newSize); } diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java index 69eb7133f46fe..66b809aeae308 100644 --- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java +++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java @@ -20,6 +20,8 @@ import android.test.AndroidTestCase; import android.util.TypedXmlSerializer; import android.util.Xml; +import com.google.common.base.Strings; + import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; @@ -276,4 +278,40 @@ public class SettingsStateTest extends AndroidTestCase { settingsState.setVersionLocked(SettingsState.SETTINGS_VERSION_NEW_ENCODING); return settingsState; } + + public void testInsertSetting_memoryUsage() { + SettingsState settingsState = new SettingsState(getContext(), mLock, mSettingsFile, 1, + SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); + // No exception should be thrown when there is no cap + settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001), + null, false, "p1"); + settingsState.deleteSettingLocked(SETTING_NAME); + + settingsState = new SettingsState(getContext(), mLock, mSettingsFile, 1, + SettingsState.MAX_BYTES_PER_APP_PACKAGE_LIMITED, Looper.getMainLooper()); + // System package doesn't have memory usage limit + settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001), + null, false, SYSTEM_PACKAGE); + settingsState.deleteSettingLocked(SETTING_NAME); + + // Should not throw if usage is under the cap + settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 19999), + null, false, "p1"); + settingsState.deleteSettingLocked(SETTING_NAME); + try { + settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001), + null, false, "p1"); + fail("Should throw because it exceeded per package memory usage"); + } catch (IllegalStateException ex) { + assertTrue(ex.getMessage().contains("p1")); + } + try { + settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001), + null, false, "p1"); + fail("Should throw because it exceeded per package memory usage"); + } catch (IllegalStateException ex) { + assertTrue(ex.getMessage().contains("p1")); + } + assertTrue(settingsState.getSettingLocked(SETTING_NAME).isNull()); + } } From e366f12457615fded2bb28057a5c7464ec97b58a Mon Sep 17 00:00:00 2001 From: Nate Myren Date: Thu, 22 Sep 2022 15:23:24 -0700 Subject: [PATCH 06/14] RESTRICT AUTOMERGE Validate permission tree size on permission update Bug: 242537498 Test: atest PermissionMemoryFootprintTest Change-Id: I15343e84c1802d6b89249106263319a6539fa73b (cherry picked from commit 973c67f7fc5d1321ff38e861dfe256a2877af5f0) Merged-In: I15343e84c1802d6b89249106263319a6539fa73b --- .../server/pm/permission/PermissionManagerServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java index 014d580e520fb..18c29fa9f3a23 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java @@ -644,8 +644,8 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt Permission bp = mRegistry.getPermission(info.name); added = bp == null; int fixedLevel = PermissionInfo.fixProtectionLevel(info.protectionLevel); + enforcePermissionCapLocked(info, tree); if (added) { - enforcePermissionCapLocked(info, tree); bp = new Permission(info.name, tree.getPackageName(), Permission.TYPE_DYNAMIC); } else if (!bp.isDynamic()) { throw new SecurityException("Not allowed to modify non-dynamic permission " From b58e58dd0b695e17885a14899f0dc3ea959aa8e5 Mon Sep 17 00:00:00 2001 From: Wenhao Wang Date: Tue, 30 Aug 2022 11:09:46 -0700 Subject: [PATCH 07/14] Enable user graularity for lockdown mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The NotificationManagerService registers a LockPatternUtils.StrongAuthTracker to observe the StrongAuth changes of every user. More specifically, it’s the STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN flag. Via this flag, NotificationManagerService can perform the following operations when the user enter or exit lockdown mode: Enter lockdown: 1. Remove all the notifications belonging to the user. 2. Set the local flag to indicate the lockdown is on for the user. The local flag will suppress the user's notifications on the post, remove and update functions. Exit lockdown: 1. Clear the local flag to indicate the lockdown is off for the user. 2. Repost the user’s notifications (suppressed during lockdown mode). The CL also updates corresponding tests. Bug: 173721373 Bug: 250743174 Test: atest NotificationManagerServiceTest Test: atest NotificationListenersTest Ignore-AOSP-First: pending fix for a security issue. Change-Id: I4f30e56550729db7d673a92d2a1250509713f36d Merged-In: I4f30e56550729db7d673a92d2a1250509713f36d (cherry picked from commit de3b12fca23178d8c821058261572449b67d5967) (cherry picked from commit ccead0884b808d68203568ce3183fc1182ed13e3) Merged-In: I4f30e56550729db7d673a92d2a1250509713f36d --- .../NotificationManagerService.java | 70 +++++---- .../NotificationListenersTest.java | 135 ++++++++++++------ .../NotificationManagerServiceTest.java | 51 ++++++- .../TestableNotificationManagerService.java | 18 +++ 4 files changed, 197 insertions(+), 77 deletions(-) diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index f11801f61d58a..63862e4992c5a 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -1977,34 +1977,39 @@ public class NotificationManagerService extends SystemService { return (haystack & needle) != 0; } - public boolean isInLockDownMode() { - return mIsInLockDownMode; + // Return whether the user is in lockdown mode. + // If the flag is not set, we assume the user is not in lockdown. + public boolean isInLockDownMode(int userId) { + return mUserInLockDownMode.get(userId, false); } @Override public synchronized void onStrongAuthRequiredChanged(int userId) { boolean userInLockDownModeNext = containsFlag(getStrongAuthForUser(userId), STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN); - mUserInLockDownMode.put(userId, userInLockDownModeNext); - boolean isInLockDownModeNext = mUserInLockDownMode.indexOfValue(true) != -1; - if (mIsInLockDownMode == isInLockDownModeNext) { + // Nothing happens if the lockdown mode of userId keeps the same. + if (userInLockDownModeNext == isInLockDownMode(userId)) { return; } - if (isInLockDownModeNext) { - cancelNotificationsWhenEnterLockDownMode(); + // When the lockdown mode is changed, we perform the following steps. + // If the userInLockDownModeNext is true, all the function calls to + // notifyPostedLocked and notifyRemovedLocked will not be executed. + // The cancelNotificationsWhenEnterLockDownMode calls notifyRemovedLocked + // and postNotificationsWhenExitLockDownMode calls notifyPostedLocked. + // So we shall call cancelNotificationsWhenEnterLockDownMode before + // we set mUserInLockDownMode as true. + // On the other hand, if the userInLockDownModeNext is false, we shall call + // postNotificationsWhenExitLockDownMode after we put false into mUserInLockDownMode + if (userInLockDownModeNext) { + cancelNotificationsWhenEnterLockDownMode(userId); } - // When the mIsInLockDownMode is true, both notifyPostedLocked and - // notifyRemovedLocked will be dismissed. So we shall call - // cancelNotificationsWhenEnterLockDownMode before we set mIsInLockDownMode - // as true and call postNotificationsWhenExitLockDownMode after we set - // mIsInLockDownMode as false. - mIsInLockDownMode = isInLockDownModeNext; + mUserInLockDownMode.put(userId, userInLockDownModeNext); - if (!isInLockDownModeNext) { - postNotificationsWhenExitLockDownMode(); + if (!userInLockDownModeNext) { + postNotificationsWhenExitLockDownMode(userId); } } } @@ -9671,11 +9676,14 @@ public class NotificationManagerService extends SystemService { } } - private void cancelNotificationsWhenEnterLockDownMode() { + private void cancelNotificationsWhenEnterLockDownMode(int userId) { synchronized (mNotificationLock) { int numNotifications = mNotificationList.size(); for (int i = 0; i < numNotifications; i++) { NotificationRecord rec = mNotificationList.get(i); + if (rec.getUser().getIdentifier() != userId) { + continue; + } mListeners.notifyRemovedLocked(rec, REASON_CANCEL_ALL, rec.getStats()); } @@ -9683,14 +9691,23 @@ public class NotificationManagerService extends SystemService { } } - private void postNotificationsWhenExitLockDownMode() { + private void postNotificationsWhenExitLockDownMode(int userId) { synchronized (mNotificationLock) { int numNotifications = mNotificationList.size(); + // Set the delay to spread out the burst of notifications. + long delay = 0; for (int i = 0; i < numNotifications; i++) { NotificationRecord rec = mNotificationList.get(i); - mListeners.notifyPostedLocked(rec, rec); + if (rec.getUser().getIdentifier() != userId) { + continue; + } + mHandler.postDelayed(() -> { + synchronized (mNotificationLock) { + mListeners.notifyPostedLocked(rec, rec); + } + }, delay); + delay += 20; } - } } @@ -9869,6 +9886,9 @@ public class NotificationManagerService extends SystemService { for (int i = 0; i < N; i++) { NotificationRecord record = mNotificationList.get(i); + if (isInLockDownMode(record.getUser().getIdentifier())) { + continue; + } if (!isVisibleToListener(record.getSbn(), record.getNotificationType(), info)) { continue; } @@ -9910,8 +9930,8 @@ public class NotificationManagerService extends SystemService { rankings.toArray(new NotificationListenerService.Ranking[0])); } - boolean isInLockDownMode() { - return mStrongAuthTracker.isInLockDownMode(); + boolean isInLockDownMode(int userId) { + return mStrongAuthTracker.isInLockDownMode(userId); } boolean hasCompanionDevice(ManagedServiceInfo info) { @@ -10967,7 +10987,7 @@ public class NotificationManagerService extends SystemService { @GuardedBy("mNotificationLock") void notifyPostedLocked(NotificationRecord r, NotificationRecord old, boolean notifyAllListeners) { - if (isInLockDownMode()) { + if (isInLockDownMode(r.getUser().getIdentifier())) { return; } @@ -11073,7 +11093,7 @@ public class NotificationManagerService extends SystemService { @GuardedBy("mNotificationLock") public void notifyRemovedLocked(NotificationRecord r, int reason, NotificationStats notificationStats) { - if (isInLockDownMode()) { + if (isInLockDownMode(r.getUser().getIdentifier())) { return; } @@ -11122,10 +11142,6 @@ public class NotificationManagerService extends SystemService { */ @GuardedBy("mNotificationLock") public void notifyRankingUpdateLocked(List changedHiddenNotifications) { - if (isInLockDownMode()) { - return; - } - boolean isHiddenRankingUpdate = changedHiddenNotifications != null && changedHiddenNotifications.size() > 0; // TODO (b/73052211): if the ranking update changed the notification type, diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java index c5131c8f8c1dd..57e403c21ac8d 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java @@ -435,63 +435,112 @@ public class NotificationListenersTest extends UiServiceTestCase { @Test public void testNotifyPostedLockedInLockdownMode() { - NotificationRecord r = mock(NotificationRecord.class); - NotificationRecord old = mock(NotificationRecord.class); + NotificationRecord r0 = mock(NotificationRecord.class); + NotificationRecord old0 = mock(NotificationRecord.class); + UserHandle uh0 = mock(UserHandle.class); - // before the lockdown mode - when(mNm.isInLockDownMode()).thenReturn(false); - mListeners.notifyPostedLocked(r, old, true); - mListeners.notifyPostedLocked(r, old, false); - verify(r, atLeast(2)).getSbn(); + NotificationRecord r1 = mock(NotificationRecord.class); + NotificationRecord old1 = mock(NotificationRecord.class); + UserHandle uh1 = mock(UserHandle.class); - // in the lockdown mode - reset(r); - reset(old); - when(mNm.isInLockDownMode()).thenReturn(true); - mListeners.notifyPostedLocked(r, old, true); - mListeners.notifyPostedLocked(r, old, false); - verify(r, never()).getSbn(); - } + // Neither user0 and user1 is in the lockdown mode + when(r0.getUser()).thenReturn(uh0); + when(uh0.getIdentifier()).thenReturn(0); + when(mNm.isInLockDownMode(0)).thenReturn(false); - @Test - public void testnotifyRankingUpdateLockedInLockdownMode() { - List chn = mock(List.class); + when(r1.getUser()).thenReturn(uh1); + when(uh1.getIdentifier()).thenReturn(1); + when(mNm.isInLockDownMode(1)).thenReturn(false); - // before the lockdown mode - when(mNm.isInLockDownMode()).thenReturn(false); - mListeners.notifyRankingUpdateLocked(chn); - verify(chn, atLeast(1)).size(); + mListeners.notifyPostedLocked(r0, old0, true); + mListeners.notifyPostedLocked(r0, old0, false); + verify(r0, atLeast(2)).getSbn(); - // in the lockdown mode - reset(chn); - when(mNm.isInLockDownMode()).thenReturn(true); - mListeners.notifyRankingUpdateLocked(chn); - verify(chn, never()).size(); + mListeners.notifyPostedLocked(r1, old1, true); + mListeners.notifyPostedLocked(r1, old1, false); + verify(r1, atLeast(2)).getSbn(); + + // Reset + reset(r0); + reset(old0); + reset(r1); + reset(old1); + + // Only user 0 is in the lockdown mode + when(r0.getUser()).thenReturn(uh0); + when(uh0.getIdentifier()).thenReturn(0); + when(mNm.isInLockDownMode(0)).thenReturn(true); + + when(r1.getUser()).thenReturn(uh1); + when(uh1.getIdentifier()).thenReturn(1); + when(mNm.isInLockDownMode(1)).thenReturn(false); + + mListeners.notifyPostedLocked(r0, old0, true); + mListeners.notifyPostedLocked(r0, old0, false); + verify(r0, never()).getSbn(); + + mListeners.notifyPostedLocked(r1, old1, true); + mListeners.notifyPostedLocked(r1, old1, false); + verify(r1, atLeast(2)).getSbn(); } @Test public void testNotifyRemovedLockedInLockdownMode() throws NoSuchFieldException { - NotificationRecord r = mock(NotificationRecord.class); - NotificationStats rs = mock(NotificationStats.class); + NotificationRecord r0 = mock(NotificationRecord.class); + NotificationStats rs0 = mock(NotificationStats.class); + UserHandle uh0 = mock(UserHandle.class); + + NotificationRecord r1 = mock(NotificationRecord.class); + NotificationStats rs1 = mock(NotificationStats.class); + UserHandle uh1 = mock(UserHandle.class); + StatusBarNotification sbn = mock(StatusBarNotification.class); FieldSetter.setField(mNm, NotificationManagerService.class.getDeclaredField("mHandler"), mock(NotificationManagerService.WorkerHandler.class)); - // before the lockdown mode - when(mNm.isInLockDownMode()).thenReturn(false); - when(r.getSbn()).thenReturn(sbn); - mListeners.notifyRemovedLocked(r, 0, rs); - mListeners.notifyRemovedLocked(r, 0, rs); - verify(r, atLeast(2)).getSbn(); + // Neither user0 and user1 is in the lockdown mode + when(r0.getUser()).thenReturn(uh0); + when(uh0.getIdentifier()).thenReturn(0); + when(mNm.isInLockDownMode(0)).thenReturn(false); + when(r0.getSbn()).thenReturn(sbn); - // in the lockdown mode - reset(r); - reset(rs); - when(mNm.isInLockDownMode()).thenReturn(true); - when(r.getSbn()).thenReturn(sbn); - mListeners.notifyRemovedLocked(r, 0, rs); - mListeners.notifyRemovedLocked(r, 0, rs); - verify(r, never()).getSbn(); + when(r1.getUser()).thenReturn(uh1); + when(uh1.getIdentifier()).thenReturn(1); + when(mNm.isInLockDownMode(1)).thenReturn(false); + when(r1.getSbn()).thenReturn(sbn); + + mListeners.notifyRemovedLocked(r0, 0, rs0); + mListeners.notifyRemovedLocked(r0, 0, rs0); + verify(r0, atLeast(2)).getSbn(); + + mListeners.notifyRemovedLocked(r1, 0, rs1); + mListeners.notifyRemovedLocked(r1, 0, rs1); + verify(r1, atLeast(2)).getSbn(); + + // Reset + reset(r0); + reset(rs0); + reset(r1); + reset(rs1); + + // Only user 0 is in the lockdown mode + when(r0.getUser()).thenReturn(uh0); + when(uh0.getIdentifier()).thenReturn(0); + when(mNm.isInLockDownMode(0)).thenReturn(true); + when(r0.getSbn()).thenReturn(sbn); + + when(r1.getUser()).thenReturn(uh1); + when(uh1.getIdentifier()).thenReturn(1); + when(mNm.isInLockDownMode(1)).thenReturn(false); + when(r1.getSbn()).thenReturn(sbn); + + mListeners.notifyRemovedLocked(r0, 0, rs0); + mListeners.notifyRemovedLocked(r0, 0, rs0); + verify(r0, never()).getSbn(); + + mListeners.notifyRemovedLocked(r1, 0, rs1); + mListeners.notifyRemovedLocked(r1, 0, rs1); + verify(r1, atLeast(2)).getSbn(); } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index a545c8344c27b..beaa6e066d7d9 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -174,6 +174,7 @@ import android.service.notification.Adjustment; import android.service.notification.ConversationChannelWrapper; import android.service.notification.NotificationListenerFilter; import android.service.notification.NotificationListenerService; +import android.service.notification.NotificationRankingUpdate; import android.service.notification.NotificationStats; import android.service.notification.StatusBarNotification; import android.service.notification.ZenPolicy; @@ -9781,10 +9782,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mStrongAuthTracker.setGetStrongAuthForUserReturnValue( STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN); mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId()); - assertTrue(mStrongAuthTracker.isInLockDownMode()); - mStrongAuthTracker.setGetStrongAuthForUserReturnValue(0); + assertTrue(mStrongAuthTracker.isInLockDownMode(mContext.getUserId())); + mStrongAuthTracker.setGetStrongAuthForUserReturnValue(mContext.getUserId()); mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId()); - assertFalse(mStrongAuthTracker.isInLockDownMode()); + assertFalse(mStrongAuthTracker.isInLockDownMode(mContext.getUserId())); } @Test @@ -9800,8 +9801,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { // when entering the lockdown mode, cancel the 2 notifications. mStrongAuthTracker.setGetStrongAuthForUserReturnValue( STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN); - mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId()); - assertTrue(mStrongAuthTracker.isInLockDownMode()); + mStrongAuthTracker.onStrongAuthRequiredChanged(0); + assertTrue(mStrongAuthTracker.isInLockDownMode(0)); // the notifyRemovedLocked function is called twice due to REASON_CANCEL_ALL. ArgumentCaptor captor = ArgumentCaptor.forClass(Integer.class); @@ -9810,10 +9811,46 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { // exit lockdown mode. mStrongAuthTracker.setGetStrongAuthForUserReturnValue(0); - mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId()); + mStrongAuthTracker.onStrongAuthRequiredChanged(0); + assertFalse(mStrongAuthTracker.isInLockDownMode(0)); // the notifyPostedLocked function is called twice. - verify(mListeners, times(2)).notifyPostedLocked(any(), any()); + verify(mWorkerHandler, times(2)).postDelayed(any(Runnable.class), anyLong()); + //verify(mListeners, times(2)).notifyPostedLocked(any(), any()); + } + + @Test + public void testMakeRankingUpdateLockedInLockDownMode() { + // post 2 notifications from a same package + NotificationRecord pkgA = new NotificationRecord(mContext, + generateSbn("a", 1000, 9, 0), mTestNotificationChannel); + mService.addNotification(pkgA); + NotificationRecord pkgB = new NotificationRecord(mContext, + generateSbn("a", 1000, 9, 1), mTestNotificationChannel); + mService.addNotification(pkgB); + + mService.setIsVisibleToListenerReturnValue(true); + NotificationRankingUpdate nru = mService.makeRankingUpdateLocked(null); + assertEquals(2, nru.getRankingMap().getOrderedKeys().length); + + // when only user 0 entering the lockdown mode, its notification will be suppressed. + mStrongAuthTracker.setGetStrongAuthForUserReturnValue( + STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN); + mStrongAuthTracker.onStrongAuthRequiredChanged(0); + assertTrue(mStrongAuthTracker.isInLockDownMode(0)); + assertFalse(mStrongAuthTracker.isInLockDownMode(1)); + + nru = mService.makeRankingUpdateLocked(null); + assertEquals(1, nru.getRankingMap().getOrderedKeys().length); + + // User 0 exits lockdown mode. Its notification will be resumed. + mStrongAuthTracker.setGetStrongAuthForUserReturnValue(0); + mStrongAuthTracker.onStrongAuthRequiredChanged(0); + assertFalse(mStrongAuthTracker.isInLockDownMode(0)); + assertFalse(mStrongAuthTracker.isInLockDownMode(1)); + + nru = mService.makeRankingUpdateLocked(null); + assertEquals(2, nru.getRankingMap().getOrderedKeys().length); } @Test diff --git a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java index b49e5cbfa9dca..8cf74fbf88b73 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java @@ -19,10 +19,12 @@ package com.android.server.notification; import android.companion.ICompanionDeviceManager; import android.content.ComponentName; import android.content.Context; +import android.service.notification.StatusBarNotification; import androidx.annotation.Nullable; import com.android.internal.logging.InstanceIdSequence; +import com.android.server.notification.ManagedServices.ManagedServiceInfo; import java.util.HashSet; import java.util.Set; @@ -37,6 +39,9 @@ public class TestableNotificationManagerService extends NotificationManagerServi @Nullable NotificationAssistantAccessGrantedCallback mNotificationAssistantAccessGrantedCallback; + @Nullable + Boolean mIsVisibleToListenerReturnValue = null; + TestableNotificationManagerService(Context context, NotificationRecordLogger logger, InstanceIdSequence notificationInstanceIdSequence) { super(context, logger, notificationInstanceIdSequence); @@ -119,6 +124,19 @@ public class TestableNotificationManagerService extends NotificationManagerServi mShowReviewPermissionsNotification = setting; } + protected void setIsVisibleToListenerReturnValue(boolean value) { + mIsVisibleToListenerReturnValue = value; + } + + @Override + boolean isVisibleToListener(StatusBarNotification sbn, int notificationType, + ManagedServiceInfo listener) { + if (mIsVisibleToListenerReturnValue != null) { + return mIsVisibleToListenerReturnValue; + } + return super.isVisibleToListener(sbn, notificationType, listener); + } + public class StrongAuthTrackerFake extends NotificationManagerService.StrongAuthTracker { private int mGetStrongAuthForUserReturnValue = 0; StrongAuthTrackerFake(Context context) { From 7b0d3908c718b60f3a0329fd9bf87c65f53aaa10 Mon Sep 17 00:00:00 2001 From: Songchun Fan Date: Tue, 11 Oct 2022 18:08:11 -0700 Subject: [PATCH 08/14] [RESTRICT AUTOMERGE][SettingsProvider] key size limit for mutating settings Prior to targetSdk 22, apps could add random system settings keys which opens an opportunity for OOM attacks. This CL adds a key size limit. BUG: 239415997 Test: manual; will add cts test Merged-In: Ic9e88c0cc3d7206c64ba5b5c7d15b50d1ffc9adc Change-Id: Ic9e88c0cc3d7206c64ba5b5c7d15b50d1ffc9adc (cherry picked from commit 783bcba343c480f6ccedaaff41ba7171a1082e0c) (cherry picked from commit f60e5a4b7ce47d21a6e707fdad7a865dc0b0f0a3) Merged-In: Ic9e88c0cc3d7206c64ba5b5c7d15b50d1ffc9adc --- .../providers/settings/SettingsState.java | 40 +++++--- .../providers/settings/SettingsStateTest.java | 94 ++++++++++++++++++- 2 files changed, 120 insertions(+), 14 deletions(-) diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java index 528af2ec25289..cd667ca05f616 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java @@ -48,6 +48,7 @@ import android.util.Xml; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FrameworkStatsLog; @@ -376,8 +377,8 @@ final class SettingsState { Setting newSetting = new Setting(name, oldSetting.getValue(), null, oldSetting.getPackageName(), oldSetting.getTag(), false, oldSetting.getId()); - int newSize = getNewMemoryUsagePerPackageLocked(newSetting.getPackageName(), oldValue, - newSetting.getValue(), oldDefaultValue, newSetting.getDefaultValue()); + int newSize = getNewMemoryUsagePerPackageLocked(newSetting.getPackageName(), 0, + oldValue, newSetting.getValue(), oldDefaultValue, newSetting.getDefaultValue()); checkNewMemoryUsagePerPackageLocked(newSetting.getPackageName(), newSize); mSettings.put(name, newSetting); updateMemoryUsagePerPackageLocked(newSetting.getPackageName(), newSize); @@ -414,8 +415,9 @@ final class SettingsState { String oldDefaultValue = (oldState != null) ? oldState.defaultValue : null; String newDefaultValue = makeDefault ? value : oldDefaultValue; - int newSize = getNewMemoryUsagePerPackageLocked(packageName, oldValue, value, - oldDefaultValue, newDefaultValue); + int newSize = getNewMemoryUsagePerPackageLocked(packageName, + oldValue == null ? name.length() : 0 /* deltaKeySize */, + oldValue, value, oldDefaultValue, newDefaultValue); checkNewMemoryUsagePerPackageLocked(packageName, newSize); Setting newState; @@ -559,8 +561,12 @@ final class SettingsState { } Setting oldState = mSettings.remove(name); - int newSize = getNewMemoryUsagePerPackageLocked(oldState.packageName, oldState.value, - null, oldState.defaultValue, null); + if (oldState == null) { + return false; + } + int newSize = getNewMemoryUsagePerPackageLocked(oldState.packageName, + -name.length() /* deltaKeySize */, + oldState.value, null, oldState.defaultValue, null); FrameworkStatsLog.write(FrameworkStatsLog.SETTING_CHANGED, name, /* value= */ "", /* newValue= */ "", oldState.value, /* tag */ "", false, getUserIdFromKey(mKey), @@ -583,15 +589,16 @@ final class SettingsState { } Setting setting = mSettings.get(name); + if (setting == null) { + return false; + } Setting oldSetting = new Setting(setting); String oldValue = setting.getValue(); String oldDefaultValue = setting.getDefaultValue(); - String newValue = oldDefaultValue; - String newDefaultValue = oldDefaultValue; - int newSize = getNewMemoryUsagePerPackageLocked(setting.packageName, oldValue, - newValue, oldDefaultValue, newDefaultValue); + int newSize = getNewMemoryUsagePerPackageLocked(setting.packageName, 0, oldValue, + oldDefaultValue, oldDefaultValue, oldDefaultValue); checkNewMemoryUsagePerPackageLocked(setting.packageName, newSize); if (!setting.reset()) { @@ -725,8 +732,8 @@ final class SettingsState { } @GuardedBy("mLock") - private int getNewMemoryUsagePerPackageLocked(String packageName, String oldValue, - String newValue, String oldDefaultValue, String newDefaultValue) { + private int getNewMemoryUsagePerPackageLocked(String packageName, int deltaKeySize, + String oldValue, String newValue, String oldDefaultValue, String newDefaultValue) { if (isExemptFromMemoryUsageCap(packageName)) { return 0; } @@ -735,7 +742,7 @@ final class SettingsState { final int newValueSize = (newValue != null) ? newValue.length() : 0; final int oldDefaultValueSize = (oldDefaultValue != null) ? oldDefaultValue.length() : 0; final int newDefaultValueSize = (newDefaultValue != null) ? newDefaultValue.length() : 0; - final int deltaSize = newValueSize + newDefaultValueSize + final int deltaSize = deltaKeySize + newValueSize + newDefaultValueSize - oldValueSize - oldDefaultValueSize; return Math.max((currentSize != null) ? currentSize + deltaSize : deltaSize, 0); } @@ -1577,4 +1584,11 @@ final class SettingsState { } return false; } + + @VisibleForTesting + public int getMemoryUsage(String packageName) { + synchronized (mLock) { + return mPackageToMemoryUsage.getOrDefault(packageName, 0); + } + } } diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java index 66b809aeae308..f6d43292eafe1 100644 --- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java +++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java @@ -295,7 +295,7 @@ public class SettingsStateTest extends AndroidTestCase { settingsState.deleteSettingLocked(SETTING_NAME); // Should not throw if usage is under the cap - settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 19999), + settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 19975), null, false, "p1"); settingsState.deleteSettingLocked(SETTING_NAME); try { @@ -313,5 +313,97 @@ public class SettingsStateTest extends AndroidTestCase { assertTrue(ex.getMessage().contains("p1")); } assertTrue(settingsState.getSettingLocked(SETTING_NAME).isNull()); + try { + settingsState.insertSettingLocked(Strings.repeat("A", 20001), "", + null, false, "p1"); + fail("Should throw because it exceeded per package memory usage"); + } catch (IllegalStateException ex) { + assertTrue(ex.getMessage().contains("You are adding too many system settings")); + } + } + + public void testMemoryUsagePerPackage() { + SettingsState settingsState = new SettingsState(getContext(), mLock, mSettingsFile, 1, + SettingsState.MAX_BYTES_PER_APP_PACKAGE_LIMITED, Looper.getMainLooper()); + + // Test inserting one key with default + final String testKey1 = SETTING_NAME; + final String testValue1 = Strings.repeat("A", 100); + settingsState.insertSettingLocked(testKey1, testValue1, null, true, TEST_PACKAGE); + int expectedMemUsage = testKey1.length() + testValue1.length() + + testValue1.length() /* size for default */; + assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); + + // Test inserting another key + final String testKey2 = SETTING_NAME + "2"; + settingsState.insertSettingLocked(testKey2, testValue1, null, false, TEST_PACKAGE); + expectedMemUsage += testKey2.length() + testValue1.length(); + assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); + + // Test updating first key with new default + final String testValue2 = Strings.repeat("A", 300); + settingsState.insertSettingLocked(testKey1, testValue2, null, true, TEST_PACKAGE); + expectedMemUsage += (testValue2.length() - testValue1.length()) * 2; + assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); + + // Test updating first key without new default + final String testValue3 = Strings.repeat("A", 50); + settingsState.insertSettingLocked(testKey1, testValue3, null, false, TEST_PACKAGE); + expectedMemUsage -= testValue2.length() - testValue3.length(); + assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); + + // Test updating second key + settingsState.insertSettingLocked(testKey2, testValue2, null, false, TEST_PACKAGE); + expectedMemUsage -= testValue1.length() - testValue2.length(); + assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); + + // Test resetting key + settingsState.resetSettingLocked(testKey1); + expectedMemUsage += testValue2.length() - testValue3.length(); + assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); + + // Test resetting default value + settingsState.resetSettingDefaultValueLocked(testKey1); + expectedMemUsage -= testValue2.length(); + assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); + + // Test deletion + settingsState.deleteSettingLocked(testKey2); + expectedMemUsage -= testValue2.length() + testKey2.length() /* key is deleted too */; + assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); + + // Test another package with a different key + final String testPackage2 = TEST_PACKAGE + "2"; + final String testKey3 = SETTING_NAME + "3"; + settingsState.insertSettingLocked(testKey3, testValue1, null, true, testPackage2); + assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); + final int expectedMemUsage2 = testKey3.length() + testValue1.length() * 2; + assertEquals(expectedMemUsage2, settingsState.getMemoryUsage(testPackage2)); + + // Test system package + settingsState.insertSettingLocked(testKey1, testValue1, null, true, SYSTEM_PACKAGE); + assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); + assertEquals(expectedMemUsage2, settingsState.getMemoryUsage(testPackage2)); + assertEquals(0, settingsState.getMemoryUsage(SYSTEM_PACKAGE)); + + // Test invalid value + try { + settingsState.insertSettingLocked(testKey1, Strings.repeat("A", 20001), null, false, + TEST_PACKAGE); + fail("Should throw because it exceeded per package memory usage"); + } catch (IllegalStateException ex) { + assertTrue(ex.getMessage().contains("You are adding too many system settings")); + } + assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); + + // Test invalid key + try { + settingsState.insertSettingLocked(Strings.repeat("A", 20001), "", null, false, + TEST_PACKAGE); + fail("Should throw because it exceeded per package memory usage"); + } catch (IllegalStateException ex) { + assertTrue(ex.getMessage().contains("You are adding too many system settings")); + } + assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE)); } } From 0218ccf3eacebf0411fbf41a066f7fd7525b2e24 Mon Sep 17 00:00:00 2001 From: Nate Myren Date: Fri, 23 Sep 2022 12:04:57 -0700 Subject: [PATCH 09/14] RESTRICT AUTOMERGE Revoke SYSTEM_ALERT_WINDOW on upgrade past api 23 Bug: 221040577 Test: atest PermissionTest23#testPre23AppsWithSystemAlertWindowGetDeniedOnUpgrade Change-Id: I4b4605aaae107875811070dea6d031c5d9f25c96 (cherry picked from commit fba194b998cf22d073a36cb5c6f9397c2dc1a50e) Merged-In: I4b4605aaae107875811070dea6d031c5d9f25c96 --- .../PermissionManagerServiceImpl.java | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java index 18c29fa9f3a23..554e2690b8780 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java @@ -2135,6 +2135,46 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt } + /** + * If the package was below api 23, got the SYSTEM_ALERT_WINDOW permission automatically, and + * then updated past api 23, and the app does not satisfy any of the other SAW permission flags, + * the permission should be revoked. + * + * @param newPackage The new package that was installed + * @param oldPackage The old package that was updated + */ + private void revokeSystemAlertWindowIfUpgradedPast23( + @NonNull AndroidPackage newPackage, + @NonNull AndroidPackage oldPackage) { + if (oldPackage.getTargetSdkVersion() >= Build.VERSION_CODES.M + || newPackage.getTargetSdkVersion() < Build.VERSION_CODES.M + || !newPackage.getRequestedPermissions() + .contains(Manifest.permission.SYSTEM_ALERT_WINDOW)) { + return; + } + + Permission saw; + synchronized (mLock) { + saw = mRegistry.getPermission(Manifest.permission.SYSTEM_ALERT_WINDOW); + } + final PackageStateInternal ps = + mPackageManagerInt.getPackageStateInternal(newPackage.getPackageName()); + if (shouldGrantPermissionByProtectionFlags(newPackage, ps, saw, new ArraySet<>()) + || shouldGrantPermissionBySignature(newPackage, saw)) { + return; + } + for (int userId : getAllUserIds()) { + try { + revokePermissionFromPackageForUser(newPackage.getPackageName(), + Manifest.permission.SYSTEM_ALERT_WINDOW, false, userId, + mDefaultPermissionCallback); + } catch (IllegalStateException | SecurityException e) { + Log.e(TAG, "unable to revoke SYSTEM_ALERT_WINDOW for " + + newPackage.getPackageName() + " user " + userId, e); + } + } + } + /** * We might auto-grant permissions if any permission of the group is already granted. Hence if * the group of a granted permission changes we need to revoke it to avoid having permissions of @@ -4661,6 +4701,7 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt if (hasOldPkg) { revokeRuntimePermissionsIfGroupChangedInternal(pkg, oldPkg); revokeStoragePermissionsIfScopeExpandedInternal(pkg, oldPkg); + revokeSystemAlertWindowIfUpgradedPast23(pkg, oldPkg); } if (hasPermissionDefinitionChanges) { revokeRuntimePermissionsIfPermissionDefinitionChangedInternal( From c224467912010cce927d206366d772f511c98728 Mon Sep 17 00:00:00 2001 From: Songchun Fan Date: Wed, 26 Oct 2022 17:03:33 -0700 Subject: [PATCH 10/14] [SettingsProvider] workaround for DevicePolicyResourcesManager DevicePolicyResourcesManager.getString() was added in T and called in many places inside system server. However, it uses DeviceConfig.getBoolean to determine if a feature is enabled or not. In the places where it is called from inside system server, `clearCallingIdentity` was not always called, which can result in DeviceConfig.getBoolean throwing SecurityException due to mismatched AttributionSource (calling uid is the caller from the binder client, while the context is "android" which should have calling uid 0). Context is "android" because it is inside the system server where the DevicePolicyManager instance is created. This bug might lead to unexpected behavior such as packages failing to be uninstalled by admin. The easiest fix is to place a bypass in SettingsProvider and manually clear the calling uid there. This fix also allows for the cleanest backporting as it can be cherry-picked into T without touching all the places where DevicePolicyResourcesManager.getString() is called in the system server. BUG: 252663068 Test: manual Merged-In: I37a6ceb29575593018b93093562c85d49770a43c Change-Id: I37a6ceb29575593018b93093562c85d49770a43c (cherry picked from commit b127850fda23698be0247e5b2110cdd01fff8fd7) (cherry picked from commit 3b873ef5c2e2a1527ded7cbcad1993af15277831) Merged-In: I37a6ceb29575593018b93093562c85d49770a43c --- core/java/android/provider/Settings.java | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 6e369d343fa2e..c9fd1295a6e17 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -3344,9 +3344,26 @@ public final class Settings { } } - // Fetch all flags for the namespace at once for caching purposes - Bundle b = cp.call(cr.getAttributionSource(), - mProviderHolder.mUri.getAuthority(), mCallListCommand, null, args); + Bundle b; + // b/252663068: if we're in system server and the caller did not call + // clearCallingIdentity, the read would fail due to mismatched AttributionSources. + // TODO(b/256013480): remove this bypass after fixing the callers in system server. + if (namespace.equals(DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER) + && Settings.isInSystemServer() + && Binder.getCallingUid() != Process.myUid()) { + final long token = Binder.clearCallingIdentity(); + try { + // Fetch all flags for the namespace at once for caching purposes + b = cp.call(cr.getAttributionSource(), + mProviderHolder.mUri.getAuthority(), mCallListCommand, null, args); + } finally { + Binder.restoreCallingIdentity(token); + } + } else { + // Fetch all flags for the namespace at once for caching purposes + b = cp.call(cr.getAttributionSource(), + mProviderHolder.mUri.getAuthority(), mCallListCommand, null, args); + } if (b == null) { // Invalid response, return an empty map return keyValues; From d8e918fc9d479388661e74638d950592af16c001 Mon Sep 17 00:00:00 2001 From: Khoa Hong Date: Fri, 23 Sep 2022 17:05:39 +0800 Subject: [PATCH 11/14] Add protections against queueing a UsbRequest when the underlying UsbDeviceConnection is closed. Bug: 204584366 Test: CTS Verifier: USB Accessory Test & USB Device Test Test: No HWASan use-after-free reports with a test app Change-Id: Ia3a9b10349efc0236b1539c81465f479cb32e02b (cherry picked from commit 1691b54b1fda4239249a3871d2c17ed1ec753061) Merged-In: Ia3a9b10349efc0236b1539c81465f479cb32e02b --- .../hardware/usb/UsbDeviceConnection.java | 28 ++++++++ .../java/android/hardware/usb/UsbRequest.java | 68 ++++++++++++++++--- 2 files changed, 86 insertions(+), 10 deletions(-) diff --git a/core/java/android/hardware/usb/UsbDeviceConnection.java b/core/java/android/hardware/usb/UsbDeviceConnection.java index 60d8cacd19be7..7c2e518b85440 100644 --- a/core/java/android/hardware/usb/UsbDeviceConnection.java +++ b/core/java/android/hardware/usb/UsbDeviceConnection.java @@ -107,6 +107,34 @@ public class UsbDeviceConnection { } } + /** + * This is meant to be called by UsbRequest's queue() in order to synchronize on + * UsbDeviceConnection's mLock to prevent the connection being closed while queueing. + */ + /* package */ boolean queueRequest(UsbRequest request, ByteBuffer buffer, int length) { + synchronized (mLock) { + if (!isOpen()) { + return false; + } + + return request.queueIfConnectionOpen(buffer, length); + } + } + + /** + * This is meant to be called by UsbRequest's queue() in order to synchronize on + * UsbDeviceConnection's mLock to prevent the connection being closed while queueing. + */ + /* package */ boolean queueRequest(UsbRequest request, @Nullable ByteBuffer buffer) { + synchronized (mLock) { + if (!isOpen()) { + return false; + } + + return request.queueIfConnectionOpen(buffer); + } + } + /** * Releases all system resources related to the device. * Once the object is closed it cannot be used again. diff --git a/core/java/android/hardware/usb/UsbRequest.java b/core/java/android/hardware/usb/UsbRequest.java index 6ac5e8de8fa7d..beb0f8d336d5e 100644 --- a/core/java/android/hardware/usb/UsbRequest.java +++ b/core/java/android/hardware/usb/UsbRequest.java @@ -113,11 +113,13 @@ public class UsbRequest { * Releases all resources related to this request. */ public void close() { - if (mNativeContext != 0) { - mEndpoint = null; - mConnection = null; - native_close(); - mCloseGuard.close(); + synchronized (mLock) { + if (mNativeContext != 0) { + mEndpoint = null; + mConnection = null; + native_close(); + mCloseGuard.close(); + } } } @@ -191,10 +193,32 @@ public class UsbRequest { */ @Deprecated public boolean queue(ByteBuffer buffer, int length) { + UsbDeviceConnection connection = mConnection; + if (connection == null) { + // The expected exception by CTS Verifier - USB Device test + throw new NullPointerException("invalid connection"); + } + + // Calling into the underlying UsbDeviceConnection to synchronize on its lock, to prevent + // the connection being closed while queueing. + return connection.queueRequest(this, buffer, length); + } + + /** + * This is meant to be called from UsbDeviceConnection after synchronizing using the lock over + * there, to prevent the connection being closed while queueing. + */ + /* package */ boolean queueIfConnectionOpen(ByteBuffer buffer, int length) { + UsbDeviceConnection connection = mConnection; + if (connection == null || !connection.isOpen()) { + // The expected exception by CTS Verifier - USB Device test + throw new NullPointerException("invalid connection"); + } + boolean out = (mEndpoint.getDirection() == UsbConstants.USB_DIR_OUT); boolean result; - if (mConnection.getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.P + if (connection.getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.P && length > MAX_USBFS_BUFFER_SIZE) { length = MAX_USBFS_BUFFER_SIZE; } @@ -243,6 +267,28 @@ public class UsbRequest { * @return true if the queueing operation succeeded */ public boolean queue(@Nullable ByteBuffer buffer) { + UsbDeviceConnection connection = mConnection; + if (connection == null) { + // The expected exception by CTS Verifier - USB Device test + throw new IllegalStateException("invalid connection"); + } + + // Calling into the underlying UsbDeviceConnection to synchronize on its lock, to prevent + // the connection being closed while queueing. + return connection.queueRequest(this, buffer); + } + + /** + * This is meant to be called from UsbDeviceConnection after synchronizing using the lock over + * there, to prevent the connection being closed while queueing. + */ + /* package */ boolean queueIfConnectionOpen(@Nullable ByteBuffer buffer) { + UsbDeviceConnection connection = mConnection; + if (connection == null || !connection.isOpen()) { + // The expected exception by CTS Verifier - USB Device test + throw new IllegalStateException("invalid connection"); + } + // Request need to be initialized Preconditions.checkState(mNativeContext != 0, "request is not initialized"); @@ -260,7 +306,7 @@ public class UsbRequest { mIsUsingNewQueue = true; wasQueued = native_queue(null, 0, 0); } else { - if (mConnection.getContext().getApplicationInfo().targetSdkVersion + if (connection.getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.P) { // Can only send/receive MAX_USBFS_BUFFER_SIZE bytes at once Preconditions.checkArgumentInRange(buffer.remaining(), 0, MAX_USBFS_BUFFER_SIZE, @@ -363,11 +409,12 @@ public class UsbRequest { * @return true if cancelling succeeded */ public boolean cancel() { - if (mConnection == null) { + UsbDeviceConnection connection = mConnection; + if (connection == null) { return false; } - return mConnection.cancelRequest(this); + return connection.cancelRequest(this); } /** @@ -382,7 +429,8 @@ public class UsbRequest { * @return true if cancelling succeeded. */ /* package */ boolean cancelIfOpen() { - if (mNativeContext == 0 || (mConnection != null && !mConnection.isOpen())) { + UsbDeviceConnection connection = mConnection; + if (mNativeContext == 0 || (connection != null && !connection.isOpen())) { Log.w(TAG, "Detected attempt to cancel a request on a connection which isn't open"); return false; From 52a404fa2c54063488c88d1722e8a90ae1cccab3 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Tue, 18 Oct 2022 05:21:30 +0000 Subject: [PATCH 12/14] Ensure that only SysUI can override pending intent launch flags - Originally added in ag/5139951, this method ensured that activities launched from widgets are always started in a new task (if the activity is launched in the home task, the task is not brough forward with the recents transition). We can restrict this to only recents callers since this only applies to 1p launchers in gesture nav (both the gesture with 3p launchers and button nav in general will always start the home intent directly, which makes adding the NEW_TASK flag unnecessary). Bug: 243794108 Test: Ensure that the original bug b/112508020 still works (with the test app in the bug, swipe up still works after launching an activity from the widget, and fails without applying the override flags) Change-Id: Id53c6a2aa6da5933d488ca06a0bfc4ef89a4c343 (cherry picked from commit c4d3106e347922610f8c554de3ae238175ed393e) Merged-In: Id53c6a2aa6da5933d488ca06a0bfc4ef89a4c343 --- .../com/android/server/am/PendingIntentRecord.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java index bda60ff2172b3..8624ee031a936 100644 --- a/services/core/java/com/android/server/am/PendingIntentRecord.java +++ b/services/core/java/com/android/server/am/PendingIntentRecord.java @@ -379,11 +379,16 @@ public final class PendingIntentRecord extends IIntentSender.Stub { resolvedType = key.requestResolvedType; } - // Apply any launch flags from the ActivityOptions. This is to ensure that the caller - // can specify a consistent launch mode even if the PendingIntent is immutable + // Apply any launch flags from the ActivityOptions. This is used only by SystemUI + // to ensure that we can launch the pending intent with a consistent launch mode even + // if the provided PendingIntent is immutable (ie. to force an activity to launch into + // a new task, or to launch multiple instances if supported by the app) final ActivityOptions opts = ActivityOptions.fromBundle(options); if (opts != null) { - finalIntent.addFlags(opts.getPendingIntentLaunchFlags()); + // TODO(b/254490217): Move this check into SafeActivityOptions + if (controller.mAtmInternal.isCallerRecents(Binder.getCallingUid())) { + finalIntent.addFlags(opts.getPendingIntentLaunchFlags()); + } } // Extract options before clearing calling identity From b1ad676f42a73c1891a4aff2ed061282e8269844 Mon Sep 17 00:00:00 2001 From: Yuri Lin Date: Mon, 7 Nov 2022 19:21:57 +0000 Subject: [PATCH 13/14] Revert "Check rule package name in ZenModeHelper.addAutomaticRule" This reverts commit b6d04416628ab29df57efcd738332912d9260cea. Reason for revert: broke DND schedules in multi-user mode b/257477671 Change-Id: Idf32ab7ebd132ee460504976ba31252fd4da47fa (cherry picked from commit 9223d270f5474dc4387ad3e6afd4cb660927bfc8) Merged-In: Idf32ab7ebd132ee460504976ba31252fd4da47fa --- .../server/notification/ZenModeHelper.java | 7 ++++- .../notification/ZenModeHelperTest.java | 30 ------------------- 2 files changed, 6 insertions(+), 31 deletions(-) diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 4c23ab84a14f4..d42667951608d 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -326,7 +326,7 @@ public class ZenModeHelper { public String addAutomaticZenRule(String pkg, AutomaticZenRule automaticZenRule, String reason) { - if (!ZenModeConfig.SYSTEM_AUTHORITY.equals(pkg)) { + if (!isSystemRule(automaticZenRule)) { PackageItemInfo component = getServiceInfo(automaticZenRule.getOwner()); if (component == null) { component = getActivityInfo(automaticZenRule.getConfigurationActivity()); @@ -582,6 +582,11 @@ public class ZenModeHelper { } } + private boolean isSystemRule(AutomaticZenRule rule) { + return rule.getOwner() != null + && ZenModeConfig.SYSTEM_AUTHORITY.equals(rule.getOwner().getPackageName()); + } + private ServiceInfo getServiceInfo(ComponentName owner) { Intent queryIntent = new Intent(); queryIntent.setComponent(owner); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java index 2ccdcaace8bf3..4550b56f6fd0a 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java @@ -1671,36 +1671,6 @@ public class ZenModeHelperTest extends UiServiceTestCase { } } - @Test - public void testAddAutomaticZenRule_claimedSystemOwner() { - // Make sure anything that claims to have a "system" owner but not actually part of the - // system package still gets limited on number of rules - for (int i = 0; i < RULE_LIMIT_PER_PACKAGE; i++) { - ScheduleInfo si = new ScheduleInfo(); - si.startHour = i; - AutomaticZenRule zenRule = new AutomaticZenRule("name" + i, - new ComponentName("android", "ScheduleConditionProvider" + i), - null, // configuration activity - ZenModeConfig.toScheduleConditionId(si), - new ZenPolicy.Builder().build(), - NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); - String id = mZenModeHelperSpy.addAutomaticZenRule("pkgname", zenRule, "test"); - assertNotNull(id); - } - try { - AutomaticZenRule zenRule = new AutomaticZenRule("name", - new ComponentName("android", "ScheduleConditionProviderFinal"), - null, // configuration activity - ZenModeConfig.toScheduleConditionId(new ScheduleInfo()), - new ZenPolicy.Builder().build(), - NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); - String id = mZenModeHelperSpy.addAutomaticZenRule("pkgname", zenRule, "test"); - fail("allowed too many rules to be created"); - } catch (IllegalArgumentException e) { - // yay - } - } - @Test public void testAddAutomaticZenRule_CA() { AutomaticZenRule zenRule = new AutomaticZenRule("name", From e0bf51a8b5ae6540a5acf53de1e942b807ca1c18 Mon Sep 17 00:00:00 2001 From: Yuri Lin Date: Mon, 7 Nov 2022 19:22:08 +0000 Subject: [PATCH 14/14] Revert "Fix system zen rules by using owner package name if caller is system" This reverts commit e5e51116fb767162966a8e0d23fafb4f0ff46e86. Reason for revert: broke DND schedules in multi-user mode b/257477671 Change-Id: Ic5126e4c95c8ef8c17b339a6fb0f08ca9cc97323 (cherry picked from commit 3535f5cb664bda21ca69a3515ac59a927bf6b90d) Merged-In: Ic5126e4c95c8ef8c17b339a6fb0f08ca9cc97323 --- .../NotificationManagerService.java | 11 +----- .../NotificationManagerServiceTest.java | 37 ------------------- 2 files changed, 1 insertion(+), 47 deletions(-) diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 63862e4992c5a..971a31294cfb6 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -4958,16 +4958,7 @@ public class NotificationManagerService extends SystemService { } enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule"); - // If the caller is system, take the package name from the rule's owner rather than - // from the caller's package. - String rulePkg = pkg; - if (isCallingUidSystem()) { - if (automaticZenRule.getOwner() != null) { - rulePkg = automaticZenRule.getOwner().getPackageName(); - } - } - - return mZenModeHelper.addAutomaticZenRule(rulePkg, automaticZenRule, + return mZenModeHelper.addAutomaticZenRule(pkg, automaticZenRule, "addAutomaticZenRule"); } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index beaa6e066d7d9..7df4b57b6cf87 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -7548,43 +7548,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mBinderService.addAutomaticZenRule(rule, mContext.getPackageName()); } - @Test - public void testAddAutomaticZenRule_systemCallTakesPackageFromOwner() throws Exception { - mService.isSystemUid = true; - ZenModeHelper mockZenModeHelper = mock(ZenModeHelper.class); - when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt())) - .thenReturn(true); - mService.setZenHelper(mockZenModeHelper); - ComponentName owner = new ComponentName("android", "ProviderName"); - ZenPolicy zenPolicy = new ZenPolicy.Builder().allowAlarms(true).build(); - boolean isEnabled = true; - AutomaticZenRule rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class), - zenPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, isEnabled); - mBinderService.addAutomaticZenRule(rule, "com.android.settings"); - - // verify that zen mode helper gets passed in a package name of "android" - verify(mockZenModeHelper).addAutomaticZenRule(eq("android"), eq(rule), anyString()); - } - - @Test - public void testAddAutomaticZenRule_nonSystemCallTakesPackageFromArg() throws Exception { - mService.isSystemUid = false; - ZenModeHelper mockZenModeHelper = mock(ZenModeHelper.class); - when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt())) - .thenReturn(true); - mService.setZenHelper(mockZenModeHelper); - ComponentName owner = new ComponentName("android", "ProviderName"); - ZenPolicy zenPolicy = new ZenPolicy.Builder().allowAlarms(true).build(); - boolean isEnabled = true; - AutomaticZenRule rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class), - zenPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, isEnabled); - mBinderService.addAutomaticZenRule(rule, "another.package"); - - // verify that zen mode helper gets passed in the package name from the arg, not the owner - verify(mockZenModeHelper).addAutomaticZenRule( - eq("another.package"), eq(rule), anyString()); - } - @Test public void testAreNotificationsEnabledForPackage() throws Exception { mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(),