Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1f3c7fd8 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Use app ops to trigger app block change notifications"

parents 2967ca7d 602b34cb
Loading
Loading
Loading
Loading
+51 −19
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.notification;

import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.Notification.FLAG_AUTOGROUP_SUMMARY;
import static android.app.Notification.FLAG_BUBBLE;
import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
@@ -259,6 +260,8 @@ import android.widget.Toast;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
import com.android.internal.compat.IPlatformCompat;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.logging.InstanceId;
@@ -559,6 +562,7 @@ public class NotificationManagerService extends SystemService {
    ArrayList<String> mLights = new ArrayList<>();

    private AppOpsManager mAppOps;
    private IAppOpsService mAppOpsService;
    private UsageStatsManagerInternal mAppUsageStats;
    private DevicePolicyManagerInternal mDpm;
    private StatsManager mStatsManager;
@@ -1667,6 +1671,18 @@ public class NotificationManagerService extends SystemService {
        }
    };

    @VisibleForTesting
    final IAppOpsCallback mAppOpsCallback = new IAppOpsCallback.Stub() {
        @Override public void opChanged(int op, int uid, String packageName) {
            if (mEnableAppSettingMigration) {
                int opValue = mAppOps.checkOpNoThrow(
                        AppOpsManager.OP_POST_NOTIFICATION, uid, packageName);
                boolean blocked = op != MODE_ALLOWED;
                sendAppBlockStateChangedBroadcast(packageName, uid, blocked);
            }
        }
    };

    private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
@@ -2176,7 +2192,8 @@ public class NotificationManagerService extends SystemService {
            ActivityManager activityManager, GroupHelper groupHelper, IActivityManager am,
            ActivityTaskManagerInternal atm, UsageStatsManagerInternal appUsageStats,
            DevicePolicyManagerInternal dpm, IUriGrantsManager ugm,
            UriGrantsManagerInternal ugmInternal, AppOpsManager appOps, UserManager userManager,
            UriGrantsManagerInternal ugmInternal, AppOpsManager appOps, IAppOpsService iAppOps,
            UserManager userManager,
            NotificationHistoryManager historyManager, StatsManager statsManager,
            TelephonyManager telephonyManager, ActivityManagerInternal ami,
            MultiRateLimiter toastRateLimiter, PermissionHelper permissionHelper) {
@@ -2196,6 +2213,13 @@ public class NotificationManagerService extends SystemService {
        mPackageManager = packageManager;
        mPackageManagerClient = packageManagerClient;
        mAppOps = appOps;
        mAppOpsService = iAppOps;
        try {
            mAppOpsService.startWatchingMode(
                    AppOpsManager.OP_POST_NOTIFICATION, null, mAppOpsCallback);
        } catch (RemoteException e) {
            Slog.e(TAG, "Could not register OP_POST_NOTIFICATION listener");
        }
        mAppUsageStats = appUsageStats;
        mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
        mCompanionManager = companionManager;
@@ -2484,7 +2508,8 @@ public class NotificationManagerService extends SystemService {
                LocalServices.getService(DevicePolicyManagerInternal.class),
                UriGrantsManager.getService(),
                LocalServices.getService(UriGrantsManagerInternal.class),
                (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE),
                getContext().getSystemService(AppOpsManager.class),
                IAppOpsService.Stub.asInterface(ServiceManager.getService(Context.APP_OPS_SERVICE)),
                getContext().getSystemService(UserManager.class),
                new NotificationHistoryManager(getContext(), handler),
                mStatsManager = (StatsManager) getContext().getSystemService(
@@ -2722,6 +2747,19 @@ public class NotificationManagerService extends SystemService {
        });
    }

    private void sendAppBlockStateChangedBroadcast(String pkg, int uid, boolean blocked) {
        try {
            getContext().sendBroadcastAsUser(
                    new Intent(ACTION_APP_BLOCK_STATE_CHANGED)
                            .putExtra(NotificationManager.EXTRA_BLOCKED_STATE, blocked)
                            .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
                            .setPackage(pkg),
                    UserHandle.of(UserHandle.getUserId(uid)), null);
        } catch (SecurityException e) {
            Slog.w(TAG, "Can't notify app about app block change", e);
        }
    }

    @Override
    public void onUserStopping(@NonNull TargetUser user) {
        mHandler.post(() -> {
@@ -3429,7 +3467,8 @@ public class NotificationManagerService extends SystemService {
                if (wasEnabled == enabled) {
                    return;
                }
                mPermissionHelper.setNotificationPermission(pkg, uid, enabled, true);
                mPermissionHelper.setNotificationPermission(
                        pkg, UserHandle.getUserId(uid), enabled, true);
            } else {
                synchronized (mNotificationLock) {
                    boolean wasEnabled = mPreferencesHelper.getImportance(pkg, uid)
@@ -3441,6 +3480,12 @@ public class NotificationManagerService extends SystemService {
                }

                mPreferencesHelper.setEnabled(pkg, uid, enabled);
                // TODO (b/194833441): this is being ignored by app ops now that the permission
                // exists
                mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
                        enabled ? MODE_ALLOWED : AppOpsManager.MODE_IGNORED);

                sendAppBlockStateChangedBroadcast(pkg, uid, !enabled);
            }
            mMetricsLogger.write(new LogMaker(MetricsEvent.ACTION_BAN_APP_NOTES)
                    .setType(MetricsEvent.TYPE_ACTION)
@@ -3452,19 +3497,6 @@ public class NotificationManagerService extends SystemService {
                        UserHandle.getUserId(uid), REASON_PACKAGE_BANNED, null);
            }

            mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
                    enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
            try {
                getContext().sendBroadcastAsUser(
                        new Intent(ACTION_APP_BLOCK_STATE_CHANGED)
                                .putExtra(NotificationManager.EXTRA_BLOCKED_STATE, !enabled)
                                .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
                                .setPackage(pkg),
                        UserHandle.of(UserHandle.getUserId(uid)), null);
            } catch (SecurityException e) {
                Slog.w(TAG, "Can't notify app about app block change", e);
            }

            handleSavePolicyFile();
        }

@@ -4198,7 +4230,7 @@ public class NotificationManagerService extends SystemService {
            // noteOp will check to make sure the callingPkg matches the uid
            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg,
                    callingAttributionTag, null)
                    == AppOpsManager.MODE_ALLOWED) {
                    == MODE_ALLOWED) {
                synchronized (mNotificationLock) {
                    tmp = new StatusBarNotification[mNotificationList.size()];
                    final int N = mNotificationList.size();
@@ -4314,7 +4346,7 @@ public class NotificationManagerService extends SystemService {
            // noteOp will check to make sure the callingPkg matches the uid
            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg,
                    callingAttributionTag, null)
                    == AppOpsManager.MODE_ALLOWED) {
                    == MODE_ALLOWED) {
                synchronized (mArchive) {
                    tmp = mArchive.getArray(count, includeSnoozed);
                }
@@ -4340,7 +4372,7 @@ public class NotificationManagerService extends SystemService {
            // noteOp will check to make sure the callingPkg matches the uid
            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg,
                    callingAttributionTag, null)
                    == AppOpsManager.MODE_ALLOWED) {
                    == MODE_ALLOWED) {
                IntArray currentUserIds = mUserProfiles.getCurrentProfileIds();
                Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "notifHistoryReadHistory");
                try {
+2 −1
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static android.content.pm.PackageManager.GET_PERMISSIONS;
import static android.permission.PermissionManager.PERMISSION_GRANTED;

import android.Manifest;
import android.annotation.UserIdInt;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.ParceledListSlice;
@@ -133,7 +134,7 @@ public final class PermissionHelper {
     * can prevent the user from seeing the in app permission dialog. Must not be called
     * with a lock held.
     */
    public void setNotificationPermission(String packageName, int userId, boolean grant,
    public void setNotificationPermission(String packageName, @UserIdInt int userId, boolean grant,
            boolean userSet) {
        assertFlag();
        try {
+17 −1
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@ package com.android.server.notification;

import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.Notification.FLAG_AUTO_CANCEL;
import static android.app.Notification.FLAG_BUBBLE;
import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
@@ -182,6 +184,7 @@ import android.widget.RemoteViews;
import androidx.annotation.Nullable;
import androidx.test.InstrumentationRegistry;

import com.android.internal.app.IAppOpsService;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.logging.InstanceIdSequence;
import com.android.internal.logging.InstanceIdSequenceFake;
@@ -314,6 +317,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
    @Mock
    AppOpsManager mAppOpsManager;
    @Mock
    IAppOpsService mAppOpsService;
    @Mock
    private TestableNotificationManagerService.NotificationAssistantAccessGrantedCallback
            mNotificationAssistantAccessGrantedCallback;
    @Mock
@@ -449,7 +454,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
                mockLightsManager, mListeners, mAssistants, mConditionProviders, mCompanionMgr,
                mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager, mGroupHelper, mAm, mAtm,
                mAppUsageStats, mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal,
                mAppOpsManager, mUm, mHistoryManager, mStatsManager, mock(TelephonyManager.class),
                mAppOpsManager, mAppOpsService, mUm, mHistoryManager, mStatsManager,
                mock(TelephonyManager.class),
                mAmi, mToastRateLimiter, mPermissionHelper);
        // Return first true for RoleObserver main-thread check
        when(mMainLooper.isCurrentThread()).thenReturn(true).thenReturn(false);
@@ -2127,6 +2133,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
    public void testUpdateAppNotifyCreatorBlock() throws Exception {
        mService.setPreferencesHelper(mPreferencesHelper);

        // should not trigger a broadcast
        when(mAppOpsManager.checkOpNoThrow(anyInt(), eq(mUid), eq(PKG))).thenReturn(MODE_IGNORED);
        mService.mAppOpsCallback.opChanged(0, mUid, PKG);

        // should trigger a broadcast
        mBinderService.setNotificationsEnabledForPackage(PKG, 0, true);
        ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
        verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null));
@@ -2149,6 +2160,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
    public void testUpdateAppNotifyCreatorUnblock() throws Exception {
        mService.setPreferencesHelper(mPreferencesHelper);

        // should not trigger a broadcast
        when(mAppOpsManager.checkOpNoThrow(anyInt(), eq(mUid), eq(PKG))).thenReturn(MODE_ALLOWED);
        mService.mAppOpsCallback.opChanged(0, mUid, PKG);

        // should trigger a broadcast
        mBinderService.setNotificationsEnabledForPackage(PKG, 0, true);
        ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
        verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null));
+44 −3
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

package com.android.server.notification;

import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.NotificationManager.EXTRA_BLOCKED_STATE;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_NONE;
import static android.content.pm.PackageManager.FEATURE_WATCH;
@@ -28,10 +31,13 @@ import static com.android.server.notification.NotificationManagerService.ACTION_

import static com.google.common.truth.Truth.assertThat;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.fail;

import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
@@ -42,6 +48,7 @@ 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;

@@ -54,6 +61,7 @@ import android.app.INotificationManager;
import android.app.IUriGrantsManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.StatsManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.usage.UsageStatsManagerInternal;
@@ -95,6 +103,7 @@ import android.util.AtomicFile;

import androidx.test.InstrumentationRegistry;

import com.android.internal.app.IAppOpsService;
import com.android.internal.logging.InstanceIdSequence;
import com.android.internal.logging.InstanceIdSequenceFake;
import com.android.server.DeviceIdleInternal;
@@ -349,8 +358,8 @@ public class NotificationPermissionMigrationTest extends UiServiceTestCase {
                mockLightsManager, mListeners, mAssistants, mConditionProviders, mCompanionMgr,
                mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager, mGroupHelper, mAm, mAtm,
                mAppUsageStats, mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal,
                mAppOpsManager, mUm, mHistoryManager, mStatsManager, mock(TelephonyManager.class),
                mAmi, mToastRateLimiter, mPermissionHelper);
                mAppOpsManager, mock(IAppOpsService.class), mUm, mHistoryManager, mStatsManager,
                mock(TelephonyManager.class), mAmi, mToastRateLimiter, mPermissionHelper);
        // Return first true for RoleObserver main-thread check
        when(mMainLooper.isCurrentThread()).thenReturn(true).thenReturn(false);
        mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY, mMainLooper);
@@ -568,7 +577,39 @@ public class NotificationPermissionMigrationTest extends UiServiceTestCase {
        when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
        mBinderService.setNotificationsEnabledForPackage(mContext.getPackageName(), mUid, false);

        verify(mPermissionHelper, never()).setNotificationPermission(
        verify(mPermissionHelper).setNotificationPermission(
                mContext.getPackageName(), UserHandle.getUserId(mUid), false, true);

        verify(mAppOpsManager, never()).setMode(anyInt(), anyInt(), anyString(), anyInt());
    }

    @Test
    public void testUpdateAppNotifyCreatorBlock() throws Exception {
        when(mAppOpsManager.checkOpNoThrow(anyInt(), eq(mUid), eq(PKG))).thenReturn(MODE_IGNORED);

        mService.mAppOpsCallback.opChanged(0, mUid, PKG);

        ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
        verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null));

        assertEquals(NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED,
                captor.getValue().getAction());
        assertEquals(PKG, captor.getValue().getPackage());
        assertFalse(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, true));
    }

    @Test
    public void testUpdateAppNotifyCreatorUnblock() throws Exception {
        when(mAppOpsManager.checkOpNoThrow(anyInt(), eq(mUid), eq(PKG))).thenReturn(MODE_ALLOWED);

        mService.mAppOpsCallback.opChanged(0, mUid, PKG);

        ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
        verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null));

        assertEquals(NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED,
                captor.getValue().getAction());
        assertEquals(PKG, captor.getValue().getPackage());
        assertFalse(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, true));
    }
}
+3 −1
Original line number Diff line number Diff line
@@ -62,6 +62,7 @@ import android.util.Pair;

import androidx.test.InstrumentationRegistry;

import com.android.internal.app.IAppOpsService;
import com.android.internal.logging.InstanceIdSequence;
import com.android.internal.logging.InstanceIdSequenceFake;
import com.android.server.LocalServices;
@@ -160,7 +161,8 @@ public class RoleObserverTest extends UiServiceTestCase {
                    mock(UsageStatsManagerInternal.class),
                    mock(DevicePolicyManagerInternal.class), mock(IUriGrantsManager.class),
                    mock(UriGrantsManagerInternal.class),
                    mock(AppOpsManager.class), mUm, mock(NotificationHistoryManager.class),
                    mock(AppOpsManager.class), mock(IAppOpsService.class),
                    mUm, mock(NotificationHistoryManager.class),
                    mock(StatsManager.class), mock(TelephonyManager.class),
                    mock(ActivityManagerInternal.class),
                    mock(MultiRateLimiter.class), mock(PermissionHelper.class));