diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-08-12 05:36:32 +0000 |
---|---|---|
committer | Justin Dunlap <justindunlap@google.com> | 2022-08-22 15:43:39 -0700 |
commit | 653a0235f96c2fefe63914cd794b06e9b9e41e84 (patch) | |
tree | b5aeb1655d590fd6d29d98db81d49be5d95a303e | |
parent | 73611c826999a8a3cdf9229efa030bfb0215a48a (diff) | |
parent | e412bbeb8e16a07f7b00922fd2a6a6fb59861ecc (diff) | |
download | base-android-security-10.0.0_r71.tar.gz |
Merge cherrypicks of [18489968, 19205944, 19105069, 19346142, 17595878, 19537069, 19197499, 19202448, 19117579] into security-aosp-qt-release.android-security-10.0.0_r71
Change-Id: I2750f951fbdb74cfcd74eeee3f4b5a2f207ae822
19 files changed, 438 insertions, 68 deletions
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java index 7180c01143a5..a6cd6d511caf 100644 --- a/core/java/android/app/AutomaticZenRule.java +++ b/core/java/android/app/AutomaticZenRule.java @@ -45,6 +45,7 @@ public final class AutomaticZenRule implements Parcelable { private long creationTime; private ZenPolicy mZenPolicy; private boolean mModified = false; + private String mPkg; /** * Creates an automatic zen rule. @@ -123,6 +124,7 @@ public final class AutomaticZenRule implements Parcelable { creationTime = source.readLong(); mZenPolicy = source.readParcelable(null); mModified = source.readInt() == ENABLED; + mPkg = source.readString(); } /** @@ -244,6 +246,20 @@ public final class AutomaticZenRule implements Parcelable { this.configurationActivity = componentName; } + /** + * @hide + */ + public void setPackageName(String pkgName) { + mPkg = pkgName; + } + + /** + * @hide + */ + public String getPackageName() { + return mPkg; + } + @Override public int describeContents() { return 0; @@ -265,6 +281,7 @@ public final class AutomaticZenRule implements Parcelable { dest.writeLong(creationTime); dest.writeParcelable(mZenPolicy, 0); dest.writeInt(mModified ? ENABLED : DISABLED); + dest.writeString(mPkg); } @Override @@ -273,6 +290,7 @@ public final class AutomaticZenRule implements Parcelable { .append("enabled=").append(enabled) .append(",name=").append(name) .append(",interruptionFilter=").append(interruptionFilter) + .append(",pkg=").append(mPkg) .append(",conditionId=").append(conditionId) .append(",owner=").append(owner) .append(",configActivity=").append(configurationActivity) @@ -294,13 +312,14 @@ public final class AutomaticZenRule implements Parcelable { && Objects.equals(other.owner, owner) && Objects.equals(other.mZenPolicy, mZenPolicy) && Objects.equals(other.configurationActivity, configurationActivity) + && Objects.equals(other.mPkg, mPkg) && other.creationTime == creationTime; } @Override public int hashCode() { return Objects.hash(enabled, name, interruptionFilter, conditionId, owner, - configurationActivity, mZenPolicy, mModified, creationTime); + configurationActivity, mZenPolicy, mModified, creationTime, mPkg); } public static final @android.annotation.NonNull Parcelable.Creator<AutomaticZenRule> CREATOR diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index e57738fbbfdd..3661876f8f5a 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -182,7 +182,7 @@ interface INotificationManager void setNotificationPolicyAccessGrantedForUser(String pkg, int userId, boolean granted); AutomaticZenRule getAutomaticZenRule(String id); List<ZenModeConfig.ZenRule> getZenRules(); - String addAutomaticZenRule(in AutomaticZenRule automaticZenRule); + String addAutomaticZenRule(in AutomaticZenRule automaticZenRule, String pkg); boolean updateAutomaticZenRule(String id, in AutomaticZenRule automaticZenRule); boolean removeAutomaticZenRule(String id); boolean removeAutomaticZenRules(String packageName); diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index dd39376f80ca..c84490a83d3f 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -923,10 +923,12 @@ public class NotificationManager { List<ZenModeConfig.ZenRule> rules = service.getZenRules(); Map<String, AutomaticZenRule> ruleMap = new HashMap<>(); for (ZenModeConfig.ZenRule rule : rules) { - ruleMap.put(rule.id, new AutomaticZenRule(rule.name, rule.component, + AutomaticZenRule azr = new AutomaticZenRule(rule.name, rule.component, rule.configurationActivity, rule.conditionId, rule.zenPolicy, zenModeToInterruptionFilter(rule.zenMode), rule.enabled, - rule.creationTime)); + rule.creationTime); + azr.setPackageName(rule.pkg); + ruleMap.put(rule.id, azr); } return ruleMap; } catch (RemoteException e) { @@ -967,7 +969,7 @@ public class NotificationManager { public String addAutomaticZenRule(AutomaticZenRule automaticZenRule) { INotificationManager service = getService(); try { - return service.addAutomaticZenRule(automaticZenRule); + return service.addAutomaticZenRule(automaticZenRule, mContext.getPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index cb7d41bdf21a..307aaa1a3a89 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -43,6 +43,7 @@ import android.util.Slog; import android.util.proto.ProtoOutputStream; import com.android.internal.R; +import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -151,6 +152,7 @@ public class ZenModeConfig implements Parcelable { private static final String RULE_ATT_ENABLED = "enabled"; private static final String RULE_ATT_SNOOZING = "snoozing"; private static final String RULE_ATT_NAME = "name"; + private static final String RULE_ATT_PKG = "pkg"; private static final String RULE_ATT_COMPONENT = "component"; private static final String RULE_ATT_CONFIG_ACTIVITY = "configActivity"; private static final String RULE_ATT_ZEN = "zen"; @@ -644,11 +646,11 @@ public class ZenModeConfig implements Parcelable { rt.conditionId = safeUri(parser, RULE_ATT_CONDITION_ID); rt.component = safeComponentName(parser, RULE_ATT_COMPONENT); rt.configurationActivity = safeComponentName(parser, RULE_ATT_CONFIG_ACTIVITY); - rt.pkg = (rt.component != null) - ? rt.component.getPackageName() - : (rt.configurationActivity != null) - ? rt.configurationActivity.getPackageName() - : null; + rt.pkg = XmlUtils.readStringAttribute(parser, RULE_ATT_PKG); + if (rt.pkg == null) { + // backfill from component, if present. configActivity is not safe to backfill from + rt.pkg = rt.component != null ? rt.component.getPackageName() : null; + } rt.creationTime = safeLong(parser, RULE_ATT_CREATION_TIME, 0); rt.enabler = parser.getAttributeValue(null, RULE_ATT_ENABLER); rt.condition = readConditionXml(parser); @@ -670,6 +672,9 @@ public class ZenModeConfig implements Parcelable { out.attribute(null, RULE_ATT_NAME, rule.name); } out.attribute(null, RULE_ATT_ZEN, Integer.toString(rule.zenMode)); + if (rule.pkg != null) { + out.attribute(null, RULE_ATT_PKG, rule.pkg); + } if (rule.component != null) { out.attribute(null, RULE_ATT_COMPONENT, rule.component.flattenToString()); } diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index d302c2b08217..3ba4d91add55 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -2841,7 +2841,7 @@ public final class InputMethodManager { @UnsupportedAppUsage public int getInputMethodWindowVisibleHeight() { try { - return mService.getInputMethodWindowVisibleHeight(); + return mService.getInputMethodWindowVisibleHeight(mClient); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index c29e823218eb..19731b27374a 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -67,7 +67,7 @@ interface IInputMethodManager { void setAdditionalInputMethodSubtypes(String id, in InputMethodSubtype[] subtypes); // This is kept due to @UnsupportedAppUsage. // TODO(Bug 113914148): Consider removing this. - int getInputMethodWindowVisibleHeight(); + int getInputMethodWindowVisibleHeight(in IInputMethodClient client); void reportActivityView(in IInputMethodClient parentClient, int childDisplayId, in float[] matrixValues); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java index 926d4b6a79b3..19ffa4f09c70 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java @@ -39,6 +39,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dependency; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationPresenter; +import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.policy.HeadsUpManager; @@ -179,6 +180,89 @@ public class NotificationInterruptionStateProvider { return true; } + public boolean shouldLaunchFullScreenIntentWhenAdded(NotificationEntry entry) { + if (entry.notification.getNotification().fullScreenIntent == null) { + return false; + } + + // Never show FSI when suppressed by DND + if (entry.shouldSuppressFullScreenIntent()) { + if (DEBUG) { + Log.d(TAG, "No FullScreenIntent: Suppressed by DND: " + entry.key); + } + return false; + } + + // Never show FSI if importance is not HIGH + if (entry.importance < NotificationManager.IMPORTANCE_HIGH) { + if (DEBUG) { + Log.d(TAG, "No FullScreenIntent: Not important enough: " + entry.key); + } + return false; + } + + // If the notification has suppressive GroupAlertBehavior, block FSI and warn. + StatusBarNotification sbn = entry.notification; + if (sbn.isGroup() && sbn.getNotification().suppressAlertingDueToGrouping()) { + // b/231322873: Detect and report an event when a notification has both an FSI and a + // suppressive groupAlertBehavior, and now correctly block the FSI from firing. + final int uid = entry.notification.getUid(); + android.util.EventLog.writeEvent(0x534e4554, "231322873", uid, "groupAlertBehavior"); + if (DEBUG) { + Log.w(TAG, "No FullScreenIntent: WARNING: GroupAlertBehavior will prevent HUN: " + + entry.key); + } + return false; + } + + // If the screen is off, then launch the FullScreenIntent + if (!mPowerManager.isInteractive()) { + if (DEBUG) { + Log.d(TAG, "FullScreenIntent: Device is not interactive: " + entry.key); + } + return true; + } + + // If the device is currently dreaming, then launch the FullScreenIntent + if (isDreaming()) { + if (DEBUG) { + Log.d(TAG, "FullScreenIntent: Device is dreaming: " + entry.key); + } + return true; + } + + // If the keyguard is showing, then launch the FullScreenIntent + if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) { + if (DEBUG) { + Log.d(TAG, "FullScreenIntent: Keyguard is showing: " + entry.key); + } + return true; + } + + // If the notification should HUN, then we don't need FSI + if (shouldHeadsUp(entry)) { + if (DEBUG) { + Log.d(TAG, "No FullScreenIntent: Expected to HUN: " + entry.key); + } + return false; + } + + // If the notification won't HUN for some other reason (DND/snooze/etc), launch FSI. + if (DEBUG) { + Log.d(TAG, "FullScreenIntent: Expected not to HUN: " + entry.key); + } + return true; + } + + private boolean isDreaming() { + try { + return mDreamManager.isDreaming(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to query dream manager.", e); + return false; + } + } + /** * Whether the notification should peek in from the top and alert the user. * @@ -222,13 +306,7 @@ public class NotificationInterruptionStateProvider { return false; } - boolean isDreaming = false; - try { - isDreaming = mDreamManager.isDreaming(); - } catch (RemoteException e) { - Log.e(TAG, "Failed to query dream manager.", e); - } - boolean inUse = mPowerManager.isScreenOn() && !isDreaming; + boolean inUse = mPowerManager.isScreenOn() && !isDreaming(); if (!inUse) { if (DEBUG) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index e00d439dc1c7..bec53a1b2d3c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -406,8 +406,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit } private void handleFullScreenIntent(NotificationEntry entry) { - boolean isHeadsUped = mNotificationInterruptionStateProvider.shouldHeadsUp(entry); - if (!isHeadsUped && entry.notification.getNotification().fullScreenIntent != null) { + if (mNotificationInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) { if (shouldSuppressFullScreenIntent(entry)) { if (DEBUG) { Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + entry.key); diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index d16244167c62..11726e028499 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -75,6 +75,7 @@ import android.text.format.DateFormat; import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.EventLog; import android.util.KeyValueListParser; import android.util.Log; import android.util.LongArrayQueue; @@ -1768,7 +1769,11 @@ class AlarmManagerService extends SystemService { mHandler.obtainMessage(AlarmHandler.UNREGISTER_CANCEL_LISTENER, operation).sendToTarget(); Slog.w(TAG, errorMsg); - throw new IllegalStateException(errorMsg); + if (callingUid != Process.SYSTEM_UID) { + throw new IllegalStateException(errorMsg); + } else { + EventLog.writeEvent(0x534e4554, "234441463", -1, errorMsg); + } } setImplLocked(type, triggerAtTime, triggerElapsed, windowLength, maxElapsed, interval, operation, directReceiver, listenerTag, flags, true, workSource, diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 0b19d762a826..d3ddc805d2ae 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -100,6 +100,7 @@ import android.util.PrintWriterPrinter; import android.util.Printer; import android.util.Slog; import android.util.SparseArray; +import android.util.SparseBooleanArray; import android.view.ContextThemeWrapper; import android.view.DisplayInfo; import android.view.IWindowManager; @@ -303,6 +304,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub final InputMethodSettings mSettings; final SettingsObserver mSettingsObserver; final IWindowManager mIWindowManager; + private final SparseBooleanArray mLoggedDeniedGetInputMethodWindowVisibleHeightForUid = + new SparseBooleanArray(0); final WindowManagerInternal mWindowManagerInternal; private final DisplayManagerInternal mDisplayManagerInternal; final HandlerCaller mCaller; @@ -1219,6 +1222,13 @@ public class InputMethodManagerService extends IInputMethodManager.Stub clearPackageChangeState(); } + @Override + public void onUidRemoved(int uid) { + synchronized (mMethodMap) { + mLoggedDeniedGetInputMethodWindowVisibleHeightForUid.delete(uid); + } + } + private void clearPackageChangeState() { // No need to lock them because we access these fields only on getRegisteredHandler(). mChangedPackages.clear(); @@ -2756,20 +2766,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } final long ident = Binder.clearCallingIdentity(); try { - if (mCurClient == null || client == null - || mCurClient.client.asBinder() != client.asBinder()) { - // We need to check if this is the current client with - // focus in the window manager, to allow this call to - // be made before input is started in it. - final ClientState cs = mClients.get(client.asBinder()); - if (cs == null) { - throw new IllegalArgumentException("unknown client " + client.asBinder()); - } - if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid, - cs.selfReportedDisplayId)) { - Slog.w(TAG, "Ignoring showSoftInput of uid " + uid + ": " + client); - return false; - } + if (!canInteractWithImeLocked(uid, client, "showSoftInput")) { + return false; } if (DEBUG) Slog.v(TAG, "Client requesting input be shown"); return showCurrentInputLocked(flags, resultReceiver); @@ -3464,9 +3462,46 @@ public class InputMethodManagerService extends IInputMethodManager.Stub * @return {@link WindowManagerInternal#getInputMethodWindowVisibleHeight()} */ @Override - public int getInputMethodWindowVisibleHeight() { - // TODO(yukawa): Should we verify the display ID? - return mWindowManagerInternal.getInputMethodWindowVisibleHeight(mCurTokenDisplayId); + @Deprecated + public int getInputMethodWindowVisibleHeight(@NonNull IInputMethodClient client) { + int callingUid = Binder.getCallingUid(); + return Binder.withCleanCallingIdentity(() -> { + final int curTokenDisplayId; + synchronized (mMethodMap) { + if (!canInteractWithImeLocked(callingUid, client, + "getInputMethodWindowVisibleHeight")) { + if (!mLoggedDeniedGetInputMethodWindowVisibleHeightForUid.get(callingUid)) { + EventLog.writeEvent(0x534e4554, "204906124", callingUid, ""); + mLoggedDeniedGetInputMethodWindowVisibleHeightForUid.put(callingUid, true); + } + return 0; + } + // This should probably use the caller's display id, but because this is unsupported + // and maintained only for compatibility, there's no point in fixing it. + curTokenDisplayId = mCurTokenDisplayId; + } + return mWindowManagerInternal.getInputMethodWindowVisibleHeight(curTokenDisplayId); + }); + } + + private boolean canInteractWithImeLocked(int callingUid, IInputMethodClient client, + String method) { + if (mCurClient == null || client == null + || mCurClient.client.asBinder() != client.asBinder()) { + // We need to check if this is the current client with + // focus in the window manager, to allow this call to + // be made before input is started in it. + final ClientState cs = mClients.get(client.asBinder()); + if (cs == null) { + throw new IllegalArgumentException("unknown client " + client.asBinder()); + } + if (!mWindowManagerInternal.isInputMethodClientFocus(cs.uid, cs.pid, + cs.selfReportedDisplayId)) { + Slog.w(TAG, "Ignoring " + method + " of uid " + callingUid + ": " + client); + return false; + } + } + return true; } @Override diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java index 3dd730471dca..bd4ea08d55d2 100644 --- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java @@ -1694,7 +1694,7 @@ public final class MultiClientInputMethodManagerService { @BinderThread @Override - public int getInputMethodWindowVisibleHeight() { + public int getInputMethodWindowVisibleHeight(IInputMethodClient client) { reportNotSupported(); return 0; } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 86ca7dc87ef6..76ed4c81d897 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -3593,7 +3593,7 @@ public class NotificationManagerService extends SystemService { } @Override - public String addAutomaticZenRule(AutomaticZenRule automaticZenRule) { + public String addAutomaticZenRule(AutomaticZenRule automaticZenRule, String pkg) { Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null"); Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null"); if (automaticZenRule.getOwner() == null @@ -3602,6 +3602,7 @@ public class NotificationManagerService extends SystemService { "Rule must have a conditionproviderservice and/or configuration activity"); } Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null"); + checkCallerIsSameApp(pkg); if (automaticZenRule.getZenPolicy() != null && automaticZenRule.getInterruptionFilter() != INTERRUPTION_FILTER_PRIORITY) { throw new IllegalArgumentException("ZenPolicy is only applicable to " @@ -3609,7 +3610,7 @@ public class NotificationManagerService extends SystemService { } enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule"); - return mZenModeHelper.addAutomaticZenRule(automaticZenRule, + return mZenModeHelper.addAutomaticZenRule(pkg, automaticZenRule, "addAutomaticZenRule"); } @@ -5262,13 +5263,17 @@ public class NotificationManagerService extends SystemService { @GuardedBy("mNotificationLock") void snoozeLocked(NotificationRecord r) { + final List<NotificationRecord> recordsToSnooze = new ArrayList<>(); if (r.sbn.isGroup()) { - final List<NotificationRecord> groupNotifications = findGroupNotificationsLocked( - r.sbn.getPackageName(), r.sbn.getGroupKey(), r.sbn.getUserId()); + final List<NotificationRecord> groupNotifications = + findGroupNotificationsLocked(r.sbn.getPackageName(), + r.sbn.getGroupKey(), r.sbn.getUserId()); if (r.getNotification().isGroupSummary()) { // snooze summary and all children for (int i = 0; i < groupNotifications.size(); i++) { - snoozeNotificationLocked(groupNotifications.get(i)); + if (!mKey.equals(groupNotifications.get(i).getKey())) { + recordsToSnooze.add(groupNotifications.get(i)); + } } } else { // if there is a valid summary for this group, and we are snoozing the only @@ -5279,7 +5284,9 @@ public class NotificationManagerService extends SystemService { } else { // snooze summary and the one child for (int i = 0; i < groupNotifications.size(); i++) { - snoozeNotificationLocked(groupNotifications.get(i)); + if (!mKey.equals(groupNotifications.get(i).getKey())) { + recordsToSnooze.add(groupNotifications.get(i)); + } } } } else { @@ -5290,6 +5297,17 @@ public class NotificationManagerService extends SystemService { // just snooze the one notification snoozeNotificationLocked(r); } + + // snooze the notification + recordsToSnooze.add(r); + + if (mSnoozeHelper.canSnooze(recordsToSnooze.size())) { + for (int i = 0; i < recordsToSnooze.size(); i++) { + snoozeNotificationLocked(recordsToSnooze.get(i)); + } + } else { + Log.w(TAG, "Cannot snooze " + r.getKey() + ": too many snoozed notifications"); + } } @GuardedBy("mNotificationLock") diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java index 037cc6066518..ba1f1e91ec45 100644 --- a/services/core/java/com/android/server/notification/SnoozeHelper.java +++ b/services/core/java/com/android/server/notification/SnoozeHelper.java @@ -57,6 +57,8 @@ import java.util.Set; * NotificationManagerService helper for handling snoozed notifications. */ public class SnoozeHelper { + static final int CONCURRENT_SNOOZE_LIMIT = 500; + private static final String TAG = "SnoozeHelper"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final String INDENT = " "; @@ -91,6 +93,13 @@ public class SnoozeHelper { mUserProfiles = userProfiles; } + protected boolean canSnooze(int numberToSnooze) { + if ((mPackages.size() + numberToSnooze) > CONCURRENT_SNOOZE_LIMIT) { + return false; + } + return true; + } + protected boolean isSnoozed(int userId, String pkg, String key) { return mSnoozedNotifications.containsKey(userId) && mSnoozedNotifications.get(userId).containsKey(pkg) diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 4e6074e854dc..40515d529bbd 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -289,7 +289,8 @@ public class ZenModeHelper { return null; } - public String addAutomaticZenRule(AutomaticZenRule automaticZenRule, String reason) { + public String addAutomaticZenRule(String pkg, AutomaticZenRule automaticZenRule, + String reason) { if (!isSystemRule(automaticZenRule)) { PackageItemInfo component = getServiceInfo(automaticZenRule.getOwner()); if (component == null) { @@ -306,7 +307,8 @@ public class ZenModeHelper { int newRuleInstanceCount = getCurrentInstanceCount(automaticZenRule.getOwner()) + getCurrentInstanceCount(automaticZenRule.getConfigurationActivity()) + 1; - if (newRuleInstanceCount > RULE_LIMIT_PER_PACKAGE + int newPackageRuleCount = getPackageRuleCount(pkg) + 1; + if (newPackageRuleCount > RULE_LIMIT_PER_PACKAGE || (ruleInstanceLimit > 0 && ruleInstanceLimit < newRuleInstanceCount)) { throw new IllegalArgumentException("Rule instance limit exceeded"); } @@ -322,7 +324,7 @@ public class ZenModeHelper { } newConfig = mConfig.copy(); ZenRule rule = new ZenRule(); - populateZenRule(automaticZenRule, rule, true); + populateZenRule(pkg, automaticZenRule, rule, true); newConfig.automaticRules.put(rule.id, rule); if (setConfigLocked(newConfig, reason, rule.component, true)) { return rule.id; @@ -352,7 +354,7 @@ public class ZenModeHelper { "Cannot update rules not owned by your condition provider"); } } - populateZenRule(automaticZenRule, rule, false); + populateZenRule(rule.pkg, automaticZenRule, rule, false); return setConfigLocked(newConfig, reason, rule.component, true); } } @@ -382,7 +384,14 @@ public class ZenModeHelper { newConfig = mConfig.copy(); for (int i = newConfig.automaticRules.size() - 1; i >= 0; i--) { ZenRule rule = newConfig.automaticRules.get(newConfig.automaticRules.keyAt(i)); - if (rule.pkg.equals(packageName) && canManageAutomaticZenRule(rule)) { + String pkg = rule.pkg != null + ? rule.pkg + : (rule.component != null) + ? rule.component.getPackageName() + : (rule.configurationActivity != null) + ? rule.configurationActivity.getPackageName() + : null; + if (Objects.equals(pkg, packageName) && canManageAutomaticZenRule(rule)) { newConfig.automaticRules.removeAt(i); } } @@ -465,6 +474,23 @@ public class ZenModeHelper { return count; } + // Equivalent method to getCurrentInstanceCount, but for all rules associated with a specific + // package rather than a condition provider service or activity. + private int getPackageRuleCount(String pkg) { + if (pkg == null) { + return 0; + } + int count = 0; + synchronized (mConfig) { + for (ZenRule rule : mConfig.automaticRules.values()) { + if (pkg.equals(rule.pkg)) { + count++; + } + } + } + return count; + } + public boolean canManageAutomaticZenRule(ZenRule rule) { final int callingUid = Binder.getCallingUid(); if (callingUid == 0 || callingUid == Process.SYSTEM_UID) { @@ -546,15 +572,14 @@ public class ZenModeHelper { return null; } - private void populateZenRule(AutomaticZenRule automaticZenRule, ZenRule rule, boolean isNew) { + private void populateZenRule(String pkg, AutomaticZenRule automaticZenRule, ZenRule rule, + boolean isNew) { if (isNew) { rule.id = ZenModeConfig.newRuleId(); rule.creationTime = System.currentTimeMillis(); rule.component = automaticZenRule.getOwner(); rule.configurationActivity = automaticZenRule.getConfigurationActivity(); - rule.pkg = (rule.component != null) - ? rule.component.getPackageName() - : rule.configurationActivity.getPackageName(); + rule.pkg = pkg; } if (rule.enabled != automaticZenRule.isEnabled()) { @@ -571,10 +596,13 @@ public class ZenModeHelper { } protected AutomaticZenRule createAutomaticZenRule(ZenRule rule) { - return new AutomaticZenRule(rule.name, rule.component, rule.configurationActivity, + AutomaticZenRule azr = new AutomaticZenRule(rule.name, rule.component, + rule.configurationActivity, rule.conditionId, rule.zenPolicy, NotificationManager.zenModeToInterruptionFilter(rule.zenMode), rule.enabled, rule.creationTime); + azr.setPackageName(rule.pkg); + return azr; } public void setManualZenMode(int zenMode, Uri conditionId, String caller, String reason) { diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 8a5f52f8c453..b9ff000a2129 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -25,6 +25,7 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SUSTAINED_PER import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_DREAM; import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG; +import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT; @@ -317,13 +318,17 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } /** - * Returns true if the callingUid has any non-toast window currently visible to the user. - * Also ignores TYPE_APPLICATION_STARTING, since those windows don't belong to apps. + * Returns {@code true} if the callingUid has any non-toast window currently visible to the + * user. Also ignores {@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_STARTING}, + * and{@link android.view.WindowManager.LayoutParams#TYPE_PRIVATE_PRESENTATION}, as they + * should not count towards the apps visibility + * @see WindowState#isNonToastOrStartingOrPrivatePresentation() */ boolean isAnyNonToastWindowVisibleForUid(int callingUid) { return forAllWindows(w -> w.getOwningUid() == callingUid && w.mAttrs.type != TYPE_TOAST - && w.mAttrs.type != TYPE_APPLICATION_STARTING && w.isVisibleNow(), + && w.mAttrs.type != TYPE_APPLICATION_STARTING + && w.mAttrs.type != TYPE_PRIVATE_PRESENTATION && w.isVisibleNow(), true /* traverseTopToBottom */); } 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 30f5fae52993..fe5082d0adc0 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -2101,6 +2101,69 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void testSnoozeRunnable_tooManySnoozed_singleNotification() { + final NotificationRecord notification = generateNotificationRecord( + mTestNotificationChannel, 1, null, true); + mService.addNotification(notification); + + when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true); + when(mSnoozeHelper.canSnooze(1)).thenReturn(false); + + NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = + mService.new SnoozeNotificationRunnable( + notification.getKey(), 100, null); + snoozeNotificationRunnable.run(); + + verify(mSnoozeHelper, never()).snooze(any(NotificationRecord.class), anyLong()); + assertEquals(1, mService.getNotificationRecordCount()); + } + + @Test + public void testSnoozeRunnable_tooManySnoozed_singleGroupChildNotification() { + final NotificationRecord notification = generateNotificationRecord( + mTestNotificationChannel, 1, "group", true); + final NotificationRecord notificationChild = generateNotificationRecord( + mTestNotificationChannel, 1, "group", false); + mService.addNotification(notification); + mService.addNotification(notificationChild); + + when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true); + when(mSnoozeHelper.canSnooze(2)).thenReturn(false); + + NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = + mService.new SnoozeNotificationRunnable( + notificationChild.getKey(), 100, null); + snoozeNotificationRunnable.run(); + + verify(mSnoozeHelper, never()).snooze(any(NotificationRecord.class), anyLong()); + assertEquals(2, mService.getNotificationRecordCount()); + } + + @Test + public void testSnoozeRunnable_tooManySnoozed_summaryNotification() { + final NotificationRecord notification = generateNotificationRecord( + mTestNotificationChannel, 1, "group", true); + final NotificationRecord notificationChild = generateNotificationRecord( + mTestNotificationChannel, 12, "group", false); + final NotificationRecord notificationChild2 = generateNotificationRecord( + mTestNotificationChannel, 13, "group", false); + mService.addNotification(notification); + mService.addNotification(notificationChild); + mService.addNotification(notificationChild2); + + when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true); + when(mSnoozeHelper.canSnooze(3)).thenReturn(false); + + NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = + mService.new SnoozeNotificationRunnable( + notification.getKey(), 100, null); + snoozeNotificationRunnable.run(); + + verify(mSnoozeHelper, never()).snooze(any(NotificationRecord.class), anyLong()); + assertEquals(3, mService.getNotificationRecordCount()); + } + + @Test public void testSnoozeRunnable_snoozeNonGrouped() throws Exception { final NotificationRecord nonGrouped = generateNotificationRecord( mTestNotificationChannel, 1, null, false); @@ -2108,6 +2171,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mTestNotificationChannel, 2, "group", false); mService.addNotification(grouped); mService.addNotification(nonGrouped); + when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true); NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = mService.new SnoozeNotificationRunnable( @@ -2130,6 +2194,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addNotification(parent); mService.addNotification(child); mService.addNotification(child2); + when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true); NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = mService.new SnoozeNotificationRunnable( @@ -2151,6 +2216,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addNotification(parent); mService.addNotification(child); mService.addNotification(child2); + when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true); NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = mService.new SnoozeNotificationRunnable( @@ -2170,6 +2236,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mTestNotificationChannel, 2, "group", false); mService.addNotification(parent); mService.addNotification(child); + when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true); NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = mService.new SnoozeNotificationRunnable( @@ -2185,6 +2252,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { final NotificationRecord child = generateNotificationRecord( mTestNotificationChannel, 2, "group", false); mService.addNotification(child); + when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true); NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = mService.new SnoozeNotificationRunnable( @@ -5037,7 +5105,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { zenPolicy, NotificationManager.INTERRUPTION_FILTER_NONE, isEnabled); try { - mBinderService.addAutomaticZenRule(rule); + mBinderService.addAutomaticZenRule(rule, mContext.getPackageName()); fail("Zen policy only applies to priority only mode"); } catch (IllegalArgumentException e) { // yay @@ -5045,11 +5113,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class), zenPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, isEnabled); - mBinderService.addAutomaticZenRule(rule); + mBinderService.addAutomaticZenRule(rule, mContext.getPackageName()); rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class), null, NotificationManager.INTERRUPTION_FILTER_NONE, isEnabled); - mBinderService.addAutomaticZenRule(rule); + mBinderService.addAutomaticZenRule(rule, mContext.getPackageName()); } @Test diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java index 2141ecc95bb8..3c389bb35535 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java @@ -15,6 +15,8 @@ */ package com.android.server.notification; +import static com.android.server.notification.SnoozeHelper.CONCURRENT_SNOOZE_LIMIT; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; @@ -104,6 +106,22 @@ public class SnoozeHelperTest extends UiServiceTestCase { } @Test + public void testSnoozeLimit() { + for (int i = 0; i < CONCURRENT_SNOOZE_LIMIT; i++ ) { + NotificationRecord r = getNotificationRecord("pkg", i, i+"", UserHandle.SYSTEM); + + assertTrue("cannot snooze record " + i, mSnoozeHelper.canSnooze(1)); + + if (i % 2 == 0) { + mSnoozeHelper.snooze(r, 1000); + } else { + mSnoozeHelper.snooze(r, 9000); + } + } + assertFalse(mSnoozeHelper.canSnooze(1)); + } + + @Test public void testCancelByApp() throws Exception { NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM); NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.SYSTEM); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java index dcab78ede287..6401987448e5 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java @@ -158,7 +158,7 @@ public class ZenModeConfigTest extends UiServiceTestCase { ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule(); rule.configurationActivity = new ComponentName("a", "a"); - rule.component = new ComponentName("a", "b"); + rule.component = new ComponentName("b", "b"); rule.conditionId = new Uri.Builder().scheme("hello").build(); rule.condition = new Condition(rule.conditionId, "", Condition.STATE_TRUE); rule.enabled = true; @@ -168,6 +168,7 @@ public class ZenModeConfigTest extends UiServiceTestCase { rule.modified = true; rule.name = "name"; rule.snoozing = true; + rule.pkg = "b"; XmlSerializer out = new FastXmlSerializer(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); @@ -183,8 +184,7 @@ public class ZenModeConfigTest extends UiServiceTestCase { new ByteArrayInputStream(baos.toByteArray())), null); parser.nextTag(); ZenModeConfig.ZenRule fromXml = ZenModeConfig.readRuleXml(parser); - // read from backing component - assertEquals("a", fromXml.pkg); + assertEquals("b", fromXml.pkg); // always resets on reboot assertFalse(fromXml.snoozing); //should all match original @@ -200,6 +200,55 @@ public class ZenModeConfigTest extends UiServiceTestCase { assertEquals(rule.zenMode, fromXml.zenMode); } + @Test + public void testRuleXml_pkg_component() throws Exception { + String tag = "tag"; + + ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule(); + rule.configurationActivity = new ComponentName("a", "a"); + rule.component = new ComponentName("b", "b"); + + XmlSerializer out = new FastXmlSerializer(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + out.setOutput(new BufferedOutputStream(baos), "utf-8"); + out.startDocument(null, true); + out.startTag(null, tag); + ZenModeConfig.writeRuleXml(rule, out); + out.endTag(null, tag); + out.endDocument(); + + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(new BufferedInputStream( + new ByteArrayInputStream(baos.toByteArray())), null); + parser.nextTag(); + ZenModeConfig.ZenRule fromXml = ZenModeConfig.readRuleXml(parser); + assertEquals("b", fromXml.pkg); + } + + @Test + public void testRuleXml_pkg_configActivity() throws Exception { + String tag = "tag"; + + ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule(); + rule.configurationActivity = new ComponentName("a", "a"); + + XmlSerializer out = new FastXmlSerializer(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + out.setOutput(new BufferedOutputStream(baos), "utf-8"); + out.startDocument(null, true); + out.startTag(null, tag); + ZenModeConfig.writeRuleXml(rule, out); + out.endTag(null, tag); + out.endDocument(); + + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(new BufferedInputStream( + new ByteArrayInputStream(baos.toByteArray())), null); + parser.nextTag(); + ZenModeConfig.ZenRule fromXml = ZenModeConfig.readRuleXml(parser); + assertNull(fromXml.pkg); + } + private ZenModeConfig getMutedNotificationsConfig() { ZenModeConfig config = new ZenModeConfig(); // Allow alarms, media, and system diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java index cac6f0994bcd..7c30aef30e11 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java @@ -1353,7 +1353,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { new ComponentName("android", "ScheduleConditionProvider"), ZenModeConfig.toScheduleConditionId(new ScheduleInfo()), NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); - String id = mZenModeHelperSpy.addAutomaticZenRule(zenRule, "test"); + String id = mZenModeHelperSpy.addAutomaticZenRule("android", zenRule, "test"); assertTrue(id != null); ZenModeConfig.ZenRule ruleInConfig = mZenModeHelperSpy.mConfig.automaticRules.get(id); @@ -1377,7 +1377,9 @@ public class ZenModeHelperTest extends UiServiceTestCase { ZenModeConfig.toScheduleConditionId(si), new ZenPolicy.Builder().build(), NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); - String id = mZenModeHelperSpy.addAutomaticZenRule(zenRule, "test"); + // We need the package name to be something that's not "android" so there aren't any + // existing rules under that package. + String id = mZenModeHelperSpy.addAutomaticZenRule("pkgname", zenRule, "test"); assertNotNull(id); } try { @@ -1387,7 +1389,37 @@ public class ZenModeHelperTest extends UiServiceTestCase { ZenModeConfig.toScheduleConditionId(new ScheduleInfo()), new ZenPolicy.Builder().build(), NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); - String id = mZenModeHelperSpy.addAutomaticZenRule(zenRule, "test"); + String id = mZenModeHelperSpy.addAutomaticZenRule("android", zenRule, "test"); + fail("allowed too many rules to be created"); + } catch (IllegalArgumentException e) { + // yay + } + } + + @Test + public void testAddAutomaticZenRule_beyondSystemLimit_differentComponents() { + // Make sure the system limit is enforced per-package even with different component provider + // names. + for (int i = 0; i < RULE_LIMIT_PER_PACKAGE; i++) { + ScheduleInfo si = new ScheduleInfo(); + si.startHour = i; + AutomaticZenRule zenRule = new AutomaticZenRule("name" + i, + null, + new ComponentName("android", "ScheduleConditionProvider" + i), + ZenModeConfig.toScheduleConditionId(si), + new ZenPolicy.Builder().build(), + NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); + String id = mZenModeHelperSpy.addAutomaticZenRule("pkgname", zenRule, "test"); + assertNotNull(id); + } + try { + AutomaticZenRule zenRule = new AutomaticZenRule("name", + null, + new ComponentName("android", "ScheduleConditionProviderFinal"), + ZenModeConfig.toScheduleConditionId(new ScheduleInfo()), + new ZenPolicy.Builder().build(), + NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); + String id = mZenModeHelperSpy.addAutomaticZenRule("pkgname", zenRule, "test"); fail("allowed too many rules to be created"); } catch (IllegalArgumentException e) { // yay |