1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.car.settings.notifications; 18 19 import static android.app.NotificationManager.IMPORTANCE_NONE; 20 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; 21 22 import android.Manifest; 23 import android.app.INotificationManager; 24 import android.app.NotificationChannel; 25 import android.car.drivingstate.CarUxRestrictions; 26 import android.content.Context; 27 import android.content.pm.ApplicationInfo; 28 import android.content.pm.PackageInfo; 29 import android.content.pm.PackageManager; 30 import android.os.Build; 31 import android.os.ServiceManager; 32 import android.os.UserHandle; 33 34 import androidx.annotation.VisibleForTesting; 35 import androidx.preference.Preference; 36 37 import com.android.car.settings.common.FragmentController; 38 import com.android.car.settings.common.Logger; 39 import com.android.car.settings.common.PreferenceController; 40 41 /** 42 * Base notifications class that handles checking and changing notification availability 43 * 44 * @param <T> The upper bound on the type of {@link Preference} on which the controller 45 * expects to operate. 46 */ 47 public abstract class BaseNotificationsPreferenceController<T extends Preference> extends 48 PreferenceController<T> { 49 50 private static final Logger LOG = new Logger(BaseNotificationsPreferenceController.class); 51 52 @VisibleForTesting 53 public INotificationManager mNotificationManager = INotificationManager.Stub.asInterface( 54 ServiceManager.getService(Context.NOTIFICATION_SERVICE)); 55 BaseNotificationsPreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions)56 public BaseNotificationsPreferenceController(Context context, String preferenceKey, 57 FragmentController fragmentController, CarUxRestrictions uxRestrictions) { 58 super(context, preferenceKey, fragmentController, uxRestrictions); 59 } 60 61 /** 62 * Changes the notifications availability of the specified app 63 * 64 * @param packageName Package name of app 65 * @param uid Uid of app 66 * @param enabled Whether to enable or disable the notification 67 * @return Whether changing the notification availability succeeded or not 68 */ toggleNotificationsSetting(String packageName, int uid, boolean enabled)69 public boolean toggleNotificationsSetting(String packageName, int uid, boolean enabled) { 70 try { 71 if (mNotificationManager.onlyHasDefaultChannel(packageName, uid)) { 72 NotificationChannel defaultChannel = 73 mNotificationManager.getNotificationChannelForPackage( 74 packageName, 75 uid, 76 NotificationChannel.DEFAULT_CHANNEL_ID, 77 /* conversationId= */ null, 78 /* includeDeleted= */ true); 79 defaultChannel.setImportance(enabled ? IMPORTANCE_UNSPECIFIED : IMPORTANCE_NONE); 80 mNotificationManager 81 .updateNotificationChannelForPackage(packageName, uid, defaultChannel); 82 } 83 mNotificationManager.setNotificationsEnabledForPackage(packageName, uid, enabled); 84 } catch (Exception e) { 85 LOG.w("Error querying notification setting for package"); 86 return false; 87 } 88 return true; 89 } 90 91 /** 92 * Checks whether notifications are enabled for specified app 93 * 94 * @param packageName Package name of the app 95 * @param uid Uid of the app 96 * @return Whether notifications are enabled for the specified app 97 */ areNotificationsEnabled(String packageName, int uid)98 public boolean areNotificationsEnabled(String packageName, int uid) { 99 try { 100 return mNotificationManager.areNotificationsEnabledForPackage(packageName, uid); 101 } catch (Exception e) { 102 LOG.w("Error querying notification setting for package"); 103 return false; 104 } 105 } 106 107 /** 108 * Checks whether notification permission can be changed for a specified app 109 * 110 * @param appInfo ApplicationInfo of the app 111 * @return Whether notification permissions can be changed for the specified app 112 */ areNotificationsChangeable(ApplicationInfo appInfo)113 public boolean areNotificationsChangeable(ApplicationInfo appInfo) { 114 String packageName = appInfo.packageName; 115 int uid = appInfo.uid; 116 117 try { 118 if (mNotificationManager.isImportanceLocked(packageName, uid)) { 119 return false; 120 } 121 122 PackageManager packageManager = getContext().getPackageManager(); 123 124 if (appInfo.targetSdkVersion < Build.VERSION_CODES.TIRAMISU) { 125 return doesNotHavePermissionFixedFlags(packageManager, packageName, 126 UserHandle.getUserHandleForUid(uid)); 127 } 128 129 PackageInfo packageInfo = packageManager.getPackageInfoAsUser( 130 packageName, PackageManager.GET_PERMISSIONS, UserHandle.getUserId(uid)); 131 String[] permissions = packageInfo.requestedPermissions; 132 for (String permission : permissions) { 133 if (permission.equals(Manifest.permission.POST_NOTIFICATIONS)) { 134 return doesNotHavePermissionFixedFlags(packageManager, packageName, 135 UserHandle.getUserHandleForUid(uid)); 136 } 137 } 138 139 return false; 140 } catch (Exception e) { 141 LOG.w("Error querying notification setting for package"); 142 return false; 143 } 144 } 145 doesNotHavePermissionFixedFlags(PackageManager packageManager, String packageName, UserHandle userHandle)146 private boolean doesNotHavePermissionFixedFlags(PackageManager packageManager, 147 String packageName, UserHandle userHandle) { 148 int flags = packageManager.getPermissionFlags( 149 Manifest.permission.POST_NOTIFICATIONS, packageName, userHandle); 150 151 return (flags & (PackageManager.FLAG_PERMISSION_SYSTEM_FIXED 152 | PackageManager.FLAG_PERMISSION_POLICY_FIXED)) == 0; 153 } 154 } 155