diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-10-11 22:51:37 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-10-11 22:51:37 +0000 |
commit | d203ccf2f9f7be2db16eee7816f6afd17586db3c (patch) | |
tree | 6b4b06c03ed9b7a05cb3cd2d7d3bdfd974acdd18 | |
parent | 51640edd49c34465ff2b1a836abaeb068821bbfc (diff) | |
parent | 1a42ae5379269b9e4dac9f5fbf803c6c731c655d (diff) | |
download | base-d203ccf2f9f7be2db16eee7816f6afd17586db3c.tar.gz |
Merge cherrypicks of ['googleplex-android-review.googlesource.com/23905843', 'googleplex-android-review.googlesource.com/23918399', 'googleplex-android-review.googlesource.com/24404277', 'googleplex-android-review.googlesource.com/20064768', 'googleplex-android-review.googlesource.com/24046929', 'googleplex-android-review.googlesource.com/24573549', 'googleplex-android-review.googlesource.com/24342145', 'googleplex-android-review.googlesource.com/23623415', 'googleplex-android-review.googlesource.com/24608667', 'googleplex-android-review.googlesource.com/24182288', 'googleplex-android-review.googlesource.com/24757286', 'googleplex-android-review.googlesource.com/24058898', 'googleplex-android-review.googlesource.com/24805388', 'googleplex-android-review.googlesource.com/22465041', 'googleplex-android-review.googlesource.com/24945646', 'googleplex-android-review.googlesource.com/24555955'] into security-aosp-tm-release.android-security-13.0.0_r12
Change-Id: I8265bdcfbebf77a1a652fd66bc73e1451970684b
22 files changed, 464 insertions, 42 deletions
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp index fbb99d2aea89..8e461afd9db0 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 fe75dd302beb..b709b7ee90c3 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -239,6 +239,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/android/app/Notification.java b/core/java/android/app/Notification.java index 4d1c7b619549..aff21ef9c5b8 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -2138,6 +2138,10 @@ public class Notification implements Parcelable } } + private void visitUris(@NonNull Consumer<Uri> visitor) { + visitIconUri(visitor, getIcon()); + } + @Override public Action clone() { return new Action( @@ -2823,7 +2827,7 @@ public class Notification implements Parcelable if (actions != null) { for (Action action : actions) { - visitIconUri(visitor, action.getIcon()); + action.visitUris(visitor); } } @@ -2846,18 +2850,14 @@ public class Notification implements Parcelable visitor.accept(Uri.parse(extras.getString(EXTRA_BACKGROUND_IMAGE_URI))); } - ArrayList<Person> people = extras.getParcelableArrayList(EXTRA_PEOPLE_LIST); + ArrayList<Person> people = extras.getParcelableArrayList(EXTRA_PEOPLE_LIST, + Person.class); if (people != null && !people.isEmpty()) { for (Person p : people) { visitor.accept(p.getIconUri()); } } - final Person person = extras.getParcelable(EXTRA_MESSAGING_PERSON, Person.class); - if (person != null) { - visitor.accept(person.getIconUri()); - } - final RemoteInputHistoryItem[] history = extras.getParcelableArray( Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS, RemoteInputHistoryItem.class); @@ -2869,10 +2869,16 @@ public class Notification implements Parcelable } } } - } - if (isStyle(MessagingStyle.class) && extras != null) { - final Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES); + // Extras for MessagingStyle. We visit them even if not isStyle(MessagingStyle), since + // Notification Listeners might use directly (without the isStyle check). + final Person person = extras.getParcelable(EXTRA_MESSAGING_PERSON, Person.class); + if (person != null) { + visitor.accept(person.getIconUri()); + } + + final Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES, + Parcelable.class); if (!ArrayUtils.isEmpty(messages)) { for (MessagingStyle.Message message : MessagingStyle.Message .getMessagesFromBundleArray(messages)) { @@ -2885,7 +2891,8 @@ public class Notification implements Parcelable } } - final Parcelable[] historic = extras.getParcelableArray(EXTRA_HISTORIC_MESSAGES); + final Parcelable[] historic = extras.getParcelableArray(EXTRA_HISTORIC_MESSAGES, + Parcelable.class); if (!ArrayUtils.isEmpty(historic)) { for (MessagingStyle.Message message : MessagingStyle.Message .getMessagesFromBundleArray(historic)) { @@ -2898,20 +2905,24 @@ public class Notification implements Parcelable } } - visitIconUri(visitor, extras.getParcelable(EXTRA_CONVERSATION_ICON)); - } + visitIconUri(visitor, extras.getParcelable(EXTRA_CONVERSATION_ICON, Icon.class)); - if (isStyle(CallStyle.class) & extras != null) { - Person callPerson = extras.getParcelable(EXTRA_CALL_PERSON); + // Extras for CallStyle (same reason for visiting without checking isStyle). + Person callPerson = extras.getParcelable(EXTRA_CALL_PERSON, Person.class); if (callPerson != null) { visitor.accept(callPerson.getIconUri()); } - visitIconUri(visitor, extras.getParcelable(EXTRA_VERIFICATION_ICON)); + visitIconUri(visitor, extras.getParcelable(EXTRA_VERIFICATION_ICON, Icon.class)); } if (mBubbleMetadata != null) { visitIconUri(visitor, mBubbleMetadata.getIcon()); } + + if (extras != null && extras.containsKey(WearableExtender.EXTRA_WEARABLE_EXTENSIONS)) { + WearableExtender extender = new WearableExtender(this); + extender.visitUris(visitor); + } } /** @@ -11597,6 +11608,12 @@ public class Notification implements Parcelable mFlags &= ~mask; } } + + private void visitUris(@NonNull Consumer<Uri> visitor) { + for (Action action : mActions) { + action.visitUris(visitor); + } + } } /** diff --git a/core/java/android/os/PersistableBundle.java b/core/java/android/os/PersistableBundle.java index f4edcb1317f4..acfd15cb005e 100644 --- a/core/java/android/os/PersistableBundle.java +++ b/core/java/android/os/PersistableBundle.java @@ -21,6 +21,7 @@ import static java.nio.charset.StandardCharsets.UTF_8; import android.annotation.NonNull; import android.annotation.Nullable; import android.util.ArrayMap; +import android.util.Slog; import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; import android.util.Xml; @@ -50,6 +51,8 @@ import java.util.ArrayList; */ public final class PersistableBundle extends BaseBundle implements Cloneable, Parcelable, XmlUtils.WriteMapCallback { + private static final String TAG = "PersistableBundle"; + private static final String TAG_PERSISTABLEMAP = "pbundle_as_map"; /** An unmodifiable {@code PersistableBundle} that is always {@link #isEmpty() empty}. */ @@ -118,7 +121,11 @@ public final class PersistableBundle extends BaseBundle implements Cloneable, Pa * @hide */ public PersistableBundle(Bundle b) { - this(b.getItemwiseMap()); + this(b, true); + } + + private PersistableBundle(Bundle b, boolean throwException) { + this(b.getItemwiseMap(), throwException); } /** @@ -127,7 +134,7 @@ public final class PersistableBundle extends BaseBundle implements Cloneable, Pa * @param map a Map containing only those items that can be persisted. * @throws IllegalArgumentException if any element of #map cannot be persisted. */ - private PersistableBundle(ArrayMap<String, Object> map) { + private PersistableBundle(ArrayMap<String, Object> map, boolean throwException) { super(); mFlags = FLAG_DEFUSABLE; @@ -136,16 +143,23 @@ public final class PersistableBundle extends BaseBundle implements Cloneable, Pa // Now verify each item throwing an exception if there is a violation. final int N = mMap.size(); - for (int i=0; i<N; i++) { + for (int i = N - 1; i >= 0; --i) { Object value = mMap.valueAt(i); if (value instanceof ArrayMap) { // Fix up any Maps by replacing them with PersistableBundles. - mMap.setValueAt(i, new PersistableBundle((ArrayMap<String, Object>) value)); + mMap.setValueAt(i, + new PersistableBundle((ArrayMap<String, Object>) value, throwException)); } else if (value instanceof Bundle) { - mMap.setValueAt(i, new PersistableBundle(((Bundle) value))); + mMap.setValueAt(i, new PersistableBundle((Bundle) value, throwException)); } else if (!isValidType(value)) { - throw new IllegalArgumentException("Bad value in PersistableBundle key=" - + mMap.keyAt(i) + " value=" + value); + final String errorMsg = "Bad value in PersistableBundle key=" + + mMap.keyAt(i) + " value=" + value; + if (throwException) { + throw new IllegalArgumentException(errorMsg); + } else { + Slog.wtfStack(TAG, errorMsg); + mMap.removeAt(i); + } } } } @@ -268,6 +282,15 @@ public final class PersistableBundle extends BaseBundle implements Cloneable, Pa /** @hide */ public void saveToXml(TypedXmlSerializer out) throws IOException, XmlPullParserException { unparcel(); + // Explicitly drop invalid types an attacker may have added before persisting. + for (int i = mMap.size() - 1; i >= 0; --i) { + final Object value = mMap.valueAt(i); + if (!isValidType(value)) { + Slog.e(TAG, "Dropping bad data before persisting: " + + mMap.keyAt(i) + "=" + value); + mMap.removeAt(i); + } + } XmlUtils.writeMapXml(mMap, out, this); } @@ -322,9 +345,12 @@ public final class PersistableBundle extends BaseBundle implements Cloneable, Pa while (((event = in.next()) != XmlPullParser.END_DOCUMENT) && (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) { if (event == XmlPullParser.START_TAG) { + // Don't throw an exception when restoring from XML since an attacker could try to + // input invalid data in the persisted file. return new PersistableBundle((ArrayMap<String, Object>) XmlUtils.readThisArrayMapXml(in, startTag, tagName, - new MyReadMapCallback())); + new MyReadMapCallback()), + /* throwException */ false); } } return new PersistableBundle(); // An empty mutable PersistableBundle 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 5d086e833e14..6365171bc578 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -1557,6 +1557,10 @@ public class KeyguardViewMediator extends CoreStartable 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 @@ -1782,9 +1786,9 @@ public class KeyguardViewMediator extends CoreStartable 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 4c2336eeb22c..db343e52b3ca 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 @@ -592,10 +594,13 @@ class MediaDataManager( Log.d(TAG, "adding track for $userId from browser: $desc") } + val currentEntry = mediaEntries.get(packageName) + val appUid = currentEntry?.appUid ?: Process.INVALID_UID + // Album art var artworkBitmap = desc.iconBitmap if (artworkBitmap == null && desc.iconUri != null) { - artworkBitmap = loadBitmapFromUri(desc.iconUri!!) + artworkBitmap = loadBitmapFromUriForUser(desc.iconUri!!, userId, appUid, packageName) } val artworkIcon = if (artworkBitmap != null) { Icon.createWithBitmap(artworkBitmap) @@ -603,9 +608,7 @@ class MediaDataManager( null } - val currentEntry = mediaEntries.get(packageName) val instanceId = currentEntry?.instanceId ?: logger.getNewInstanceId() - val appUid = currentEntry?.appUid ?: Process.INVALID_UID val mediaAction = getResumeMediaAction(resumeAction) val lastActive = systemClock.elapsedRealtime() @@ -1000,6 +1003,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 c9c7fe9e0ab6..b9c912ff13cd 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; @@ -160,6 +161,7 @@ public interface NotificationsModule { HighPriorityProvider highPriorityProvider, INotificationManager notificationManager, NotificationEntryManager notificationEntryManager, + UserManager userManager, PeopleSpaceWidgetManager peopleSpaceWidgetManager, LauncherApps launcherApps, ShortcutManager shortcutManager, @@ -180,6 +182,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 8a7155ac72d0..fb1462ae03fa 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; @@ -126,6 +127,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; @@ -145,6 +149,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx HighPriorityProvider highPriorityProvider, INotificationManager notificationManager, NotificationEntryManager notificationEntryManager, + UserManager userManager, PeopleSpaceWidgetManager peopleSpaceWidgetManager, LauncherApps launcherApps, ShortcutManager shortcutManager, @@ -163,6 +168,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 52266f983fdd..347ea3b2982d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt @@ -1,18 +1,22 @@ 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.graphics.drawable.Icon import android.media.MediaDescription import android.media.MediaMetadata import android.media.session.MediaController import android.media.session.MediaSession import android.media.session.PlaybackState +import android.net.Uri import android.os.Bundle import android.provider.Settings import android.service.notification.StatusBarNotification @@ -20,6 +24,7 @@ import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import androidx.media.utils.MediaConstants import androidx.test.filters.SmallTest +import com.android.dx.mockito.inline.extended.ExtendedMockito import com.android.internal.logging.InstanceId import com.android.systemui.InstanceIdSequenceFake import com.android.systemui.R @@ -53,8 +58,10 @@ import org.mockito.Mockito.reset import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions +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" @@ -111,14 +118,25 @@ 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 instanceIdSequence = InstanceIdSequenceFake(1 shl 20) 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() @@ -195,6 +213,7 @@ class MediaDataManagerTest : SysuiTestCase() { @After fun tearDown() { + staticMockSession.finishMocking() session.release() mediaDataManager.destroy() Settings.Secure.putInt(context.contentResolver, @@ -1093,6 +1112,66 @@ class MediaDataManagerTest : SysuiTestCase() { anyBoolean()) } + @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 */ @@ -1103,4 +1182,32 @@ class MediaDataManagerTest : SysuiTestCase() { 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 e9d8f5830131..6da97404e742 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(mCentralSurfaces), 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/PendingIntentUtils.java b/services/core/java/com/android/server/PendingIntentUtils.java index 1600101b20f4..a72a4d254a2a 100644 --- a/services/core/java/com/android/server/PendingIntentUtils.java +++ b/services/core/java/com/android/server/PendingIntentUtils.java @@ -34,6 +34,7 @@ public class PendingIntentUtils { public static Bundle createDontSendToRestrictedAppsBundle(@Nullable Bundle bundle) { final BroadcastOptions options = BroadcastOptions.makeBasic(); options.setDontSendToRestrictedApps(true); + options.setPendingIntentBackgroundActivityLaunchAllowed(false); if (bundle == null) { return options.toBundle(); } diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 7a19d034c2c8..5238595fe2a2 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -4923,7 +4923,7 @@ public class AccountManagerService p.setDataPosition(0); Bundle simulateBundle = p.readBundle(); p.recycle(); - Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT); + Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT, Intent.class); if (intent != null && intent.getClass() != Intent.class) { return false; } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index a71cd356afa2..5f5912bf2143 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -9232,6 +9232,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 8507ad028a19..1b7687e6fc16 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -376,6 +376,7 @@ class ShortcutPackage extends ShortcutPackageItem { // Extract Icon and update the icon res ID and the bitmap path. s.saveIconAndFixUpShortcutLocked(this, newShortcut); s.fixUpShortcutResourceNamesAndValues(newShortcut); + ensureShortcutCountBeforePush(); saveShortcut(newShortcut); } @@ -430,7 +431,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 2f3056e89614..63a6328ea6d7 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; @@ -1728,6 +1729,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( @@ -1913,11 +1918,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 6aa06e8ee068..92329cf4a8ff 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; @@ -1304,6 +1306,46 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub implements 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; + } + @Override public ArrayList<UriPermission> providePersistentUriGrants() { final ArrayList<UriPermission> result = new ArrayList<>(); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index aa154292fe7e..71ca8523ce71 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; @@ -3394,6 +3395,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 { 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 0f391cb67f0e..8c60178f44fe 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -5356,6 +5356,69 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void testVisitUris_styleExtrasWithoutStyle() { + Notification notification = new Notification.Builder(mContext, "a") + .setSmallIcon(android.R.drawable.sym_def_app_icon) + .build(); + + Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle( + personWithIcon("content://user")) + .addHistoricMessage(new Notification.MessagingStyle.Message("Heyhey!", + System.currentTimeMillis(), + personWithIcon("content://historicalMessenger"))) + .addMessage(new Notification.MessagingStyle.Message("Are you there", + System.currentTimeMillis(), + personWithIcon("content://messenger"))) + .setShortcutIcon( + Icon.createWithContentUri("content://conversationShortcut")); + messagingStyle.addExtras(notification.extras); // Instead of Builder.setStyle(style). + + Notification.CallStyle callStyle = Notification.CallStyle.forOngoingCall( + personWithIcon("content://caller"), + PendingIntent.getActivity(mContext, 0, new Intent(), + PendingIntent.FLAG_IMMUTABLE)) + .setVerificationIcon(Icon.createWithContentUri("content://callVerification")); + callStyle.addExtras(notification.extras); // Same. + + Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class); + notification.visitUris(visitor); + + verify(visitor).accept(eq(Uri.parse("content://user"))); + verify(visitor).accept(eq(Uri.parse("content://historicalMessenger"))); + verify(visitor).accept(eq(Uri.parse("content://messenger"))); + verify(visitor).accept(eq(Uri.parse("content://conversationShortcut"))); + verify(visitor).accept(eq(Uri.parse("content://caller"))); + verify(visitor).accept(eq(Uri.parse("content://callVerification"))); + } + + private static Person personWithIcon(String iconUri) { + return new Person.Builder() + .setName("Mr " + iconUri) + .setIcon(Icon.createWithContentUri(iconUri)) + .build(); + } + + @Test + public void testVisitUris_wearableExtender() { + Icon actionIcon = Icon.createWithContentUri("content://media/action"); + Icon wearActionIcon = Icon.createWithContentUri("content://media/wearAction"); + PendingIntent intent = PendingIntent.getActivity(mContext, 0, new Intent(), + PendingIntent.FLAG_IMMUTABLE); + Notification n = new Notification.Builder(mContext, "a") + .setSmallIcon(android.R.drawable.sym_def_app_icon) + .addAction(new Notification.Action.Builder(actionIcon, "Hey!", intent).build()) + .extend(new Notification.WearableExtender().addAction( + new Notification.Action.Builder(wearActionIcon, "Wear!", intent).build())) + .build(); + + Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class); + n.visitUris(visitor); + + verify(visitor).accept(eq(actionIcon.getUri())); + verify(visitor).accept(eq(wearActionIcon.getUri())); + } + + @Test public void testSetNotificationPolicy_preP_setOldFields() { ZenModeHelper mZenModeHelper = mock(ZenModeHelper.class); mService.mZenModeHelper = mZenModeHelper; |