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

Commit b95505f1 authored by Julia Reynolds's avatar Julia Reynolds Committed by Android (Google) Code Review
Browse files

Merge "Migrate are/setNotificationsEnabled to PM"

parents 00b3b61f 1184a696
Loading
Loading
Loading
Loading
+54 −16
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import static android.app.NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_
import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
import static android.app.NotificationManager.EXTRA_AUTOMATIC_ZEN_RULE_ID;
import static android.app.NotificationManager.EXTRA_AUTOMATIC_ZEN_RULE_STATUS;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_MIN;
import static android.app.NotificationManager.IMPORTANCE_NONE;
@@ -291,6 +292,7 @@ import com.android.server.notification.toast.CustomToastRecord;
import com.android.server.notification.toast.TextToastRecord;
import com.android.server.notification.toast.ToastRecord;
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.utils.quota.MultiRateLimiter;
@@ -489,6 +491,7 @@ public class NotificationManagerService extends SystemService {
    private UserManager mUm;
    private IPlatformCompat mPlatformCompat;
    private ShortcutHelper mShortcutHelper;
    private PermissionHelper mPermissionHelper;

    final IBinder mForegroundToken = new Binder();
    private WorkerHandler mHandler;
@@ -606,6 +609,7 @@ public class NotificationManagerService extends SystemService {

    private int mWarnRemoteViewsSizeBytes;
    private int mStripRemoteViewsSizeBytes;
    final boolean mEnableAppSettingMigration;

    private MetricsLogger mMetricsLogger;
    private TriPredicate<String, Integer, String> mAllowedManagedServicePackages;
@@ -2003,6 +2007,12 @@ public class NotificationManagerService extends SystemService {
        mNotificationRecordLogger = notificationRecordLogger;
        mNotificationInstanceIdSequence = notificationInstanceIdSequence;
        Notification.processAllowlistToken = ALLOWLIST_TOKEN;
        // TODO (b/194833441): remove when OS is ready for migration. This flag is checked once
        // rather than having a settings observer because some of the behaviors (e.g. readXml) only
        // happen on reboot
        mEnableAppSettingMigration = Settings.Secure.getIntForUser(
                getContext().getContentResolver(),
                Settings.Secure.NOTIFICATION_PERMISSION_ENABLED, 0, USER_SYSTEM) == 1;
    }

    // TODO - replace these methods with new fields in the VisibleForTesting constructor
@@ -2169,7 +2179,7 @@ public class NotificationManagerService extends SystemService {
            UriGrantsManagerInternal ugmInternal, AppOpsManager appOps, UserManager userManager,
            NotificationHistoryManager historyManager, StatsManager statsManager,
            TelephonyManager telephonyManager, ActivityManagerInternal ami,
            MultiRateLimiter toastRateLimiter) {
            MultiRateLimiter toastRateLimiter, PermissionHelper permissionHelper) {
        mHandler = handler;
        Resources resources = getContext().getResources();
        mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
@@ -2275,6 +2285,7 @@ public class NotificationManagerService extends SystemService {
        mGroupHelper = groupHelper;
        mVibratorHelper = new VibratorHelper(getContext());
        mHistoryManager = historyManager;
        mPermissionHelper = permissionHelper;

        // This is a ManagedServices object that keeps track of the listeners.
        mListeners = notificationListeners;
@@ -2480,7 +2491,9 @@ public class NotificationManagerService extends SystemService {
                        Context.STATS_MANAGER),
                getContext().getSystemService(TelephonyManager.class),
                LocalServices.getService(ActivityManagerInternal.class),
                createToastRateLimiter());
                createToastRateLimiter(), new PermissionHelper(LocalServices.getService(
                        PermissionManagerServiceInternal.class), AppGlobals.getPackageManager(),
                        AppGlobals.getPermissionManager(), mEnableAppSettingMigration));

        publishBinderService(Context.NOTIFICATION_SERVICE, mService, /* allowIsolated= */ false,
                DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL);
@@ -3411,7 +3424,13 @@ public class NotificationManagerService extends SystemService {
        @Override
        public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
            enforceSystemOrSystemUI("setNotificationsEnabledForPackage");

            if (mEnableAppSettingMigration) {
                boolean wasEnabled = mPermissionHelper.hasPermission(uid);
                if (wasEnabled == enabled) {
                    return;
                }
                mPermissionHelper.setNotificationPermission(pkg, uid, enabled, true);
            } else {
                synchronized (mNotificationLock) {
                    boolean wasEnabled = mPreferencesHelper.getImportance(pkg, uid)
                            != NotificationManager.IMPORTANCE_NONE;
@@ -3422,6 +3441,7 @@ public class NotificationManagerService extends SystemService {
                }

                mPreferencesHelper.setEnabled(pkg, uid, enabled);
            }
            mMetricsLogger.write(new LogMaker(MetricsEvent.ACTION_BAN_APP_NOTES)
                    .setType(MetricsEvent.TYPE_ACTION)
                    .setPackageName(pkg)
@@ -3489,8 +3509,12 @@ public class NotificationManagerService extends SystemService {
                        "canNotifyAsPackage for uid " + uid);
            }

            if (mEnableAppSettingMigration) {
                return mPermissionHelper.hasPermission(uid);
            } else {
                return mPreferencesHelper.getImportance(pkg, uid) != IMPORTANCE_NONE;
            }
        }

        /**
         * @return true if and only if "all" bubbles are allowed from the provided package.
@@ -3581,8 +3605,16 @@ public class NotificationManagerService extends SystemService {
        @Override
        public int getPackageImportance(String pkg) {
            checkCallerIsSystemOrSameApp(pkg);
            if (mEnableAppSettingMigration) {
                if (mPermissionHelper.hasPermission(Binder.getCallingUid())) {
                    return IMPORTANCE_DEFAULT;
                } else {
                    return IMPORTANCE_NONE;
                }
            } else {
                return mPreferencesHelper.getImportance(pkg, Binder.getCallingUid());
            }
        }

        @Override
        public boolean canShowBadge(String pkg, int uid) {
@@ -6253,11 +6285,17 @@ public class NotificationManagerService extends SystemService {
                    + ", notificationUid=" + notificationUid
                    + ", notification=" + notification;
            Slog.e(TAG, noChannelStr);
            boolean appNotificationsOff = mPreferencesHelper.getImportance(pkg, notificationUid)
            boolean appNotificationsOff;
            if (mEnableAppSettingMigration) {
                appNotificationsOff = !mPermissionHelper.hasPermission(notificationUid);
            } else {
                appNotificationsOff = mPreferencesHelper.getImportance(pkg, notificationUid)
                        == NotificationManager.IMPORTANCE_NONE;
            }

            if (!appNotificationsOff) {
                doChannelWarningToast("Developer warning for package \"" + pkg + "\"\n" +
                doChannelWarningToast(notificationUid,
                        "Developer warning for package \"" + pkg + "\"\n" +
                        "Failed to post notification on channel \"" + channelId + "\"\n" +
                        "See log for more details");
            }
@@ -6503,7 +6541,7 @@ public class NotificationManagerService extends SystemService {
                }
            };

    private void doChannelWarningToast(CharSequence toastText) {
    protected void doChannelWarningToast(int forUid, CharSequence toastText) {
        Binder.withCleanCallingIdentity(() -> {
            final int defaultWarningEnabled = Build.IS_DEBUGGABLE ? 1 : 0;
            final boolean warningEnabled = Settings.Global.getInt(getContext().getContentResolver(),
@@ -10876,7 +10914,7 @@ public class NotificationManagerService extends SystemService {
        }
    }


    // TODO (b/194833441): remove when we've fully migrated to a permission
    class RoleObserver implements OnRoleHoldersChangedListener {
        // Role name : user id : list of approved packages
        private ArrayMap<String, ArrayMap<Integer, ArraySet<String>>> mNonBlockableDefaultApps;
+13 −78
Original line number Diff line number Diff line
@@ -246,6 +246,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
    private PackageManager mPackageManagerClient;
    @Mock
    private WindowManagerInternal mWindowManagerInternal;
    @Mock
    private PermissionHelper mPermissionHelper;
    private TestableContext mContext = spy(getContext());
    private final String PKG = mContext.getPackageName();
    private TestableLooper mTestableLooper;
@@ -334,83 +336,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {

    private NotificationManagerService.WorkerHandler mWorkerHandler;

    // Use a Testable subclass so we can simulate calls from the system without failing.
    private static class TestableNotificationManagerService extends NotificationManagerService {
        int countSystemChecks = 0;
        boolean isSystemUid = true;
        int countLogSmartSuggestionsVisible = 0;
        @Nullable
        NotificationAssistantAccessGrantedCallback mNotificationAssistantAccessGrantedCallback;

        TestableNotificationManagerService(Context context, NotificationRecordLogger logger,
                InstanceIdSequence notificationInstanceIdSequence) {
            super(context, logger, notificationInstanceIdSequence);
        }

        RankingHelper getRankingHelper() {
            return mRankingHelper;
        }

        @Override
        protected boolean isCallingUidSystem() {
            countSystemChecks++;
            return isSystemUid;
        }

        @Override
        protected boolean isCallerSystemOrPhone() {
            countSystemChecks++;
            return isSystemUid;
        }

        @Override
        protected ICompanionDeviceManager getCompanionManager() {
            return null;
        }

        @Override
        protected void reportUserInteraction(NotificationRecord r) {
            return;
        }

        @Override
        protected void handleSavePolicyFile() {
            return;
        }

        @Override
        void logSmartSuggestionsVisible(NotificationRecord r, int notificationLocation) {
            super.logSmartSuggestionsVisible(r, notificationLocation);
            countLogSmartSuggestionsVisible++;
        }

        @Override
        protected void setNotificationAssistantAccessGrantedForUserInternal(
                ComponentName assistant, int userId, boolean granted, boolean userSet) {
            if (mNotificationAssistantAccessGrantedCallback != null) {
                mNotificationAssistantAccessGrantedCallback.onGranted(assistant, userId, granted,
                        userSet);
                return;
            }
            super.setNotificationAssistantAccessGrantedForUserInternal(assistant, userId, granted,
                    userSet);
        }

        @Override
        protected String[] getStringArrayResource(int key) {
            return new String[] {PKG_O};
        }

        private void setNotificationAssistantAccessGrantedCallback(
                @Nullable NotificationAssistantAccessGrantedCallback callback) {
            this.mNotificationAssistantAccessGrantedCallback = callback;
        }

        interface NotificationAssistantAccessGrantedCallback {
            void onGranted(ComponentName assistant, int userId, boolean granted, boolean userSet);
        }
    }

    private class TestableToastCallback extends ITransientNotification.Stub {
        @Override
        public void show(IBinder windowToken) {
@@ -516,13 +441,16 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {

        when(mAssistants.isAdjustmentAllowed(anyString())).thenReturn(true);

        // apps allowed as convos
        mService.setStringArrayResourceValue(PKG_O);

        mWorkerHandler = spy(mService.new WorkerHandler(mTestableLooper.getLooper()));
        mService.init(mWorkerHandler, mRankingHandler, mPackageManager, mPackageManagerClient,
                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);
                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);
@@ -6685,6 +6613,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        enableInteractAcrossUsers();
        mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(),
                mUid + UserHandle.PER_USER_RANGE);

        verify(mPermissionHelper, never()).hasPermission(anyInt());
    }

    @Test
@@ -8335,4 +8265,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
        assertTrue(captor.getValue().isPackageAllowed(new VersionedPackage("apples", 1001)));
        assertFalse(captor.getValue().isPackageAllowed(new VersionedPackage("test", 1002)));
    }

    @Test
    public void testMigrationDisabledByDefault() {
        assertThat(mService.mEnableAppSettingMigration).isFalse();
    }
}
+574 −0

File added.

Preview size limit exceeded, changes collapsed.

+2 −1
Original line number Diff line number Diff line
@@ -87,6 +87,7 @@ import java.util.List;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
// TODO (b/194833441): remove when notification permission is enabled
public class RoleObserverTest extends UiServiceTestCase {
    private TestableNotificationManagerService mService;
    private NotificationManagerService.RoleObserver mRoleObserver;
@@ -162,7 +163,7 @@ public class RoleObserverTest extends UiServiceTestCase {
                    mock(AppOpsManager.class), mUm, mock(NotificationHistoryManager.class),
                    mock(StatsManager.class), mock(TelephonyManager.class),
                    mock(ActivityManagerInternal.class),
                    mock(MultiRateLimiter.class));
                    mock(MultiRateLimiter.class), mock(PermissionHelper.class));
        } catch (SecurityException e) {
            if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
                throw e;
+116 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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 android.companion.ICompanionDeviceManager;
import android.content.ComponentName;
import android.content.Context;

import androidx.annotation.Nullable;

import com.android.internal.logging.InstanceIdSequence;

import java.util.HashSet;
import java.util.Set;

public class TestableNotificationManagerService extends NotificationManagerService {
    int countSystemChecks = 0;
    boolean isSystemUid = true;
    int countLogSmartSuggestionsVisible = 0;
    Set<Integer> mChannelToastsSent = new HashSet<>();

    String stringArrayResourceValue;
    @Nullable
    NotificationAssistantAccessGrantedCallback mNotificationAssistantAccessGrantedCallback;

    TestableNotificationManagerService(Context context, NotificationRecordLogger logger,
            InstanceIdSequence notificationInstanceIdSequence) {
        super(context, logger, notificationInstanceIdSequence);
    }

    RankingHelper getRankingHelper() {
        return mRankingHelper;
    }

    @Override
    protected boolean isCallingUidSystem() {
        countSystemChecks++;
        return isSystemUid;
    }

    @Override
    protected boolean isCallerSystemOrPhone() {
        countSystemChecks++;
        return isSystemUid;
    }

    @Override
    protected ICompanionDeviceManager getCompanionManager() {
        return null;
    }

    @Override
    protected void reportUserInteraction(NotificationRecord r) {
        return;
    }

    @Override
    protected void handleSavePolicyFile() {
        return;
    }

    @Override
    void logSmartSuggestionsVisible(NotificationRecord r, int notificationLocation) {
        super.logSmartSuggestionsVisible(r, notificationLocation);
        countLogSmartSuggestionsVisible++;
    }

    @Override
    protected void setNotificationAssistantAccessGrantedForUserInternal(
            ComponentName assistant, int userId, boolean granted, boolean userSet) {
        if (mNotificationAssistantAccessGrantedCallback != null) {
            mNotificationAssistantAccessGrantedCallback.onGranted(assistant, userId, granted,
                    userSet);
            return;
        }
        super.setNotificationAssistantAccessGrantedForUserInternal(assistant, userId, granted,
                userSet);
    }

    @Override
    protected String[] getStringArrayResource(int key) {
        return new String[] {stringArrayResourceValue};
    }

    protected void setStringArrayResourceValue(String value) {
        stringArrayResourceValue = value;
    }

    void setNotificationAssistantAccessGrantedCallback(
            @Nullable NotificationAssistantAccessGrantedCallback callback) {
        this.mNotificationAssistantAccessGrantedCallback = callback;
    }

    interface NotificationAssistantAccessGrantedCallback {
        void onGranted(ComponentName assistant, int userId, boolean granted, boolean userSet);
    }

    @Override
    protected void doChannelWarningToast(int uid, CharSequence toastText) {
        mChannelToastsSent.add(uid);
    }
}