本例仍旧基于lineageos18.1(android 11, api 30)
1. NotificationManagerService
首先,NotificationManager,这里什么也没做,只看下怎么进到NMS的。
%lineage%/frameworks/base/core/java/android/app/NotificationManager.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
public void notifyAsPackage(@NonNull String targetPackage, @Nullable String tag, int id, @NonNull Notification notification) { INotificationManager service = getService(); String sender = mContext.getPackageName(); try { if (localLOGV) Log.v(TAG, sender + ": notify(" + id + ", " + notification + ")"); service.enqueueNotificationWithTag(targetPackage, sender, tag, id, fixNotification(notification), mContext.getUser().getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } @UnsupportedAppUsage public void notifyAsUser(String tag, int id, Notification notification, UserHandle user) { // 搜到的帖子都说是进入此函数 INotificationManager service = getService(); // 这里拿到NMS 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()); // 这里调用NMS } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } |
然后,是NotificationManagerService,虽然还有后续各种类,姑且在这里拦截吧。
%lineage%/frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
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); } 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) { enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification, incomingUserId, false); } 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, boolean postSilently) { Log.i("aosp_acer", "要发送通知的pkg:"+pkg); Log.i("aosp_acer", "要发送通知的opPkg:"+opPkg); CharSequence title = notification.extras.getCharSequence(Notification.EXTRA_TITLE); CharSequence text = notification.extras.getCharSequence(Notification.EXTRA_TEXT); Log.i("aosp_acer", "要发送的通知title:"+title); Log.i("aosp_acer", "要发送的通知text:"+text); String value = SystemProperties.get(pkg, "able"); // 这里调用SystemProperties获取指定的key,这个key须要提前注册 if("disable".equals(value)){ // disable是我们自定义的表示‘禁止通知’。tts语言提示,log日志等 return; } if (DBG) { Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id + " notification=" + notification); } // ... 后续代码略 |
NMS的启动是在SystemServer里,
%lineage%/frameworks/base/services/java/com/android/server/SystemServer.java 中有一句
1 2 3 |
mSystemServiceManager.startService(NotificationManagerService.class); |
2. SystemProperties
要想使用SystemProperties,必须是系统权限的app,然后,它操作的key必须是经过注册的,而且注册还是有作用域的-对作用域也要注册。
先说 怎么注册key:
%lineage%/system/sepolicy/private/property_contexts 插入1行
1 2 |
# 仿照已有的配置填写 自定义的key u:object_r:system_prop:s0 |
这里的“system_prop”就是作用域。SELinux权限的范畴。
同时 %lineage%/system/sepolicy/prebuilts/api/ 下有好几个以api版本数字命名的文件夹,要适配哪个版本的手机就进入哪个文件下,如本例
%lineage%/system/sepolicy/prebuilts/api/30.0/private/property_contexts ,同样的内容添加1行。
接着 注册作用域:
%lineage%/system/sepolicy/private/platform_app.te 中添加1行
1 2 3 |
allow platform_app system_prop:property_service set; |
同时 %lineage%/system/sepolicy/prebuilts/api/30.0/private/platform_app.te ,同样的内容添加1行。
3. 自己开发的app
首先 AndroidManifest.xml 里 application 节点增加属性 android:sharedUserId="android.uid.system" :
1 2 3 4 5 6 |
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.cuiweiyou.notificationtester"> <application android:sharedUserId="android.uid.system" > |
然后 通过反射调用SystemProperties,但是后续既然要对app进行系统签名,所以 直接引入SystemProperties使用 应该也是可以的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class SystemUtil { private static SystemUtil instance; private Method methodSet; private Method methodGet; public static SystemUtil getInstance() { if (null == instance) { synchronized (SystemUtil.class) { if (null == instance) { instance = new SystemUtil(); } } } return instance; } private SystemUtil() { try { Class<?> clazz = Class.forName("android.os.SystemProperties"); methodSet = clazz.getMethod("set", String.class, String.class); methodSet.setAccessible(true); methodGet = clazz.getMethod("get", String.class); methodGet.setAccessible(true); } catch (ClassNotFoundException | NoSuchMethodException e) { e.printStackTrace(); } } public void set(String pkg, String flag) { try { methodSet.invoke(null, pkg, flag); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } public String get(String pkg) { String flag = null; try { flag = (String) methodGet.invoke((Object) null, pkg); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return flag; } } |
最后,把自定义签名文件发布release的apk文件(假设文件名 mytest.apk),放到 %lineageos%/packages/mytest/ 下,
并创建 Android.mk 文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) # 生成的模块名称 LOCAL_MODULE := mytest # 生成的模块类型 LOCAL_MODULE_CLASS := APPS # 生成的模块后缀名,此处为apk LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX) # 设置模块tag,tags取值可以为:user debug eng tests optional # optional表示全平台编译 LOCAL_MODULE_TAGS := optional # LOCAL_PRIVILEGED_MODULE := true LOCAL_BUILT_MODULE_STEM := package.apk # 设置源文件 LOCAL_SRC_FILES := mytest.apk # 设置签名,platform表示签名为系统应用 LOCAL_CERTIFICATE := platform include $(BUILD_PREBUILT) |
最最后 打开 %lineage%/build/make/target/product/handheld_product.mk ,把 ‘mytest’ 加入到 PRODUCT_PACKAGES 一项里。
4. 编译系统刷机
测试后,就可以打开mytest app通过 SystemUtil.getInstance().set("自定义的key", "able"); 或者 "disable",对这个app的通知进行控制了。
- end
本文由崔维友 威格灵 cuiweiyou vigiles cuiweiyou 原创,转载请注明出处:http://www.gaohaiyan.com/3968.html
承接App定制、企业web站点、办公系统软件 设计开发,外包项目,毕设