diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-06-11 16:37:14 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-06-11 16:37:14 +0000 |
commit | 5e464b76228de791b3ad3b0b8cd2d44f13169089 (patch) | |
tree | c5218bf3740dfeb95fbeba192260691f8cc70ff6 | |
parent | 8be6a2dc5e4aaf7208b3b3afc8d1ccf8c7956ade (diff) | |
parent | 25e9539fad62287b05fba53bbb90c1050ef6f840 (diff) | |
download | base-android-security-10.0.0_r69.tar.gz |
Merge cherrypicks of [16564590, 17823648, 18670434, 18715266, 18701369] into security-aosp-qt-release.android-security-10.0.0_r69
Change-Id: Iddd72295c0f836acaf477f3ac187cc0c576362fd
9 files changed, 387 insertions, 35 deletions
diff --git a/core/java/android/content/AbstractThreadedSyncAdapter.java b/core/java/android/content/AbstractThreadedSyncAdapter.java index a086a308d0d9..da4ecdd8c1f2 100644 --- a/core/java/android/content/AbstractThreadedSyncAdapter.java +++ b/core/java/android/content/AbstractThreadedSyncAdapter.java @@ -21,6 +21,7 @@ import static com.android.internal.util.function.pooled.PooledLambda.obtainMessa import android.accounts.Account; import android.annotation.MainThread; import android.annotation.NonNull; +import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.Handler; @@ -171,8 +172,20 @@ public abstract class AbstractThreadedSyncAdapter { } private class ISyncAdapterImpl extends ISyncAdapter.Stub { + private boolean isCallerSystem() { + final long callingUid = Binder.getCallingUid(); + if (callingUid != Process.SYSTEM_UID) { + android.util.EventLog.writeEvent(0x534e4554, "203229608", -1, ""); + return false; + } + return true; + } + @Override public void onUnsyncableAccount(ISyncAdapterUnsyncableAccountCallback cb) { + if (!isCallerSystem()) { + return; + } Handler.getMain().sendMessage(obtainMessage( AbstractThreadedSyncAdapter::handleOnUnsyncableAccount, AbstractThreadedSyncAdapter.this, cb)); @@ -181,12 +194,16 @@ public abstract class AbstractThreadedSyncAdapter { @Override public void startSync(ISyncContext syncContext, String authority, Account account, Bundle extras) { + if (!isCallerSystem()) { + return; + } if (ENABLE_LOG) { if (extras != null) { extras.size(); // Unparcel so its toString() will show the contents. } Log.d(TAG, "startSync() start " + authority + " " + account + " " + extras); } + try { final SyncContext syncContextClient = new SyncContext(syncContext); @@ -242,6 +259,9 @@ public abstract class AbstractThreadedSyncAdapter { @Override public void cancelSync(ISyncContext syncContext) { + if (!isCallerSystem()) { + return; + } try { // synchronize to make sure that mSyncThreads doesn't change between when we // check it and when we use it diff --git a/core/java/com/android/internal/notification/NotificationAccessConfirmationActivityContract.java b/core/java/com/android/internal/notification/NotificationAccessConfirmationActivityContract.java index 4ce6f609ef73..fdf0e9046eef 100644 --- a/core/java/com/android/internal/notification/NotificationAccessConfirmationActivityContract.java +++ b/core/java/com/android/internal/notification/NotificationAccessConfirmationActivityContract.java @@ -17,6 +17,7 @@ package com.android.internal.notification; import android.content.ComponentName; +import android.content.Context; import android.content.Intent; public final class NotificationAccessConfirmationActivityContract { @@ -25,13 +26,14 @@ public final class NotificationAccessConfirmationActivityContract { "com.android.settings.notification.NotificationAccessConfirmationActivity"); public static final String EXTRA_USER_ID = "user_id"; public static final String EXTRA_COMPONENT_NAME = "component_name"; - public static final String EXTRA_PACKAGE_TITLE = "package_title"; - public static Intent launcherIntent(int userId, ComponentName component, String packageTitle) { + /** + * Creates a launcher intent for NotificationAccessConfirmationActivity. + */ + public static Intent launcherIntent(Context context, int userId, ComponentName component) { return new Intent() .setComponent(COMPONENT_NAME) .putExtra(EXTRA_USER_ID, userId) - .putExtra(EXTRA_COMPONENT_NAME, component) - .putExtra(EXTRA_PACKAGE_TITLE, packageTitle); + .putExtra(EXTRA_COMPONENT_NAME, component); } } diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index d7d457395798..c11a9b058562 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -39,7 +39,6 @@ import android.content.Intent; import android.content.ServiceConnection; import android.content.pm.FeatureInfo; import android.content.pm.PackageInfo; -import android.content.pm.PackageItemInfo; import android.content.pm.PackageManager; import android.net.NetworkPolicyManager; import android.os.Binder; @@ -310,20 +309,12 @@ public class CompanionDeviceManagerService extends SystemService implements Bind String callingPackage = component.getPackageName(); checkCanCallNotificationApi(callingPackage); int userId = getCallingUserId(); - String packageTitle = BidiFormatter.getInstance().unicodeWrap( - getPackageInfo(callingPackage, userId) - .applicationInfo - .loadSafeLabel(getContext().getPackageManager(), - PackageItemInfo.DEFAULT_MAX_LABEL_SIZE_PX, - PackageItemInfo.SAFE_LABEL_FLAG_TRIM - | PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE) - .toString()); - long identity = Binder.clearCallingIdentity(); + final long identity = Binder.clearCallingIdentity(); try { return PendingIntent.getActivity(getContext(), 0 /* request code */, NotificationAccessConfirmationActivityContract.launcherIntent( - userId, component, packageTitle), + getContext(), userId, component), PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_CANCEL_CURRENT); } finally { diff --git a/services/core/java/com/android/server/connectivity/PacManager.java b/services/core/java/com/android/server/connectivity/PacManager.java index f6ce2dc68b99..621c9c71df58 100644 --- a/services/core/java/com/android/server/connectivity/PacManager.java +++ b/services/core/java/com/android/server/connectivity/PacManager.java @@ -37,6 +37,7 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.provider.Settings; import android.util.Log; +import android.webkit.URLUtil; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.TrafficStatsConstants; @@ -215,8 +216,22 @@ public class PacManager { * @throws IOException if the URL is malformed, or the PAC file is too big. */ private static String get(Uri pacUri) throws IOException { - URL url = new URL(pacUri.toString()); - URLConnection urlConnection = url.openConnection(java.net.Proxy.NO_PROXY); + if (!URLUtil.isValidUrl(pacUri.toString())) { + throw new IOException("Malformed URL:" + pacUri); + } + + final URL url = new URL(pacUri.toString()); + URLConnection urlConnection; + try { + urlConnection = url.openConnection(java.net.Proxy.NO_PROXY); + // Catch the possible exceptions and rethrow as IOException to not to crash the system + // for illegal input. + } catch (IllegalArgumentException e) { + throw new IOException("Incorrect proxy type for " + pacUri); + } catch (UnsupportedOperationException e) { + throw new IOException("Unsupported URL connection type for " + pacUri); + } + long contentLength = -1; try { contentLength = Long.parseLong(urlConnection.getHeaderField("Content-Length")); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index f370335e6a79..86ca7dc87ef6 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -203,6 +203,7 @@ import android.util.Log; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; +import android.util.SparseBooleanArray; import android.util.StatsLog; import android.util.Xml; import android.util.proto.ProtoOutputStream; @@ -228,6 +229,7 @@ import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.internal.util.function.TriPredicate; +import com.android.internal.widget.LockPatternUtils; import com.android.server.DeviceIdleController; import com.android.server.EventLogTags; import com.android.server.IoThread; @@ -1455,6 +1457,54 @@ public class NotificationManagerService extends SystemService { return out; } + protected class StrongAuthTracker extends LockPatternUtils.StrongAuthTracker { + + SparseBooleanArray mUserInLockDownMode = new SparseBooleanArray(); + boolean mIsInLockDownMode = false; + + StrongAuthTracker(Context context) { + super(context); + } + + private boolean containsFlag(int haystack, int needle) { + return (haystack & needle) != 0; + } + + public boolean isInLockDownMode() { + return mIsInLockDownMode; + } + + @Override + public synchronized void onStrongAuthRequiredChanged(int userId) { + boolean userInLockDownModeNext = containsFlag(getStrongAuthForUser(userId), + STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN); + mUserInLockDownMode.put(userId, userInLockDownModeNext); + boolean isInLockDownModeNext = mUserInLockDownMode.indexOfValue(true) != -1; + + if (mIsInLockDownMode == isInLockDownModeNext) { + return; + } + + if (isInLockDownModeNext) { + cancelNotificationsWhenEnterLockDownMode(); + } + + // When the mIsInLockDownMode is true, both notifyPostedLocked and + // notifyRemovedLocked will be dismissed. So we shall call + // cancelNotificationsWhenEnterLockDownMode before we set mIsInLockDownMode + // as true and call postNotificationsWhenExitLockDownMode after we set + // mIsInLockDownMode as false. + mIsInLockDownMode = isInLockDownModeNext; + + if (!isInLockDownModeNext) { + postNotificationsWhenExitLockDownMode(); + } + } + } + + private LockPatternUtils mLockPatternUtils; + private StrongAuthTracker mStrongAuthTracker; + public NotificationManagerService(Context context) { super(context); Notification.processWhitelistToken = WHITELIST_TOKEN; @@ -1467,6 +1517,11 @@ public class NotificationManagerService extends SystemService { } @VisibleForTesting + void setStrongAuthTracker(StrongAuthTracker strongAuthTracker) { + mStrongAuthTracker = strongAuthTracker; + } + + @VisibleForTesting void setKeyguardManager(KeyguardManager keyguardManager) { mKeyguardManager = keyguardManager; } @@ -1641,6 +1696,8 @@ public class NotificationManagerService extends SystemService { mHandler = new WorkerHandler(looper); mRankingThread.start(); + mLockPatternUtils = new LockPatternUtils(getContext()); + mStrongAuthTracker = new StrongAuthTracker(getContext()); String[] extractorNames; try { extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors); @@ -1783,7 +1840,8 @@ public class NotificationManagerService extends SystemService { init(Looper.myLooper(), AppGlobals.getPackageManager(), getContext().getPackageManager(), getLocalService(LightsManager.class), - new NotificationListeners(AppGlobals.getPackageManager()), + new NotificationListeners(getContext(), mNotificationLock, mUserProfiles, + AppGlobals.getPackageManager()), new NotificationAssistants(getContext(), mNotificationLock, mUserProfiles, AppGlobals.getPackageManager()), new ConditionProviders(getContext(), mUserProfiles, AppGlobals.getPackageManager()), @@ -1926,6 +1984,7 @@ public class NotificationManagerService extends SystemService { mRoleObserver = new RoleObserver(getContext().getSystemService(RoleManager.class), mPackageManager, getContext().getMainExecutor()); mRoleObserver.init(); + mLockPatternUtils.registerStrongAuthTracker(mStrongAuthTracker); } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) { // This observer will force an update when observe is called, causing us to // bind to listener services. @@ -7169,6 +7228,29 @@ public class NotificationManagerService extends SystemService { } } + private void cancelNotificationsWhenEnterLockDownMode() { + synchronized (mNotificationLock) { + int numNotifications = mNotificationList.size(); + for (int i = 0; i < numNotifications; i++) { + NotificationRecord rec = mNotificationList.get(i); + mListeners.notifyRemovedLocked(rec, REASON_CANCEL_ALL, + rec.getStats()); + } + + } + } + + private void postNotificationsWhenExitLockDownMode() { + synchronized (mNotificationLock) { + int numNotifications = mNotificationList.size(); + for (int i = 0; i < numNotifications; i++) { + NotificationRecord rec = mNotificationList.get(i); + mListeners.notifyPostedLocked(rec, rec); + } + + } + } + private void updateNotificationPulse() { synchronized (mNotificationLock) { updateLightsLocked(); @@ -7378,6 +7460,10 @@ public class NotificationManagerService extends SystemService { rankings.toArray(new NotificationListenerService.Ranking[0])); } + boolean isInLockDownMode() { + return mStrongAuthTracker.isInLockDownMode(); + } + boolean hasCompanionDevice(ManagedServiceInfo info) { if (mCompanionManager == null) { mCompanionManager = getCompanionManager(); @@ -7885,9 +7971,9 @@ public class NotificationManagerService extends SystemService { private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>(); - public NotificationListeners(IPackageManager pm) { - super(getContext(), mNotificationLock, mUserProfiles, pm); - + public NotificationListeners(Context context, Object lock, UserProfiles userProfiles, + IPackageManager pm) { + super(context, lock, userProfiles, pm); } @Override @@ -7996,8 +8082,12 @@ public class NotificationManagerService extends SystemService { * targetting <= O_MR1 */ @GuardedBy("mNotificationLock") - private void notifyPostedLocked(NotificationRecord r, NotificationRecord old, + void notifyPostedLocked(NotificationRecord r, NotificationRecord old, boolean notifyAllListeners) { + if (isInLockDownMode()) { + return; + } + // Lazily initialized snapshots of the notification. StatusBarNotification sbn = r.sbn; StatusBarNotification oldSbn = (old != null) ? old.sbn : null; @@ -8060,8 +8150,11 @@ public class NotificationManagerService extends SystemService { @GuardedBy("mNotificationLock") public void notifyRemovedLocked(NotificationRecord r, int reason, NotificationStats notificationStats) { - final StatusBarNotification sbn = r.sbn; + if (isInLockDownMode()) { + return; + } + final StatusBarNotification sbn = r.sbn; // make a copy in case changes are made to the underlying Notification object // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the // notification @@ -8112,6 +8205,10 @@ public class NotificationManagerService extends SystemService { */ @GuardedBy("mNotificationLock") public void notifyRankingUpdateLocked(List<NotificationRecord> changedHiddenNotifications) { + if (isInLockDownMode()) { + return; + } + boolean isHiddenRankingUpdate = changedHiddenNotifications != null && changedHiddenNotifications.size() > 0; diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java index 3fd4e83b9494..361f66e3106a 100644 --- a/services/core/java/com/android/server/wm/TaskRecord.java +++ b/services/core/java/com/android/server/wm/TaskRecord.java @@ -110,6 +110,7 @@ import android.content.pm.PackageManager; import android.content.res.Configuration; import android.graphics.Rect; import android.os.Debug; +import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.os.Trace; @@ -204,6 +205,11 @@ class TaskRecord extends ConfigurationContainer { static final int REPARENT_LEAVE_STACK_IN_PLACE = 2; /** + * Used to identify if the activity that is installed from device's system image. + */ + boolean mIsEffectivelySystemApp; + + /** * The factory used to create {@link TaskRecord}. This allows OEM subclass {@link TaskRecord}. */ private static TaskRecordFactory sTaskRecordFactory; @@ -869,17 +875,25 @@ class TaskRecord extends ConfigurationContainer { /** Sets the original intent, and the calling uid and package. */ void setIntent(ActivityRecord r) { - mCallingUid = r.launchedFromUid; - mCallingPackage = r.launchedFromPackage; - setIntent(r.intent, r.info); + boolean updateIdentity = false; + if (this.intent == null) { + updateIdentity = true; + } else if (!mNeverRelinquishIdentity) { + updateIdentity = (effectiveUid == Process.SYSTEM_UID || mIsEffectivelySystemApp + || effectiveUid == r.info.applicationInfo.uid); + } + if (updateIdentity) { + mCallingUid = r.launchedFromUid; + mCallingPackage = r.launchedFromPackage; + setIntent(r.intent, r.info); + } setLockTaskAuth(r); } /** Sets the original intent, _without_ updating the calling uid or package. */ private void setIntent(Intent _intent, ActivityInfo info) { if (intent == null) { - mNeverRelinquishIdentity = - (info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0; + mNeverRelinquishIdentity = (info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0; } else if (mNeverRelinquishIdentity) { return; } @@ -892,6 +906,7 @@ class TaskRecord extends ConfigurationContainer { rootAffinity = affinity; } effectiveUid = info.applicationInfo.uid; + mIsEffectivelySystemApp = info.applicationInfo.isSystemApp(); stringName = null; if (info.targetActivity == null) { @@ -1662,12 +1677,12 @@ class TaskRecord extends ConfigurationContainer { // utility activities. int activityNdx; final int numActivities = mActivities.size(); - final boolean relinquish = numActivities != 0 && - (mActivities.get(0).info.flags & FLAG_RELINQUISH_TASK_IDENTITY) != 0; - for (activityNdx = Math.min(numActivities, 1); activityNdx < numActivities; - ++activityNdx) { + for (activityNdx = 0; activityNdx < numActivities; ++activityNdx) { final ActivityRecord r = mActivities.get(activityNdx); - if (relinquish && (r.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0) { + if ((r.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0 + || (r.info.applicationInfo.uid != Process.SYSTEM_UID + && !r.info.applicationInfo.isSystemApp() + && r.info.applicationInfo.uid != effectiveUid)) { // This will be the top activity for determining taskDescription. Pre-inc to // overcome initial decrement below. ++activityNdx; @@ -1739,15 +1754,27 @@ class TaskRecord extends ConfigurationContainer { int findEffectiveRootIndex() { int effectiveNdx = 0; final int topActivityNdx = mActivities.size() - 1; + ActivityRecord root = null; for (int activityNdx = 0; activityNdx <= topActivityNdx; ++activityNdx) { final ActivityRecord r = mActivities.get(activityNdx); if (r.finishing) { continue; } - effectiveNdx = activityNdx; - if ((r.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0) { + + if (root == null) { + // Set this as the candidate root since it isn't finishing. + root = r; + effectiveNdx = activityNdx; + } + final int uid = root == r ? effectiveUid : r.info.applicationInfo.uid; + if ((root.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0 + || (root.info.applicationInfo.uid != Process.SYSTEM_UID + && !root.info.applicationInfo.isSystemApp() + && root.info.applicationInfo.uid != uid)) { break; } + effectiveNdx = activityNdx; + root = r; } return effectiveNdx; } diff --git a/services/tests/uiservicestests/AndroidManifest.xml b/services/tests/uiservicestests/AndroidManifest.xml index 7453c489ecc8..5b926e041b42 100644 --- a/services/tests/uiservicestests/AndroidManifest.xml +++ b/services/tests/uiservicestests/AndroidManifest.xml @@ -31,6 +31,7 @@ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.OBSERVE_ROLE_HOLDERS" /> <uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT"/> + <uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" /> <application android:debuggable="true"> <uses-library android:name="android.test.runner" /> diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java new file mode 100644 index 000000000000..793739bfe8f5 --- /dev/null +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.notification; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.INotificationManager; +import android.content.pm.IPackageManager; +import android.content.pm.PackageManager; +import android.service.notification.NotificationStats; +import android.service.notification.StatusBarNotification; +import android.testing.TestableContext; + +import com.android.server.UiServiceTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.internal.util.reflection.FieldSetter; + +import java.util.List; + +public class NotificationListenersTest extends UiServiceTestCase { + + @Mock + private PackageManager mPm; + @Mock + private IPackageManager miPm; + + @Mock + NotificationManagerService mNm; + @Mock + private INotificationManager mINm; + private TestableContext mContext = spy(getContext()); + + NotificationManagerService.NotificationListeners mListeners; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + getContext().setMockPackageManager(mPm); + doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any()); + + mListeners = spy(mNm.new NotificationListeners( + mContext, new Object(), mock(ManagedServices.UserProfiles.class), miPm)); + when(mNm.getBinderService()).thenReturn(mINm); + } + + @Test + public void testNotifyPostedLockedInLockdownMode() { + NotificationRecord r = mock(NotificationRecord.class); + NotificationRecord old = mock(NotificationRecord.class); + + // before the lockdown mode + when(mNm.isInLockDownMode()).thenReturn(false); + mListeners.notifyPostedLocked(r, old, true); + mListeners.notifyPostedLocked(r, old, false); + verify(mListeners, times(2)).getServices(); + + // in the lockdown mode + reset(r); + reset(old); + reset(mListeners); + when(mNm.isInLockDownMode()).thenReturn(true); + mListeners.notifyPostedLocked(r, old, true); + mListeners.notifyPostedLocked(r, old, false); + verify(mListeners, never()).getServices(); + } + + @Test + public void testnotifyRankingUpdateLockedInLockdownMode() { + List chn = mock(List.class); + + // before the lockdown mode + when(mNm.isInLockDownMode()).thenReturn(false); + mListeners.notifyRankingUpdateLocked(chn); + verify(chn, times(1)).size(); + + // in the lockdown mode + reset(chn); + when(mNm.isInLockDownMode()).thenReturn(true); + mListeners.notifyRankingUpdateLocked(chn); + verify(chn, never()).size(); + } + + @Test + public void testNotifyRemovedLockedInLockdownMode() throws NoSuchFieldException { + StatusBarNotification sbn = mock(StatusBarNotification.class); + NotificationRecord r = mock(NotificationRecord.class); + NotificationStats rs = mock(NotificationStats.class); + FieldSetter.setField(r, + NotificationRecord.class.getDeclaredField("sbn"), + sbn); + FieldSetter.setField(mNm, + NotificationManagerService.class.getDeclaredField("mHandler"), + mock(NotificationManagerService.WorkerHandler.class)); + + // before the lockdown mode + when(mNm.isInLockDownMode()).thenReturn(false); + mListeners.notifyRemovedLocked(r, 0, rs); + mListeners.notifyRemovedLocked(r, 0, rs); + verify(sbn, times(2)).cloneLight(); + + // in the lockdown mode + reset(sbn); + reset(r); + reset(rs); + when(mNm.isInLockDownMode()).thenReturn(true); + mListeners.notifyRemovedLocked(r, 0, rs); + mListeners.notifyRemovedLocked(r, 0, rs); + verify(sbn, never()).cloneLight(); + } +} 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 997cc266f550..30f5fae52993 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -45,9 +45,12 @@ import static android.os.Build.VERSION_CODES.O_MR1; import static android.os.Build.VERSION_CODES.P; import static android.service.notification.Adjustment.KEY_IMPORTANCE; import static android.service.notification.Adjustment.KEY_USER_SENTIMENT; +import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL; import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE; import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL; +import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotNull; @@ -321,8 +324,26 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { // Tests for this not being true are in CTS NotificationManagerTest return true; } + + class StrongAuthTrackerFake extends NotificationManagerService.StrongAuthTracker { + private int mGetStrongAuthForUserReturnValue = 0; + StrongAuthTrackerFake(Context context) { + super(context); + } + + public void setGetStrongAuthForUserReturnValue(int val) { + mGetStrongAuthForUserReturnValue = val; + } + + @Override + public int getStrongAuthForUser(int userId) { + return mGetStrongAuthForUserReturnValue; + } + } } + TestableNotificationManagerService.StrongAuthTrackerFake mStrongAuthTracker; + private class TestableToastCallback extends ITransientNotification.Stub { @Override public void show(IBinder windowToken) { @@ -411,6 +432,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.setAudioManager(mAudioManager); + mStrongAuthTracker = mService.new StrongAuthTrackerFake(mContext); + mService.setStrongAuthTracker(mStrongAuthTracker); + // Tests call directly into the Binder. mBinderService = mService.getBinderService(); mInternalService = mService.getInternalService(); @@ -5219,4 +5243,44 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } } } + + @Test + public void testStrongAuthTracker_isInLockDownMode() { + mStrongAuthTracker.setGetStrongAuthForUserReturnValue( + STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN); + mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId()); + assertTrue(mStrongAuthTracker.isInLockDownMode()); + mStrongAuthTracker.setGetStrongAuthForUserReturnValue(0); + mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId()); + assertFalse(mStrongAuthTracker.isInLockDownMode()); + } + + @Test + public void testCancelAndPostNotificationsWhenEnterAndExitLockDownMode() { + // post 2 notifications from 2 packages + NotificationRecord pkgA = new NotificationRecord(mContext, + generateSbn("a", 1000, 9, 0), mTestNotificationChannel); + mService.addNotification(pkgA); + NotificationRecord pkgB = new NotificationRecord(mContext, + generateSbn("b", 1001, 9, 0), mTestNotificationChannel); + mService.addNotification(pkgB); + + // when entering the lockdown mode, cancel the 2 notifications. + mStrongAuthTracker.setGetStrongAuthForUserReturnValue( + STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN); + mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId()); + assertTrue(mStrongAuthTracker.isInLockDownMode()); + + // the notifyRemovedLocked function is called twice due to REASON_LOCKDOWN. + ArgumentCaptor<Integer> captor = ArgumentCaptor.forClass(Integer.class); + verify(mListeners, times(2)).notifyRemovedLocked(any(), captor.capture(), any()); + assertEquals(REASON_CANCEL_ALL, captor.getValue().intValue()); + + // exit lockdown mode. + mStrongAuthTracker.setGetStrongAuthForUserReturnValue(0); + mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId()); + + // the notifyPostedLocked function is called twice. + verify(mListeners, times(2)).notifyPostedLocked(any(), any()); + } } |