diff options
17 files changed, 352 insertions, 21 deletions
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp index 13bf197aa9dc..421301adbff0 100644 --- a/cmds/incidentd/src/IncidentService.cpp +++ b/cmds/incidentd/src/IncidentService.cpp @@ -500,9 +500,13 @@ status_t IncidentService::onTransact(uint32_t code, const Parcel& data, Parcel* switch (code) { case SHELL_COMMAND_TRANSACTION: { - int in = data.readFileDescriptor(); - int out = data.readFileDescriptor(); - int err = data.readFileDescriptor(); + unique_fd in, out, err; + if (status_t status = data.readUniqueFileDescriptor(&in); status != OK) return status; + + if (status_t status = data.readUniqueFileDescriptor(&out); status != OK) return status; + + if (status_t status = data.readUniqueFileDescriptor(&err); status != OK) return status; + int argc = data.readInt32(); Vector<String8> args; for (int i = 0; i < argc && data.dataAvail() > 0; i++) { @@ -512,15 +516,15 @@ status_t IncidentService::onTransact(uint32_t code, const Parcel& data, Parcel* sp<IResultReceiver> resultReceiver = IResultReceiver::asInterface(data.readStrongBinder()); - FILE* fin = fdopen(in, "r"); - FILE* fout = fdopen(out, "w"); - FILE* ferr = fdopen(err, "w"); + FILE* fin = fdopen(in.release(), "r"); + FILE* fout = fdopen(out.release(), "w"); + FILE* ferr = fdopen(err.release(), "w"); if (fin == NULL || fout == NULL || ferr == NULL) { resultReceiver->send(NO_MEMORY); } else { - err = command(fin, fout, ferr, args); - resultReceiver->send(err); + status_t result = command(fin, fout, ferr, args); + resultReceiver->send(result); } if (fin != NULL) { diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index cfda7e284c6b..5c8403daf0ee 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -245,6 +245,7 @@ interface IActivityTaskManager { * {@link android.view.WindowManagerPolicyConstants#KEYGUARD_GOING_AWAY_FLAG_TO_SHADE} * etc. */ + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.CONTROL_KEYGUARD)") void keyguardGoingAway(int flags); void suppressResizeConfigChanges(boolean suppress); diff --git a/core/java/android/app/IUriGrantsManager.aidl b/core/java/android/app/IUriGrantsManager.aidl index 9e7f2fecfea0..b630d034dca9 100644 --- a/core/java/android/app/IUriGrantsManager.aidl +++ b/core/java/android/app/IUriGrantsManager.aidl @@ -39,4 +39,7 @@ interface IUriGrantsManager { void clearGrantedUriPermissions(in String packageName, int userId); ParceledListSlice getUriPermissions(in String packageName, boolean incoming, boolean persistedOnly); + + int checkGrantUriPermission_ignoreNonSystem( + int sourceUid, String targetPkg, in Uri uri, int modeFlags, int userId); } diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl index 88447daf7338..ff3c015cf66f 100644 --- a/core/java/com/android/internal/app/IAppOpsService.aidl +++ b/core/java/com/android/internal/app/IAppOpsService.aidl @@ -52,6 +52,8 @@ interface IAppOpsService { int checkAudioOperation(int code, int usage, int uid, String packageName); boolean shouldCollectNotes(int opCode); void setCameraAudioRestriction(int mode); + void startWatchingModeWithFlags(int op, String packageName, int flags, + IAppOpsCallback callback); // End of methods also called by native code. // Any new method exposed to native must be added after the last one, do not reorder @@ -110,8 +112,6 @@ interface IAppOpsService { void startWatchingStarted(in int[] ops, IAppOpsStartedCallback callback); void stopWatchingStarted(IAppOpsStartedCallback callback); - void startWatchingModeWithFlags(int op, String packageName, int flags, IAppOpsCallback callback); - void startWatchingNoted(in int[] ops, IAppOpsNotedCallback callback); void stopWatchingNoted(IAppOpsNotedCallback callback); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 46e1cfa3ad4b..b1b4f43911e5 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -1325,6 +1325,10 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, mExternallyEnabled = enabled; if (!enabled && mShowing) { + if (mLockPatternUtils.isUserInLockdown(KeyguardUpdateMonitor.getCurrentUser())) { + Log.d(TAG, "keyguardEnabled(false) overridden by user lockdown"); + return; + } if (mExitSecureCallback != null) { if (DEBUG) Log.d(TAG, "in process of verifyUnlock request, ignoring"); // we're in the process of handling a request to verify the user @@ -1541,9 +1545,9 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, return; } - // if another app is disabling us, don't show + // if another app is disabling us, don't show unless we're in lockdown mode if (!mExternallyEnabled - && !mLockPatternUtils.isUserInLockdown(KeyguardUpdateMonitor.getCurrentUser())) { + && !mLockPatternUtils.isUserInLockdown(KeyguardUpdateMonitor.getCurrentUser())) { if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled"); mNeedToReshowWhenReenabled = true; diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt index 4bad1c5ebd9f..ca9d37e0603e 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt @@ -18,11 +18,13 @@ package com.android.systemui.media import android.app.Notification import android.app.PendingIntent +import android.app.UriGrantsManager import android.app.smartspace.SmartspaceConfig import android.app.smartspace.SmartspaceManager import android.app.smartspace.SmartspaceSession import android.app.smartspace.SmartspaceTarget import android.content.BroadcastReceiver +import android.content.ContentProvider import android.content.ContentResolver import android.content.Context import android.content.Intent @@ -40,6 +42,7 @@ import android.media.session.MediaController import android.media.session.MediaSession import android.net.Uri import android.os.Parcelable +import android.os.Process import android.os.UserHandle import android.provider.Settings import android.service.notification.StatusBarNotification @@ -509,7 +512,13 @@ class MediaDataManager( // Album art var artworkBitmap = desc.iconBitmap if (artworkBitmap == null && desc.iconUri != null) { - artworkBitmap = loadBitmapFromUri(desc.iconUri!!) + val appUid = try { + context.packageManager.getApplicationInfo(packageName, 0)?.uid!! + } catch (e: PackageManager.NameNotFoundException) { + Log.w(TAG, "Could not get app UID for $packageName", e) + Process.INVALID_UID + } + artworkBitmap = loadBitmapFromUriForUser(desc.iconUri!!, userId, appUid, packageName) } val artworkIcon = if (artworkBitmap != null) { Icon.createWithBitmap(artworkBitmap) @@ -689,6 +698,30 @@ class MediaDataManager( false } } + + /** Returns a bitmap if the user can access the given URI, else null */ + private fun loadBitmapFromUriForUser( + uri: Uri, + userId: Int, + appUid: Int, + packageName: String, + ): Bitmap? { + try { + val ugm = UriGrantsManager.getService() + ugm.checkGrantUriPermission_ignoreNonSystem( + appUid, + packageName, + ContentProvider.getUriWithoutUserId(uri), + Intent.FLAG_GRANT_READ_URI_PERMISSION, + ContentProvider.getUserIdFromUri(uri, userId) + ) + return loadBitmapFromUri(uri) + } catch (e: SecurityException) { + Log.e(TAG, "Failed to get URI permission: $e") + } + return null + } + /** * Load a bitmap from a URI * @param uri the uri to load diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java index 1eb007e345ec..16beeb3ae5bb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java @@ -21,6 +21,7 @@ import android.content.Context; import android.content.pm.LauncherApps; import android.content.pm.ShortcutManager; import android.os.Handler; +import android.os.UserManager; import android.view.accessibility.AccessibilityManager; import com.android.internal.logging.UiEventLogger; @@ -144,6 +145,7 @@ public interface NotificationsModule { HighPriorityProvider highPriorityProvider, INotificationManager notificationManager, NotificationEntryManager notificationEntryManager, + UserManager userManager, PeopleSpaceWidgetManager peopleSpaceWidgetManager, LauncherApps launcherApps, ShortcutManager shortcutManager, @@ -164,6 +166,7 @@ public interface NotificationsModule { highPriorityProvider, notificationManager, notificationEntryManager, + userManager, peopleSpaceWidgetManager, launcherApps, shortcutManager, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java index 134f24e7e646..d817244223c2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java @@ -48,6 +48,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.RemoteException; import android.os.UserHandle; +import android.os.UserManager; import android.service.notification.StatusBarNotification; import android.text.TextUtils; import android.transition.ChangeBounds; @@ -118,6 +119,8 @@ public class NotificationConversationInfo extends LinearLayout implements private NotificationGuts mGutsContainer; private OnConversationSettingsClickListener mOnConversationSettingsClickListener; + private UserManager mUm; + @VisibleForTesting boolean mSkipPost = false; private int mActualHeight; @@ -155,7 +158,9 @@ public class NotificationConversationInfo extends LinearLayout implements // People Tile add request. if (mSelectedAction == ACTION_FAVORITE && getPriority() != mSelectedAction) { mShadeController.animateCollapsePanels(); - mPeopleSpaceWidgetManager.requestPinAppWidget(mShortcutInfo, new Bundle()); + if (mUm.isSameProfileGroup(UserHandle.USER_SYSTEM, mSbn.getNormalizedUserId())) { + mPeopleSpaceWidgetManager.requestPinAppWidget(mShortcutInfo, new Bundle()); + } } mGutsContainer.closeControls(v, true); }; @@ -189,6 +194,7 @@ public class NotificationConversationInfo extends LinearLayout implements @Action int selectedAction, ShortcutManager shortcutManager, PackageManager pm, + UserManager um, PeopleSpaceWidgetManager peopleSpaceWidgetManager, INotificationManager iNotificationManager, OnUserInteractionCallback onUserInteractionCallback, @@ -214,6 +220,7 @@ public class NotificationConversationInfo extends LinearLayout implements mEntry = entry; mSbn = entry.getSbn(); mPm = pm; + mUm = um; mAppName = mPackageName; mOnSettingsClickListener = onSettingsClick; mNotificationChannel = notificationChannel; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java index 8e02d9f635d3..d6a26f29edb9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java @@ -31,6 +31,7 @@ import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.UserHandle; +import android.os.UserManager; import android.provider.Settings; import android.service.notification.StatusBarNotification; import android.util.ArraySet; @@ -127,6 +128,9 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx private Runnable mOpenRunnable; private final INotificationManager mNotificationManager; private final PeopleSpaceWidgetManager mPeopleSpaceWidgetManager; + + private final UserManager mUserManager; + private final LauncherApps mLauncherApps; private final ShortcutManager mShortcutManager; private final UserContextProvider mContextTracker; @@ -146,6 +150,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx HighPriorityProvider highPriorityProvider, INotificationManager notificationManager, NotificationEntryManager notificationEntryManager, + UserManager userManager, PeopleSpaceWidgetManager peopleSpaceWidgetManager, LauncherApps launcherApps, ShortcutManager shortcutManager, @@ -164,6 +169,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx mAccessibilityManager = accessibilityManager; mHighPriorityProvider = highPriorityProvider; mNotificationManager = notificationManager; + mUserManager = userManager; mPeopleSpaceWidgetManager = peopleSpaceWidgetManager; mLauncherApps = launcherApps; mShortcutManager = shortcutManager; @@ -491,6 +497,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx notificationInfoView.getSelectedAction(), mShortcutManager, pmUser, + mUserManager, mPeopleSpaceWidgetManager, mNotificationManager, mOnUserInteractionCallback, diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt index 5762a87eed50..d1221fe83ed1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt @@ -1,22 +1,27 @@ package com.android.systemui.media +import android.app.IUriGrantsManager import android.app.Notification import android.app.Notification.MediaStyle import android.app.PendingIntent +import android.app.UriGrantsManager import android.app.smartspace.SmartspaceAction import android.app.smartspace.SmartspaceTarget import android.content.Intent import android.graphics.Bitmap +import android.graphics.ImageDecoder import android.media.MediaDescription import android.media.MediaMetadata import android.media.session.MediaController import android.media.session.MediaSession +import android.net.Uri import android.os.Bundle import android.provider.Settings import android.service.notification.StatusBarNotification import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import androidx.test.filters.SmallTest +import com.android.dx.mockito.inline.extended.ExtendedMockito import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.BroadcastDispatcher @@ -25,6 +30,7 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.statusbar.SbnBuilder import com.android.systemui.tuner.TunerService import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.capture import com.android.systemui.util.mockito.eq import com.android.systemui.util.time.FakeSystemClock @@ -36,6 +42,7 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.anyBoolean +import org.mockito.ArgumentMatchers.anyInt import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito @@ -43,8 +50,10 @@ import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.reset import org.mockito.Mockito.verify +import org.mockito.MockitoSession import org.mockito.junit.MockitoJUnit import org.mockito.Mockito.`when` as whenever +import org.mockito.quality.Strictness private const val KEY = "KEY" private const val KEY_2 = "KEY_2" @@ -96,12 +105,23 @@ class MediaDataManagerTest : SysuiTestCase() { private val clock = FakeSystemClock() @Mock private lateinit var tunerService: TunerService @Captor lateinit var tunableCaptor: ArgumentCaptor<TunerService.Tunable> + @Mock private lateinit var ugm: IUriGrantsManager + @Mock private lateinit var imageSource: ImageDecoder.Source private val originalSmartspaceSetting = Settings.Secure.getInt(context.contentResolver, Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, 1) + private lateinit var staticMockSession: MockitoSession + @Before fun setup() { + staticMockSession = + ExtendedMockito.mockitoSession() + .mockStatic<UriGrantsManager>(UriGrantsManager::class.java) + .mockStatic<ImageDecoder>(ImageDecoder::class.java) + .strictness(Strictness.LENIENT) + .startMocking() + whenever(UriGrantsManager.getService()).thenReturn(ugm) foregroundExecutor = FakeExecutor(clock) backgroundExecutor = FakeExecutor(clock) smartspaceMediaDataProvider = SmartspaceMediaDataProvider() @@ -168,6 +188,7 @@ class MediaDataManagerTest : SysuiTestCase() { @After fun tearDown() { + staticMockSession.finishMocking() session.release() mediaDataManager.destroy() Settings.Secure.putInt(context.contentResolver, @@ -176,12 +197,9 @@ class MediaDataManagerTest : SysuiTestCase() { @Test fun testSetTimedOut_active_deactivatesMedia() { - val data = MediaData(userId = USER_ID, initialized = true, backgroundColor = 0, app = null, - appIcon = null, artist = null, song = null, artwork = null, actions = emptyList(), - actionsToShowInCompact = emptyList(), packageName = "INVALID", token = null, - clickIntent = null, device = null, active = true, resumeAction = null) - mediaDataManager.onNotificationAdded(KEY, mediaNotification) - mediaDataManager.onMediaDataLoaded(KEY, oldKey = null, data = data) + addNotificationAndLoad() + val data = mediaDataCaptor.value + assertThat(data.active).isTrue() mediaDataManager.setTimedOut(KEY, timedOut = true) assertThat(data.active).isFalse() @@ -678,4 +696,103 @@ class MediaDataManagerTest : SysuiTestCase() { assertThat(mediaDataCaptor.value.actionsToShowInCompact.size).isEqualTo( MediaDataManager.MAX_COMPACT_ACTIONS) } + + @Test + fun testResumeMediaLoaded_hasArtPermission_artLoaded() { + // When resume media is loaded and user/app has permission to access the art URI, + whenever( + ugm.checkGrantUriPermission_ignoreNonSystem( + anyInt(), + any(), + any(), + anyInt(), + anyInt() + ) + ) + .thenReturn(1) + val artwork = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888) + val uri = Uri.parse("content://example") + whenever(ImageDecoder.createSource(any(), eq(uri))).thenReturn(imageSource) + whenever(ImageDecoder.decodeBitmap(any(), any())).thenReturn(artwork) + + val desc = + MediaDescription.Builder().run { + setTitle(SESSION_TITLE) + setIconUri(uri) + build() + } + addResumeControlAndLoad(desc) + + // Then the artwork is loaded + assertThat(mediaDataCaptor.value.artwork).isNotNull() + } + + @Test + fun testResumeMediaLoaded_noArtPermission_noArtLoaded() { + // When resume media is loaded and user/app does not have permission to access the art URI + whenever( + ugm.checkGrantUriPermission_ignoreNonSystem( + anyInt(), + any(), + any(), + anyInt(), + anyInt() + ) + ) + .thenThrow(SecurityException("Test no permission")) + val artwork = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888) + val uri = Uri.parse("content://example") + whenever(ImageDecoder.createSource(any(), eq(uri))).thenReturn(imageSource) + whenever(ImageDecoder.decodeBitmap(any(), any())).thenReturn(artwork) + + val desc = + MediaDescription.Builder().run { + setTitle(SESSION_TITLE) + setIconUri(uri) + build() + } + addResumeControlAndLoad(desc) + + // Then the artwork is not loaded + assertThat(mediaDataCaptor.value.artwork).isNull() + } + + /** + * Helper function to add a media notification and capture the resulting MediaData + */ + private fun addNotificationAndLoad() { + mediaDataManager.onNotificationAdded(KEY, mediaNotification) + assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) + assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true), + eq(0), eq(false)) + } + + /** Helper function to add a resumption control and capture the resulting MediaData */ + private fun addResumeControlAndLoad( + desc: MediaDescription, + packageName: String = PACKAGE_NAME + ) { + mediaDataManager.addResumptionControls( + USER_ID, + desc, + Runnable {}, + session.sessionToken, + APP_NAME, + pendingIntent, + packageName + ) + assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) + assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) + + verify(listener) + .onMediaDataLoaded( + eq(packageName), + eq(null), + capture(mediaDataCaptor), + eq(true), + eq(0), + eq(false) + ) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java index dc6d744637b5..f76a40f99310 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java @@ -62,6 +62,7 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.os.Handler; import android.os.UserHandle; +import android.os.UserManager; import android.service.notification.StatusBarNotification; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; @@ -132,6 +133,8 @@ public class NotificationConversationInfoTest extends SysuiTestCase { @Mock private PackageManager mMockPackageManager; @Mock + private UserManager mUserManager; + @Mock private OnUserInteractionCallback mOnUserInteractionCallback; @Mock private BubblesManager mBubblesManager; @@ -239,6 +242,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mUserManager, mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, @@ -264,6 +268,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mUserManager, mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, @@ -317,6 +322,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mUserManager, mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, @@ -343,6 +349,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mUserManager, mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, @@ -368,6 +375,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mUserManager, mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, @@ -404,6 +412,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mUserManager, mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, @@ -430,6 +439,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mUserManager, mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, @@ -460,6 +470,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mUserManager, mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, @@ -485,6 +496,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mUserManager, mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, @@ -514,6 +526,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mUserManager, mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, @@ -543,6 +556,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mUserManager, mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, @@ -575,6 +589,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mUserManager, mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, @@ -613,6 +628,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mUserManager, mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, @@ -642,6 +658,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mUserManager, mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, @@ -678,6 +695,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mUserManager, mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, @@ -707,6 +725,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mUserManager, mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, @@ -752,6 +771,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mUserManager, mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, @@ -796,6 +816,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mUserManager, mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, @@ -841,6 +862,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mUserManager, mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, @@ -879,6 +901,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mUserManager, mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, @@ -916,6 +939,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mUserManager, mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, @@ -957,6 +981,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, // no action selected by default mShortcutManager, mMockPackageManager, + mUserManager, mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, @@ -988,6 +1013,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { NotificationConversationInfo.ACTION_FAVORITE, // "Favorite" selected by default mShortcutManager, mMockPackageManager, + mUserManager, mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, @@ -1018,6 +1044,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mUserManager, mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, @@ -1055,6 +1082,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mUserManager, mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, @@ -1092,6 +1120,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mUserManager, mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, @@ -1128,6 +1157,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mUserManager, mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, @@ -1163,6 +1193,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mUserManager, mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, @@ -1189,6 +1220,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mUserManager, mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, @@ -1210,12 +1242,14 @@ public class NotificationConversationInfoTest extends SysuiTestCase { @Test public void testSelectPriorityRequestsPinPeopleTile() { + when(mUserManager.isSameProfileGroup(anyInt(), anyInt())).thenReturn(true); //WHEN channel is default importance mNotificationChannel.setImportantConversation(false); mNotificationInfo.bindNotification( -1, mShortcutManager, mMockPackageManager, + mUserManager, mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, @@ -1242,11 +1276,47 @@ public class NotificationConversationInfoTest extends SysuiTestCase { } @Test + public void testSelectPriorityRequestsPinPeopleTile_noMultiuser() { + when(mUserManager.isSameProfileGroup(anyInt(), anyInt())).thenReturn(false); + //WHEN channel is default importance + mNotificationChannel.setImportantConversation(false); + mNotificationInfo.bindNotification( + -1, + mShortcutManager, + mMockPackageManager, + mUserManager, + mPeopleSpaceWidgetManager, + mMockINotificationManager, + mOnUserInteractionCallback, + TEST_PACKAGE_NAME, + mNotificationChannel, + mEntry, + mBubbleMetadata, + null, + mIconFactory, + mContext, + true, + mTestHandler, + mTestHandler, null, Optional.of(mBubblesManager), + mShadeController); + + // WHEN user clicks "priority" + mNotificationInfo.setSelectedAction(NotificationConversationInfo.ACTION_FAVORITE); + + // and then done + mNotificationInfo.findViewById(R.id.done).performClick(); + + // No widget prompt; on a secondary user + verify(mPeopleSpaceWidgetManager, never()).requestPinAppWidget(any(), any()); + } + + @Test public void testSelectDefaultDoesNotRequestPinPeopleTile() { mNotificationInfo.bindNotification( -1, mShortcutManager, mMockPackageManager, + mUserManager, mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, @@ -1282,6 +1352,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { -1, mShortcutManager, mMockPackageManager, + mUserManager, mPeopleSpaceWidgetManager, mMockINotificationManager, mOnUserInteractionCallback, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java index 7d8e0d26464c..36a1d2b6d07b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java @@ -54,6 +54,7 @@ import android.content.pm.ShortcutManager; import android.graphics.Color; import android.os.Binder; import android.os.Handler; +import android.os.UserManager; import android.provider.Settings; import android.service.notification.StatusBarNotification; import android.testing.AndroidTestingRunner; @@ -139,6 +140,8 @@ public class NotificationGutsManagerTest extends SysuiTestCase { @Mock private PeopleSpaceWidgetManager mPeopleSpaceWidgetManager; @Mock private AssistantFeedbackController mAssistantFeedbackController; + @Mock private UserManager mUserManager; + @Before public void setUp() { mTestableLooper = TestableLooper.get(this); @@ -157,6 +160,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { mGutsManager = new NotificationGutsManager(mContext, () -> Optional.of(mStatusBar), mHandler, mHandler, mAccessibilityManager, mHighPriorityProvider, mINotificationManager, mNotificationEntryManager, + mUserManager, mPeopleSpaceWidgetManager, mLauncherApps, mShortcutManager, mChannelEditorDialogController, mContextTracker, mAssistantFeedbackController, Optional.of(mBubblesManager), new UiEventLoggerFake(), mOnUserInteractionCallback, diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 884770bc5f3c..ffa290649580 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -8715,6 +8715,13 @@ public class ActivityManagerService extends IActivityManager.Stub public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver) { + final int callingUid = Binder.getCallingUid(); + if (callingUid != ROOT_UID && callingUid != Process.SHELL_UID) { + if (resultReceiver != null) { + resultReceiver.send(-1, null); + } + throw new SecurityException("Shell commands are only callable by root or shell"); + } (new ActivityManagerShellCommand(this, false)).exec( this, in, out, err, args, callback, resultReceiver); } diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index e40075371910..1a71ca8d4e97 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -359,6 +359,7 @@ class ShortcutPackage extends ShortcutPackageItem { // Extract Icon and update the icon res ID and the bitmap path. s.saveIconAndFixUpShortcutLocked(newShortcut); s.fixUpShortcutResourceNamesAndValues(newShortcut); + ensureShortcutCountBeforePush(); saveShortcut(newShortcut); } @@ -405,7 +406,6 @@ class ShortcutPackage extends ShortcutPackageItem { @NonNull List<ShortcutInfo> changedShortcuts) { Preconditions.checkArgument(newShortcut.isEnabled(), "pushDynamicShortcuts() cannot publish disabled shortcuts"); - ensureShortcutCountBeforePush(); newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC); diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 940eb345c06d..550e0e2622d7 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -34,6 +34,7 @@ import android.app.usage.UsageStatsManagerInternal; import android.appwidget.AppWidgetProviderInfo; import android.content.BroadcastReceiver; import android.content.ComponentName; +import android.content.ContentProvider; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -1732,6 +1733,10 @@ public class ShortcutService extends IShortcutService.Stub { android.util.EventLog.writeEvent(0x534e4554, "109824443", -1, ""); throw new SecurityException("Shortcut package name mismatch"); } + final int callingUid = injectBinderCallingUid(); + if (UserHandle.getUserId(callingUid) != si.getUserId()) { + throw new SecurityException("User-ID in shortcut doesn't match the caller"); + } } private void verifyShortcutInfoPackages( @@ -1900,11 +1905,32 @@ public class ShortcutService extends IShortcutService.Stub { } if (shortcut.getIcon() != null) { ShortcutInfo.validateIcon(shortcut.getIcon()); + validateIconURI(shortcut); } shortcut.replaceFlags(shortcut.getFlags() & ShortcutInfo.FLAG_LONG_LIVED); } + // Validates the calling process has permission to access shortcut icon's image uri + private void validateIconURI(@NonNull final ShortcutInfo si) { + final int callingUid = injectBinderCallingUid(); + final Icon icon = si.getIcon(); + if (icon == null) { + // There's no icon in this shortcut, nothing to validate here. + return; + } + int iconType = icon.getType(); + if (iconType != Icon.TYPE_URI && iconType != Icon.TYPE_URI_ADAPTIVE_BITMAP) { + // The icon is not URI-based, nothing to validate. + return; + } + final Uri uri = icon.getUri(); + mUriGrantsManagerInternal.checkGrantUriPermission(callingUid, si.getPackage(), + ContentProvider.getUriWithoutUserId(uri), + Intent.FLAG_GRANT_READ_URI_PERMISSION, + ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(callingUid))); + } + private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut, boolean forUpdate) { fixUpIncomingShortcutInfo(shortcut, forUpdate, /*forPinRequest=*/ false); } diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java index 4e453f378cbc..d261e7f4c9ec 100644 --- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java +++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java @@ -41,6 +41,7 @@ import static org.xmlpull.v1.XmlPullParser.START_TAG; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.AppGlobals; @@ -62,6 +63,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; @@ -1302,6 +1304,46 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { return false; } + /** + * Check if the targetPkg can be granted permission to access uri by + * the callingUid using the given modeFlags. See {@link #checkGrantUriPermissionUnlocked}. + * + * @param callingUid The uid of the grantor app that has permissions to the uri. + * @param targetPkg The package name of the granted app that needs permissions to the uri. + * @param uri The uri for which permissions should be granted. + * @param modeFlags The modes to grant. See {@link Intent#FLAG_GRANT_READ_URI_PERMISSION}, etc. + * @param userId The userId in which the uri is to be resolved. + * @return uid of the target or -1 if permission grant not required. Returns -1 if the caller + * does not hold INTERACT_ACROSS_USERS_FULL + * @throws SecurityException if the grant is not allowed. + */ + @Override + @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) + public int checkGrantUriPermission_ignoreNonSystem(int callingUid, String targetPkg, Uri uri, + int modeFlags, int userId) { + if (!isCallerIsSystemOrPrivileged()) { + return Process.INVALID_UID; + } + final long origId = Binder.clearCallingIdentity(); + try { + return checkGrantUriPermissionUnlocked(callingUid, targetPkg, uri, modeFlags, + userId); + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + private boolean isCallerIsSystemOrPrivileged() { + final int uid = Binder.getCallingUid(); + if (uid == Process.SYSTEM_UID || uid == Process.ROOT_UID) { + return true; + } + return ActivityManager.checkComponentPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, + uid, /* owningUid = */-1, /* exported = */ true) + == PackageManager.PERMISSION_GRANTED; + } + @GuardedBy("mLock") private void writeGrantedUriPermissionsLocked() { if (DEBUG) Slog.v(TAG, "writeGrantedUriPermissions()"); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 0965709454f6..b0f40a714c27 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -18,6 +18,7 @@ package com.android.server.wm; import static android.Manifest.permission.BIND_VOICE_INTERACTION; import static android.Manifest.permission.CHANGE_CONFIGURATION; +import static android.Manifest.permission.CONTROL_KEYGUARD; import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS; import static android.Manifest.permission.INTERACT_ACROSS_USERS; import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; @@ -3369,6 +3370,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public void keyguardGoingAway(int flags) { + mAmInternal.enforceCallingPermission(CONTROL_KEYGUARD, "unlock keyguard"); enforceNotIsolatedCaller("keyguardGoingAway"); final long token = Binder.clearCallingIdentity(); try { |