diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-06-11 16:37:21 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-06-11 16:37:21 +0000 |
commit | 4d8dfca9f61dc15e4c2d4225bb9fc5ea26071341 (patch) | |
tree | 6d5c82fb138f8f3993658b846938fc18286441ea | |
parent | 066e147f48a13525889a069b6f2715e096fc1998 (diff) | |
parent | 7bfdb190aaa8f48e7af2fade700ccd360fe8b423 (diff) | |
download | base-android-security-11.0.0_r58.tar.gz |
Merge cherrypicks of [16961349, 17874414, 18356706, 18279994, 18670434, 18629266, 18715266, 18701369] into security-aosp-rvc-release.android-security-11.0.0_r58
Change-Id: Icdaacc26d0bbeca8189a5adb3798eb9af5b7cc02
13 files changed, 466 insertions, 46 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 29fc1674bab9..868cbbe969b0 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -43,7 +43,6 @@ import android.content.Intent; import android.content.SharedPreferences; 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; @@ -384,20 +383,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/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index c82a45feb682..3781ffab7a6e 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -4983,10 +4983,17 @@ public final class ActiveServices { return true; } - final boolean isWhiteListedPackage = - mWhiteListAllowWhileInUsePermissionInFgs.contains(callingPackage); - if (isWhiteListedPackage) { - return true; + + if (verifyPackage(callingPackage, callingUid)) { + final boolean isWhiteListedPackage = + mWhiteListAllowWhileInUsePermissionInFgs.contains(callingPackage); + if (isWhiteListedPackage) { + return true; + } + } else { + EventLog.writeEvent(0x534e4554, "215003903", callingUid, + "callingPackage:" + callingPackage + " does not belong to callingUid:" + + callingUid); } // Is the calling UID a device owner app? @@ -5025,4 +5032,21 @@ public final class ActiveServices { r.mAllowWhileInUsePermissionInFgs = false; r.mLastSetFgsRestrictionTime = 0; } + + /** + * Checks if a given packageName belongs to a given uid. + * @param packageName the package of the caller + * @param uid the uid of the caller + * @return true or false + */ + private boolean verifyPackage(String packageName, int uid) { + if (uid == ROOT_UID || uid == SYSTEM_UID) { + //System and Root are always allowed + return true; + } + final int userId = UserHandle.getUserId(uid); + final int packageUid = mAm.getPackageManagerInternalLocked() + .getPackageUid(packageName, PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId); + return UserHandle.isSameApp(uid, packageUid); + } } 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/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index ea1924f2d844..8cf6e33f93a6 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -2388,6 +2388,9 @@ public class Vpn { return; // VPN has been shut down. } + // Clear mInterface to prevent Ikev2VpnRunner being cleared when + // interfaceRemoved() is called. + mInterface = null; // Without MOBIKE, we have no way to seamlessly migrate. Close on old // (non-default) network, and start the new one. resetIkeState(); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index f5efd33e7cd0..d5e7a15be722 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -223,6 +223,7 @@ import android.util.Log; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; +import android.util.SparseBooleanArray; import android.util.StatsEvent; import android.util.Xml; import android.util.proto.ProtoOutputStream; @@ -253,6 +254,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.DeviceIdleInternal; import com.android.server.EventLogTags; import com.android.server.IoThread; @@ -1711,6 +1713,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) { this(context, new NotificationRecordLoggerImpl(), @@ -1737,6 +1787,11 @@ public class NotificationManagerService extends SystemService { } @VisibleForTesting + void setStrongAuthTracker(StrongAuthTracker strongAuthTracker) { + mStrongAuthTracker = strongAuthTracker; + } + + @VisibleForTesting void setKeyguardManager(KeyguardManager keyguardManager) { mKeyguardManager = keyguardManager; } @@ -1923,6 +1978,8 @@ public class NotificationManagerService extends SystemService { ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); mUiHandler = new Handler(UiThread.get().getLooper()); + mLockPatternUtils = new LockPatternUtils(getContext()); + mStrongAuthTracker = new StrongAuthTracker(getContext()); String[] extractorNames; try { extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors); @@ -2111,7 +2168,8 @@ public class NotificationManagerService extends SystemService { init(handler, new RankingHandlerWorker(mRankingThread.getLooper()), 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()), @@ -2353,6 +2411,7 @@ public class NotificationManagerService extends SystemService { bubbsExtractor.setShortcutHelper(mShortcutHelper); } registerNotificationPreferencesPullers(); + 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. @@ -8491,6 +8550,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(); @@ -8720,6 +8802,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(); @@ -9300,8 +9386,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 @@ -9433,8 +9520,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; + } + try { // Lazily initialized snapshots of the notification. StatusBarNotification sbn = r.getSbn(); @@ -9502,6 +9593,10 @@ public class NotificationManagerService extends SystemService { @GuardedBy("mNotificationLock") public void notifyRemovedLocked(NotificationRecord r, int reason, NotificationStats notificationStats) { + if (isInLockDownMode()) { + return; + } + final StatusBarNotification sbn = r.getSbn(); // make a copy in case changes are made to the underlying Notification object @@ -9554,6 +9649,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/LaunchParamsPersister.java b/services/core/java/com/android/server/wm/LaunchParamsPersister.java index a974332fd852..b037e59942cf 100644 --- a/services/core/java/com/android/server/wm/LaunchParamsPersister.java +++ b/services/core/java/com/android/server/wm/LaunchParamsPersister.java @@ -214,6 +214,9 @@ class LaunchParamsPersister { void saveTask(Task task, DisplayContent display) { final ComponentName name = task.realActivity; + if (name == null) { + return; + } final int userId = task.mUserId; PersistableLaunchParams params; ArrayMap<ComponentName, PersistableLaunchParams> map = mLaunchParamsMap.get(userId); diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 7e8221df12ee..22b59f4a5149 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -119,6 +119,7 @@ import android.graphics.Point; import android.graphics.Rect; import android.os.Debug; import android.os.IBinder; +import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.os.Trace; @@ -227,6 +228,11 @@ class Task extends WindowContainer<WindowContainer> { // Do not move the stack as a part of reparenting 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; + String affinity; // The affinity name for this task, or null; may change identity. String rootAffinity; // Initial base affinity, or null; does not change from initial root. String mWindowLayoutAffinity; // Launch param affinity of this task or null. Used when saving @@ -477,11 +483,24 @@ class Task extends WindowContainer<WindowContainer> { if (r.finishing) return false; - // Set this as the candidate root since it isn't finishing. - mRoot = r; + if (mRoot == null || mRoot.finishing) { + // Set this as the candidate root since it isn't finishing. + mRoot = r; + } - // Only end search if we are ignore relinquishing identity or we are not relinquishing. - return ignoreRelinquishIdentity || (r.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0; + final int uid = mRoot == r ? effectiveUid : r.info.applicationInfo.uid; + if (ignoreRelinquishIdentity + || (mRoot.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0 + || (mRoot.info.applicationInfo.uid != Process.SYSTEM_UID + && !mRoot.info.applicationInfo.isSystemApp() + && mRoot.info.applicationInfo.uid != uid)) { + // No need to relinquish identity, end search. + return true; + } + + // Relinquish to next activity + mRoot = r; + return false; } } @@ -929,32 +948,33 @@ class Task extends WindowContainer<WindowContainer> { * @param info The activity info which could be different from {@code r.info} if set. */ void setIntent(ActivityRecord r, @Nullable Intent intent, @Nullable ActivityInfo info) { - mCallingUid = r.launchedFromUid; - mCallingPackage = r.launchedFromPackage; - mCallingFeatureId = r.launchedFromFeatureId; - setIntent(intent != null ? intent : r.intent, info != null ? info : r.info); - setLockTaskAuth(r); - - final WindowContainer parent = getParent(); - if (parent != null) { - final Task t = parent.asTask(); - if (t != null) { - t.setIntent(r); - } + boolean updateIdentity = false; + if (this.intent == null) { + updateIdentity = true; + } else if (!mNeverRelinquishIdentity) { + final ActivityInfo activityInfo = info != null ? info : r.info; + updateIdentity = (effectiveUid == Process.SYSTEM_UID || mIsEffectivelySystemApp + || effectiveUid == activityInfo.applicationInfo.uid); + } + if (updateIdentity) { + mCallingUid = r.launchedFromUid; + mCallingPackage = r.launchedFromPackage; + mCallingFeatureId = r.launchedFromFeatureId; + setIntent(intent != null ? intent : r.intent, info != null ? info : r.info); } + setLockTaskAuth(r); } /** Sets the original intent, _without_ updating the calling uid or package. */ private void setIntent(Intent _intent, ActivityInfo info) { - final boolean isLeaf = isLeafTask(); + if (!isLeafTask()) return; if (intent == null) { - mNeverRelinquishIdentity = - (info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0; - } else if (mNeverRelinquishIdentity && isLeaf) { + mNeverRelinquishIdentity = (info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0; + } else if (mNeverRelinquishIdentity) { return; } - affinity = isLeaf ? info.taskAffinity : null; + affinity = info.taskAffinity; if (intent == null) { // If this task already has an intent associated with it, don't set the root // affinity -- we don't want it changing after initially set, but the initially @@ -962,6 +982,7 @@ class Task extends WindowContainer<WindowContainer> { rootAffinity = affinity; } effectiveUid = info.applicationInfo.uid; + mIsEffectivelySystemApp = info.applicationInfo.isSystemApp(); stringName = null; if (info.targetActivity == null) { diff --git a/services/tests/uiservicestests/AndroidManifest.xml b/services/tests/uiservicestests/AndroidManifest.xml index 767857bf2de8..e8e3a8f84f21 100644 --- a/services/tests/uiservicestests/AndroidManifest.xml +++ b/services/tests/uiservicestests/AndroidManifest.xml @@ -33,6 +33,7 @@ <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.WRITE_DEVICE_CONFIG" /> + <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..7244fcdda731 --- /dev/null +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java @@ -0,0 +1,133 @@ +/* + * 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.atLeast; +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.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(r, atLeast(2)).getSbn(); + + // in the lockdown mode + reset(r); + reset(old); + when(mNm.isInLockDownMode()).thenReturn(true); + mListeners.notifyPostedLocked(r, old, true); + mListeners.notifyPostedLocked(r, old, false); + verify(r, never()).getSbn(); + } + + @Test + public void testnotifyRankingUpdateLockedInLockdownMode() { + List chn = mock(List.class); + + // before the lockdown mode + when(mNm.isInLockDownMode()).thenReturn(false); + mListeners.notifyRankingUpdateLocked(chn); + verify(chn, atLeast(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 { + NotificationRecord r = mock(NotificationRecord.class); + NotificationStats rs = mock(NotificationStats.class); + StatusBarNotification sbn = mock(StatusBarNotification.class); + FieldSetter.setField(mNm, + NotificationManagerService.class.getDeclaredField("mHandler"), + mock(NotificationManagerService.WorkerHandler.class)); + + // before the lockdown mode + when(mNm.isInLockDownMode()).thenReturn(false); + when(r.getSbn()).thenReturn(sbn); + mListeners.notifyRemovedLocked(r, 0, rs); + mListeners.notifyRemovedLocked(r, 0, rs); + verify(r, atLeast(2)).getSbn(); + + // in the lockdown mode + reset(r); + reset(rs); + when(mNm.isInLockDownMode()).thenReturn(true); + when(r.getSbn()).thenReturn(sbn); + mListeners.notifyRemovedLocked(r, 0, rs); + mListeners.notifyRemovedLocked(r, 0, rs); + verify(r, never()).getSbn(); + } +} 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 62a423e32772..e12eda92ef2b 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -52,9 +52,12 @@ import static android.os.Build.VERSION_CODES.P; import static android.os.UserHandle.USER_SYSTEM; 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; @@ -395,8 +398,26 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { interface NotificationAssistantAccessGrantedCallback { void onGranted(ComponentName assistant, int userId, boolean granted); } + + 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) { @@ -513,6 +534,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.setAudioManager(mAudioManager); + mStrongAuthTracker = mService.new StrongAuthTrackerFake(mContext); + mService.setStrongAuthTracker(mStrongAuthTracker); + mShortcutHelper = mService.getShortcutHelper(); mShortcutHelper.setLauncherApps(mLauncherApps); mShortcutHelper.setShortcutServiceInternal(mShortcutServiceInternal); @@ -7106,4 +7130,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()); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java index fb24d868e970..d04e2ba3f438 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java @@ -591,6 +591,7 @@ public class TaskRecordTests extends ActivityTestsBase { // one above as finishing. final ActivityRecord activity0 = task.getBottomMostActivity(); activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; + task.effectiveUid = activity0.getUid(); final ActivityRecord activity1 = new ActivityBuilder(mService).setTask(task).build(); activity1.finishing = true; new ActivityBuilder(mService).setTask(task).build(); @@ -625,6 +626,7 @@ public class TaskRecordTests extends ActivityTestsBase { // Set relinquishTaskIdentity for all activities in the task final ActivityRecord activity0 = task.getBottomMostActivity(); activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; + task.effectiveUid = activity0.getUid(); final ActivityRecord activity1 = new ActivityBuilder(mService).setTask(task).build(); activity1.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; @@ -777,6 +779,7 @@ public class TaskRecordTests extends ActivityTestsBase { // Make the current root activity relinquish task identity final ActivityRecord activity0 = task.getBottomMostActivity(); activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; + task.effectiveUid = activity0.getUid(); // Add an extra activity on top - this will be the new root final ActivityRecord activity1 = new ActivityBuilder(mService).setTask(task).build(); // Add one more on top @@ -871,6 +874,47 @@ public class TaskRecordTests extends ActivityTestsBase { verify(task).setIntent(eq(activity0)); } + /** + * Test {@link Task#updateEffectiveIntent()} when activity with relinquishTaskIdentity but + * another with different uid. This should make the task use the root activity when updating the + * intent. + */ + @Test + public void testUpdateEffectiveIntent_relinquishingWithDifferentUid() { + final ActivityRecord activity0 = new ActivityBuilder(mService) + .setActivityFlags(FLAG_RELINQUISH_TASK_IDENTITY).setCreateTask(true).build(); + final Task task = activity0.getTask(); + + // Add an extra activity on top + new ActivityBuilder(mService).setUid(11).setTask(task).build(); + + spyOn(task); + task.updateEffectiveIntent(); + verify(task).setIntent(eq(activity0)); + } + + /** + * Test {@link Task#updateEffectiveIntent()} with activities set as relinquishTaskIdentity. + * This should make the task use the topmost activity when updating the intent. + */ + @Test + public void testUpdateEffectiveIntent_relinquishingMultipleActivities() { + final ActivityRecord activity0 = new ActivityBuilder(mService) + .setActivityFlags(FLAG_RELINQUISH_TASK_IDENTITY).setCreateTask(true).build(); + final Task task = activity0.getTask(); + task.effectiveUid = activity0.getUid(); + // Add an extra activity on top + final ActivityRecord activity1 = new ActivityBuilder(mService).setTask(task).build(); + activity1.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; + + // Add an extra activity on top + final ActivityRecord activity2 = new ActivityBuilder(mService).setTask(task).build(); + + spyOn(task); + task.updateEffectiveIntent(); + verify(task).setIntent(eq(activity2)); + } + @Test public void testSaveLaunchingStateWhenConfigurationChanged() { LaunchParamsPersister persister = mService.mStackSupervisor.mLaunchParamsPersister; |