最近在开发电子白板项目,系统用的是Android8.0
最近一个需求是:系统自带的应用和预装的应用可以弹出通知,其他应用不能弹出
8.0的通知使用可以查看 Android8.0中通知无法正常使用问题
要弹出通知,会使用到NotificationManager的notify方法
那就来看NotificationManager的代码吧,NotificationManager的代码位置是在(framework/base/core/java/andtoid/app/)
notify的代码如下
public void notify(int id, Notification notification)
{
notify(null, id, notification);
}
public void notify(String tag, int id, Notification notification)
{
notifyAsUser(tag, id, notification, new UserHandle(UserHandle.myUserId()));
}
这里走到notifyAsUser,notifyAsUser的代码如下
public void notifyAsUser(String tag, int id, Notification notification, UserHandle user)
{
INotificationManager service = getService();
String pkg = mContext.getPackageName();
// Fix the notification as best we can.
Notification.addFieldsFromContext(mContext, notification);
if (notification.sound != null) {
notification.sound = notification.sound.getCanonicalUri();
if (StrictMode.vmFileUriExposureEnabled()) {
notification.sound.checkFileUriExposed("Notification.sound");
}
}
fixLegacySmallIcon(notification, pkg);
if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
if (notification.getSmallIcon() == null) {
throw new IllegalArgumentException("Invalid notification (no valid small icon): "
+ notification);
}
}
if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
final Notification copy = Builder.maybeCloneStrippedForDelivery(notification);
try {
service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
copy, user.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
这里最后调了service的enqueueNotificationWithTag,这里的service是NotificationManagerService,
NotificationManagerService里的enqueueNotificationWithTag代码如下
@Override
public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
Notification notification, int userId) throws RemoteException {
enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
Binder.getCallingPid(), tag, id, notification, userId);
}
这里调了 enqueueNotificationInternal,enqueueNotificationInternal的代码如下
void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
final int callingPid, final String tag, final int id, final Notification notification,
int incomingUserId) {
if (DBG) {
Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
+ " notification=" + notification);
}
checkCallerIsSystemOrSameApp(pkg);
//zhongxiang.huang add begin
//Notification interception
if(!isSystemApp(pkg) && !isNoticeWhiteApp(pkg)){
Slog.i(TAG, ">>>>>>>>>>>>>>>>>>>>>>Notification interception return<<<<<<<<<<<<<<<<<");
return;
}
//zhongxiang.huang add end
final int userId = ActivityManager.handleIncomingUser(callingPid,
callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
final UserHandle user = new UserHandle(userId);
if (pkg == null || notification == null) {
throw new IllegalArgumentException("null not allowed: pkg=" + pkg
+ " id=" + id + " notification=" + notification);
}
// The system can post notifications for any package, let us resolve that.
final int notificationUid = resolveNotificationUid(opPkg, callingUid, userId);
// Fix the notification as best we can.
try {
final ApplicationInfo ai = mPackageManagerClient.getApplicationInfoAsUser(
pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
(userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);
Notification.addFieldsFromContext(ai, notification);
} catch (NameNotFoundException e) {
Slog.e(TAG, "Cannot create a context for sending app", e);
return;
}
mUsageStats.registerEnqueuedByApp(pkg);
// setup local book-keeping
String channelId = notification.getChannelId();
if (mIsTelevision && (new Notification.TvExtender(notification)).getChannelId() != null) {
channelId = (new Notification.TvExtender(notification)).getChannelId();
}
final NotificationChannel channel = mRankingHelper.getNotificationChannel(pkg,
notificationUid, channelId, false /* includeDeleted */);
if (channel == null) {
final String noChannelStr = "No Channel found for "
+ "pkg=" + pkg
+ ", channelId=" + channelId
+ ", id=" + id
+ ", tag=" + tag
+ ", opPkg=" + opPkg
+ ", callingUid=" + callingUid
+ ", userId=" + userId
+ ", incomingUserId=" + incomingUserId
+ ", notificationUid=" + notificationUid
+ ", notification=" + notification;
Log.e(TAG, noChannelStr);
doChannelWarningToast("Developer warning for package \"" + pkg + "\"\n" +
"Failed to post notification on channel \"" + channelId + "\"\n" +
"See log for more details");
return;
}
final StatusBarNotification n = new StatusBarNotification(
pkg, opPkg, id, tag, notificationUid, callingPid, notification,
user, null, System.currentTimeMillis());
final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r)) {
return;
}
// Whitelist pending intents.
if (notification.allPendingIntents != null) {
final int intentCount = notification.allPendingIntents.size();
if (intentCount > 0) {
final ActivityManagerInternal am = LocalServices
.getService(ActivityManagerInternal.class);
final long duration = LocalServices.getService(
DeviceIdleController.LocalService.class).getNotificationWhitelistDuration();
for (int i = 0; i < intentCount; i++) {
PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i);
if (pendingIntent != null) {
am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(),
WHITELIST_TOKEN, duration);
}
}
}
}
mHandler.post(new EnqueueNotificationRunnable(userId, r));
}
这里最终会走到mHandler.post(new EnqueueNotificationRunnable(userId, r)),把消息发出去
拦截通知的话在这里处理就好了,在方法里我加了如下代码
//zhongxiang.huang add begin
//fix bug - asc07-561(617977), Notification interception
if(!isSystemApp(pkg) && !isNoticeWhiteApp(pkg)){
Slog.i(TAG, ">>>>>>>>>>>>>>>>>>>>>>Notification interception return<<<<<<<<<<<<<<<<<");
return;
}
//zhongxiang.huang add end
判断是否是系统应用,是否在我的白名单应用列表里的,两者都不是的话直接return
isSystemApp的代码如下
private boolean isSystemApp(String pkg){
if(null != pkg){
try{
PackageInfo info = mPackageManagerClient.getPackageInfo(pkg, 0);
return (info != null)
&&(info.applicationInfo != null)
&&((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
}catch(PackageManager.NameNotFoundException e){
Slog.e(TAG, ">>>>isSystemApp error<<<<<<<e:"+e);
return false;
}
}
return false;
}
isNoticeWhiteApp的代码如下
private ArrayList<String> mNotificationAppList = new ArrayList<String>();
private static final String XML_NOTIFICATIONAPPS_FILE = "/system/etc/notification_app.xml";
private boolean isAppXMLLoaded = false;
private boolean isNoticeWhiteApp(String pkgName) {
if (!isAppXMLLoaded) {
loadNotificationAppList();
isAppXMLLoaded = true;
}
boolean isWhiteApp = false;
if (pkgName != null && mNotificationAppList.size() > 0) {
for (int i = 0; i < mNotificationAppList.size(); i++) {
if (pkgName.equals(mNotificationAppList.get(i))) {
isWhiteApp = true;
break;
}
}
}
return isWhiteApp;
}
private void loadNotificationAppList() {
Slog.i(TAG, "loadNotificationAppList: app white list");
try{
InputStream inStream = new FileInputStream(new File(XML_NOTIFICATIONAPPS_FILE));
XmlPullParser parser = Xml.newPullParser();
parser.setInput(inStream, "utf-8");
int eventType = parser.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
switch (eventType) {
case XmlPullParser.START_TAG:
String name = parser.getName();
if (name.equalsIgnoreCase("app")) {
String app_name = parser.getAttributeValue(null, "name");
if (!app_name.isEmpty()) {
mNotificationAppList.add(app_name);
}
}
break;
}
eventType = parser.next();
}
inStream.close();
} catch (IOException eo) {
eo.printStackTrace();
} catch (XmlPullParserException ex) {
ex.printStackTrace();
}
Log.i(TAG, "loadNotificationAppList: app white list. app_num="+mNotificationAppList.size());
}
loadNotificationAppList方法是读取notification_app.xml文件里的应用列表,该文件是在系统的/system/etc/目录下
源码是放在/device/hisilicon/bigfish/etc/目录下,在编译的时候把它拷贝到/system/etc/目录下
<?xml version="1.0" encoding="utf-8" ?>
<Apps_notification>
<app name="com.android.calendar"/>
<app name="com.android.email"/>
<app name="com.android.calculator2"/>
......
</Apps_notification>
/device/tpv / EBONY_V811/device_copyfile.mk添加以下代码,意思是将notification_app.xml文件从/device/hisilicon/bigfish/etc/目录拷拷贝到/system/etc/目录下
PRODUCT_COPY_FILES += \
device/hisilicon/bigfish/etc/notification_app.xml:/system/etc/notification_app.xml