Anroid 11 关于NotificationManager && NotificationManagerService -- 衍生到权限管理
NotificationManager && NotificationManagerService
frameworks/base/core/java/android/app/NotificationManager.java
几个比较重要的函数:
//移除mContext.getUser发送的通知
public void cancel(@Nullable String tag, int id)
{
cancelAsUser(tag, id, mContext.getUser());
}
//移除所有通知
public void cancelAll()
{
INotificationManager service = getService();
String pkg = mContext.getPackageName();
if (localLOGV) Log.v(TAG, pkg + ": cancelAll()");
try {
service.cancelAllNotifications(pkg, mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
//发送通知,这里可以做判断屏蔽不发送通知
public void notify(int id, Notification notification)
public void notify(String tag, int id, Notification notification)
public void notifyAsUser(String tag, int id, Notification notification, UserHandle user)
{
INotificationManager service = getService();
String pkg = mContext.getPackageName();
try {
if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
fixNotification(notification), user.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java
enqueueNotificationInternal(...)
{
...
if (notificationUid == INVALID_UID) {
throw new SecurityException("Caller " + opPkg + ":" + callingUid
+ " trying to post for invalid pkg " + pkg + " in user " + incomingUserId);
}
//检查通知是否属于仅限于系统使用的类别类型
checkRestrictedCategories(pkg, notification);
...
//Checks if a notification can be posted. checks rate limiter, snooze helper, and blocking.
//检查是否可以发送通知。检查速率限制器、贪睡助手和阻塞。
if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r,
r.getSbn().getOverrideGroupKey() != null)) {
return;
}
...
mHandler.post(new EnqueueNotificationRunnable(userId, r, isAppForeground));
}
-> postPostNotificationRunnableMaybeDelayedLocked(r, new PostNotificationRunnable(r.getKey()));
->class PostNotificationRunnable implements Runnable
{
@Override
public void run() {
//通知放在通知队列中,按照顺序处理
synchronized (mNotificationLock) {
try {
NotificationRecord r = null;
int N = mEnqueuedNotifications.size();
for (int i = 0; i < N; i++) {
final NotificationRecord enqueued = mEnqueuedNotifications.get(i);
if (Objects.equals(key, enqueued.getKey())) {
r = enqueued;
break;
}
}
if (r == null) {
Slog.i(TAG, "Cannot find enqueued record for key: " + key);
return;
}
if (isBlocked(r)) {
Slog.i(TAG, "notification blocked by assistant request");
return;
}
}
//负责处理通知声音的函数.
//判断通知是否应尝试发出噪音、振动或闪烁LED,
//@return buzzBeepBlink - bitfield (buzz ? 1 : 0) | (beep ? 2 : 0) | (blink ? 4 : 0)
int buzzBeepBlinkLocked(NotificationRecord record) {
if (mIsAutomotive && !mNotificationEffectsEnabledForAutomotive) {
return 0;
}
boolean buzz = false;
boolean beep = false;
boolean blink = false;
...
if (hasAudibleAlert && !shouldMuteNotificationLocked(record)) {
if (!sentAccessibilityEvent) {
sendAccessibilityEvent(record);
sentAccessibilityEvent = true;
}
if (DBG) Slog.v(TAG, "Interrupting!");
if (hasValidSound) {
if (isInCall()) {
playInCallNotification();//电话铃声?
beep = true;
} else {
beep = playSound(record, soundUri);//播放通知的声音函数,soundUri.getPath()通知声音文件路径
}
if(beep) {
mSoundNotificationKey = key;
}
}
final boolean ringerModeSilent =
mAudioManager.getRingerModeInternal()
== AudioManager.RINGER_MODE_SILENT;
if (!isInCall() && hasValidVibrate && !ringerModeSilent) {
buzz = playVibration(record, vibration, hasValidSound);
if(buzz) {
mVibrateNotificationKey = key;
}
}
} else if ((record.getFlags() & Notification.FLAG_INSISTENT) != 0) {
hasValidSound = false;
}
}
...
}
Notification的setNotificationsEnabledForPackage接口的使用:屏蔽特定应用的通知提示
Notification的setNotificationsEnabledForPackage接口详解
./packages/apps/Settings/src/com/android/settings/notification/app/..
Settings界面负责通知权限开关的...
/**
* Parent class for preferences appearing on notification setting pages at the app,
* notification channel group, or notification channel level.
*/
public abstract class NotificationPreferenceController extends AbstractPreferenceController {
protected final NotificationManager mNm;
protected final NotificationBackend mBackend;
...
public NotificationPreferenceController(Context context, NotificationBackend backend) {
super(context);
mContext = context;
mNm = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
mBackend = backend;
mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
mPm = mContext.getPackageManager();
}
}
public class BlockPreferenceController extends NotificationPreferenceController
implements PreferenceControllerMixin, SwitchBar.OnSwitchChangeListener {
...
@Override
public void onSwitchChanged(Switch switchView, boolean isChecked) {
...
} else if (mChannelGroup != null) {
mChannelGroup.setBlocked(blocked);
mBackend.updateChannelGroup(mAppRow.pkg, mAppRow.uid, mChannelGroup);
} else if (mAppRow != null) {
mAppRow.banned = blocked;
mBackend.setNotificationsEnabledForPackage(mAppRow.pkg, mAppRow.uid, !blocked);
}
}
}
./packages/apps/Settings/src/com/android/settings/notification/NotificationBackend.java
//查询某个应用通知是否启用
public boolean getNotificationsBanned(String pkg, int uid) {
try {
final boolean enabled = sINM.areNotificationsEnabledForPackage(pkg, uid);
return !enabled;
} catch (Exception e) {
Log.w(TAG, "Error calling NoMan", e);
return false;
}
}
//打开或关闭某个应用通知
public boolean setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
try {
if (onlyHasDefaultChannel(pkg, uid)) {
NotificationChannel defaultChannel =
getChannel(pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID, null);
defaultChannel.setImportance(enabled ? IMPORTANCE_UNSPECIFIED : IMPORTANCE_NONE);
updateChannel(pkg, uid, defaultChannel);
}
sINM.setNotificationsEnabledForPackage(pkg, uid, enabled);
return true;
} catch (Exception e) {
Log.w(TAG, "Error calling NoMan", e);
return false;
}
}
frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java
@Override
public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
...
// Now, cancel any outstanding notifications that are part of a just-disabled app
if (!enabled) {
//取消某个应用中发送的所有通知
cancelAllNotificationsInt(MY_UID, MY_PID, pkg, null, 0, 0, true,
UserHandle.getUserId(uid), REASON_PACKAGE_BANNED, null);
}
//重新授予通知权限,true->MODE_ALLOWED 0(授予) false->MODE_IGNORED 1
mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
...
}
./frameworks/base/services/core/java/com/android/server/appop/AppOpsService.java
private void setMode(int code, int uid, @NonNull String packageName, int mode,
@Nullable IAppOpsCallback permissionPolicyCallback) {
...
if (repCbs != null) {
mHandler.sendMessage(PooledLambda.obtainMessage(
AppOpsService::notifyOpChanged,
this, repCbs, code, uid, packageName));
}
notifyOpChangedSync(code, uid, packageName, mode, previousMode);
}
AppOpsServicey详解
Android10原生设置中INotificationManager
关于Android的权限管理:一套是Android Runtime运行时权限机制,对应Framework中的PermissionManager和PermissionService服务;
一套是AppOpsManager和AppOpsService服务:App-ops提供两个用途:一个是访问控制,这个具体是和runtime运行时权限配合;
负责app权限管理的是AppOpsManager和AppOpsService,每次开机它都会走一遍流程,对相应的app进行权限的授予
./frameworks/base/core/java/android/app/AppOpsManager.java
public int checkOp(int op, int uid, String packageName) {
try {
int mode = mService.checkOperation(op, uid, packageName);
if (mode == MODE_ERRORED) {
throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
}
return mode;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
./frameworks/base/services/core/java/com/android/server/appop/AppOpsService.java
checkOperation->checkOperationInternal->checkOperationImpl
private @Mode int checkOperationUnchecked(int code, int uid, @NonNull String packageName,boolean raw) {
...
synchronized (this) {
if(packageName.equals("com.sclick.aura2")){
android.util.Log.d("tww","set synchronized!");
}
if (isOpRestrictedLocked(uid, code, packageName, bypass)) {
return AppOpsManager.MODE_IGNORED;
}
//add text
if(code == AppOpsManager.OP_POST_NOTIFICATION && packageName.equals("com.xxxx.xx")){
return AppOpsManager.MODE_IGNORED;
}
//add text
code = AppOpsManager.opToSwitch(code);
UidState uidState = getUidStateLocked(uid, false);
if (uidState != null && uidState.opModes != null
&& uidState.opModes.indexOfKey(code) >= 0) {
final int rawMode = uidState.opModes.get(code);
return raw ? rawMode : uidState.evalMode(code, rawMode);
}
Op op = getOpLocked(code, uid, packageName, null, bypass, false);
if (op == null) {
return AppOpsManager.opToDefaultMode(code);
}
return raw ? op.mode : op.evalMode();
}
//adb命令可以看应用当前的权限状态
adb shell dumpsys package com.android.systemui(包名)
declared permissions -- 自定义权限
requested permissions ——应用在AndroidManifest.xml中请求的权限
install permissions —— normal权限授权情况,包括signature权限
runtime permissions —— 危险权限授权情况
Framework中的Permission.java和PermissionManagerService.java;
updatePermissions->restorePermissionState{
//首先判断这个权限的属性是运行时权限还是安装权限,
isNormal()来进行判断,返回true,代表是安装权限;在isRuntime()中返回false就代表不是运行时权限
}
Settings负责管理应用通知动作的相关类
//控制
./packages/apps/Settings/src/com/android/settings/notification/app/BlockPreferenceController.java
./packages/apps/Settings/src/com/android/settings/notification/app/NotificationPreferenceController.java
./packages/apps/Settings/src/com/android/settings/notification/NotificationBackend.java
//通知--Suggested actions and replies 开关
./packages/apps/Settings/src/com/android/settings/notification/AssistantCapabilityPreferenceController.java
//应用图标上的通知原点--dot 开关
./packages/apps/Settings/src/com/android/settings/notification/BadgingNotificationPreferenceController.java
//有这个Secure属性控制
Settings.Secure.putInt(mContext.getContentResolver(),NOTIFICATION_BADGING,0);
adb shell settings get secure notification_badging
思路:接受开机广播,然后调用对应函数关闭它们
//shell 命令获取Uid ps -A | grep com.android.systemui
u0_a123 2140 269 14460252 246692 0 0 S com.android.systemui
a=10 十六进制 Uid = 10123
//获取系统内置应用的Uid
private void getAppUid(){
PackageManager mPm = getPackageManager();
try {
ApplicationInfo applicationInfo = mPm.getApplicationInfo("com.android.providers.downloads", 0);
int uid = applicationInfo.uid;
Log.d("tag","--->uid:"+uid);
}catch (Exception e){
e.printStackTrace();
}
}
import android.service.notification.Adjustment;
import com.android.settings.notification.NotificationBackend;
private boolean flag = false;
private void closeNotificationsEnabledApps() {
if (!flag) {
NotificationBackend mBackend = new NotificationBackend();
mBackend.setNotificationsEnabledForPackage("com.xx.xx", 10132, false);//10132 app的Uid 唯一固定不变的
//Suggested actions and replies
mBackend.allowAssistantAdjustment(Adjustment.KEY_CONTEXTUAL_ACTIONS, false);
mBackend.allowAssistantAdjustment(Adjustment.KEY_TEXT_REPLIES, false);
flag = true;
}
}