diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-10-11 22:51:13 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-10-11 22:51:13 +0000 |
commit | cb74878e9347fdcf8bbc32f8b379a55ba1c35c64 (patch) | |
tree | aa8b2281a90948f8c2f9f35f172b7c5569cfe521 | |
parent | 1f5394dee6bbf9f8ed37c143e402393f9d131591 (diff) | |
parent | 6fab80f141163de4d0008f7cd081cfc4917c3c68 (diff) | |
download | base-android-security-12.0.0_r54.tar.gz |
Merge cherrypicks of ['googleplex-android-review.googlesource.com/23905843', 'googleplex-android-review.googlesource.com/23918399', 'googleplex-android-review.googlesource.com/24420396', 'googleplex-android-review.googlesource.com/20064766', 'googleplex-android-review.googlesource.com/24046929', 'googleplex-android-review.googlesource.com/24573550', 'googleplex-android-review.googlesource.com/24339446', 'googleplex-android-review.googlesource.com/23623415', 'googleplex-android-review.googlesource.com/24605805', 'googleplex-android-review.googlesource.com/24182288', 'googleplex-android-review.googlesource.com/24761005', 'googleplex-android-review.googlesource.com/24058898', 'googleplex-android-review.googlesource.com/24805807', 'googleplex-android-review.googlesource.com/22465294', 'googleplex-android-review.googlesource.com/24642267'] into security-aosp-sc-release.android-security-12.0.0_r54
Change-Id: I1abcb72496bdcacd22c899b01fd459eb006e7d30
21 files changed, 484 insertions, 49 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 74d51a0bcf63..fa3a5d51f0cc 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/android/app/Notification.java b/core/java/android/app/Notification.java index cdab7d474161..ed8c6be73256 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -2084,6 +2084,10 @@ public class Notification implements Parcelable } } + private void visitUris(@NonNull Consumer<Uri> visitor) { + visitIconUri(visitor, getIcon()); + } + @Override public Action clone() { return new Action( @@ -2769,7 +2773,7 @@ public class Notification implements Parcelable if (actions != null) { for (Action action : actions) { - visitIconUri(visitor, action.getIcon()); + action.visitUris(visitor); } } @@ -2799,11 +2803,6 @@ public class Notification implements Parcelable } } - final Person person = extras.getParcelable(EXTRA_MESSAGING_PERSON); - if (person != null) { - visitor.accept(person.getIconUri()); - } - final RemoteInputHistoryItem[] history = getParcelableArrayFromBundle(extras, Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS, RemoteInputHistoryItem.class); if (history != null) { @@ -2814,9 +2813,14 @@ public class Notification implements Parcelable } } } - } - if (isStyle(MessagingStyle.class) && extras != null) { + // 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); + if (person != null) { + visitor.accept(person.getIconUri()); + } + final Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES); if (!ArrayUtils.isEmpty(messages)) { for (MessagingStyle.Message message : MessagingStyle.Message @@ -2844,9 +2848,8 @@ public class Notification implements Parcelable } visitIconUri(visitor, extras.getParcelable(EXTRA_CONVERSATION_ICON)); - } - if (isStyle(CallStyle.class) & extras != null) { + // Extras for CallStyle (same reason for visiting without checking isStyle). Person callPerson = extras.getParcelable(EXTRA_CALL_PERSON); if (callPerson != null) { visitor.accept(callPerson.getIconUri()); @@ -2857,6 +2860,11 @@ public class Notification implements Parcelable if (mBubbleMetadata != null) { visitIconUri(visitor, mBubbleMetadata.getIcon()); } + + if (extras != null && extras.containsKey(WearableExtender.EXTRA_WEARABLE_EXTENSIONS)) { + WearableExtender extender = new WearableExtender(this); + extender.visitUris(visitor); + } } /** @@ -11350,6 +11358,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 e5e9b5f6f53c..9718b52eafeb 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; @@ -46,6 +47,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}. */ @@ -110,7 +113,11 @@ public final class PersistableBundle extends BaseBundle implements Cloneable, Pa * @hide */ public PersistableBundle(Bundle b) { - this(b.getMap()); + this(b, true); + } + + private PersistableBundle(Bundle b, boolean throwException) { + this(b.getMap(), throwException); } /** @@ -119,7 +126,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; @@ -128,16 +135,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); + } } } } @@ -257,6 +271,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); } @@ -311,9 +334,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 EMPTY; 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 27759a62094c..807af1b1382d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -664,6 +664,13 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, } @Override + public void onStrongAuthStateChanged(int userId) { + if (mLockPatternUtils.isUserInLockdown(KeyguardUpdateMonitor.getCurrentUser())) { + doKeyguardLocked(null); + } + } + + @Override public void onTrustChanged(int userId) { if (userId == KeyguardUpdateMonitor.getCurrentUser()) { synchronized (KeyguardViewMediator.this) { @@ -678,13 +685,6 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable, notifyHasLockscreenWallpaperChanged(hasLockscreenWallpaper); } } - - @Override - public void onStrongAuthStateChanged(int userId) { - if (mLockPatternUtils.isUserInLockdown(KeyguardUpdateMonitor.getCurrentUser())) { - doKeyguardLocked(null); - } - } }; ViewMediatorCallback mViewMediatorCallback = new ViewMediatorCallback() { @@ -1314,6 +1314,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 @@ -1530,9 +1534,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 c8b72e21efc8..70af5b647362 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt @@ -18,15 +18,18 @@ 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 import android.content.IntentFilter +import android.content.pm.PackageManager import android.graphics.Bitmap import android.graphics.Canvas import android.graphics.ImageDecoder @@ -38,6 +41,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 @@ -492,7 +496,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) @@ -669,6 +679,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 6964838e7e41..b9db5b7644a4 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; @@ -133,6 +134,7 @@ public interface NotificationsModule { HighPriorityProvider highPriorityProvider, INotificationManager notificationManager, NotificationEntryManager notificationEntryManager, + UserManager userManager, PeopleSpaceWidgetManager peopleSpaceWidgetManager, LauncherApps launcherApps, ShortcutManager shortcutManager, @@ -152,6 +154,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..513447fd1315 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; @@ -152,10 +155,12 @@ public class NotificationConversationInfo extends LinearLayout implements mPressedApply = true; // If the user selected Priority and the previous selection was not priority, show a - // People Tile add request. + // People Tile add request if for same profile group. 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 4319e29985d8..17f1fc419acf 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; @@ -124,6 +125,9 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx private final INotificationManager mNotificationManager; private final NotificationEntryManager mNotificationEntryManager; private final PeopleSpaceWidgetManager mPeopleSpaceWidgetManager; + + private final UserManager mUserManager; + private final LauncherApps mLauncherApps; private final ShortcutManager mShortcutManager; private final UserContextProvider mContextTracker; @@ -142,6 +146,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx HighPriorityProvider highPriorityProvider, INotificationManager notificationManager, NotificationEntryManager notificationEntryManager, + UserManager userManager, PeopleSpaceWidgetManager peopleSpaceWidgetManager, LauncherApps launcherApps, ShortcutManager shortcutManager, @@ -160,6 +165,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx mHighPriorityProvider = highPriorityProvider; mNotificationManager = notificationManager; mNotificationEntryManager = notificationEntryManager; + mUserManager = userManager; mPeopleSpaceWidgetManager = peopleSpaceWidgetManager; mLauncherApps = launcherApps; mShortcutManager = shortcutManager; @@ -482,6 +488,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 e990993ab507..b00267cdd95c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt @@ -1,20 +1,25 @@ package com.android.systemui.media +import android.app.IUriGrantsManager 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.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 @@ -23,6 +28,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 @@ -34,6 +40,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 @@ -41,8 +48,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" @@ -92,12 +101,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() @@ -159,6 +179,7 @@ class MediaDataManagerTest : SysuiTestCase() { @After fun tearDown() { + staticMockSession.finishMocking() session.release() mediaDataManager.destroy() Settings.Secure.putInt(context.contentResolver, @@ -166,13 +187,10 @@ class MediaDataManagerTest : SysuiTestCase() { } @Test - fun testSetTimedOut_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) + fun testSetTimedOut_active_deactivatesMedia() { + addNotificationAndLoad() + val data = mediaDataCaptor.value + assertThat(data.active).isTrue() mediaDataManager.setTimedOut(KEY, timedOut = true) assertThat(data.active).isFalse() @@ -598,4 +616,102 @@ 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(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(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 9f537f5b6afc..d052a1c84285 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; @@ -141,6 +142,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); @@ -158,7 +161,8 @@ public class NotificationGutsManagerTest extends SysuiTestCase { mGutsManager = new NotificationGutsManager(mContext, () -> mStatusBar, mHandler, mHandler, mAccessibilityManager, mHighPriorityProvider, - mINotificationManager, mNotificationEntryManager, mPeopleSpaceWidgetManager, + mINotificationManager, mNotificationEntryManager, mUserManager, + mPeopleSpaceWidgetManager, mLauncherApps, mShortcutManager, mChannelEditorDialogController, mContextTracker, mAssistantFeedbackController, Optional.of(mBubblesManager), new UiEventLoggerFake(), mOnUserInteractionCallback, mShadeController); 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/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 27d6a0a2f6d3..123bb6c856f3 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -8663,6 +8663,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 fd7da4b21066..e83f14fdadab 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; @@ -1719,6 +1720,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( @@ -1887,11 +1892,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 987cff95bba3..bcb5e0db15e8 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; @@ -3312,6 +3313,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 9ca0f557ba1d..85fc65d16785 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -4510,6 +4510,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; |