本文基于Android 14源码
1 NotificationManagerService的启动
1.1 添加服务
和其他系统服务一样,NotificationManagerService也是在SystemServer中启动的。
java
//framework/base/services/java/com/android/server/SystemServer.java
private void run() {
t.traceBegin("StartServices");
startBootstrapServices(t);
startCoreServices(t);
startOtherServices(t);
startApexServices(t);
}
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
t.traceBegin("StartNotificationManager");
mSystemServiceManager.startService(NotificationManagerService.class);
SystemNotificationChannels.removeDeprecated(context);
SystemNotificationChannels.createAll(context);
notification = INotificationManager.Stub.asInterface(
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
t.traceEnd();
}
NotificationManagerService是在startOtherServices中启动的,调用SystemServiceManager的startService之后,SystemServiceManager会通过反射创建NotificationManagerService实例对象,然后调用它的onStart()来启动服务。
java
//framework/base/services/core/java/com/android/server/notification/NotificationManagerService.java
@Override
public void onStart() {
SnoozeHelper snoozeHelper = new SnoozeHelper(getContext(), (userId, r, muteOnReturn) -> {
try {
if (DBG) {
Slog.d(TAG, "Reposting " + r.getKey() + " " + muteOnReturn);
}
enqueueNotificationInternal(r.getSbn().getPackageName(), r.getSbn().getOpPkg(),
r.getSbn().getUid(), r.getSbn().getInitialPid(), r.getSbn().getTag(),
r.getSbn().getId(), r.getSbn().getNotification(), userId, muteOnReturn,
false /* byForegroundService */);
} catch (Exception e) {
Slog.e(TAG, "Cannot un-snooze notification", e);
}
}, mUserProfiles);
final File systemDir = new File(Environment.getDataDirectory(), "system");
mRankingThread.start();
WorkerHandler handler = new WorkerHandler(Looper.myLooper());
mShowReviewPermissionsNotification = getContext().getResources().getBoolean(
R.bool.config_notificationReviewPermissions);
init(handler, new RankingHandlerWorker(mRankingThread.getLooper()),
AppGlobals.getPackageManager(), getContext().getPackageManager(),
getLocalService(LightsManager.class),
new NotificationListeners(getContext(), mNotificationLock, mUserProfiles,
AppGlobals.getPackageManager()),
new NotificationAssistants(getContext(), mNotificationLock, mUserProfiles,
AppGlobals.getPackageManager()),
new ConditionProviders(getContext(), mUserProfiles, AppGlobals.getPackageManager()),
null, snoozeHelper, new NotificationUsageStats(getContext()),
new AtomicFile(new File(
systemDir, "notification_policy.xml"), "notification-policy"),
(ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE),
getGroupHelper(), ActivityManager.getService(),
LocalServices.getService(ActivityTaskManagerInternal.class),
LocalServices.getService(UsageStatsManagerInternal.class),
LocalServices.getService(DevicePolicyManagerInternal.class),
UriGrantsManager.getService(),
LocalServices.getService(UriGrantsManagerInternal.class),
getContext().getSystemService(AppOpsManager.class),
getContext().getSystemService(UserManager.class),
new NotificationHistoryManager(getContext(), handler),
mStatsManager = (StatsManager) getContext().getSystemService(
Context.STATS_MANAGER),
getContext().getSystemService(TelephonyManager.class),
LocalServices.getService(ActivityManagerInternal.class),
createToastRateLimiter(), new PermissionHelper(getContext(),
AppGlobals.getPackageManager(),
AppGlobals.getPermissionManager()),
LocalServices.getService(UsageStatsManagerInternal.class),
getContext().getSystemService(TelecomManager.class),
new NotificationChannelLoggerImpl(), SystemUiSystemPropertiesFlags.getResolver(),
getContext().getSystemService(PermissionManager.class),
getContext().getSystemService(PowerManager.class),
new PostNotificationTrackerFactory() {});
publishBinderService(Context.NOTIFICATION_SERVICE, mService, /* allowIsolated= */ false,
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL);
publishLocalService(NotificationManagerInternal.class, mInternalService);
}
1.2 加载policy
在NotificationManagerService的init里面会指定notification policy保存路径,也就是/data/system/notification_policy.xml
// Persistent storage for notification policy
private AtomicFile mPolicyFile;
new AtomicFile(new File(systemDir, "notification_policy.xml"), "notification-policy")
然后将其保存到mPolicyFile,接着是加载policy文件。
mPolicyFile = policyFile;
loadPolicyFile();
来看loadPolicyFile
java
protected void loadPolicyFile() {
if (DBG) Slog.d(TAG, "loadPolicyFile");
synchronized (mPolicyFile) {
InputStream infile = null;
try {
infile = mPolicyFile.openRead();
readPolicyXml(infile, false /*forRestore*/, UserHandle.USER_ALL);
} catch (FileNotFoundException e) {
// No data yet
// Load default managed services approvals
//第一次的话文件没找到,加载默认的允许的管理服务
loadDefaultApprovedServices(USER_SYSTEM);
//末尾会保存文件,之后就不会走到这个异常里了
allowDefaultApprovedServices(USER_SYSTEM);
} catch (IOException e) {
Log.wtf(TAG, "Unable to read notification policy", e);
} catch (NumberFormatException e) {
Log.wtf(TAG, "Unable to parse notification policy", e);
} catch (XmlPullParserException e) {
Log.wtf(TAG, "Unable to parse notification policy", e);
} finally {
IoUtils.closeQuietly(infile);
}
}
}
loadPolicyFile主要工作是在readPolicyXml来解析xml。
不过,首次加载会走catch方法里,第一次文件肯定不存在,catch里会加载默认数据。
先来看loadDefaultApprovedServices:
java
void loadDefaultApprovedServices(int userId) {
mListeners.loadDefaultsFromConfig();
mConditionProviders.loadDefaultsFromConfig();
mAssistants.loadDefaultsFromConfig();
}
loadDefaultApprovedServices主要是加载一些默认的配置。
再来看allowDefaultApprovedServices。
java
protected void allowDefaultApprovedServices(int userId) {
ArraySet<ComponentName> defaultListeners = mListeners.getDefaultComponents();
for (int i = 0; i < defaultListeners.size(); i++) {
ComponentName cn = defaultListeners.valueAt(i);
allowNotificationListener(userId, cn);
}
ArraySet<String> defaultDnds = mConditionProviders.getDefaultPackages();
for (int i = 0; i < defaultDnds.size(); i++) {
allowDndPackage(userId, defaultDnds.valueAt(i));
}
setDefaultAssistantForUser(userId);
}
defaultListeners是从config_defaultListenerAccessPackages中获取的
java
String defaultListenerAccess = mContext.getResources().getString(
R.string.config_defaultListenerAccessPackages);
不过,源码里面这个值是空的
xml
<!-- Colon separated list of package names that should be granted Notification Listener access -->
<string name="config_defaultListenerAccessPackages" translatable="false"></string>
但是在GMS里有配置
xml
<string name="config_defaultListenerAccessPackages" translatable="false">com.android.launcher3:com.google.android.projection.gearhead</string>
再来看readPolicyXml
java
void readPolicyXml(InputStream stream, boolean forRestore, int userId)
throws XmlPullParserException, NumberFormatException, IOException {
final TypedXmlPullParser parser;
if (forRestore) {
parser = Xml.newFastPullParser();
parser.setInput(stream, StandardCharsets.UTF_8.name());
} else {
parser = Xml.resolvePullParser(stream);
}
XmlUtils.beginDocument(parser, TAG_NOTIFICATION_POLICY);
boolean migratedManagedServices = false;
UserInfo userInfo = mUmInternal.getUserInfo(userId);
boolean ineligibleForManagedServices = forRestore &&
(userInfo.isManagedProfile() || userInfo.isCloneProfile());
int outerDepth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
if (ZenModeConfig.ZEN_TAG.equals(parser.getName())) {
mZenModeHelper.readXml(parser, forRestore, userId);
} else if (PreferencesHelper.TAG_RANKING.equals(parser.getName())){
mPreferencesHelper.readXml(parser, forRestore, userId);
}
if (mListeners.getConfig().xmlTag.equals(parser.getName())) {
if (ineligibleForManagedServices) {
continue;
}
mListeners.readXml(parser, mAllowedManagedServicePackages, forRestore, userId);
migratedManagedServices = true;
} else if (mAssistants.getConfig().xmlTag.equals(parser.getName())) {
if (ineligibleForManagedServices) {
continue;
}
mAssistants.readXml(parser, mAllowedManagedServicePackages, forRestore, userId);
migratedManagedServices = true;
} else if (mConditionProviders.getConfig().xmlTag.equals(parser.getName())) {
if (ineligibleForManagedServices) {
continue;
}
mConditionProviders.readXml(
parser, mAllowedManagedServicePackages, forRestore, userId);
migratedManagedServices = true;
} else if (mSnoozeHelper.XML_TAG_NAME.equals(parser.getName())) {
mSnoozeHelper.readXml(parser, System.currentTimeMillis());
}
if (LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_TAG.equals(parser.getName())) {
if (forRestore && userId != UserHandle.USER_SYSTEM) {
continue;
}
mLockScreenAllowSecureNotifications = parser.getAttributeBoolean(null,
LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_VALUE, true);
}
}
if (!migratedManagedServices) {
mListeners.migrateToXml();
mAssistants.migrateToXml();
mConditionProviders.migrateToXml();
handleSavePolicyFile();
}
mAssistants.resetDefaultAssistantsIfNecessary();
}
xml的解析的tag是从notification-policy开始的。
如果tag是zen,则调用mZenModeHelper.readXml(parser, forRestore, userId)
如果是ranking,调用mPreferencesHelper.readXml(parser, forRestore, userId)
然后将配置保存的对应的config对象里面。
2 重要类
2.1 Notification
java
/**
* A class that represents how a persistent notification is to be presented to
* the user using the {@link android.app.NotificationManager}.
*
* <p>The {@link Notification.Builder Notification.Builder} has been added to make it
* easier to construct Notifications.</p>
*
* <div class="special reference">
* <h3>Developer Guides</h3>
* <p>For a guide to creating notifications, read the
* <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Status Bar Notifications</a>
* developer guide.</p>
* </div>
*/
public class Notification implements Parcelable
从描述看,Notification是通过NotificationManager来组装通知,呈现给用户。它是App层创建Notification使用的数据结构。Notification包含title,icon,discription等内容。
通过使用Notification.Builder可以使创建通知更加简单。
Notification实现了Parcelable,因此可以跨进程传递。
再来按他的成员变量:
java
public long when;
public int icon;
public PendingIntent contentIntent;
public PendingIntent deleteIntent;
public PendingIntent fullScreenIntent;
public CharSequence tickerText;
public RemoteViews tickerView;
public RemoteViews contentView;
public RemoteViews bigContentView;
public RemoteViews headsUpContentView;
public Uri sound;
Notification的成变量和成员函数比较多,从代码看,主要是提供了描述通知的逻辑。
Notification还至少有一个Action,Action是以PendingIntent的形式关联到Notification中的,也可以通过NotificationCompat.Builder.addAction(int icon, CharSequence title, PendingIntent intent)函数设定其他的action。Action在UI中是以Button的形式体现的。
2.2 NotificationRecord
java
/**
* Holds data about notifications that should not be shared with the
* {@link android.service.notification.NotificationListenerService}s.
*
* <p>These objects should not be mutated unless the code is synchronized
* on {@link NotificationManagerService#mNotificationLock}, and any
* modification should be followed by a sorting of that list.</p>
*
* <p>Is sortable by {@link NotificationComparator}.</p>
*
* {@hide}
*/
public final class NotificationRecord {}
NotificationRecord是NotificationManagerService用来管理所有Notification的数据结构。包含Notification数据结构,package,userid,id,tag,statusBarKey等内容,其中package,userid,id,tag可以用来唯一标识一个NotificationRecord,statusBarKey是可以唯一标识StatusBarManagerService中StatusBarNotification的。
再来看他的一些变量和函数。
java
private final StatusBarNotification sbn;
NotificationUsageStats.SingleNotificationStats stats;
private final NotificationStats mStats;
private ArraySet<String> mPhoneNumbers;
public NotificationRecord(Context context, StatusBarNotification sbn,
NotificationChannel channel) {
this.sbn = sbn;
mTargetSdkVersion = LocalServices.getService(PackageManagerInternal.class)
.getPackageTargetSdkVersion(sbn.getPackageName());
mAm = ActivityManager.getService();
mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
mOriginalFlags = sbn.getNotification().flags;
mRankingTimeMs = calculateRankingTimeMs(0L);
mCreationTimeMs = sbn.getPostTime();
mUpdateTimeMs = mCreationTimeMs;
mInterruptionTimeMs = mCreationTimeMs;
mContext = context;
stats = new NotificationUsageStats.SingleNotificationStats();
mChannel = channel;
mPreChannelsNotification = isPreChannelsNotification();
mSound = calculateSound();
mVibration = calculateVibration();
mAttributes = calculateAttributes();
mImportance = calculateInitialImportance();
mLight = calculateLights();
mAdjustments = new ArrayList<>();
mStats = new NotificationStats();
calculateUserSentiment();
calculateGrantableUris();
}
2.2.1 Sound获取
java
private Uri calculateSound() {
final Notification n = getSbn().getNotification();
// No notification sounds on tv
if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
return null;
}
Uri sound = mChannel.getSound();
if (mPreChannelsNotification && (getChannel().getUserLockedFields()
& NotificationChannel.USER_LOCKED_SOUND) == 0) {
final boolean useDefaultSound = (n.defaults & Notification.DEFAULT_SOUND) != 0;
if (useDefaultSound) {
sound = Settings.System.DEFAULT_NOTIFICATION_URI;
} else {
sound = n.sound;
}
}
return sound;
}
主要是获取声音的uri,可以看到默认位置是Settings.System.DEFAULT_NOTIFICATION_URI,当然也可以从Notification创建的时候进行指定。
2.2.2 light获取
java
private Light calculateLights() {
int defaultLightColor = mContext.getResources().getColor(
com.android.internal.R.color.config_defaultNotificationColor);
int defaultLightOn = mContext.getResources().getInteger(
com.android.internal.R.integer.config_defaultNotificationLedOn);
int defaultLightOff = mContext.getResources().getInteger(
com.android.internal.R.integer.config_defaultNotificationLedOff);
int channelLightColor = getChannel().getLightColor() != 0 ? getChannel().getLightColor()
: defaultLightColor;
Light light = getChannel().shouldShowLights() ? new Light(channelLightColor,
defaultLightOn, defaultLightOff) : null;
if (mPreChannelsNotification
&& (getChannel().getUserLockedFields()
& NotificationChannel.USER_LOCKED_LIGHTS) == 0) {
final Notification notification = getSbn().getNotification();
if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0) {
light = new Light(notification.ledARGB, notification.ledOnMS,
notification.ledOffMS);
if ((notification.defaults & Notification.DEFAULT_LIGHTS) != 0) {
light = new Light(defaultLightColor, defaultLightOn,
defaultLightOff);
}
} else {
light = null;
}
}
return light;
}
也是有默认颜色和默认开关设置,当然也支持自定义设置。
2.3 StatusBarNotification
java
/**
* Class encapsulating a Notification. Sent by the NotificationManagerService to clients including
* the status bar and any {@link android.service.notification.NotificationListenerService}s.
*/
public StatusBarNotification(String pkg, String opPkg, int id,
String tag, int uid, int initialPid,
Notification notification, UserHandle user,
String overrideGroupKey, long postTime) {
if (pkg == null) throw new NullPointerException();
if (notification == null) throw new NullPointerException();
this.pkg = pkg;
this.opPkg = opPkg;
this.id = id;
this.tag = tag;
this.uid = uid;
this.initialPid = initialPid;
this.notification = notification;
this.user = user;
this.postTime = postTime;
this.overrideGroupKey = overrideGroupKey;
this.key = key();
this.groupKey = groupKey();
}
StatusBarNotification是StatusBarManagerService中notification的数据结构,包含package,userid,id,tag和Notification数据结构等。
NotificationManagerService会将其发送给客户端,如SystemUI,因此它也实现了Parcelable。
2.3.1 key
可以看到key是一堆唯一的表示组合到一起的字符串.
java
private String key() {
String sbnKey = user.getIdentifier() + "|" + pkg + "|" + id + "|" + tag + "|" + uid;
if (overrideGroupKey != null && getNotification().isGroupSummary()) {
sbnKey = sbnKey + "|" + overrideGroupKey;
}
return sbnKey;
}
2.3.2 groupKey
groupKey和key类似。
java
private String groupKey() {
if (overrideGroupKey != null) {
return user.getIdentifier() + "|" + pkg + "|" + "g:" + overrideGroupKey;
}
final String group = getNotification().getGroup();
final String sortKey = getNotification().getSortKey();
if (group == null && sortKey == null) {
// a group of one
return key;
}
return user.getIdentifier() + "|" + pkg + "|" +
(group == null
? "c:" + notification.getChannelId()
: "g:" + group);
}
2.2.3 isGroup
java
/**
* Returns true if this notification is part of a group.
*/
public boolean isGroup() {
if (overrideGroupKey != null || isAppGroup()) {
return true;
}
return false;
}
/**
* Returns true if application asked that this notification be part of a group.
*/
public boolean isAppGroup() {
if (getNotification().getGroup() != null
|| getNotification().getSortKey() != null) {
return true;
}
return false;
}
2.4 StatusBarManagerService
java
/**
* A note on locking: We rely on the fact that calls onto mBar are oneway or
* if they are local, that they just enqueue messages to not deadlock.
*/
public StatusBarManagerService(Context context) {
mContext = context;
LocalServices.addService(StatusBarManagerInternal.class, mInternalService);
// We always have a default display.
final UiState state = new UiState();
mDisplayUiState.put(DEFAULT_DISPLAY, state);
final DisplayManager displayManager =
(DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
displayManager.registerDisplayListener(this, mHandler);
mActivityTaskManager = LocalServices.getService(ActivityTaskManagerInternal.class);
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
mTileRequestTracker = new TileRequestTracker(mContext);
mSessionMonitor = new SessionMonitor(mContext);
}
StatusBarManagerService是StatusBar的后台管理服务,处理StatusBar相关事件,例如显示Notification,Buttery/Signal Status,System Time等。同时也会处理Notification显示,消去,并且点击Notification时发送PendingIntent等也都需要通过这个Service。StatusBarManagerService也会跟NotificationManagerService交互,处理Notification相关的动作。
StatusBarManagerService的构造函数中,添加了StatusBarManagerInternal服务。
java
/**
* Private API used by NotificationManagerService.
*/
private final StatusBarManagerInternal mInternalService = new StatusBarManagerInternal() {
@Override
public void onCameraLaunchGestureDetected(int source) {
if (mBar != null) {
try {
mBar.onCameraLaunchGestureDetected(source);
} catch (RemoteException e) {
}
}
}
@Override
public void setDisableFlags(int displayId, int flags, String cause) {
StatusBarManagerService.this.setDisableFlags(displayId, flags, cause);
}
@Override
public void toggleSplitScreen() {
enforceStatusBarService();
if (mBar != null) {
try {
mBar.toggleSplitScreen();
} catch (RemoteException ex) {}
}
}
}