diff --git a/aosp_diff/preliminary/build/make/04_0004-Update-security_patch_level-string.patch b/aosp_diff/preliminary/build/make/04_0004-Update-security_patch_level-string.patch index 67eabcecd5..21af7a1a99 100644 --- a/aosp_diff/preliminary/build/make/04_0004-Update-security_patch_level-string.patch +++ b/aosp_diff/preliminary/build/make/04_0004-Update-security_patch_level-string.patch @@ -20,7 +20,7 @@ index 47bb92c142..2d0ac256a4 100644 # It must match one of the Android Security Patch Level strings of the Public Security Bulletins. # If there is no $PLATFORM_SECURITY_PATCH set, keep it empty. - PLATFORM_SECURITY_PATCH := 2022-02-05 -+ PLATFORM_SECURITY_PATCH := 2023-08-01 ++ PLATFORM_SECURITY_PATCH := 2023-09-01 endif .KATI_READONLY := PLATFORM_SECURITY_PATCH diff --git a/aosp_diff/preliminary/frameworks/av/25_0025-Fix-Segv-on-unknown-address-error-flagged-by-fuzzer-test-.bulletin.patch b/aosp_diff/preliminary/frameworks/av/25_0025-Fix-Segv-on-unknown-address-error-flagged-by-fuzzer-test-.bulletin.patch new file mode 100644 index 0000000000..28b3c3ad75 --- /dev/null +++ b/aosp_diff/preliminary/frameworks/av/25_0025-Fix-Segv-on-unknown-address-error-flagged-by-fuzzer-test-.bulletin.patch @@ -0,0 +1,35 @@ +From 9f042d16d4d51b0a87a0a89c78c2e2a9a7e3b544 Mon Sep 17 00:00:00 2001 +From: Shruti Bihani +Date: Thu, 6 Jul 2023 08:41:56 +0000 +Subject: [PATCH] Fix Segv on unknown address error flagged by fuzzer test. + +The error is thrown when the destructor tries to free pointer memory. +This is happening for cases where the pointer was not initialized. Initializing it to a default value fixes the error. + +Bug: 245135112 +Test: Build mtp_host_property_fuzzer and run on the target device +(cherry picked from commit 3afa6e80e8568fe63f893fa354bc79ef91d3dcc0) +(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:c138d20635694857754f2b7de2342089de13d556) +Merged-In: I255cd68b7641e96ac47ab81479b9b46b78c15580 +Change-Id: I255cd68b7641e96ac47ab81479b9b46b78c15580 +--- + media/mtp/MtpProperty.h | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/media/mtp/MtpProperty.h b/media/mtp/MtpProperty.h +index 36d736065f..2bdbfd3262 100644 +--- a/media/mtp/MtpProperty.h ++++ b/media/mtp/MtpProperty.h +@@ -26,6 +26,9 @@ namespace android { + class MtpDataPacket; + + struct MtpPropertyValue { ++ // pointer str initialized to NULL so that free operation ++ // is not called for pre-assigned value ++ MtpPropertyValue() : str (NULL) {} + union { + int8_t i8; + uint8_t u8; +-- +2.41.0.585.gd2178a4bd4-goog + diff --git a/aosp_diff/preliminary/frameworks/base/99_0183--DO-NOT-MERGE-Update-quickshare-intent-rather-than-recreati.bulletin.patch b/aosp_diff/preliminary/frameworks/base/99_0183--DO-NOT-MERGE-Update-quickshare-intent-rather-than-recreati.bulletin.patch new file mode 100644 index 0000000000..c07fc8e458 --- /dev/null +++ b/aosp_diff/preliminary/frameworks/base/99_0183--DO-NOT-MERGE-Update-quickshare-intent-rather-than-recreati.bulletin.patch @@ -0,0 +1,590 @@ +From 14da8e7df8c18b92d95b6bb39916a7436884c1c6 Mon Sep 17 00:00:00 2001 +From: Miranda Kephart +Date: Fri, 28 Apr 2023 10:58:46 -0400 +Subject: [PATCH] [DO NOT MERGE] Update quickshare intent rather than + recreating + +Currently, we extract the quickshare intent and re-wrap it as a new +PendingIntent once we get the screenshot URI. This is insecure as +it leads to executing the original with SysUI's permissions, which +the app may not have. This change switches to using Intent.fillin +to add the URI, keeping the original PendingIntent and original +permission set. + +Bug: 278720336 +Test: manual (to test successful quickshare), atest +SaveImageInBackgroundTaskTest (to verify original pending intent +unchanged) +(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:573d31ef204bc8bbf24a7341afe17870708c0904) +Merged-In: Icad3d5f939fcfb894e2038948954bc2735dbe326 +Change-Id: Icad3d5f939fcfb894e2038948954bc2735dbe326 +--- + .../screenshot/SaveImageInBackgroundTask.java | 113 +++++--- + .../screenshot/ScreenshotController.java | 8 +- + .../screenshot/SmartActionsReceiver.java | 4 +- + .../SaveImageInBackgroundTaskTest.kt | 274 ++++++++++++++++++ + ...creenshotNotificationSmartActionsTest.java | 6 +- + 5 files changed, 353 insertions(+), 52 deletions(-) + create mode 100644 packages/SystemUI/tests/src/com/android/systemui/screenshot/SaveImageInBackgroundTaskTest.kt + +diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java +index e9dea65c2078..fe761c3e8d2e 100644 +--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java ++++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java +@@ -89,7 +89,8 @@ class SaveImageInBackgroundTask extends AsyncTask { + SaveImageInBackgroundTask(Context context, ImageExporter exporter, + ScreenshotSmartActions screenshotSmartActions, + ScreenshotController.SaveImageInBackgroundData data, +- Supplier sharedElementTransition) { ++ Supplier sharedElementTransition, ++ boolean smartActionsEnabled) { + mContext = context; + mScreenshotSmartActions = screenshotSmartActions; + mImageData = new ScreenshotController.SavedImageData(); +@@ -101,8 +102,7 @@ class SaveImageInBackgroundTask extends AsyncTask { + mParams = data; + + // Initialize screenshot notification smart actions provider. +- mSmartActionsEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI, +- SystemUiDeviceConfigFlags.ENABLE_SCREENSHOT_NOTIFICATION_SMART_ACTIONS, true); ++ mSmartActionsEnabled = smartActionsEnabled; + if (mSmartActionsEnabled) { + mSmartActionsProvider = + SystemUIFactory.getInstance() +@@ -135,7 +135,12 @@ class SaveImageInBackgroundTask extends AsyncTask { + // Since Quick Share target recommendation does not rely on image URL, it is + // queried and surfaced before image compress/export. Action intent would not be + // used, because it does not contain image URL. +- queryQuickShareAction(image, user); ++ Notification.Action quickShare = ++ queryQuickShareAction(mScreenshotId, image, user, null); ++ if (quickShare != null) { ++ mQuickShareData.quickShareAction = quickShare; ++ mParams.mQuickShareActionsReadyListener.onActionsReady(mQuickShareData); ++ } + } + + // Call synchronously here since already on a background thread. +@@ -168,8 +173,9 @@ class SaveImageInBackgroundTask extends AsyncTask { + mImageData.shareTransition = createShareAction(mContext, mContext.getResources(), uri); + mImageData.editTransition = createEditAction(mContext, mContext.getResources(), uri); + mImageData.deleteAction = createDeleteAction(mContext, mContext.getResources(), uri); +- mImageData.quickShareAction = createQuickShareAction(mContext, +- mQuickShareData.quickShareAction, uri); ++ mImageData.quickShareAction = createQuickShareAction( ++ mQuickShareData.quickShareAction, mScreenshotId, uri, mImageTime, image, ++ user); + + mParams.mActionsReadyListener.onActionsReady(mImageData); + if (DEBUG_CALLBACK) { +@@ -407,60 +413,73 @@ class SaveImageInBackgroundTask extends AsyncTask { + } + + /** +- * Populate image uri into intent of Quick Share action. ++ * Wrap the quickshare intent and populate the fillin intent with the URI + */ + @VisibleForTesting +- private Notification.Action createQuickShareAction(Context context, Notification.Action action, +- Uri uri) { +- if (action == null) { ++ Notification.Action createQuickShareAction( ++ Notification.Action quickShare, String screenshotId, Uri uri, long imageTime, ++ Bitmap image, UserHandle user) { ++ if (quickShare == null) { + return null; ++ } else if (quickShare.actionIntent.isImmutable()) { ++ Notification.Action quickShareWithUri = ++ queryQuickShareAction(screenshotId, image, user, uri); ++ if (quickShareWithUri == null ++ || !quickShareWithUri.title.toString().contentEquals(quickShare.title)) { ++ return null; ++ } ++ quickShare = quickShareWithUri; + } +- // Populate image URI into Quick Share chip intent +- Intent sharingIntent = action.actionIntent.getIntent(); +- sharingIntent.setType("image/png"); +- sharingIntent.putExtra(Intent.EXTRA_STREAM, uri); +- String subjectDate = DateFormat.getDateTimeInstance().format(new Date(mImageTime)); ++ ++ Intent wrappedIntent = new Intent(mContext, SmartActionsReceiver.class) ++ .putExtra(ScreenshotController.EXTRA_ACTION_INTENT, quickShare.actionIntent) ++ .putExtra(ScreenshotController.EXTRA_ACTION_INTENT_FILLIN, ++ createFillInIntent(uri, imageTime)) ++ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND); ++ Bundle extras = quickShare.getExtras(); ++ String actionType = extras.getString( ++ ScreenshotNotificationSmartActionsProvider.ACTION_TYPE, ++ ScreenshotNotificationSmartActionsProvider.DEFAULT_ACTION_TYPE); ++ addIntentExtras(screenshotId, wrappedIntent, actionType, mSmartActionsEnabled); ++ PendingIntent broadcastIntent = ++ PendingIntent.getBroadcast(mContext, mRandom.nextInt(), wrappedIntent, ++ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE); ++ return new Notification.Action.Builder(quickShare.getIcon(), quickShare.title, ++ broadcastIntent) ++ .setContextual(true) ++ .addExtras(extras) ++ .build(); ++ } ++ ++ private Intent createFillInIntent(Uri uri, long imageTime) { ++ Intent fillIn = new Intent(); ++ fillIn.setType("image/png"); ++ fillIn.putExtra(Intent.EXTRA_STREAM, uri); ++ String subjectDate = DateFormat.getDateTimeInstance().format(new Date(imageTime)); + String subject = String.format(SCREENSHOT_SHARE_SUBJECT_TEMPLATE, subjectDate); +- sharingIntent.putExtra(Intent.EXTRA_SUBJECT, subject); ++ fillIn.putExtra(Intent.EXTRA_SUBJECT, subject); + // Include URI in ClipData also, so that grantPermission picks it up. + // We don't use setData here because some apps interpret this as "to:". +- ClipData clipdata = new ClipData(new ClipDescription("content", +- new String[]{"image/png"}), ++ ClipData clipData = new ClipData( ++ new ClipDescription("content", new String[]{"image/png"}), + new ClipData.Item(uri)); +- sharingIntent.setClipData(clipdata); +- sharingIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); +- PendingIntent updatedPendingIntent = PendingIntent.getActivity( +- context, 0, sharingIntent, +- PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE); +- +- // Proxy smart actions through {@link GlobalScreenshot.SmartActionsReceiver} +- // for logging smart actions. +- Bundle extras = action.getExtras(); +- String actionType = extras.getString( +- ScreenshotNotificationSmartActionsProvider.ACTION_TYPE, +- ScreenshotNotificationSmartActionsProvider.DEFAULT_ACTION_TYPE); +- Intent intent = new Intent(context, SmartActionsReceiver.class) +- .putExtra(ScreenshotController.EXTRA_ACTION_INTENT, updatedPendingIntent) +- .addFlags(Intent.FLAG_RECEIVER_FOREGROUND); +- addIntentExtras(mScreenshotId, intent, actionType, mSmartActionsEnabled); +- PendingIntent broadcastIntent = PendingIntent.getBroadcast(context, +- mRandom.nextInt(), +- intent, +- PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE); +- return new Notification.Action.Builder(action.getIcon(), action.title, +- broadcastIntent).setContextual(true).addExtras(extras).build(); ++ fillIn.setClipData(clipData); ++ fillIn.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); ++ return fillIn; + } + + /** + * Query and surface Quick Share chip if it is available. Action intent would not be used, + * because it does not contain image URL which would be populated in {@link +- * #createQuickShareAction(Context, Notification.Action, Uri)} ++ * #createQuickShareAction(Notification.Action, String, Uri, long, Bitmap, UserHandle)} + */ +- private void queryQuickShareAction(Bitmap image, UserHandle user) { ++ ++ @VisibleForTesting ++ Notification.Action queryQuickShareAction( ++ String screenshotId, Bitmap image, UserHandle user, Uri uri) { + CompletableFuture> quickShareActionsFuture = + mScreenshotSmartActions.getSmartActionsFuture( +- mScreenshotId, null, image, mSmartActionsProvider, +- QUICK_SHARE_ACTION, ++ screenshotId, uri, image, mSmartActionsProvider, QUICK_SHARE_ACTION, + mSmartActionsEnabled, user); + int timeoutMs = DeviceConfig.getInt( + DeviceConfig.NAMESPACE_SYSTEMUI, +@@ -468,11 +487,11 @@ class SaveImageInBackgroundTask extends AsyncTask { + 500); + List quickShareActions = + mScreenshotSmartActions.getSmartActions( +- mScreenshotId, quickShareActionsFuture, timeoutMs, ++ screenshotId, quickShareActionsFuture, timeoutMs, + mSmartActionsProvider, QUICK_SHARE_ACTION); + if (!quickShareActions.isEmpty()) { +- mQuickShareData.quickShareAction = quickShareActions.get(0); +- mParams.mQuickShareActionsReadyListener.onActionsReady(mQuickShareData); ++ return quickShareActions.get(0); + } ++ return null; + } + } +diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +index 16872b08b9c8..8bb5bb581355 100644 +--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java ++++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +@@ -56,6 +56,7 @@ import android.os.IBinder; + import android.os.Looper; + import android.os.Message; + import android.os.RemoteException; ++import android.provider.DeviceConfig; + import android.provider.Settings; + import android.util.DisplayMetrics; + import android.util.Log; +@@ -82,6 +83,7 @@ import android.widget.Toast; + import android.window.WindowContext; + + import com.android.internal.app.ChooserActivity; ++import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; + import com.android.internal.logging.UiEventLogger; + import com.android.internal.policy.PhoneWindow; + import com.android.settingslib.applications.InterestingConfigChanges; +@@ -226,6 +228,7 @@ public class ScreenshotController { + static final String EXTRA_SMART_ACTIONS_ENABLED = "android:smart_actions_enabled"; + static final String EXTRA_OVERRIDE_TRANSITION = "android:screenshot_override_transition"; + static final String EXTRA_ACTION_INTENT = "android:screenshot_action_intent"; ++ static final String EXTRA_ACTION_INTENT_FILLIN = "android:screenshot_action_intent_fillin"; + + static final String SCREENSHOT_URI_ID = "android:screenshot_uri_id"; + static final String EXTRA_CANCEL_NOTIFICATION = "android:screenshot_cancel_notification"; +@@ -851,8 +854,11 @@ public class ScreenshotController { + mSaveInBgTask.setActionsReadyListener(this::logSuccessOnActionsReady); + } + ++ boolean smartActionsEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI, ++ SystemUiDeviceConfigFlags.ENABLE_SCREENSHOT_NOTIFICATION_SMART_ACTIONS, true); ++ + mSaveInBgTask = new SaveImageInBackgroundTask(mContext, mImageExporter, +- mScreenshotSmartActions, data, getActionTransitionSupplier()); ++ mScreenshotSmartActions, data, getActionTransitionSupplier(), smartActionsEnabled); + mSaveInBgTask.execute(); + } + +diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java +index f703058f4a0f..6152f940d12d 100644 +--- a/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java ++++ b/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java +@@ -18,6 +18,7 @@ package com.android.systemui.screenshot; + + import static com.android.systemui.screenshot.LogConfig.DEBUG_ACTIONS; + import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ACTION_INTENT; ++import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ACTION_INTENT_FILLIN; + import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ACTION_TYPE; + import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ID; + +@@ -47,6 +48,7 @@ public class SmartActionsReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + PendingIntent pendingIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT); ++ Intent fillIn = intent.getParcelableExtra(EXTRA_ACTION_INTENT_FILLIN); + String actionType = intent.getStringExtra(EXTRA_ACTION_TYPE); + if (DEBUG_ACTIONS) { + Log.d(TAG, "Executing smart action [" + actionType + "]:" + pendingIntent.getIntent()); +@@ -54,7 +56,7 @@ public class SmartActionsReceiver extends BroadcastReceiver { + ActivityOptions opts = ActivityOptions.makeBasic(); + + try { +- pendingIntent.send(context, 0, null, null, null, null, opts.toBundle()); ++ pendingIntent.send(context, 0, fillIn, null, null, null, opts.toBundle()); + } catch (PendingIntent.CanceledException e) { + Log.e(TAG, "Pending intent canceled", e); + } +diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/SaveImageInBackgroundTaskTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/SaveImageInBackgroundTaskTest.kt +new file mode 100644 +index 000000000000..f1c2169b87f8 +--- /dev/null ++++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/SaveImageInBackgroundTaskTest.kt +@@ -0,0 +1,274 @@ ++/* ++ * Copyright (C) 2023 The Android Open Source Project ++ * ++ * Licensed under the Apache License, Version 2.0 (the "License"); ++ * you may not use this file except in compliance with the License. ++ * You may obtain a copy of the License at ++ * ++ * http://www.apache.org/licenses/LICENSE-2.0 ++ * ++ * Unless required by applicable law or agreed to in writing, software ++ * distributed under the License is distributed on an "AS IS" BASIS, ++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++ * See the License for the specific language governing permissions and ++ * limitations under the License. ++ */ ++ ++package com.android.systemui.screenshot ++ ++import android.app.Notification ++import android.app.PendingIntent ++import android.content.ComponentName ++import android.content.Intent ++import android.graphics.Bitmap ++import android.graphics.drawable.Icon ++import android.net.Uri ++import android.os.UserHandle ++import androidx.test.filters.SmallTest ++import com.android.systemui.SysuiTestCase ++import com.android.systemui.screenshot.ScreenshotController.SaveImageInBackgroundData ++import com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider.ScreenshotSmartActionType ++import java.util.concurrent.CompletableFuture ++import java.util.function.Supplier ++import org.junit.Assert.assertEquals ++import org.junit.Assert.assertNull ++import org.junit.Before ++import org.junit.Test ++import org.mockito.Mockito ++ ++@SmallTest ++class SaveImageInBackgroundTaskTest : SysuiTestCase() { ++ private val imageExporter = mock() ++ private val smartActions = mock() ++ private val saveImageData = SaveImageInBackgroundData() ++ private val sharedTransitionSupplier = ++ mock>() ++ private val testScreenshotId: String = "testScreenshotId" ++ private val testBitmap = mock() ++ private val testUser = UserHandle.getUserHandleForUid(0) ++ private val testIcon = mock() ++ private val testImageTime = 1234.toLong() ++ ++ private val smartActionsUriFuture = mock>>() ++ private val smartActionsFuture = mock>>() ++ ++ private val testUri: Uri = Uri.parse("testUri") ++ private val intent = ++ Intent(Intent.ACTION_SEND) ++ .setComponent( ++ ComponentName.unflattenFromString( ++ "com.google.android.test/com.google.android.test.TestActivity" ++ ) ++ ) ++ private val immutablePendingIntent = ++ PendingIntent.getBroadcast( ++ mContext, ++ 0, ++ intent, ++ PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE ++ ) ++ private val mutablePendingIntent = ++ PendingIntent.getBroadcast( ++ mContext, ++ 0, ++ intent, ++ PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_MUTABLE ++ ) ++ ++ private val saveImageTask = ++ SaveImageInBackgroundTask( ++ mContext, ++ imageExporter, ++ smartActions, ++ saveImageData, ++ sharedTransitionSupplier, ++ false, // forces a no-op implementation; we're mocking out the behavior anyway ++ ) ++ ++ @Before ++ fun setup() { ++ Mockito.`when`( ++ smartActions.getSmartActionsFuture( ++ Mockito.eq(testScreenshotId), ++ Mockito.any(Uri::class.java), ++ Mockito.eq(testBitmap), ++ Mockito.any(ScreenshotNotificationSmartActionsProvider::class.java), ++ Mockito.any(ScreenshotSmartActionType::class.java), ++ Mockito.any(Boolean::class.java), ++ Mockito.eq(testUser) ++ ) ++ ) ++ .thenReturn(smartActionsUriFuture) ++ Mockito.`when`( ++ smartActions.getSmartActionsFuture( ++ Mockito.eq(testScreenshotId), ++ Mockito.eq(null), ++ Mockito.eq(testBitmap), ++ Mockito.any(ScreenshotNotificationSmartActionsProvider::class.java), ++ Mockito.any(ScreenshotSmartActionType::class.java), ++ Mockito.any(Boolean::class.java), ++ Mockito.eq(testUser) ++ ) ++ ) ++ .thenReturn(smartActionsFuture) ++ } ++ ++ @Test ++ fun testQueryQuickShare_noAction() { ++ Mockito.`when`( ++ smartActions.getSmartActions( ++ Mockito.eq(testScreenshotId), ++ Mockito.eq(smartActionsFuture), ++ Mockito.any(Int::class.java), ++ Mockito.any(ScreenshotNotificationSmartActionsProvider::class.java), ++ Mockito.eq(ScreenshotSmartActionType.QUICK_SHARE_ACTION) ++ ) ++ ) ++ .thenReturn(ArrayList()) ++ ++ val quickShareAction = ++ saveImageTask.queryQuickShareAction(testScreenshotId, testBitmap, testUser, testUri) ++ ++ assertNull(quickShareAction) ++ } ++ ++ @Test ++ fun testQueryQuickShare_withActions() { ++ val actions = ArrayList() ++ actions.add(constructAction("Action One", mutablePendingIntent)) ++ actions.add(constructAction("Action Two", mutablePendingIntent)) ++ Mockito.`when`( ++ smartActions.getSmartActions( ++ Mockito.eq(testScreenshotId), ++ Mockito.eq(smartActionsUriFuture), ++ Mockito.any(Int::class.java), ++ Mockito.any(ScreenshotNotificationSmartActionsProvider::class.java), ++ Mockito.eq(ScreenshotSmartActionType.QUICK_SHARE_ACTION) ++ ) ++ ) ++ .thenReturn(actions) ++ ++ val quickShareAction = ++ saveImageTask.queryQuickShareAction(testScreenshotId, testBitmap, testUser, testUri)!! ++ ++ assertEquals("Action One", quickShareAction.title) ++ assertEquals(mutablePendingIntent, quickShareAction.actionIntent) ++ } ++ ++ @Test ++ fun testCreateQuickShareAction_originalWasNull_returnsNull() { ++ val quickShareAction = ++ saveImageTask.createQuickShareAction( ++ null, ++ testScreenshotId, ++ testUri, ++ testImageTime, ++ testBitmap, ++ testUser ++ ) ++ ++ assertNull(quickShareAction) ++ } ++ ++ @Test ++ fun testCreateQuickShareAction_immutableIntentDifferentAction_returnsNull() { ++ val actions = ArrayList() ++ actions.add(constructAction("New Test Action", immutablePendingIntent)) ++ Mockito.`when`( ++ smartActions.getSmartActions( ++ Mockito.eq(testScreenshotId), ++ Mockito.eq(smartActionsUriFuture), ++ Mockito.any(Int::class.java), ++ Mockito.any(ScreenshotNotificationSmartActionsProvider::class.java), ++ Mockito.eq(ScreenshotSmartActionType.QUICK_SHARE_ACTION) ++ ) ++ ) ++ .thenReturn(actions) ++ val origAction = constructAction("Old Test Action", immutablePendingIntent) ++ ++ val quickShareAction = ++ saveImageTask.createQuickShareAction( ++ origAction, ++ testScreenshotId, ++ testUri, ++ testImageTime, ++ testBitmap, ++ testUser, ++ ) ++ ++ assertNull(quickShareAction) ++ } ++ ++ @Test ++ fun testCreateQuickShareAction_mutableIntent_returnsSafeIntent() { ++ val actions = ArrayList() ++ val action = constructAction("Action One", mutablePendingIntent) ++ actions.add(action) ++ Mockito.`when`( ++ smartActions.getSmartActions( ++ Mockito.eq(testScreenshotId), ++ Mockito.eq(smartActionsUriFuture), ++ Mockito.any(Int::class.java), ++ Mockito.any(ScreenshotNotificationSmartActionsProvider::class.java), ++ Mockito.eq(ScreenshotSmartActionType.QUICK_SHARE_ACTION) ++ ) ++ ) ++ .thenReturn(actions) ++ ++ val quickShareAction = ++ saveImageTask.createQuickShareAction( ++ constructAction("Test Action", mutablePendingIntent), ++ testScreenshotId, ++ testUri, ++ testImageTime, ++ testBitmap, ++ testUser ++ ) ++ val quickSharePendingIntent : PendingIntent = ++ quickShareAction.actionIntent.intent.extras!!.getParcelable( ++ ScreenshotController.EXTRA_ACTION_INTENT)!! ++ ++ assertEquals("Test Action", quickShareAction.title) ++ assertEquals(mutablePendingIntent, quickSharePendingIntent) ++ } ++ ++ @Test ++ fun testCreateQuickShareAction_immutableIntent_returnsSafeIntent() { ++ val actions = ArrayList() ++ val action = constructAction("Test Action", immutablePendingIntent) ++ actions.add(action) ++ Mockito.`when`( ++ smartActions.getSmartActions( ++ Mockito.eq(testScreenshotId), ++ Mockito.eq(smartActionsUriFuture), ++ Mockito.any(Int::class.java), ++ Mockito.any(ScreenshotNotificationSmartActionsProvider::class.java), ++ Mockito.eq(ScreenshotSmartActionType.QUICK_SHARE_ACTION) ++ ) ++ ) ++ .thenReturn(actions) ++ ++ val quickShareAction = ++ saveImageTask.createQuickShareAction( ++ constructAction("Test Action", immutablePendingIntent), ++ testScreenshotId, ++ testUri, ++ testImageTime, ++ testBitmap, ++ testUser, ++ )!! ++ val quickSharePendingIntent : PendingIntent = ++ quickShareAction.actionIntent.intent.extras!!.getParcelable( ++ ScreenshotController.EXTRA_ACTION_INTENT)!! ++ ++ assertEquals("Test Action", quickShareAction.title) ++ assertEquals(immutablePendingIntent, quickSharePendingIntent) ++ } ++ ++ private fun constructAction(title: String, intent: PendingIntent): Notification.Action { ++ return Notification.Action.Builder(testIcon, title, intent).build() ++ } ++ ++ inline fun mock(apply: T.() -> Unit = {}): T = ++ Mockito.mock(T::class.java).apply(apply) ++} +diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java +index 3d658ec8e811..98bde2c86b40 100644 +--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java ++++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java +@@ -183,7 +183,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase { + data.mActionsReadyListener = null; + SaveImageInBackgroundTask task = + new SaveImageInBackgroundTask(mContext, null, mScreenshotSmartActions, data, +- ActionTransition::new); ++ ActionTransition::new, false); + + Notification.Action shareAction = task.createShareAction(mContext, mContext.getResources(), + Uri.parse("Screenshot_123.png")).get().action; +@@ -211,7 +211,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase { + data.mActionsReadyListener = null; + SaveImageInBackgroundTask task = + new SaveImageInBackgroundTask(mContext, null, mScreenshotSmartActions, data, +- ActionTransition::new); ++ ActionTransition::new, false); + + Notification.Action editAction = task.createEditAction(mContext, mContext.getResources(), + Uri.parse("Screenshot_123.png")).get().action; +@@ -239,7 +239,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase { + data.mActionsReadyListener = null; + SaveImageInBackgroundTask task = + new SaveImageInBackgroundTask(mContext, null, mScreenshotSmartActions, data, +- ActionTransition::new); ++ ActionTransition::new, false); + + Notification.Action deleteAction = task.createDeleteAction(mContext, + mContext.getResources(), +-- +2.41.0.585.gd2178a4bd4-goog + diff --git a/aosp_diff/preliminary/frameworks/base/99_0184-Ignore-virtual-presentation-windows-RESTRICT-AUTOMERGE.bulletin.patch b/aosp_diff/preliminary/frameworks/base/99_0184-Ignore-virtual-presentation-windows-RESTRICT-AUTOMERGE.bulletin.patch new file mode 100644 index 0000000000..9e3f1bb29f --- /dev/null +++ b/aosp_diff/preliminary/frameworks/base/99_0184-Ignore-virtual-presentation-windows-RESTRICT-AUTOMERGE.bulletin.patch @@ -0,0 +1,50 @@ +From 412d9b6ab47950f4a932d296636c2510efe1b4aa Mon Sep 17 00:00:00 2001 +From: Achim Thesmann +Date: Tue, 23 May 2023 00:26:33 +0000 +Subject: [PATCH] Ignore virtual presentation windows - RESTRICT AUTOMERGE + +Windows of TYPE_PRESENTATION on virtual displays should not be counted +as visible windows to determine if BAL is allowed. + +Test: manual test, atest BackgroundActivityLaunchTest +Bug: 264029851, 205130886 +(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:bf60a0c6f153a55714d4879bb6cf5b239381a22a) +Merged-In: I08b16ba1c155e951286ddc22019180cbd6334dfa +Change-Id: I08b16ba1c155e951286ddc22019180cbd6334dfa +--- + .../core/java/com/android/server/wm/WindowState.java | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java +index 9a71d8b050d6..09fd71412fea 100644 +--- a/services/core/java/com/android/server/wm/WindowState.java ++++ b/services/core/java/com/android/server/wm/WindowState.java +@@ -3532,8 +3532,12 @@ class WindowState extends WindowContainer implements WindowManagerP + // apps won't always be considered as foreground state. + // Exclude private presentations as they can only be shown on private virtual displays and + // shouldn't be the cause of an app be considered foreground. +- if (mAttrs.type >= FIRST_SYSTEM_WINDOW && mAttrs.type != TYPE_TOAST +- && mAttrs.type != TYPE_PRIVATE_PRESENTATION) { ++ // Exclude presentations on virtual displays as they are not actually visible. ++ if (mAttrs.type >= FIRST_SYSTEM_WINDOW ++ && mAttrs.type != TYPE_TOAST ++ && mAttrs.type != TYPE_PRIVATE_PRESENTATION ++ && !(mAttrs.type == TYPE_PRESENTATION && isOnVirtualDisplay()) ++ ) { + mWmService.mAtmService.mActiveUids.onNonAppSurfaceVisibilityChanged(mOwnerUid, shown); + } + if (mIsImWindow && mWmService.mAccessibilityController != null) { +@@ -3541,6 +3545,10 @@ class WindowState extends WindowContainer implements WindowManagerP + } + } + ++ private boolean isOnVirtualDisplay() { ++ return getDisplayContent().mDisplay.getType() == Display.TYPE_VIRTUAL; ++ } ++ + private void logExclusionRestrictions(int side) { + if (!logsGestureExclusionRestrictions(this) + || SystemClock.uptimeMillis() < mLastExclusionLogUptimeMillis[side] +-- +2.41.0.585.gd2178a4bd4-goog + diff --git a/aosp_diff/preliminary/frameworks/base/99_0185-Update-AccountManagerService-checkKeyIntentParceledCorrectly.bulletin.patch b/aosp_diff/preliminary/frameworks/base/99_0185-Update-AccountManagerService-checkKeyIntentParceledCorrectly.bulletin.patch new file mode 100644 index 0000000000..d26162327e --- /dev/null +++ b/aosp_diff/preliminary/frameworks/base/99_0185-Update-AccountManagerService-checkKeyIntentParceledCorrectly.bulletin.patch @@ -0,0 +1,31 @@ +From 9d4a401222fe5ef9e9fc4a07de5f82ce920f2d31 Mon Sep 17 00:00:00 2001 +From: Dmitry Dementyev +Date: Fri, 30 Jun 2023 14:36:44 -0700 +Subject: [PATCH] Update AccountManagerService checkKeyIntentParceledCorrectly. + +Bug: 265798288 +Test: manual +(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:b117b506ec0504ff9eb2fa523e82f1879ecb8cc1) +Merged-In: Iad33851af32a11c99d11bc2b5c76d124c3e97ebb +Change-Id: Iad33851af32a11c99d11bc2b5c76d124c3e97ebb +--- + .../com/android/server/accounts/AccountManagerService.java | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java +index c0aa36a0fb77..215bd2b02cc7 100644 +--- a/services/core/java/com/android/server/accounts/AccountManagerService.java ++++ b/services/core/java/com/android/server/accounts/AccountManagerService.java +@@ -4923,6 +4923,9 @@ public class AccountManagerService + Bundle simulateBundle = p.readBundle(); + p.recycle(); + Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT); ++ if (intent != null && intent.getClass() != Intent.class) { ++ return false; ++ } + Intent simulateIntent = simulateBundle.getParcelable(AccountManager.KEY_INTENT); + if (intent == null) { + return (simulateIntent == null); +-- +2.41.0.585.gd2178a4bd4-goog + diff --git a/aosp_diff/preliminary/frameworks/base/99_0186-Forbid-granting-access-to-NLSes-with-too-long-component-name.bulletin.patch b/aosp_diff/preliminary/frameworks/base/99_0186-Forbid-granting-access-to-NLSes-with-too-long-component-name.bulletin.patch new file mode 100644 index 0000000000..29fe0f4638 --- /dev/null +++ b/aosp_diff/preliminary/frameworks/base/99_0186-Forbid-granting-access-to-NLSes-with-too-long-component-name.bulletin.patch @@ -0,0 +1,116 @@ +From 54959a7d14b311d4ae3032470d68e68e4e499a6f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Mat=C3=ADas=20Hern=C3=A1ndez?= +Date: Thu, 15 Jun 2023 18:31:34 +0200 +Subject: [PATCH] Forbid granting access to NLSes with too-long component names + +This makes the limitation, which was previously only checked on the Settings UI, enforced everywhere. + +Fixes: 260570119 +Fixes: 286043036 +Test: atest + manually +(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:8a40b0b3a17658af16922b4ba99ccc4258af89f5) +Merged-In: I4c25d80978cb37a8fa1531f5045259d25ac64692 +Change-Id: I4c25d80978cb37a8fa1531f5045259d25ac64692 +--- + .../java/android/app/NotificationManager.java | 6 +++++ + .../NotificationManagerService.java | 5 ++++ + .../android/server/vr/VrManagerService.java | 6 ++++- + .../NotificationManagerServiceTest.java | 25 +++++++++++++++++++ + 4 files changed, 41 insertions(+), 1 deletion(-) + +diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java +index ccf1edb3fecc..d6835e31bab1 100644 +--- a/core/java/android/app/NotificationManager.java ++++ b/core/java/android/app/NotificationManager.java +@@ -561,6 +561,12 @@ public class NotificationManager { + */ + public static final int BUBBLE_PREFERENCE_SELECTED = 2; + ++ /** ++ * Maximum length of the component name of a registered NotificationListenerService. ++ * @hide ++ */ ++ public static int MAX_SERVICE_COMPONENT_NAME_LENGTH = 500; ++ + @UnsupportedAppUsage + private static INotificationManager sService; + +diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java +index 7cc05765e9c8..dea8c52927fe 100755 +--- a/services/core/java/com/android/server/notification/NotificationManagerService.java ++++ b/services/core/java/com/android/server/notification/NotificationManagerService.java +@@ -5381,6 +5381,11 @@ public class NotificationManagerService extends SystemService { + boolean granted, boolean userSet) { + Objects.requireNonNull(listener); + checkNotificationListenerAccess(); ++ if (granted && listener.flattenToString().length() ++ > NotificationManager.MAX_SERVICE_COMPONENT_NAME_LENGTH) { ++ throw new IllegalArgumentException( ++ "Component name too long: " + listener.flattenToString()); ++ } + if (!userSet && isNotificationListenerAccessUserSet(listener)) { + // Don't override user's choice + return; +diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java +index b296ef2a1443..1ff01a6c70bf 100644 +--- a/services/core/java/com/android/server/vr/VrManagerService.java ++++ b/services/core/java/com/android/server/vr/VrManagerService.java +@@ -1049,7 +1049,11 @@ public class VrManagerService extends SystemService + + for (ComponentName c : possibleServices) { + if (Objects.equals(c.getPackageName(), pkg)) { +- nm.setNotificationListenerAccessGrantedForUser(c, userId, true); ++ try { ++ nm.setNotificationListenerAccessGrantedForUser(c, userId, true); ++ } catch (Exception e) { ++ Slog.w(TAG, "Could not grant NLS access to package " + pkg, e); ++ } + } + } + } +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 a3c8cfabd3c5..2dff80ece44a 100755 +--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java ++++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +@@ -76,6 +76,7 @@ import static junit.framework.Assert.assertNull; + import static junit.framework.Assert.assertTrue; + import static junit.framework.Assert.fail; + ++import static org.junit.Assert.assertThrows; + import static org.mockito.ArgumentMatchers.isNull; + import static org.mockito.Matchers.anyBoolean; + import static org.mockito.Matchers.anyLong; +@@ -3146,6 +3147,30 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { + any(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean()); + } + ++ @Test ++ public void testSetListenerAccessForUser_grantWithNameTooLong_throws() { ++ UserHandle user = UserHandle.of(mContext.getUserId() + 10); ++ ComponentName c = new ComponentName("com.example.package", ++ com.google.common.base.Strings.repeat("Blah", 150)); ++ ++ assertThrows(IllegalArgumentException.class, ++ () -> mBinderService.setNotificationListenerAccessGrantedForUser( ++ c, user.getIdentifier(), /* enabled= */ true, true)); ++ } ++ ++ @Test ++ public void testSetListenerAccessForUser_revokeWithNameTooLong_okay() throws Exception { ++ UserHandle user = UserHandle.of(mContext.getUserId() + 10); ++ ComponentName c = new ComponentName("com.example.package", ++ com.google.common.base.Strings.repeat("Blah", 150)); ++ ++ mBinderService.setNotificationListenerAccessGrantedForUser( ++ c, user.getIdentifier(), /* enabled= */ false, true); ++ ++ verify(mListeners).setPackageOrComponentEnabled( ++ c.flattenToString(), user.getIdentifier(), true, /* enabled= */ false, true); ++ } ++ + @Test + public void testSetAssistantAccessForUser() throws Exception { + UserInfo ui = new UserInfo(); +-- +2.41.0.585.gd2178a4bd4-goog + diff --git a/aosp_diff/preliminary/frameworks/base/99_0187-Improve-user-handling-when-querying-for-resumable-me.patch b/aosp_diff/preliminary/frameworks/base/99_0187-Improve-user-handling-when-querying-for-resumable-me.patch new file mode 100644 index 0000000000..c951272de7 --- /dev/null +++ b/aosp_diff/preliminary/frameworks/base/99_0187-Improve-user-handling-when-querying-for-resumable-me.patch @@ -0,0 +1,409 @@ +From b5556deb5b52128962199358de8190056654d2f2 Mon Sep 17 00:00:00 2001 +From: Beth Thibodeau +Date: Thu, 22 Jun 2023 18:26:44 -0500 +Subject: [PATCH] Improve user handling when querying for resumable media + +- Before trying to query recent media from a saved component, check + whether the current user actually has that component installed +- Track user when creating the MediaBrowser, in case the user changes + before the MBS returns a result + +Test: atest MediaResumeListenerTest +Bug: 284297711 +(cherry picked from commit e566a250ad61e269119b475c7ebdae6ca962c4a7) +(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:8dccce4f519c91354d5bc6850137269fec715e5d) +Merged-In: I838ff0e125acadabc8436a00dbff707cc4be6249 +Change-Id: I838ff0e125acadabc8436a00dbff707cc4be6249 +--- + .../systemui/media/MediaResumeListener.kt | 49 +++++++-- + .../systemui/media/ResumeMediaBrowser.java | 21 +++- + .../media/ResumeMediaBrowserFactory.java | 7 +- + .../systemui/media/MediaResumeListenerTest.kt | 104 ++++++++++++++---- + .../systemui/media/ResumeMediaBrowserTest.kt | 15 ++- + 5 files changed, 157 insertions(+), 39 deletions(-) + +diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt +index 608c784f5d39..e349e593f903 100644 +--- a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt ++++ b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt +@@ -97,9 +97,16 @@ class MediaResumeListener @Inject constructor( + Log.e(TAG, "Error getting package information", e) + } + +- Log.d(TAG, "Adding resume controls $desc") +- mediaDataManager.addResumptionControls(currentUserId, desc, resumeAction, token, +- appName.toString(), appIntent, component.packageName) ++ Log.d(TAG, "Adding resume controls for ${browser.userId}: $desc") ++ mediaDataManager.addResumptionControls( ++ browser.userId, ++ desc, ++ resumeAction, ++ token, ++ appName.toString(), ++ appIntent, ++ component.packageName ++ ) + } + } + +@@ -154,7 +161,11 @@ class MediaResumeListener @Inject constructor( + } + resumeComponents.add(component to lastPlayed) + } +- Log.d(TAG, "loaded resume components ${resumeComponents.toArray().contentToString()}") ++ Log.d( ++ TAG, ++ "loaded resume components for $currentUserId: " + ++ "${resumeComponents.toArray().contentToString()}" ++ ) + + if (needsUpdate) { + // Save any missing times that we had to fill in +@@ -169,12 +180,23 @@ class MediaResumeListener @Inject constructor( + if (!useMediaResumption) { + return + } +- ++ ++ val pm = context.packageManager + val now = systemClock.currentTimeMillis() + resumeComponents.forEach { + if (now.minus(it.second) <= RESUME_MEDIA_TIMEOUT) { +- val browser = mediaBrowserFactory.create(mediaBrowserCallback, it.first) ++ // Verify that the service exists for this user ++ val intent = Intent(MediaBrowserService.SERVICE_INTERFACE) ++ intent.component = it.first ++ val inf = pm.resolveServiceAsUser(intent, 0, currentUserId) ++ if (inf != null) { ++ val browser = ++ mediaBrowserFactory.create(mediaBrowserCallback, it.first, currentUserId) + browser.findRecentMedia() ++ } else { ++ Log.d(TAG, "User $currentUserId does not have component $it.first") ++ } ++ + } + } + } +@@ -198,7 +220,7 @@ class MediaResumeListener @Inject constructor( + Log.d(TAG, "Checking for service component for " + data.packageName) + val pm = context.packageManager + val serviceIntent = Intent(MediaBrowserService.SERVICE_INTERFACE) +- val resumeInfo = pm.queryIntentServices(serviceIntent, 0) ++ val resumeInfo = pm.queryIntentServicesAsUser(serviceIntent, 0, currentUserId) + + val inf = resumeInfo?.filter { + it.serviceInfo.packageName == data.packageName +@@ -241,13 +263,18 @@ class MediaResumeListener @Inject constructor( + browser: ResumeMediaBrowser + ) { + // Since this is a test, just save the component for later +- Log.d(TAG, "Can get resumable media from $componentName") ++ Log.d( ++ TAG, ++ "Can get resumable media for ${browser.userId} from $componentName" ++ ) + mediaDataManager.setResumeAction(key, getResumeAction(componentName)) + updateResumptionList(componentName) + mediaBrowser = null + } + }, +- componentName) ++ componentName, ++ currentUserId ++ ) + mediaBrowser?.testConnection() + } + +@@ -287,7 +314,7 @@ class MediaResumeListener @Inject constructor( + */ + private fun getResumeAction(componentName: ComponentName): Runnable { + return Runnable { +- mediaBrowser = mediaBrowserFactory.create(null, componentName) ++ mediaBrowser = mediaBrowserFactory.create(null, componentName, currentUserId) + mediaBrowser?.restart() + } + } +@@ -297,4 +324,4 @@ class MediaResumeListener @Inject constructor( + println("resumeComponents: $resumeComponents") + } + } +-} +\ No newline at end of file ++} +diff --git a/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java +index fecc903326f5..bcd597814354 100644 +--- a/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java ++++ b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java +@@ -17,6 +17,7 @@ + package com.android.systemui.media; + + import android.annotation.Nullable; ++import android.annotation.UserIdInt; + import android.app.PendingIntent; + import android.content.ComponentName; + import android.content.Context; +@@ -50,6 +51,8 @@ public class ResumeMediaBrowser { + private final Context mContext; + @Nullable private final Callback mCallback; + private MediaBrowserFactory mBrowserFactory; ++ @UserIdInt private final int mUserId; ++ + private MediaBrowser mMediaBrowser; + private ComponentName mComponentName; + +@@ -58,13 +61,19 @@ public class ResumeMediaBrowser { + * @param context the context + * @param callback used to report media items found + * @param componentName Component name of the MediaBrowserService this browser will connect to ++ * @param userId ID of the current user + */ +- public ResumeMediaBrowser(Context context, @Nullable Callback callback, +- ComponentName componentName, MediaBrowserFactory browserFactory) { ++ public ResumeMediaBrowser( ++ Context context, ++ @Nullable Callback callback, ++ ComponentName componentName, ++ MediaBrowserFactory browserFactory, ++ @UserIdInt int userId) { + mContext = context; + mCallback = callback; + mComponentName = componentName; + mBrowserFactory = browserFactory; ++ mUserId = userId; + } + + /** +@@ -259,6 +268,14 @@ public class ResumeMediaBrowser { + return new MediaController(mContext, token); + } + ++ /** ++ * Get the ID of the user associated with this broswer ++ * @return the user ID ++ */ ++ public @UserIdInt int getUserId() { ++ return mUserId; ++ } ++ + /** + * Get the media session token + * @return the token, or null if the MediaBrowser is null or disconnected +diff --git a/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowserFactory.java b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowserFactory.java +index 2261aa5ac265..3f4104906281 100644 +--- a/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowserFactory.java ++++ b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowserFactory.java +@@ -16,6 +16,7 @@ + + package com.android.systemui.media; + ++import android.annotation.UserIdInt; + import android.content.ComponentName; + import android.content.Context; + +@@ -39,10 +40,12 @@ public class ResumeMediaBrowserFactory { + * + * @param callback will be called on connection or error, and addTrack when media item found + * @param componentName component to browse ++ * @param userId ID of the current user + * @return + */ + public ResumeMediaBrowser create(ResumeMediaBrowser.Callback callback, +- ComponentName componentName) { +- return new ResumeMediaBrowser(mContext, callback, componentName, mBrowserFactory); ++ ComponentName componentName, @UserIdInt int userId) { ++ return new ResumeMediaBrowser(mContext, callback, componentName, mBrowserFactory, ++ userId); + } + } +diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt +index 359746bca0ff..a97d885c6d88 100644 +--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt ++++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt +@@ -92,6 +92,7 @@ class MediaResumeListenerTest : SysuiTestCase() { + @Captor lateinit var callbackCaptor: ArgumentCaptor + @Captor lateinit var actionCaptor: ArgumentCaptor + @Captor lateinit var componentCaptor: ArgumentCaptor ++ @Captor lateinit var userIdCaptor: ArgumentCaptor + + private lateinit var executor: FakeExecutor + private lateinit var data: MediaData +@@ -112,7 +113,7 @@ class MediaResumeListenerTest : SysuiTestCase() { + Settings.Secure.putInt(context.contentResolver, + Settings.Secure.MEDIA_CONTROLS_RESUME, 1) + +- whenever(resumeBrowserFactory.create(capture(callbackCaptor), any())) ++ whenever(resumeBrowserFactory.create(capture(callbackCaptor), any(), capture(userIdCaptor))) + .thenReturn(resumeBrowser) + + // resume components are stored in sharedpreferences +@@ -123,6 +124,7 @@ class MediaResumeListenerTest : SysuiTestCase() { + whenever(sharedPrefsEditor.putString(any(), any())).thenReturn(sharedPrefsEditor) + whenever(mockContext.packageManager).thenReturn(context.packageManager) + whenever(mockContext.contentResolver).thenReturn(context.contentResolver) ++ whenever(mockContext.userId).thenReturn(context.userId) + + executor = FakeExecutor(clock) + resumeListener = MediaResumeListener(mockContext, broadcastDispatcher, executor, +@@ -223,15 +225,7 @@ class MediaResumeListenerTest : SysuiTestCase() { + @Test + fun testOnLoad_checksForResume_hasService() { + // Set up mocks to successfully find a MBS that returns valid media +- val pm = mock(PackageManager::class.java) +- whenever(mockContext.packageManager).thenReturn(pm) +- val resolveInfo = ResolveInfo() +- val serviceInfo = ServiceInfo() +- serviceInfo.packageName = PACKAGE_NAME +- resolveInfo.serviceInfo = serviceInfo +- resolveInfo.serviceInfo.name = CLASS_NAME +- val resumeInfo = listOf(resolveInfo) +- whenever(pm.queryIntentServices(any(), anyInt())).thenReturn(resumeInfo) ++ setUpMbsWithValidResolveInfo() + + val description = MediaDescription.Builder().setTitle(TITLE).build() + val component = ComponentName(PACKAGE_NAME, CLASS_NAME) +@@ -270,6 +264,7 @@ class MediaResumeListenerTest : SysuiTestCase() { + @Test + fun testOnUserUnlock_loadsTracks() { + // Set up mock service to successfully find valid media ++ setUpMbsWithValidResolveInfo() + val description = MediaDescription.Builder().setTitle(TITLE).build() + val component = ComponentName(PACKAGE_NAME, CLASS_NAME) + whenever(resumeBrowser.token).thenReturn(token) +@@ -298,15 +293,7 @@ class MediaResumeListenerTest : SysuiTestCase() { + @Test + fun testGetResumeAction_restarts() { + // Set up mocks to successfully find a MBS that returns valid media +- val pm = mock(PackageManager::class.java) +- whenever(mockContext.packageManager).thenReturn(pm) +- val resolveInfo = ResolveInfo() +- val serviceInfo = ServiceInfo() +- serviceInfo.packageName = PACKAGE_NAME +- resolveInfo.serviceInfo = serviceInfo +- resolveInfo.serviceInfo.name = CLASS_NAME +- val resumeInfo = listOf(resolveInfo) +- whenever(pm.queryIntentServices(any(), anyInt())).thenReturn(resumeInfo) ++ setUpMbsWithValidResolveInfo() + + val description = MediaDescription.Builder().setTitle(TITLE).build() + val component = ComponentName(PACKAGE_NAME, CLASS_NAME) +@@ -435,4 +422,81 @@ class MediaResumeListenerTest : SysuiTestCase() { + } + verify(sharedPrefsEditor, times(1)).apply() + } +-} +\ No newline at end of file ++ ++ @Test ++ fun testUserUnlocked_userChangeWhileQuerying() { ++ val firstUserId = context.userId ++ val secondUserId = firstUserId + 1 ++ val description = MediaDescription.Builder().setTitle(TITLE).build() ++ val component = ComponentName(PACKAGE_NAME, CLASS_NAME) ++ ++ setUpMbsWithValidResolveInfo() ++ whenever(resumeBrowser.token).thenReturn(token) ++ whenever(resumeBrowser.appIntent).thenReturn(pendingIntent) ++ ++ val unlockIntent = ++ Intent(Intent.ACTION_USER_UNLOCKED).apply { ++ putExtra(Intent.EXTRA_USER_HANDLE, firstUserId) ++ } ++ ++ // When the first user unlocks and we query their recent media ++ resumeListener.userChangeReceiver.onReceive(context, unlockIntent) ++ whenever(resumeBrowser.userId).thenReturn(userIdCaptor.value) ++ verify(resumeBrowser, times(3)).findRecentMedia() ++ ++ // And the user changes before the MBS response is received ++ val changeIntent = ++ Intent(Intent.ACTION_USER_SWITCHED).apply { ++ putExtra(Intent.EXTRA_USER_HANDLE, secondUserId) ++ } ++ resumeListener.userChangeReceiver.onReceive(context, changeIntent) ++ callbackCaptor.value.addTrack(description, component, resumeBrowser) ++ ++ // Then the loaded media is correctly associated with the first user ++ verify(mediaDataManager) ++ .addResumptionControls( ++ eq(firstUserId), ++ eq(description), ++ any(), ++ eq(token), ++ eq(PACKAGE_NAME), ++ eq(pendingIntent), ++ eq(PACKAGE_NAME) ++ ) ++ } ++ ++ @Test ++ fun testUserUnlocked_noComponent_doesNotQuery() { ++ // Set up a valid MBS, but user does not have the service available ++ setUpMbsWithValidResolveInfo() ++ val pm = mock(PackageManager::class.java) ++ whenever(mockContext.packageManager).thenReturn(pm) ++ whenever(pm.resolveServiceAsUser(any(), anyInt(), anyInt())).thenReturn(null) ++ ++ val unlockIntent = ++ Intent(Intent.ACTION_USER_UNLOCKED).apply { ++ putExtra(Intent.EXTRA_USER_HANDLE, context.userId) ++ } ++ ++ // When the user is unlocked, but does not have the component installed ++ resumeListener.userChangeReceiver.onReceive(context, unlockIntent) ++ ++ // Then we never attempt to connect to it ++ verify(resumeBrowser, never()).findRecentMedia() ++ } ++ ++ /** Sets up mocks to successfully find a MBS that returns valid media. */ ++ private fun setUpMbsWithValidResolveInfo() { ++ val pm = mock(PackageManager::class.java) ++ whenever(mockContext.packageManager).thenReturn(pm) ++ val resolveInfo = ResolveInfo() ++ val serviceInfo = ServiceInfo() ++ serviceInfo.packageName = PACKAGE_NAME ++ resolveInfo.serviceInfo = serviceInfo ++ resolveInfo.serviceInfo.name = CLASS_NAME ++ val resumeInfo = listOf(resolveInfo) ++ whenever(pm.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(resumeInfo) ++ whenever(pm.resolveServiceAsUser(any(), anyInt(), anyInt())).thenReturn(resolveInfo) ++ whenever(pm.getApplicationLabel(any())).thenReturn(PACKAGE_NAME) ++ } ++} +diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/ResumeMediaBrowserTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/ResumeMediaBrowserTest.kt +index dfa7c66b38f9..5620467b2959 100644 +--- a/packages/SystemUI/tests/src/com/android/systemui/media/ResumeMediaBrowserTest.kt ++++ b/packages/SystemUI/tests/src/com/android/systemui/media/ResumeMediaBrowserTest.kt +@@ -81,8 +81,14 @@ public class ResumeMediaBrowserTest : SysuiTestCase() { + + whenever(mediaController.transportControls).thenReturn(transportControls) + +- resumeBrowser = TestableResumeMediaBrowser(context, callback, component, browserFactory, +- mediaController) ++ resumeBrowser = TestableResumeMediaBrowser( ++ context, ++ callback, ++ component, ++ browserFactory, ++ mediaController, ++ context.userId ++ ) + } + + @Test +@@ -282,8 +288,9 @@ public class ResumeMediaBrowserTest : SysuiTestCase() { + callback: Callback, + componentName: ComponentName, + browserFactory: MediaBrowserFactory, +- private val fakeController: MediaController +- ) : ResumeMediaBrowser(context, callback, componentName, browserFactory) { ++ private val fakeController: MediaController, ++ userId: Int ++ ) : ResumeMediaBrowser(context, callback, componentName, browserFactory, userId) { + + override fun createMediaController(token: MediaSession.Token): MediaController { + return fakeController +-- +2.17.1 + diff --git a/aosp_diff/preliminary/frameworks/native/18_0018-Allow-sensors-list-to-be-empty.bulletin.patch b/aosp_diff/preliminary/frameworks/native/18_0018-Allow-sensors-list-to-be-empty.bulletin.patch new file mode 100644 index 0000000000..c2f35f6561 --- /dev/null +++ b/aosp_diff/preliminary/frameworks/native/18_0018-Allow-sensors-list-to-be-empty.bulletin.patch @@ -0,0 +1,37 @@ +From ae0f9c6114d32785599ee10a7154ab0666de7237 Mon Sep 17 00:00:00 2001 +From: Devin Moore +Date: Tue, 25 Apr 2023 00:17:13 +0000 +Subject: [PATCH] Allow sensors list to be empty + +Test: atest VtsHalSensorManagerV1_0TargetTest +Bug: 278013275 +Bug: 269014004 +(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:49600b10aa5675d4e7e985203d69f252ead13e45) +(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:55e73922b24ac620dcf8f4d221c4abead055222b) +Merged-In: I091f57de9570b0ace3a8da76f16fe0e83f0aa624 +Change-Id: I091f57de9570b0ace3a8da76f16fe0e83f0aa624 +--- + libs/sensor/SensorManager.cpp | 7 ++----- + 1 file changed, 2 insertions(+), 5 deletions(-) + +diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp +index cc5bcd842..4e4bdcfb7 100644 +--- a/libs/sensor/SensorManager.cpp ++++ b/libs/sensor/SensorManager.cpp +@@ -172,11 +172,8 @@ status_t SensorManager::assertStateLocked() { + + mSensors = mSensorServer->getSensorList(mOpPackageName); + size_t count = mSensors.size(); +- if (count == 0) { +- ALOGE("Failed to get Sensor list"); +- mSensorServer.clear(); +- return UNKNOWN_ERROR; +- } ++ // If count is 0, mSensorList will be non-null. This is old ++ // existing behavior and callers expect this. + mSensorList = + static_cast(malloc(count * sizeof(Sensor*))); + LOG_ALWAYS_FATAL_IF(mSensorList == nullptr, "mSensorList NULL"); +-- +2.41.0.585.gd2178a4bd4-goog + diff --git a/aosp_diff/preliminary/packages/apps/Launcher3/02_0002-Fix-permission-issue-in-legacy-shortcut.bulletin.patch b/aosp_diff/preliminary/packages/apps/Launcher3/02_0002-Fix-permission-issue-in-legacy-shortcut.bulletin.patch new file mode 100644 index 0000000000..c924196084 --- /dev/null +++ b/aosp_diff/preliminary/packages/apps/Launcher3/02_0002-Fix-permission-issue-in-legacy-shortcut.bulletin.patch @@ -0,0 +1,51 @@ +From 8cf9fd52b04627769598241172adcaed26db56a4 Mon Sep 17 00:00:00 2001 +From: Pinyao Ting +Date: Thu, 1 Jun 2023 18:12:44 -0700 +Subject: [PATCH] Fix permission issue in legacy shortcut + +When building legacy shortcut, Launcher calls +PackageManager#resolveActivity to retrieve necessary permission to +launch the intent. + +However, when the source app wraps an arbitrary intent within +Intent#createChooser, the existing logic will fail because launching +Chooser doesn't require additional permission. + +This CL fixes the security vulnerability by performing the permission +check against the intent that is wrapped within. + +Bug: 270152142 +Test: manual +(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:c53818a16b4322a823497726ac7e7a44501b4442) +Merged-In: If35344c08975e35085c7c2b9b814a3c457a144b0 +Change-Id: If35344c08975e35085c7c2b9b814a3c457a144b0 +--- + .../android/launcher3/util/PackageManagerHelper.java | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java +index 08ec5912a..1bcdab142 100644 +--- a/src/com/android/launcher3/util/PackageManagerHelper.java ++++ b/src/com/android/launcher3/util/PackageManagerHelper.java +@@ -136,6 +136,18 @@ public class PackageManagerHelper { + * any permissions + */ + public boolean hasPermissionForActivity(Intent intent, String srcPackage) { ++ // b/270152142 ++ if (Intent.ACTION_CHOOSER.equals(intent.getAction())) { ++ final Bundle extras = intent.getExtras(); ++ if (extras == null) { ++ return true; ++ } ++ // If given intent is ACTION_CHOOSER, verify srcPackage has permission over EXTRA_INTENT ++ intent = (Intent) extras.getParcelable(Intent.EXTRA_INTENT); ++ if (intent == null) { ++ return true; ++ } ++ } + ResolveInfo target = mPm.resolveActivity(intent, 0); + if (target == null) { + // Not a valid target +-- +2.41.0.585.gd2178a4bd4-goog + diff --git a/aosp_diff/preliminary/packages/apps/Nfc/02_0002-Ensure-that-SecureNFC-setting-cannot-be-bypassed.bulletin.patch b/aosp_diff/preliminary/packages/apps/Nfc/02_0002-Ensure-that-SecureNFC-setting-cannot-be-bypassed.bulletin.patch new file mode 100644 index 0000000000..eea39755dd --- /dev/null +++ b/aosp_diff/preliminary/packages/apps/Nfc/02_0002-Ensure-that-SecureNFC-setting-cannot-be-bypassed.bulletin.patch @@ -0,0 +1,51 @@ +From 046e68a8da32f3bb972edd67419d0d093415b5d5 Mon Sep 17 00:00:00 2001 +From: Alisher Alikhodjaev +Date: Thu, 1 Jun 2023 13:44:28 -0700 +Subject: [PATCH] Ensure that SecureNFC setting cannot be bypassed + +Bug: 268038643 +Test: ctsverifier +(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:d6d8f79fd8d605b3cb460895a8e3a11bcf0c22b0) +Merged-In: Ic408b3ef9e35b646b728f9b76a0ba8922ed6e25f +Change-Id: Ic408b3ef9e35b646b728f9b76a0ba8922ed6e25f +--- + src/com/android/nfc/NfcService.java | 6 ++++++ + src/com/android/nfc/cardemulation/HostEmulationManager.java | 5 +++-- + 2 files changed, 9 insertions(+), 2 deletions(-) + +diff --git a/src/com/android/nfc/NfcService.java b/src/com/android/nfc/NfcService.java +index 633b8f46..bf988845 100644 +--- a/src/com/android/nfc/NfcService.java ++++ b/src/com/android/nfc/NfcService.java +@@ -1092,6 +1092,12 @@ public class NfcService implements DeviceHostListener { + } + } + ++ public boolean isSecureNfcEnabled() { ++ synchronized (NfcService.this) { ++ return mIsSecureNfcEnabled; ++ } ++ } ++ + final class NfcAdapterService extends INfcAdapter.Stub { + @Override + public boolean enable() throws RemoteException { +diff --git a/src/com/android/nfc/cardemulation/HostEmulationManager.java b/src/com/android/nfc/cardemulation/HostEmulationManager.java +index e26f6f2b..71a04c40 100644 +--- a/src/com/android/nfc/cardemulation/HostEmulationManager.java ++++ b/src/com/android/nfc/cardemulation/HostEmulationManager.java +@@ -177,8 +177,9 @@ public class HostEmulationManager { + // Resolve to default + // Check if resolvedService requires unlock + ApduServiceInfo defaultServiceInfo = resolveInfo.defaultService; +- if (defaultServiceInfo.requiresUnlock() && +- mKeyguard.isKeyguardLocked() && mKeyguard.isKeyguardSecure()) { ++ if ((defaultServiceInfo.requiresUnlock() ++ || NfcService.getInstance().isSecureNfcEnabled()) ++ && mKeyguard.isKeyguardLocked() && mKeyguard.isKeyguardSecure()) { + // Just ignore all future APDUs until next tap + mState = STATE_W4_DEACTIVATE; + launchTapAgain(resolveInfo.defaultService, resolveInfo.category); +-- +2.41.0.585.gd2178a4bd4-goog + diff --git a/aosp_diff/preliminary/packages/apps/Settings/32_0032-Don-t-hide-approved-NLSes-in-Settings.bulletin.patch b/aosp_diff/preliminary/packages/apps/Settings/32_0032-Don-t-hide-approved-NLSes-in-Settings.bulletin.patch new file mode 100644 index 0000000000..de27e318e4 --- /dev/null +++ b/aosp_diff/preliminary/packages/apps/Settings/32_0032-Don-t-hide-approved-NLSes-in-Settings.bulletin.patch @@ -0,0 +1,265 @@ +From fcf426577a24acdd86dcc26a9529e9f24a0cce78 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Mat=C3=ADas=20Hern=C3=A1ndez?= +Date: Mon, 5 Jun 2023 18:24:04 +0200 +Subject: [PATCH] Don't hide approved NLSes in Settings + +Note that an NLS that shouldn't be approvable (because its name is too long) but was already approved (either before the max length check was introduced, or through other means) will disappear from the list if the user revokes its access. This might be somewhat confusing, but since this is a very-edge case already it's fine. + +Bug: 282932362 +Test: manual +(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:ff255c6eda1528f01a167a9a65b7f8e414d28584) +Merged-In: I4c9faea68e6d16b1a4ec7f472b5433cac1704c06 +Change-Id: I4c9faea68e6d16b1a4ec7f472b5433cac1704c06 +--- + .../NotificationAccessSettings.java | 25 +-- + .../notification/NotificationBackend.java | 3 + + .../NotificationAccessSettingsTest.java | 144 ++++++++++++++++++ + 3 files changed, 160 insertions(+), 12 deletions(-) + create mode 100644 tests/robotests/src/com/android/settings/notification/NotificationAccessSettingsTest.java + +diff --git a/src/com/android/settings/notification/NotificationAccessSettings.java b/src/com/android/settings/notification/NotificationAccessSettings.java +index d94498e68f..ab2a177bcf 100644 +--- a/src/com/android/settings/notification/NotificationAccessSettings.java ++++ b/src/com/android/settings/notification/NotificationAccessSettings.java +@@ -40,6 +40,7 @@ import android.widget.Toast; + import androidx.preference.PreferenceCategory; + import androidx.preference.PreferenceScreen; + ++import com.android.internal.annotations.VisibleForTesting; + import com.android.settings.R; + import com.android.settings.Utils; + import com.android.settings.applications.AppInfoBase; +@@ -60,8 +61,8 @@ import java.util.List; + @SearchIndexable + public class NotificationAccessSettings extends EmptyTextSettings { + private static final String TAG = "NotifAccessSettings"; +- private static final String ALLOWED_KEY = "allowed"; +- private static final String NOT_ALLOWED_KEY = "not_allowed"; ++ static final String ALLOWED_KEY = "allowed"; ++ static final String NOT_ALLOWED_KEY = "not_allowed"; + private static final int MAX_CN_LENGTH = 500; + + private static final ManagedServiceSettings.Config CONFIG = +@@ -77,9 +78,9 @@ public class NotificationAccessSettings extends EmptyTextSettings { + .setEmptyText(R.string.no_notification_listeners) + .build(); + +- private NotificationManager mNm; ++ @VisibleForTesting NotificationManager mNm; + protected Context mContext; +- private PackageManager mPm; ++ @VisibleForTesting PackageManager mPm; + private DevicePolicyManager mDpm; + private ServiceListing mServiceListing; + private IconDrawableFactory mIconDrawableFactory; +@@ -99,12 +100,6 @@ public class NotificationAccessSettings extends EmptyTextSettings { + .setNoun(CONFIG.noun) + .setSetting(CONFIG.setting) + .setTag(CONFIG.tag) +- .setValidator(info -> { +- if (info.getComponentName().flattenToString().length() > MAX_CN_LENGTH) { +- return false; +- } +- return true; +- }) + .build(); + mServiceListing.addCallback(this::updateList); + +@@ -135,7 +130,8 @@ public class NotificationAccessSettings extends EmptyTextSettings { + mServiceListing.setListening(false); + } + +- private void updateList(List services) { ++ @VisibleForTesting ++ void updateList(List services) { + final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); + final int managedProfileId = Utils.getManagedProfileId(um, UserHandle.myUserId()); + +@@ -148,6 +144,11 @@ public class NotificationAccessSettings extends EmptyTextSettings { + services.sort(new PackageItemInfo.DisplayNameComparator(mPm)); + for (ServiceInfo service : services) { + final ComponentName cn = new ComponentName(service.packageName, service.name); ++ boolean isAllowed = mNm.isNotificationListenerAccessGranted(cn); ++ if (!isAllowed && cn.flattenToString().length() > MAX_CN_LENGTH) { ++ continue; ++ } ++ + CharSequence title = null; + try { + title = mPm.getApplicationInfoAsUser( +@@ -192,7 +193,7 @@ public class NotificationAccessSettings extends EmptyTextSettings { + return true; + }); + pref.setKey(cn.flattenToString()); +- if (mNm.isNotificationListenerAccessGranted(cn)) { ++ if (isAllowed) { + allowedCategory.addPreference(pref); + } else { + notAllowedCategory.addPreference(pref); +diff --git a/src/com/android/settings/notification/NotificationBackend.java b/src/com/android/settings/notification/NotificationBackend.java +index e448dda20a..d95b48f393 100644 +--- a/src/com/android/settings/notification/NotificationBackend.java ++++ b/src/com/android/settings/notification/NotificationBackend.java +@@ -143,6 +143,9 @@ public class NotificationBackend { + + static public CharSequence getDeviceList(ICompanionDeviceManager cdm, LocalBluetoothManager lbm, + String pkg, int userId) { ++ if (cdm == null) { ++ return ""; ++ } + boolean multiple = false; + StringBuilder sb = new StringBuilder(); + +diff --git a/tests/robotests/src/com/android/settings/notification/NotificationAccessSettingsTest.java b/tests/robotests/src/com/android/settings/notification/NotificationAccessSettingsTest.java +new file mode 100644 +index 0000000000..e644c2975b +--- /dev/null ++++ b/tests/robotests/src/com/android/settings/notification/NotificationAccessSettingsTest.java +@@ -0,0 +1,144 @@ ++/* ++ * Copyright (C) 2023 The Android Open Source Project ++ * ++ * Licensed under the Apache License, Version 2.0 (the "License"); ++ * you may not use this file except in compliance with the License. ++ * You may obtain a copy of the License at ++ * ++ * http://www.apache.org/licenses/LICENSE-2.0 ++ * ++ * Unless required by applicable law or agreed to in writing, software ++ * distributed under the License is distributed on an "AS IS" BASIS, ++ * WITHOUT 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.settings.notification; ++ ++import static com.android.settings.notification.NotificationAccessSettings.ALLOWED_KEY; ++import static com.android.settings.notification.NotificationAccessSettings.NOT_ALLOWED_KEY; ++ ++import static com.google.common.base.Preconditions.checkNotNull; ++import static com.google.common.truth.Truth.assertThat; ++ ++import static org.mockito.ArgumentMatchers.any; ++import static org.mockito.ArgumentMatchers.anyInt; ++import static org.mockito.Mockito.mock; ++import static org.mockito.Mockito.when; ++ ++import android.app.NotificationManager; ++import android.content.ComponentName; ++import android.content.Context; ++import android.content.pm.ApplicationInfo; ++import android.content.pm.PackageManager; ++import android.content.pm.ServiceInfo; ++ ++import androidx.fragment.app.FragmentActivity; ++import androidx.preference.PreferenceCategory; ++import androidx.preference.PreferenceScreen; ++ ++import com.android.settings.testutils.shadow.ShadowBluetoothUtils; ++import com.android.settingslib.bluetooth.LocalBluetoothManager; ++ ++import com.google.common.base.Strings; ++ ++import org.junit.Before; ++import org.junit.Test; ++import org.junit.runner.RunWith; ++import org.mockito.Mock; ++import org.mockito.MockitoAnnotations; ++import org.mockito.stubbing.Answer; ++import org.robolectric.Robolectric; ++import org.robolectric.RobolectricTestRunner; ++import org.robolectric.RuntimeEnvironment; ++import org.robolectric.annotation.Config; ++import org.robolectric.shadows.ShadowApplication; ++ ++import java.util.ArrayList; ++ ++@RunWith(RobolectricTestRunner.class) ++@Config(shadows = {ShadowBluetoothUtils.class}) ++public class NotificationAccessSettingsTest { ++ ++ private Context mContext; ++ private NotificationAccessSettings mAccessSettings; ++ @Mock ++ private NotificationManager mNotificationManager; ++ @Mock ++ private PackageManager mPackageManager; ++ ++ @Before ++ public void setUp() throws Exception { ++ MockitoAnnotations.initMocks(this); ++ ++ mContext = RuntimeEnvironment.application; ++ ShadowApplication shadowApp = ShadowApplication.getInstance(); ++ shadowApp.setSystemService(Context.NOTIFICATION_SERVICE, mNotificationManager); ++ ++ mAccessSettings = new NotificationAccessSettings(); ++ FragmentActivity activity = Robolectric.buildActivity(FragmentActivity.class).setup().get(); ++ activity.getSupportFragmentManager().beginTransaction().add(mAccessSettings, null).commit(); ++ ++ when(mPackageManager.getApplicationInfoAsUser(any(), anyInt(), anyInt())).then( ++ (Answer) invocation -> { ++ ApplicationInfo appInfo = mock(ApplicationInfo.class); ++ when(appInfo.loadLabel(any())).thenReturn(invocation.getArgument(0)); ++ return appInfo; ++ }); ++ ++ mAccessSettings.mNm = mNotificationManager; ++ mAccessSettings.mPm = mPackageManager; ++ ShadowBluetoothUtils.sLocalBluetoothManager = mock(LocalBluetoothManager.class); ++ } ++ ++ @Test ++ public void updateList_enabledLongName_shown() { ++ ComponentName longCn = new ComponentName("test.pkg1", ++ Strings.repeat("Blah", 200) + "Service"); ++ ComponentName shortCn = new ComponentName("test.pkg2", "ReasonableService"); ++ ArrayList services = new ArrayList<>(); ++ services.add(newServiceInfo(longCn.getPackageName(), longCn.getClassName(), 1)); ++ services.add(newServiceInfo(shortCn.getPackageName(), shortCn.getClassName(), 2)); ++ when(mNotificationManager.isNotificationListenerAccessGranted(any())).thenReturn(true); ++ ++ mAccessSettings.updateList(services); ++ ++ PreferenceScreen screen = mAccessSettings.getPreferenceScreen(); ++ PreferenceCategory allowed = checkNotNull(screen.findPreference(ALLOWED_KEY)); ++ PreferenceCategory notAllowed = checkNotNull(screen.findPreference(NOT_ALLOWED_KEY)); ++ assertThat(allowed.getPreferenceCount()).isEqualTo(2); ++ assertThat(allowed.getPreference(0).getKey()).isEqualTo(longCn.flattenToString()); ++ assertThat(allowed.getPreference(1).getKey()).isEqualTo(shortCn.flattenToString()); ++ assertThat(notAllowed.getPreferenceCount()).isEqualTo(0); ++ } ++ ++ @Test ++ public void updateList_disabledLongName_notShown() { ++ ComponentName longCn = new ComponentName("test.pkg1", ++ Strings.repeat("Blah", 200) + "Service"); ++ ComponentName shortCn = new ComponentName("test.pkg2", "ReasonableService"); ++ ArrayList services = new ArrayList<>(); ++ services.add(newServiceInfo(longCn.getPackageName(), longCn.getClassName(), 1)); ++ services.add(newServiceInfo(shortCn.getPackageName(), shortCn.getClassName(), 2)); ++ when(mNotificationManager.isNotificationListenerAccessGranted(any())).thenReturn(false); ++ ++ mAccessSettings.updateList(services); ++ ++ PreferenceScreen screen = mAccessSettings.getPreferenceScreen(); ++ PreferenceCategory allowed = checkNotNull(screen.findPreference(ALLOWED_KEY)); ++ PreferenceCategory notAllowed = checkNotNull(screen.findPreference(NOT_ALLOWED_KEY)); ++ assertThat(allowed.getPreferenceCount()).isEqualTo(0); ++ assertThat(notAllowed.getPreferenceCount()).isEqualTo(1); ++ assertThat(notAllowed.getPreference(0).getKey()).isEqualTo(shortCn.flattenToString()); ++ } ++ ++ private static ServiceInfo newServiceInfo(String packageName, String serviceName, int uid) { ++ ServiceInfo serviceInfo = new ServiceInfo(); ++ serviceInfo.packageName = packageName; ++ serviceInfo.name = serviceName; ++ serviceInfo.applicationInfo = new ApplicationInfo(); ++ serviceInfo.applicationInfo.uid = uid; ++ return serviceInfo; ++ } ++} +-- +2.41.0.585.gd2178a4bd4-goog + diff --git a/aosp_diff/preliminary/packages/apps/Settings/33_0033-DO-NOT-MERGE-Prevent-non-system-IME-from-becoming-device-ad.bulletin.patch b/aosp_diff/preliminary/packages/apps/Settings/33_0033-DO-NOT-MERGE-Prevent-non-system-IME-from-becoming-device-ad.bulletin.patch new file mode 100644 index 0000000000..1b24fa0616 --- /dev/null +++ b/aosp_diff/preliminary/packages/apps/Settings/33_0033-DO-NOT-MERGE-Prevent-non-system-IME-from-becoming-device-ad.bulletin.patch @@ -0,0 +1,219 @@ +From b8a46b8ac930f4aa33df0689b341789515bcf5ce Mon Sep 17 00:00:00 2001 +From: Taran Singh +Date: Fri, 19 May 2023 23:17:47 +0000 +Subject: [PATCH] DO NOT MERGE: Prevent non-system IME from becoming device + admin + +Currently selected IME can inject KeyEvent on DeviceAdminAdd screen to +activate itself as device admin and cause various DoS attacks. + +This CL ensures KeyEvent on "Activate" button can only come from system +apps. + +Bug: 280793427 +Test: atest DeviceAdminActivationTest +(cherry picked from commit 70a501d02e0a6aefd874767a15378ba998759373) +(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:cb2b75309466a624e75be6967657f312be91ed30) +Merged-In: I6470d1684d707f4b1e86f8b456be0b4e0af5f188 +Change-Id: I6470d1684d707f4b1e86f8b456be0b4e0af5f188 +--- + .../deviceadmin/DeviceAdminAdd.java | 125 +++++++++--------- + 1 file changed, 66 insertions(+), 59 deletions(-) + +diff --git a/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminAdd.java b/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminAdd.java +index 7b2f4ba15a..2a4250a4fc 100644 +--- a/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminAdd.java ++++ b/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminAdd.java +@@ -52,6 +52,7 @@ import android.text.TextUtils.TruncateAt; + import android.util.EventLog; + import android.util.Log; + import android.view.Display; ++import android.view.KeyEvent; + import android.view.LayoutInflater; + import android.view.View; + import android.view.ViewGroup; +@@ -142,12 +143,12 @@ public class DeviceAdminAdd extends CollapsingToolbarBaseActivity { + + mHandler = new Handler(getMainLooper()); + +- mDPM = (DevicePolicyManager)getSystemService(Context.DEVICE_POLICY_SERVICE); +- mAppOps = (AppOpsManager)getSystemService(Context.APP_OPS_SERVICE); +- mLayoutInflaternflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); ++ mDPM = getSystemService(DevicePolicyManager.class); ++ mAppOps = getSystemService(AppOpsManager.class); ++ mLayoutInflaternflater = getSystemService(LayoutInflater.class); + PackageManager packageManager = getPackageManager(); + +- if ((getIntent().getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) { ++ if ((getIntent().getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) { + Log.w(TAG, "Cannot start ADD_DEVICE_ADMIN as a new task"); + finish(); + return; +@@ -157,7 +158,7 @@ public class DeviceAdminAdd extends CollapsingToolbarBaseActivity { + EXTRA_CALLED_FROM_SUPPORT_DIALOG, false); + + String action = getIntent().getAction(); +- ComponentName who = (ComponentName)getIntent().getParcelableExtra( ++ ComponentName who = (ComponentName) getIntent().getParcelableExtra( + DevicePolicyManager.EXTRA_DEVICE_ADMIN); + if (who == null) { + String packageName = getIntent().getStringExtra(EXTRA_DEVICE_ADMIN_PACKAGE_NAME); +@@ -215,7 +216,7 @@ public class DeviceAdminAdd extends CollapsingToolbarBaseActivity { + PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS); + int count = avail == null ? 0 : avail.size(); + boolean found = false; +- for (int i=0; i { ++ if (!mActionButton.isEnabled()) { ++ showPolicyTransparencyDialogIfRequired(); ++ return; ++ } ++ if (mAdding) { ++ addAndFinish(); ++ } else if (isManagedProfile(mDeviceAdmin) ++ && mDeviceAdmin.getComponent().equals(mDPM.getProfileOwner())) { ++ final int userId = UserHandle.myUserId(); ++ UserDialogs.createRemoveDialog(DeviceAdminAdd.this, userId, ++ new DialogInterface.OnClickListener() { ++ @Override ++ public void onClick(DialogInterface dialog, int which) { ++ UserManager um = UserManager.get(DeviceAdminAdd.this); ++ um.removeUser(userId); ++ finish(); + } +- ).show(); +- } else if (mUninstalling) { +- mDPM.uninstallPackageWithActiveAdmins(mDeviceAdmin.getPackageName()); +- finish(); +- } else if (!mWaitingForRemoveMsg) { +- try { +- // Don't allow the admin to put a dialog up in front +- // of us while we interact with the user. +- ActivityManager.getService().stopAppSwitches(); +- } catch (RemoteException e) { +- } +- mWaitingForRemoveMsg = true; +- mDPM.getRemoveWarning(mDeviceAdmin.getComponent(), +- new RemoteCallback(new RemoteCallback.OnResultListener() { +- @Override +- public void onResult(Bundle result) { +- CharSequence msg = result != null +- ? result.getCharSequence( +- DeviceAdminReceiver.EXTRA_DISABLE_WARNING) +- : null; +- continueRemoveAction(msg); +- } +- }, mHandler)); +- // Don't want to wait too long. +- getWindow().getDecorView().getHandler().postDelayed(new Runnable() { +- @Override public void run() { +- continueRemoveAction(null); + } +- }, 2*1000); ++ ).show(); ++ } else if (mUninstalling) { ++ mDPM.uninstallPackageWithActiveAdmins(mDeviceAdmin.getPackageName()); ++ finish(); ++ } else if (!mWaitingForRemoveMsg) { ++ try { ++ // Don't allow the admin to put a dialog up in front ++ // of us while we interact with the user. ++ ActivityManager.getService().stopAppSwitches(); ++ } catch (RemoteException e) { + } ++ mWaitingForRemoveMsg = true; ++ mDPM.getRemoveWarning(mDeviceAdmin.getComponent(), ++ new RemoteCallback(new RemoteCallback.OnResultListener() { ++ @Override ++ public void onResult(Bundle result) { ++ CharSequence msg = result != null ++ ? result.getCharSequence( ++ DeviceAdminReceiver.EXTRA_DISABLE_WARNING) ++ : null; ++ continueRemoveAction(msg); ++ } ++ }, mHandler)); ++ // Don't want to wait too long. ++ getWindow().getDecorView().getHandler().postDelayed( ++ () -> continueRemoveAction(null), 2 * 1000); ++ } ++ }; ++ restrictedAction.setOnKeyListener((view, keyCode, keyEvent) -> { ++ if ((keyEvent.getFlags() & KeyEvent.FLAG_FROM_SYSTEM) == 0) { ++ Log.e(TAG, "Can not activate device-admin with KeyEvent from non-system app."); ++ // Consume event to suppress click. ++ return true; + } ++ // Fallback to view click handler. ++ return false; + }); ++ restrictedAction.setOnClickListener(restrictedActionClickListener); + } + + /** +-- +2.41.0.585.gd2178a4bd4-goog + diff --git a/aosp_diff/preliminary/packages/apps/Settings/34_0034-Settings-don-t-try-to-allow-NLSes-with-too-long-component-n.bulletin.patch b/aosp_diff/preliminary/packages/apps/Settings/34_0034-Settings-don-t-try-to-allow-NLSes-with-too-long-component-n.bulletin.patch new file mode 100644 index 0000000000..fca5db23cb --- /dev/null +++ b/aosp_diff/preliminary/packages/apps/Settings/34_0034-Settings-don-t-try-to-allow-NLSes-with-too-long-component-n.bulletin.patch @@ -0,0 +1,107 @@ +From c0e31c75aa1f52a928432f92d0675b82549943f5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Mat=C3=ADas=20Hern=C3=A1ndez?= +Date: Thu, 15 Jun 2023 18:37:52 +0200 +Subject: [PATCH] Settings: don't try to allow NLSes with too-long component + names + +* NotificationAccessConfirmationActivity (triggered through CompanionDeviceManager) -> Don't show the dialog, bail out early similarly to other invalid inputs. +* NotificationAccessSettings (from Special App Access) -> No changes, but use the canonical constant now. +* ApprovalPreferenceController (used in NotificationAccessDetails) -> Disable the toggle, unless the NLS was previously approved (in which case it can still be removed). + +Fixes: 260570119 +Fixes: 286043036 +Test: atest + manually +(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:f0367c98d0510f325f8fe76166b188be8820373c) +Merged-In: Ifc048311746c027e3683cdcf65f1079d04cf7c56 +Change-Id: Ifc048311746c027e3683cdcf65f1079d04cf7c56 +--- + .../ApprovalPreferenceController.java | 3 +++ + ...otificationAccessConfirmationActivity.java | 4 +++- + .../NotificationAccessSettings.java | 4 ++-- + .../ApprovalPreferenceControllerTest.java | 19 +++++++++++++++++++ + 4 files changed, 27 insertions(+), 3 deletions(-) + +diff --git a/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceController.java b/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceController.java +index a43b9fd914..9235494f35 100644 +--- a/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceController.java ++++ b/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceController.java +@@ -81,6 +81,9 @@ public class ApprovalPreferenceController extends BasePreferenceController { + final SwitchPreference preference = (SwitchPreference) pref; + final CharSequence label = mPkgInfo.applicationInfo.loadLabel(mPm); + preference.setChecked(isServiceEnabled(mCn)); ++ final boolean isAllowedCn = mCn.flattenToShortString().length() ++ <= NotificationManager.MAX_SERVICE_COMPONENT_NAME_LENGTH; ++ preference.setEnabled(preference.isChecked() || isAllowedCn); + preference.setOnPreferenceChangeListener((p, newValue) -> { + final boolean access = (Boolean) newValue; + if (!access) { +diff --git a/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java b/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java +index dfe6df2a5c..a6b565ae6b 100644 +--- a/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java ++++ b/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java +@@ -67,7 +67,9 @@ public class NotificationAccessConfirmationActivity extends Activity + mUserId = getIntent().getIntExtra(EXTRA_USER_ID, UserHandle.USER_NULL); + CharSequence mAppLabel; + +- if (mComponentName == null || mComponentName.getPackageName() == null) { ++ if (mComponentName == null || mComponentName.getPackageName() == null ++ || mComponentName.flattenToString().length() ++ > NotificationManager.MAX_SERVICE_COMPONENT_NAME_LENGTH) { + finish(); + return; + } +diff --git a/src/com/android/settings/notification/NotificationAccessSettings.java b/src/com/android/settings/notification/NotificationAccessSettings.java +index ab2a177bcf..00543a2f3c 100644 +--- a/src/com/android/settings/notification/NotificationAccessSettings.java ++++ b/src/com/android/settings/notification/NotificationAccessSettings.java +@@ -63,7 +63,6 @@ public class NotificationAccessSettings extends EmptyTextSettings { + private static final String TAG = "NotifAccessSettings"; + static final String ALLOWED_KEY = "allowed"; + static final String NOT_ALLOWED_KEY = "not_allowed"; +- private static final int MAX_CN_LENGTH = 500; + + private static final ManagedServiceSettings.Config CONFIG = + new ManagedServiceSettings.Config.Builder() +@@ -145,7 +144,8 @@ public class NotificationAccessSettings extends EmptyTextSettings { + for (ServiceInfo service : services) { + final ComponentName cn = new ComponentName(service.packageName, service.name); + boolean isAllowed = mNm.isNotificationListenerAccessGranted(cn); +- if (!isAllowed && cn.flattenToString().length() > MAX_CN_LENGTH) { ++ if (!isAllowed && cn.flattenToString().length() ++ > NotificationManager.MAX_SERVICE_COMPONENT_NAME_LENGTH) { + continue; + } + +diff --git a/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceControllerTest.java b/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceControllerTest.java +index 064f8134c6..5316adc1c3 100644 +--- a/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceControllerTest.java ++++ b/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceControllerTest.java +@@ -77,6 +77,25 @@ public class ApprovalPreferenceControllerTest { + mController.setPkgInfo(mPkgInfo); + } + ++ @Test ++ public void updateState_enabled() { ++ SwitchPreference pref = new SwitchPreference(mContext); ++ mController.updateState(pref); ++ assertThat(pref.isEnabled()).isTrue(); ++ } ++ ++ @Test ++ public void updateState_invalidCn_disabled() { ++ ComponentName longCn = new ComponentName("com.example.package", ++ com.google.common.base.Strings.repeat("Blah", 150)); ++ mController.setCn(longCn); ++ SwitchPreference pref = new SwitchPreference(mContext); ++ ++ mController.updateState(pref); ++ ++ assertThat(pref.isEnabled()).isFalse(); ++ } ++ + @Test + public void updateState_checked() { + when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(true); +-- +2.41.0.585.gd2178a4bd4-goog + diff --git a/aosp_diff/preliminary/packages/modules/NeuralNetworks/0006-Fix-out-of-Bounds-Read-in-convertSubgraphFromHAL-in-ShimConv.bulletin.patch b/aosp_diff/preliminary/packages/modules/NeuralNetworks/0006-Fix-out-of-Bounds-Read-in-convertSubgraphFromHAL-in-ShimConv.bulletin.patch new file mode 100644 index 0000000000..e8fe1efa96 --- /dev/null +++ b/aosp_diff/preliminary/packages/modules/NeuralNetworks/0006-Fix-out-of-Bounds-Read-in-convertSubgraphFromHAL-in-ShimConv.bulletin.patch @@ -0,0 +1,34 @@ +From 5b2c3bb86450cd0b5611d9abad9ece7a4ca9a70e Mon Sep 17 00:00:00 2001 +From: Ian Hua +Date: Thu, 6 Jul 2023 10:05:36 +0000 +Subject: [PATCH] Fix out of Bounds Read in convertSubgraphFromHAL in + ShimConverter.cpp in libneuralnetworks_shim_static + +Bug: 269270167 +Test: N/A +(cherry picked from commit 4bf7bb6b50b412678a681d29f7ced70a4d737762) +(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:6e1bbe89e08936a1cad152a59ae82a3773c51cca) +Merged-In: I33272284b965efcbb531f64cbf838a0d59c28e00 +Change-Id: I33272284b965efcbb531f64cbf838a0d59c28e00 +--- + shim_and_sl/ShimConverter.cpp | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/shim_and_sl/ShimConverter.cpp b/shim_and_sl/ShimConverter.cpp +index b0f914c3..b8619d0f 100644 +--- a/shim_and_sl/ShimConverter.cpp ++++ b/shim_and_sl/ShimConverter.cpp +@@ -150,6 +150,10 @@ ANeuralNetworksModel* convertSubgraphFromHAL( + break; + } + case OperandLifeTime::CONSTANT_POOL: { ++ if (operand.location.poolIndex >= memoryPools.size()) { ++ *errorStatus = ErrorStatus::INVALID_ARGUMENT; ++ return nullptr; ++ } + resultModel.setOperandValueFromMemory( + i, memoryPools[operand.location.poolIndex].get(), operand.location.offset, + operand.location.length); +-- +2.41.0.585.gd2178a4bd4-goog + diff --git a/aosp_diff/preliminary/packages/providers/MediaProvider/06_0006-Remove-invalid-surrogates-during-bindSelection-am-b5aadde5f.bulletin.patch b/aosp_diff/preliminary/packages/providers/MediaProvider/06_0006-Remove-invalid-surrogates-during-bindSelection-am-b5aadde5f.bulletin.patch new file mode 100644 index 0000000000..96ce0c7517 --- /dev/null +++ b/aosp_diff/preliminary/packages/providers/MediaProvider/06_0006-Remove-invalid-surrogates-during-bindSelection-am-b5aadde5f.bulletin.patch @@ -0,0 +1,108 @@ +From 2425470e478630804c88f84a969d4d61450f4dc5 Mon Sep 17 00:00:00 2001 +From: Krishang Garodia +Date: Thu, 6 Jul 2023 11:46:28 +0000 +Subject: [PATCH] Remove invalid surrogates during bindSelection am: b5aadde5f1 + am: a09d52e382 + +Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/providers/MediaProvider/+/23764857 + +Signed-off-by: Automerger Merge Worker +(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:31e3d9369d7778ca6258281a0f2d94e5d7c094b5) +Merged-In: I961a1e6e5bc909561c27be6cbb81aa1f3c46235d +Change-Id: I961a1e6e5bc909561c27be6cbb81aa1f3c46235d +--- + .../providers/media/util/DatabaseUtils.java | 36 +++++++++++++++++-- + .../media/util/DatabaseUtilsTest.java | 23 ++++++++++++ + 2 files changed, 57 insertions(+), 2 deletions(-) + +diff --git a/src/com/android/providers/media/util/DatabaseUtils.java b/src/com/android/providers/media/util/DatabaseUtils.java +index 66b77516..e6e7ffa0 100644 +--- a/src/com/android/providers/media/util/DatabaseUtils.java ++++ b/src/com/android/providers/media/util/DatabaseUtils.java +@@ -127,8 +127,9 @@ public class DatabaseUtils { + res.append(((Boolean) arg).booleanValue() ? 1 : 0); + } else { + res.append('\''); +- // Escape single quote character while appending the string. +- res.append(arg.toString().replace("'", "''")); ++ // Escape single quote character while appending the string and reject ++ // invalid unicode. ++ res.append(escapeSingleQuoteAndRejectInvalidUnicode(arg.toString())); + res.append('\''); + } + break; +@@ -142,6 +143,37 @@ public class DatabaseUtils { + return res.toString(); + } + ++ private static String escapeSingleQuoteAndRejectInvalidUnicode(@NonNull String target) { ++ final int len = target.length(); ++ final StringBuilder res = new StringBuilder(len); ++ boolean lastHigh = false; ++ ++ for (int i = 0; i < len; ) { ++ final char c = target.charAt(i++); ++ ++ if (lastHigh != Character.isLowSurrogate(c)) { ++ Log.e(TAG, "Invalid surrogate in string " + target); ++ throw new IllegalArgumentException("Invalid surrogate in string " + target); ++ } ++ ++ lastHigh = Character.isHighSurrogate(c); ++ ++ // Escape the single quotes by duplicating them ++ if (c == '\'') { ++ res.append(c); ++ } ++ ++ res.append(c); ++ } ++ ++ if (lastHigh) { ++ Log.e(TAG, "Invalid surrogate in string " + target); ++ throw new IllegalArgumentException("Invalid surrogate in string " + target); ++ } ++ ++ return res.toString(); ++ } ++ + /** + * Returns data type of the given object's value. + *

+diff --git a/tests/src/com/android/providers/media/util/DatabaseUtilsTest.java b/tests/src/com/android/providers/media/util/DatabaseUtilsTest.java +index 685d8970..c547f545 100644 +--- a/tests/src/com/android/providers/media/util/DatabaseUtilsTest.java ++++ b/tests/src/com/android/providers/media/util/DatabaseUtilsTest.java +@@ -126,6 +126,29 @@ public class DatabaseUtilsTest { + bindSelection("DATA=?", "Fo''o")); + } + ++ @Test ++ public void testBindSelection_RejectInvalidUnicode() { ++ try { ++ bindSelection("DATA=?", "Fo\uD83Do"); ++ fail(); ++ } catch (IllegalArgumentException ignored) { ++ } ++ ++ try { ++ bindSelection("DATA=?", "Fo\uDE00o"); ++ fail(); ++ } catch (IllegalArgumentException ignored) { ++ } ++ ++ assertEquals("DATA='Fo\uD83D\uDE00o'", bindSelection("DATA=?", "Fo\uD83D\uDE00o")); ++ ++ try { ++ bindSelection("DATA=?", "Fo\uDE00\uD83Do"); ++ fail(); ++ } catch (IllegalArgumentException ignored) { ++ } ++ } ++ + @Test + public void testResolveQueryArgs_GroupBy() throws Exception { + args.putStringArray(QUERY_ARG_GROUP_COLUMNS, new String[] { "foo", "bar" }); +-- +2.41.0.585.gd2178a4bd4-goog + diff --git a/aosp_diff/preliminary/packages/providers/MediaProvider/07_0007-Canonicalize-file-path-for-insertion-by-legacy-apps.bulletin.patch b/aosp_diff/preliminary/packages/providers/MediaProvider/07_0007-Canonicalize-file-path-for-insertion-by-legacy-apps.bulletin.patch new file mode 100644 index 0000000000..69eefc68b4 --- /dev/null +++ b/aosp_diff/preliminary/packages/providers/MediaProvider/07_0007-Canonicalize-file-path-for-insertion-by-legacy-apps.bulletin.patch @@ -0,0 +1,44 @@ +From 05cb9e8148a79b7babc268ea53cbbd26062d0b16 Mon Sep 17 00:00:00 2001 +From: Dipankar Bhardwaj +Date: Thu, 6 Jul 2023 10:01:20 +0000 +Subject: [PATCH] Canonicalize file path for insertion by legacy apps + +Apps with legacy external storage can try to create entries in MP for +file paths in other apps external private directories by using a +non-canonical path in insertion calls. + +Test: atest LegacyStorageHostTest#testInsertToOtherAppPrivateDirFails +Bug: 276898626 +(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:3c0f583f5dc3f4d395fa2423ab72dbd902c0c6c8) +Merged-In: If4c941c8156f19459b3ec6cbaf705824ecc2ba77 +Change-Id: If4c941c8156f19459b3ec6cbaf705824ecc2ba77 +--- + src/com/android/providers/media/util/FileUtils.java | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +diff --git a/src/com/android/providers/media/util/FileUtils.java b/src/com/android/providers/media/util/FileUtils.java +index 72880bd0..a0f666fb 100644 +--- a/src/com/android/providers/media/util/FileUtils.java ++++ b/src/com/android/providers/media/util/FileUtils.java +@@ -1297,9 +1297,17 @@ public class FileUtils { + values.remove(MediaColumns.BUCKET_ID); + values.remove(MediaColumns.BUCKET_DISPLAY_NAME); + +- final String data = values.getAsString(MediaColumns.DATA); ++ String data = values.getAsString(MediaColumns.DATA); + if (TextUtils.isEmpty(data)) return; + ++ try { ++ data = new File(data).getCanonicalPath(); ++ values.put(MediaColumns.DATA, data); ++ } catch (IOException e) { ++ throw new IllegalArgumentException( ++ String.format(Locale.ROOT, "Invalid file path:%s in request.", data)); ++ } ++ + final File file = new File(data); + final File fileLower = new File(data.toLowerCase(Locale.ROOT)); + +-- +2.41.0.585.gd2178a4bd4-goog + diff --git a/aosp_diff/preliminary/packages/services/Telephony/0003-Fixed-leak-of-cross-user-data-in-multiple-settings-.bulletin.patch b/aosp_diff/preliminary/packages/services/Telephony/0003-Fixed-leak-of-cross-user-data-in-multiple-settings-.bulletin.patch new file mode 100644 index 0000000000..885d2093e2 --- /dev/null +++ b/aosp_diff/preliminary/packages/services/Telephony/0003-Fixed-leak-of-cross-user-data-in-multiple-settings-.bulletin.patch @@ -0,0 +1,171 @@ +From 36f4b2c3c64fbb662f23029066a88b0f4243a9b3 Mon Sep 17 00:00:00 2001 +From: Ashish Kumar +Date: Fri, 26 May 2023 14:18:46 +0000 +Subject: [PATCH] Fixed leak of cross user data in multiple settings. + + - Any app is allowed to receive GET_CONTENT intent. Using this, an user puts back in the intent an uri with data of another user. + - Telephony service has INTERACT_ACROSS_USER permission. Using this, it reads and shows the deta to the evil user. + +Fix: When telephony service gets the intent result, it checks if the uri is from the current user or not. + +Bug: b/256591023 , b/256819787 + +Test: The malicious behaviour was not being reproduced. Unable to import contact from other users data. +Test2: Able to import contact from the primary user or uri with no user id +(These settings are not available for secondary users) +(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:ab593467e900d4a6d25a34024a06195ae863f6dc) +Merged-In: I1e3a643f17948153aecc1d0df9ffd9619ad678c1 +Change-Id: I1e3a643f17948153aecc1d0df9ffd9619ad678c1 +--- + src/com/android/phone/CdmaCallForwardOptions.java | 12 ++++++++++++ + .../android/phone/GsmUmtsCallForwardOptions.java | 12 ++++++++++++ + .../phone/settings/VoicemailSettingsActivity.java | 14 ++++++++++++++ + .../phone/settings/fdn/EditFdnContactScreen.java | 11 +++++++++++ + 4 files changed, 49 insertions(+) + +diff --git a/src/com/android/phone/CdmaCallForwardOptions.java b/src/com/android/phone/CdmaCallForwardOptions.java +index a8d2e93d6..d70e7099b 100644 +--- a/src/com/android/phone/CdmaCallForwardOptions.java ++++ b/src/com/android/phone/CdmaCallForwardOptions.java +@@ -17,10 +17,13 @@ + package com.android.phone; + + import android.app.ActionBar; ++import android.content.ContentProvider; + import android.content.Intent; + import android.database.Cursor; + import android.os.Bundle; + import android.os.PersistableBundle; ++import android.os.Process; ++import android.os.UserHandle; + import android.preference.Preference; + import android.preference.PreferenceScreen; + import android.telephony.CarrierConfigManager; +@@ -212,6 +215,15 @@ public class CdmaCallForwardOptions extends TimeConsumingPreferenceActivity { + } + Cursor cursor = null; + try { ++ // check if the URI returned by the user belongs to the user ++ final int currentUser = UserHandle.getUserId(Process.myUid()); ++ if (currentUser ++ != ContentProvider.getUserIdFromUri(data.getData(), currentUser)) { ++ ++ Log.w(LOG_TAG, "onActivityResult: Contact data of different user, " ++ + "cannot access"); ++ return; ++ } + cursor = getContentResolver().query(data.getData(), + NUM_PROJECTION, null, null, null); + if ((cursor == null) || (!cursor.moveToFirst())) { +diff --git a/src/com/android/phone/GsmUmtsCallForwardOptions.java b/src/com/android/phone/GsmUmtsCallForwardOptions.java +index fda0ea526..db830deb6 100644 +--- a/src/com/android/phone/GsmUmtsCallForwardOptions.java ++++ b/src/com/android/phone/GsmUmtsCallForwardOptions.java +@@ -1,10 +1,13 @@ + package com.android.phone; + + import android.app.ActionBar; ++import android.content.ContentProvider; + import android.content.Intent; + import android.database.Cursor; + import android.os.Bundle; + import android.os.PersistableBundle; ++import android.os.Process; ++import android.os.UserHandle; + import android.preference.Preference; + import android.preference.PreferenceScreen; + import android.telephony.CarrierConfigManager; +@@ -203,6 +206,15 @@ public class GsmUmtsCallForwardOptions extends TimeConsumingPreferenceActivity { + } + Cursor cursor = null; + try { ++ // check if the URI returned by the user belongs to the user ++ final int currentUser = UserHandle.getUserId(Process.myUid()); ++ if (currentUser ++ != ContentProvider.getUserIdFromUri(data.getData(), currentUser)) { ++ ++ Log.w(LOG_TAG, "onActivityResult: Contact data of different user, " ++ + "cannot access"); ++ return; ++ } + cursor = getContentResolver().query(data.getData(), + NUM_PROJECTION, null, null, null); + if ((cursor == null) || (!cursor.moveToFirst())) { +diff --git a/src/com/android/phone/settings/VoicemailSettingsActivity.java b/src/com/android/phone/settings/VoicemailSettingsActivity.java +index 02bf4b25d..c940748a3 100644 +--- a/src/com/android/phone/settings/VoicemailSettingsActivity.java ++++ b/src/com/android/phone/settings/VoicemailSettingsActivity.java +@@ -17,6 +17,7 @@ + package com.android.phone.settings; + + import android.app.Dialog; ++import android.content.ContentProvider; + import android.content.DialogInterface; + import android.content.Intent; + import android.database.Cursor; +@@ -25,6 +26,8 @@ import android.os.Bundle; + import android.os.Handler; + import android.os.Message; + import android.os.PersistableBundle; ++import android.os.Process; ++import android.os.UserHandle; + import android.os.UserManager; + import android.preference.Preference; + import android.preference.PreferenceActivity; +@@ -520,6 +523,17 @@ public class VoicemailSettingsActivity extends PreferenceActivity + + Cursor cursor = null; + try { ++ // check if the URI returned by the user belongs to the user ++ final int currentUser = UserHandle.getUserId(Process.myUid()); ++ if (currentUser ++ != ContentProvider.getUserIdFromUri(data.getData(), currentUser)) { ++ ++ if (DBG) { ++ log("onActivityResult: Contact data of different user, " ++ + "cannot access"); ++ } ++ return; ++ } + cursor = getContentResolver().query(data.getData(), + new String[] { CommonDataKinds.Phone.NUMBER }, null, null, null); + if ((cursor == null) || (!cursor.moveToFirst())) { +diff --git a/src/com/android/phone/settings/fdn/EditFdnContactScreen.java b/src/com/android/phone/settings/fdn/EditFdnContactScreen.java +index 468d38f65..0884e1262 100644 +--- a/src/com/android/phone/settings/fdn/EditFdnContactScreen.java ++++ b/src/com/android/phone/settings/fdn/EditFdnContactScreen.java +@@ -19,6 +19,7 @@ package com.android.phone.settings.fdn; + + import static android.app.Activity.RESULT_OK; + ++import android.content.ContentProvider; + import android.content.ContentValues; + import android.content.Intent; + import android.content.res.Resources; +@@ -26,6 +27,8 @@ import android.database.Cursor; + import android.net.Uri; + import android.os.Bundle; + import android.os.PersistableBundle; ++import android.os.Process; ++import android.os.UserHandle; + import android.provider.ContactsContract.CommonDataKinds; + import android.telephony.CarrierConfigManager; + import android.telephony.PhoneNumberUtils; +@@ -137,6 +140,14 @@ public class EditFdnContactScreen extends BaseFdnContactScreen { + } + Cursor cursor = null; + try { ++ // check if the URI returned by the user belongs to the user ++ final int currentUser = UserHandle.getUserId(Process.myUid()); ++ if (currentUser ++ != ContentProvider.getUserIdFromUri(intent.getData(), currentUser)) { ++ Log.w(LOG_TAG, "onActivityResult: Contact data of different user, " ++ + "cannot access"); ++ return; ++ } + cursor = getContentResolver().query(intent.getData(), + NUM_PROJECTION, null, null, null); + if ((cursor == null) || (!cursor.moveToFirst())) { +-- +2.41.0.585.gd2178a4bd4-goog + diff --git a/aosp_diff/preliminary/system/bt/35_0035-Fix-an-integer-overflow-bug-in-avdt_msg_asmbl.bulletin.patch b/aosp_diff/preliminary/system/bt/35_0035-Fix-an-integer-overflow-bug-in-avdt_msg_asmbl.bulletin.patch new file mode 100644 index 0000000000..7ef421699b --- /dev/null +++ b/aosp_diff/preliminary/system/bt/35_0035-Fix-an-integer-overflow-bug-in-avdt_msg_asmbl.bulletin.patch @@ -0,0 +1,44 @@ +From 0cccf6b9f547f1ec27a6045b380e48fa59d9dbd4 Mon Sep 17 00:00:00 2001 +From: Hui Peng +Date: Tue, 16 May 2023 21:24:07 +0000 +Subject: [PATCH] Fix an integer overflow bug in avdt_msg_asmbl + +This is a backport of +Iaa4d603921fc4ffb8cfb5783f99ec0963affd6a2 +to rvc-dev + +Bug: 280633699 +Test: manual +Ignore-AOSP-First: security +Tag: #security +(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:26347d4bdba646bbba4d27337d2888a04de42639) +Merged-In: Iaa4d603921fc4ffb8cfb5783f99ec0963affd6a2 +Change-Id: Iaa4d603921fc4ffb8cfb5783f99ec0963affd6a2 +--- + stack/avdt/avdt_msg.cc | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/stack/avdt/avdt_msg.cc b/stack/avdt/avdt_msg.cc +index d0c54343c..5e08d0287 100644 +--- a/stack/avdt/avdt_msg.cc ++++ b/stack/avdt/avdt_msg.cc +@@ -1289,14 +1289,14 @@ BT_HDR* avdt_msg_asmbl(AvdtpCcb* p_ccb, BT_HDR* p_buf) { + * NOTE: The buffer is allocated above at the beginning of the + * reassembly, and is always of size BT_DEFAULT_BUFFER_SIZE. + */ +- uint16_t buf_len = BT_DEFAULT_BUFFER_SIZE - sizeof(BT_HDR); ++ size_t buf_len = BT_DEFAULT_BUFFER_SIZE - sizeof(BT_HDR); + + /* adjust offset and len of fragment for header byte */ + p_buf->offset += AVDT_LEN_TYPE_CONT; + p_buf->len -= AVDT_LEN_TYPE_CONT; + + /* verify length */ +- if ((p_ccb->p_rx_msg->offset + p_buf->len) > buf_len) { ++ if (((size_t) p_ccb->p_rx_msg->offset + (size_t) p_buf->len) > buf_len) { + /* won't fit; free everything */ + AVDT_TRACE_WARNING("%s: Fragmented message too big!", __func__); + osi_free_and_reset((void**)&p_ccb->p_rx_msg); +-- +2.41.0.585.gd2178a4bd4-goog + diff --git a/aosp_diff/preliminary/system/bt/36_0036-Fix-integer-overflow-in-build_read_multi_rsp.bulletin.patch b/aosp_diff/preliminary/system/bt/36_0036-Fix-integer-overflow-in-build_read_multi_rsp.bulletin.patch new file mode 100644 index 0000000000..234d3cb424 --- /dev/null +++ b/aosp_diff/preliminary/system/bt/36_0036-Fix-integer-overflow-in-build_read_multi_rsp.bulletin.patch @@ -0,0 +1,69 @@ +From ddd52e178c76979475ed429fcaedecdf4b5614d8 Mon Sep 17 00:00:00 2001 +From: Brian Delwiche +Date: Tue, 16 May 2023 22:49:36 +0000 +Subject: [PATCH] Fix integer overflow in build_read_multi_rsp + +Local variables tracking structure size in build_read_multi_rsp are of +uint16 type but accept a full uint16 range from function arguments while +appending a fixed-length offset. This can lead to an integer overflow +and unexpected behavior. + +Change the locals to size_t, and add a check during reasssignment. + +Bug: 273966636 +Test: atest bluetooth_test_gd_unit, net_test_stack_btm +Tag: #security +Ignore-AOSP-First: Security +(cherry picked from commit 70a4d628fa016a9487fae07f211644b95e1f0000) +(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:c57ccd2b96e5ffcb9a57bc0e78038b5de28857d7) +Merged-In: I3a74bdb0d003cb6bf4f282615be8c68836676715 +Change-Id: I3a74bdb0d003cb6bf4f282615be8c68836676715 +--- + stack/gatt/gatt_sr.cc | 17 ++++++++++++----- + 1 file changed, 12 insertions(+), 5 deletions(-) + +diff --git a/stack/gatt/gatt_sr.cc b/stack/gatt/gatt_sr.cc +index 39c7125b7..2c736f332 100644 +--- a/stack/gatt/gatt_sr.cc ++++ b/stack/gatt/gatt_sr.cc +@@ -136,7 +136,8 @@ void gatt_dequeue_sr_cmd(tGATT_TCB& tcb, uint16_t cid) { + } + + static void build_read_multi_rsp(tGATT_SR_CMD* p_cmd, uint16_t mtu) { +- uint16_t ii, total_len, len; ++ uint16_t ii; ++ size_t total_len, len; + uint8_t* p; + bool is_overflow = false; + +@@ -181,7 +182,7 @@ static void build_read_multi_rsp(tGATT_SR_CMD* p_cmd, uint16_t mtu) { + len = p_rsp->attr_value.len - (total_len - mtu); + is_overflow = true; + VLOG(1) << StringPrintf( +- "multi read overflow available len=%d val_len=%d", len, ++ "multi read overflow available len=%zu val_len=%d", len, + p_rsp->attr_value.len); + } else { + len = p_rsp->attr_value.len; +@@ -193,9 +194,15 @@ static void build_read_multi_rsp(tGATT_SR_CMD* p_cmd, uint16_t mtu) { + } + + if (p_rsp->attr_value.handle == p_cmd->multi_req.handles[ii]) { +- memcpy(p, p_rsp->attr_value.value, len); +- if (!is_overflow) p += len; +- p_buf->len += len; ++ // check for possible integer overflow ++ if (p_buf->len + len <= UINT16_MAX) { ++ memcpy(p, p_rsp->attr_value.value, len); ++ if (!is_overflow) p += len; ++ p_buf->len += len; ++ } else { ++ p_cmd->status = GATT_NOT_FOUND; ++ break; ++ } + } else { + p_cmd->status = GATT_NOT_FOUND; + break; +-- +2.41.0.585.gd2178a4bd4-goog + diff --git a/aosp_diff/preliminary/system/bt/37_0037-Fix-potential-abort-in-btu_av_act-cc.bulletin.patch b/aosp_diff/preliminary/system/bt/37_0037-Fix-potential-abort-in-btu_av_act-cc.bulletin.patch new file mode 100644 index 0000000000..5445f50df9 --- /dev/null +++ b/aosp_diff/preliminary/system/bt/37_0037-Fix-potential-abort-in-btu_av_act-cc.bulletin.patch @@ -0,0 +1,43 @@ +From a76c58ffc943a10d2e2d6a5c079f7805a5020915 Mon Sep 17 00:00:00 2001 +From: Brian Delwiche +Date: Thu, 27 Apr 2023 20:43:58 +0000 +Subject: [PATCH] Fix potential abort in btu_av_act.cc + +Partner analysis shows that bta_av_rc_msg does not respect handling +established for a null browse packet, instead dispatching the null +pointer to bta_av_rc_free_browse_msg. Strictly speaking this does +not cause a UAF, as osi_free_and_reset will find the null and abort, +but it will lead to improper program termination. + +Handle the case instead. + +Bug: 269253349 +Test: atest bluetooth_test_gd_unit +Tag: #security +Ignore-AOSP-First: Security +(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:91f6d6215c101acc99a7397c5fb5a12fe6d7b8e9) +Merged-In: I4df7045798b663fbefd7434288dc9383216171a7 +Change-Id: I4df7045798b663fbefd7434288dc9383216171a7 +--- + bta/av/bta_av_act.cc | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/bta/av/bta_av_act.cc b/bta/av/bta_av_act.cc +index 771faa6c6..6f7a0e7c6 100644 +--- a/bta/av/bta_av_act.cc ++++ b/bta/av/bta_av_act.cc +@@ -1002,7 +1002,10 @@ void bta_av_rc_msg(tBTA_AV_CB* p_cb, tBTA_AV_DATA* p_data) { + av.remote_cmd.rc_handle = p_data->rc_msg.handle; + (*p_cb->p_cback)(evt, &av); + /* If browsing message, then free the browse message buffer */ +- bta_av_rc_free_browse_msg(p_cb, p_data); ++ if (p_data->rc_msg.opcode == AVRC_OP_BROWSE && ++ p_data->rc_msg.msg.browse.p_browse_pkt != NULL) { ++ bta_av_rc_free_browse_msg(p_cb, p_data); ++ } + } + } + +-- +2.41.0.585.gd2178a4bd4-goog + diff --git a/aosp_diff/preliminary/system/bt/38_0038-Fix-UAF-in-gatt_cl-cc.bulletin.patch b/aosp_diff/preliminary/system/bt/38_0038-Fix-UAF-in-gatt_cl-cc.bulletin.patch new file mode 100644 index 0000000000..cca6a660ad --- /dev/null +++ b/aosp_diff/preliminary/system/bt/38_0038-Fix-UAF-in-gatt_cl-cc.bulletin.patch @@ -0,0 +1,47 @@ +From 44a4efdf055115721c5c3e6e6e6ec598e3087c1f Mon Sep 17 00:00:00 2001 +From: Brian Delwiche +Date: Thu, 1 Jun 2023 23:57:58 +0000 +Subject: [PATCH] Fix UAF in gatt_cl.cc + +gatt_cl.cc accesses a header field after the buffer holding it may have +been freed. + +Track the relevant state as a local variable instead. + +Bug: 274617156 +Test: atest: bluetooth, validated against fuzzer +Tag: #security +Ignore-AOSP-First: Security +(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:d7a7f7f3311202065de4b2c17b49994053dd1244) +Merged-In: I085ecfa1a9ba098ecbfecbd3cb3e263ae13f9724 +Change-Id: I085ecfa1a9ba098ecbfecbd3cb3e263ae13f9724 +--- + stack/gatt/gatt_cl.cc | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/stack/gatt/gatt_cl.cc b/stack/gatt/gatt_cl.cc +index 174c9ce7d..998b21b6e 100644 +--- a/stack/gatt/gatt_cl.cc ++++ b/stack/gatt/gatt_cl.cc +@@ -599,12 +599,17 @@ void gatt_process_prep_write_rsp(tGATT_TCB& tcb, tGATT_CLCB* p_clcb, + + memcpy(value.value, p, value.len); + ++ bool subtype_is_write_prepare = (p_clcb->op_subtype == GATT_WRITE_PREPARE); ++ + if (!gatt_check_write_long_terminate(tcb, p_clcb, &value)) { + gatt_send_prepare_write(tcb, p_clcb); + return; + } + +- if (p_clcb->op_subtype == GATT_WRITE_PREPARE) { ++ // We now know that we have not terminated, or else we would have returned ++ // early. We free the buffer only if the subtype is not equal to ++ // GATT_WRITE_PREPARE, so checking here is adequate to prevent UAF. ++ if (subtype_is_write_prepare) { + /* application should verify handle offset + and value are matched or not */ + gatt_end_operation(p_clcb, p_clcb->status, &value); +-- +2.41.0.585.gd2178a4bd4-goog +