summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorandroid-build-team Robot <android-build-team-robot@google.com>2021-04-08 16:40:31 +0000
committerandroid-build-team Robot <android-build-team-robot@google.com>2021-04-08 16:40:31 +0000
commitea1b439cba8fa45473c84c7ced2072200a47940c (patch)
tree3dd60703446fb062c3f8f7a8bbbcbc685a7b8d39
parent3eb437536b377b8f8f177b28a0f55940f6736919 (diff)
parenteea63284bd4b7cbab4f6f83c22818320d53809d1 (diff)
downloadbase-ea1b439cba8fa45473c84c7ced2072200a47940c.tar.gz
Merge cherrypicks of [14126780, 14127201, 14128410, 14127515, 14128647, 14128745, 14128746, 14128251, 14128748, 14126431, 14125341, 14128805, 14128806] into security-aosp-oc-mr1-releaseandroid-security-8.1.0_r89
Change-Id: I7deb26c3a7a20ab8a75503ff5b56f90cfaf4c3bb
-rw-r--r--core/java/android/app/ActivityManagerInternal.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java27
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java10
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java40
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java17
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java36
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java5
-rw-r--r--services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java5
8 files changed, 150 insertions, 5 deletions
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 9dceb7f9e433..768393c0c763 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -273,4 +273,19 @@ public abstract class ActivityManagerInternal {
* Returns {@code true} if {@code uid} is running an activity from {@code packageName}.
*/
public abstract boolean hasRunningActivity(int uid, @Nullable String packageName);
+
+ /**
+ * Returns {@code true} if the given notification channel currently has a
+ * notification associated with a foreground service. This is an AMS check
+ * because that is the source of truth for the FGS state.
+ */
+ public abstract boolean hasForegroundServiceNotification(String pkg, int userId,
+ String channelId);
+
+ /**
+ * If the given app has any FGSs whose notifications are in the given channel,
+ * stop them.
+ */
+ public abstract void stopForegroundServicesForChannel(String pkg, int userId,
+ String channelId);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 6cfd42fa00eb..0183ef83fe73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -31,6 +31,7 @@ import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.Parcelable;
@@ -72,6 +73,16 @@ public class StatusBarIconView extends AnimatedImageView {
public static final int STATE_DOT = 1;
public static final int STATE_HIDDEN = 2;
+ /**
+ * Maximum allowed byte count for an icon bitmap
+ * @see android.graphics.RecordingCanvas.MAX_BITMAP_SIZE
+ */
+ private static final int MAX_BITMAP_SIZE = 100 * 1024 * 1024; // 100 MB
+ /**
+ * Maximum allowed width or height for an icon drawable, if we can't get byte count
+ */
+ private static final int MAX_IMAGE_SIZE = 5000;
+
private static final String TAG = "StatusBarIconView";
private static final Property<StatusBarIconView, Float> ICON_APPEAR_AMOUNT
= new FloatProperty<StatusBarIconView>("iconAppearAmount") {
@@ -328,6 +339,22 @@ public class StatusBarIconView extends AnimatedImageView {
Log.w(TAG, "No icon for slot " + mSlot + "; " + mIcon.icon);
return false;
}
+
+ if (drawable instanceof BitmapDrawable && ((BitmapDrawable) drawable).getBitmap() != null) {
+ // If it's a bitmap we can check the size directly
+ int byteCount = ((BitmapDrawable) drawable).getBitmap().getByteCount();
+ if (byteCount > MAX_BITMAP_SIZE) {
+ Log.w(TAG, "Drawable is too large (" + byteCount + " bytes) " + mIcon);
+ return false;
+ }
+ } else if (drawable.getIntrinsicWidth() > MAX_IMAGE_SIZE
+ || drawable.getIntrinsicHeight() > MAX_IMAGE_SIZE) {
+ // Otherwise, check dimensions
+ Log.w(TAG, "Drawable is too large (" + drawable.getIntrinsicWidth() + "x"
+ + drawable.getIntrinsicHeight() + ") " + mIcon);
+ return false;
+ }
+
if (withClear) {
setImageDrawable(null);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
index 7b2071ca14fd..64f1e1ca1bef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
@@ -35,6 +35,7 @@ import android.content.ContextWrapper;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
+import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.Icon;
import android.os.UserHandle;
@@ -122,4 +123,13 @@ public class StatusBarIconViewTest extends SysuiTestCase {
assertEquals("Transparent backgrounds should fallback to drawable color",
color, mIconView.getStaticDrawableColor());
}
+
+ @Test
+ public void testGiantImageNotAllowed() {
+ Bitmap largeBitmap = Bitmap.createBitmap(6000, 6000, Bitmap.Config.ARGB_8888);
+ Icon icon = Icon.createWithBitmap(largeBitmap);
+ StatusBarIcon largeIcon = new StatusBarIcon(UserHandle.ALL, "mockPackage",
+ icon, 0, 0, "");
+ assertFalse(mIconView.set(largeIcon));
+ }
} \ No newline at end of file
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 201390a7745b..e371fde5015a 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -27,6 +27,7 @@ import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
import android.app.ActivityThread;
@@ -311,6 +312,45 @@ public final class ActiveServices {
return smap != null ? smap.mStartingBackground.size() >= mMaxStartingBackground : false;
}
+ boolean hasForegroundServiceNotificationLocked(String pkg, int userId, String channelId) {
+ final ServiceMap smap = mServiceMap.get(userId);
+ if (smap != null) {
+ for (int i = 0; i < smap.mServicesByName.size(); i++) {
+ final ServiceRecord sr = smap.mServicesByName.valueAt(i);
+ if (sr.appInfo.packageName.equals(pkg) && sr.isForeground) {
+ if (Objects.equals(sr.foregroundNoti.getChannelId(), channelId)) {
+ if (DEBUG_FOREGROUND_SERVICE) {
+ Slog.d(TAG_SERVICE, "Channel u" + userId + "/pkg=" + pkg
+ + "/channelId=" + channelId
+ + " has fg service notification");
+ }
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ void stopForegroundServicesForChannelLocked(String pkg, int userId, String channelId) {
+ final ServiceMap smap = mServiceMap.get(userId);
+ if (smap != null) {
+ for (int i = 0; i < smap.mServicesByName.size(); i++) {
+ final ServiceRecord sr = smap.mServicesByName.valueAt(i);
+ if (sr.appInfo.packageName.equals(pkg) && sr.isForeground) {
+ if (Objects.equals(sr.foregroundNoti.getChannelId(), channelId)) {
+ if (DEBUG_FOREGROUND_SERVICE) {
+ Slog.d(TAG_SERVICE, "Stopping FGS u" + userId + "/pkg=" + pkg
+ + "/channelId=" + channelId
+ + " for conversation channel clear");
+ }
+ stopServiceLocked(sr);
+ }
+ }
+ }
+ }
+ }
+
private ServiceMap getServiceMapLocked(int callingUser) {
ServiceMap smap = mServiceMap.get(callingUser);
if (smap == null) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6921dfe7c753..b586a5ca2d2f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -24297,6 +24297,23 @@ public class ActivityManagerService extends IActivityManager.Stub
}
return false;
}
+
+ @Override
+ public boolean hasForegroundServiceNotification(String pkg, int userId,
+ String channelId) {
+ synchronized (ActivityManagerService.this) {
+ return mServices.hasForegroundServiceNotificationLocked(pkg, userId, channelId);
+ }
+ }
+
+ @Override
+ public void stopForegroundServicesForChannel(String pkg, int userId,
+ String channelId) {
+ synchronized (ActivityManagerService.this) {
+ mServices.stopForegroundServicesForChannelLocked(pkg, userId, channelId);
+ }
+ }
+
}
/**
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 797ce37af560..39cb2e079db2 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -272,6 +272,7 @@ public class NotificationManagerService extends SystemService {
private IActivityManager mAm;
private ActivityManager mActivityManager;
+ private ActivityManagerInternal mAmi;
private IPackageManager mPackageManager;
private PackageManager mPackageManagerClient;
AudioManager mAudioManager;
@@ -1209,7 +1210,8 @@ public class NotificationManagerService extends SystemService {
NotificationAssistants notificationAssistants, ConditionProviders conditionProviders,
ICompanionDeviceManager companionManager, SnoozeHelper snoozeHelper,
NotificationUsageStats usageStats, AtomicFile policyFile,
- ActivityManager activityManager, GroupHelper groupHelper) {
+ ActivityManager activityManager, GroupHelper groupHelper,
+ ActivityManagerInternal ami) {
Resources resources = getContext().getResources();
mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
@@ -1226,6 +1228,7 @@ public class NotificationManagerService extends SystemService {
mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
mCompanionManager = companionManager;
mActivityManager = activityManager;
+ mAmi = ami;
mHandler = new WorkerHandler(looper);
mRankingThread.start();
@@ -1353,7 +1356,8 @@ public class NotificationManagerService extends SystemService {
null, snoozeHelper, new NotificationUsageStats(getContext()),
new AtomicFile(new File(systemDir, "notification_policy.xml")),
(ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE),
- getGroupHelper());
+ getGroupHelper(),
+ LocalServices.getService(ActivityManagerInternal.class));
// register for various Intents
IntentFilter filter = new IntentFilter();
@@ -1863,15 +1867,32 @@ public class NotificationManagerService extends SystemService {
return mRankingHelper.getNotificationChannel(pkg, uid, channelId, includeDeleted);
}
+ // Returns 'true' if the given channel has a notification associated
+ // with an active foreground service.
+ private void enforceDeletingChannelHasNoFgService(String pkg, int userId,
+ String channelId) {
+ if (mAmi.hasForegroundServiceNotification(pkg, userId, channelId)) {
+ // Would be a behavioral change to introduce a throw here, so
+ // we simply return without affecting the channel.
+ Slog.w(TAG, "Package u" + userId + "/" + pkg
+ + " may not delete notification channel '"
+ + channelId + "' with fg service");
+ throw new SecurityException("Not allowed to delete channel " + channelId
+ + " with a foreground service");
+ }
+ }
+
@Override
public void deleteNotificationChannel(String pkg, String channelId) {
checkCallerIsSystemOrSameApp(pkg);
final int callingUid = Binder.getCallingUid();
+ final int callingUser = UserHandle.getUserId(callingUid);
if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) {
throw new IllegalArgumentException("Cannot delete default channel");
}
+ enforceDeletingChannelHasNoFgService(pkg, callingUser, channelId);
cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channelId, 0, 0, true,
- UserHandle.getUserId(callingUid), REASON_CHANNEL_BANNED, null);
+ callingUser, REASON_CHANNEL_BANNED, null);
mRankingHelper.deleteNotificationChannel(pkg, callingUid, channelId);
mListeners.notifyNotificationChannelChanged(pkg,
UserHandle.getUserHandleForUid(callingUid),
@@ -1896,13 +1917,20 @@ public class NotificationManagerService extends SystemService {
NotificationChannelGroup groupToDelete =
mRankingHelper.getNotificationChannelGroup(groupId, pkg, callingUid);
if (groupToDelete != null) {
+ // Preflight for allowability
+ final int userId = UserHandle.getUserId(callingUid);
+ List<NotificationChannel> groupChannels = groupToDelete.getChannels();
+ for (int i = 0; i < groupChannels.size(); i++) {
+ enforceDeletingChannelHasNoFgService(pkg, userId,
+ groupChannels.get(i).getId());
+ }
List<NotificationChannel> deletedChannels =
mRankingHelper.deleteNotificationChannelGroup(pkg, callingUid, groupId);
for (int i = 0; i < deletedChannels.size(); i++) {
final NotificationChannel deletedChannel = deletedChannels.get(i);
cancelAllNotificationsInt(MY_UID, MY_PID, pkg, deletedChannel.getId(), 0, 0,
true,
- UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED,
+ userId, REASON_CHANNEL_BANNED,
null);
mListeners.notifyNotificationChannelChanged(pkg,
UserHandle.getUserHandleForUid(callingUid),
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 73207f6ec11b..022d077a9f97 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -6515,6 +6515,11 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public List<String> getAllPackages() {
final int callingUid = Binder.getCallingUid();
+ // enforceSystemOrRootOrShell:
+ if (callingUid != Process.SYSTEM_UID && callingUid != Process.ROOT_UID
+ && callingUid != Process.SHELL_UID) {
+ throw new SecurityException("getAllPackages is limited to privileged callers");
+ }
final int callingUserId = UserHandle.getUserId(callingUid);
synchronized (mPackages) {
if (canViewInstantApps(callingUid, callingUserId)) {
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
index 51de2c21e3a8..1e788c9ab7df 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -45,6 +45,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
import android.app.INotificationManager;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -124,6 +125,8 @@ public class NotificationManagerServiceTest extends NotificationTestCase {
private AudioManager mAudioManager;
@Mock
ActivityManager mActivityManager;
+ @Mock
+ ActivityManagerInternal mAmi;
NotificationManagerService.WorkerHandler mHandler;
private NotificationChannel mTestNotificationChannel = new NotificationChannel(
@@ -213,7 +216,7 @@ public class NotificationManagerServiceTest extends NotificationTestCase {
mPackageManager, mPackageManagerClient, mockLightsManager,
mListeners, mAssistants, mConditionProviders,
mCompanionMgr, mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager,
- mGroupHelper);
+ mGroupHelper, mAmi);
} catch (SecurityException e) {
if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
throw e;