Disable app toggle for some T+ apps

Specifically, apps that haven't requested the notif permission
in their manifest, as we cannot grant/revoke the permission unless
they do

Test: NotificationBackendTest, AppStateNotificationBridgeTest
Fixes: 218315122

Change-Id: Icd936de806d7642809ef6c79d2d169bd673c2659
diff --git a/src/com/android/settings/applications/AppStateNotificationBridge.java b/src/com/android/settings/applications/AppStateNotificationBridge.java
index 3bcf94f..964eae4 100644
--- a/src/com/android/settings/applications/AppStateNotificationBridge.java
+++ b/src/com/android/settings/applications/AppStateNotificationBridge.java
@@ -24,11 +24,7 @@
 import android.text.format.DateUtils;
 import android.util.ArrayMap;
 import android.util.Log;
-import android.util.Slog;
-import android.view.View;
-import android.view.ViewGroup;
 import android.widget.CompoundButton;
-import android.widget.Switch;
 
 import com.android.settings.R;
 import com.android.settings.Utils;
@@ -127,8 +123,7 @@
     private void addBlockStatus(AppEntry entry, NotificationsSentState stats) {
         if (stats != null) {
             stats.blocked = mBackend.getNotificationsBanned(entry.info.packageName, entry.info.uid);
-            stats.systemApp = mBackend.isSystemApp(mContext, entry.info);
-            stats.blockable = !stats.systemApp || (stats.systemApp && stats.blocked);
+            stats.blockable = mBackend.enableSwitch(mContext, entry.info);
         }
     }
 
@@ -229,11 +224,13 @@
             return null;
         }
         return (buttonView, isChecked) -> {
-            mBackend.setNotificationsEnabledForPackage(
-                    entry.info.packageName, entry.info.uid, isChecked);
             NotificationsSentState stats = getNotificationsSentState(entry);
             if (stats != null) {
-                stats.blocked = !isChecked;
+                if (stats.blocked == isChecked) {
+                    mBackend.setNotificationsEnabledForPackage(
+                            entry.info.packageName, entry.info.uid, isChecked);
+                    stats.blocked = !isChecked;
+                }
             }
         };
     }
@@ -329,7 +326,6 @@
         if (stats == null) {
             return false;
         }
-
         return !stats.blocked;
     }
 
@@ -344,6 +340,5 @@
         public int sentCount = 0;
         public boolean blockable;
         public boolean blocked;
-        public boolean systemApp;
     }
 }
diff --git a/src/com/android/settings/notification/NotificationBackend.java b/src/com/android/settings/notification/NotificationBackend.java
index dbc36d0..2ae91e2 100644
--- a/src/com/android/settings/notification/NotificationBackend.java
+++ b/src/com/android/settings/notification/NotificationBackend.java
@@ -22,15 +22,20 @@
 import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER;
 import static android.os.UserHandle.USER_SYSTEM;
 
+import android.Manifest;
 import android.app.INotificationManager;
 import android.app.NotificationChannel;
 import android.app.NotificationChannelGroup;
 import android.app.NotificationHistory;
 import android.app.NotificationManager;
+import android.app.compat.CompatChanges;
 import android.app.role.RoleManager;
 import android.app.usage.IUsageStatsManager;
 import android.app.usage.UsageEvents;
 import android.companion.ICompanionDeviceManager;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
+import android.compat.annotation.EnabledSince;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -42,6 +47,7 @@
 import android.content.pm.ShortcutInfo;
 import android.content.pm.ShortcutManager;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
@@ -100,12 +106,6 @@
         return row;
     }
 
-    public boolean isBlockable(Context context, ApplicationInfo info) {
-        final boolean blocked = getNotificationsBanned(info.packageName, info.uid);
-        final boolean systemApp = isSystemApp(context, info);
-        return !systemApp || (systemApp && blocked);
-    }
-
     public AppRow loadAppRow(Context context, PackageManager pm,
             RoleManager roleManager, PackageInfo app) {
         final AppRow row = loadAppRow(context, pm, app.applicationInfo);
@@ -130,6 +130,15 @@
                     || roles.contains(RoleManager.ROLE_EMERGENCY)) {
                 row.systemApp = row.lockedImportance = true;
             }
+            // if the app targets T but has not requested the permission, we cannot change the
+            // permission state
+            if (app.applicationInfo.targetSdkVersion > Build.VERSION_CODES.S_V2) {
+                if (app.requestedPermissions == null || Arrays.stream(app.requestedPermissions)
+                        .noneMatch(p -> p.equals(android.Manifest.permission.POST_NOTIFICATIONS))) {
+                    row.lockedImportance = true;
+                }
+            }
+
         } else {
             row.systemApp = Utils.isSystemPackage(context.getResources(), pm, app);
             List<String> roles = rm.getHeldRolesFromController(app.packageName);
@@ -192,14 +201,15 @@
         return sb.toString();
     }
 
-    public boolean isSystemApp(Context context, ApplicationInfo app) {
+    public boolean enableSwitch(Context context, ApplicationInfo app) {
         try {
             PackageInfo info = context.getPackageManager().getPackageInfo(
                     app.packageName, PackageManager.GET_SIGNATURES);
             RoleManager rm = context.getSystemService(RoleManager.class);
             final AppRow row = new AppRow();
             recordCanBeBlocked(context, context.getPackageManager(), rm, info, row);
-            return row.systemApp;
+            boolean systemBlockable = !row.systemApp || (row.systemApp && row.banned);
+            return systemBlockable && !row.lockedImportance;
         } catch (PackageManager.NameNotFoundException e) {
             e.printStackTrace();
         }
diff --git a/tests/robotests/src/com/android/settings/applications/AppStateNotificationBridgeTest.java b/tests/robotests/src/com/android/settings/applications/AppStateNotificationBridgeTest.java
index 21a2947..b725fc3 100644
--- a/tests/robotests/src/com/android/settings/applications/AppStateNotificationBridgeTest.java
+++ b/tests/robotests/src/com/android/settings/applications/AppStateNotificationBridgeTest.java
@@ -99,7 +99,7 @@
         when(mState.newSession(any())).thenReturn(mSession);
         when(mState.getBackgroundLooper()).thenReturn(mock(Looper.class));
         when(mBackend.getNotificationsBanned(anyString(), anyInt())).thenReturn(true);
-        when(mBackend.isSystemApp(any(), any())).thenReturn(true);
+        when(mBackend.enableSwitch(any(), any())).thenReturn(true);
         // most tests assume no work profile
         when(mUserManager.getProfileIdsWithDisabled(anyInt())).thenReturn(new int[]{});
         mContext = RuntimeEnvironment.application.getApplicationContext();
@@ -245,7 +245,6 @@
         assertThat(((NotificationsSentState) apps.get(0).extraInfo).avgSentDaily).isEqualTo(1);
         assertThat(((NotificationsSentState) apps.get(0).extraInfo).avgSentWeekly).isEqualTo(0);
         assertThat(((NotificationsSentState) apps.get(0).extraInfo).blocked).isTrue();
-        assertThat(((NotificationsSentState) apps.get(0).extraInfo).systemApp).isTrue();
         assertThat(((NotificationsSentState) apps.get(0).extraInfo).blockable).isTrue();
     }
 
@@ -376,7 +375,6 @@
         assertThat(((NotificationsSentState) entry.extraInfo).avgSentDaily).isEqualTo(2);
         assertThat(((NotificationsSentState) entry.extraInfo).avgSentWeekly).isEqualTo(0);
         assertThat(((NotificationsSentState) entry.extraInfo).blocked).isTrue();
-        assertThat(((NotificationsSentState) entry.extraInfo).systemApp).isTrue();
         assertThat(((NotificationsSentState) entry.extraInfo).blockable).isTrue();
     }
 
@@ -563,11 +561,11 @@
         entry.extraInfo = new NotificationsSentState();
 
         CompoundButton.OnCheckedChangeListener listener = mBridge.getSwitchOnCheckedListener(entry);
-        listener.onCheckedChanged(toggle, true);
+        listener.onCheckedChanged(toggle, false);
 
         verify(mBackend).setNotificationsEnabledForPackage(
-                entry.info.packageName, entry.info.uid, true);
-        assertThat(((NotificationsSentState) entry.extraInfo).blocked).isFalse();
+                entry.info.packageName, entry.info.uid, false);
+        assertThat(((NotificationsSentState) entry.extraInfo).blocked).isTrue();
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/notification/NotificationBackendTest.java b/tests/robotests/src/com/android/settings/notification/NotificationBackendTest.java
index 64cde5a..a7ddec3 100644
--- a/tests/robotests/src/com/android/settings/notification/NotificationBackendTest.java
+++ b/tests/robotests/src/com/android/settings/notification/NotificationBackendTest.java
@@ -37,7 +37,9 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PermissionInfo;
 import android.net.MacAddress;
+import android.os.Build;
 import android.os.Parcel;
 import android.provider.Settings;
 
@@ -178,6 +180,51 @@
     }
 
     @Test
+    public void testMarkAppRow_targetsT_noPermissionRequest() throws Exception {
+        Secure.putIntForUser(RuntimeEnvironment.application.getContentResolver(),
+                Settings.Secure.NOTIFICATION_PERMISSION_ENABLED, 1, USER_SYSTEM);
+
+        PackageInfo pi = new PackageInfo();
+        pi.packageName = "test";
+        pi.applicationInfo = new ApplicationInfo();
+        pi.applicationInfo.packageName = "test";
+        pi.applicationInfo.uid = 123;
+        pi.applicationInfo.targetSdkVersion= Build.VERSION_CODES.TIRAMISU;
+        pi.requestedPermissions = new String[] {"something"};
+
+        when(mInm.isPermissionFixed(pi.packageName, 0)).thenReturn(false);
+
+        AppRow appRow = new NotificationBackend().loadAppRow(RuntimeEnvironment.application,
+                mock(PackageManager.class), mock(RoleManager.class), pi);
+
+        assertFalse(appRow.systemApp);
+        assertTrue(appRow.lockedImportance);
+    }
+
+    @Test
+    public void testMarkAppRow_targetsT_permissionRequest() throws Exception {
+        Secure.putIntForUser(RuntimeEnvironment.application.getContentResolver(),
+                Settings.Secure.NOTIFICATION_PERMISSION_ENABLED, 1, USER_SYSTEM);
+
+        PackageInfo pi = new PackageInfo();
+        pi.packageName = "test";
+        pi.applicationInfo = new ApplicationInfo();
+        pi.applicationInfo.packageName = "test";
+        pi.applicationInfo.uid = 123;
+        pi.applicationInfo.targetSdkVersion= Build.VERSION_CODES.TIRAMISU;
+        pi.requestedPermissions = new String[] {"something",
+                android.Manifest.permission.POST_NOTIFICATIONS};
+
+        when(mInm.isPermissionFixed(pi.packageName, 0)).thenReturn(false);
+
+        AppRow appRow = new NotificationBackend().loadAppRow(RuntimeEnvironment.application,
+                mock(PackageManager.class), mock(RoleManager.class), pi);
+
+        assertFalse(appRow.systemApp);
+        assertFalse(appRow.lockedImportance);
+    }
+
+    @Test
     public void testMarkAppRow_notDefaultPackage() {
         PackageInfo pi = new PackageInfo();
         pi.packageName = "test";