Android操作系统下禁用通知栏下拉功能一般使用反射调用StatusBarManager类中函数实现,但该方式需应用具有系统权限。本文介绍的方式采用修改framework层,绕过系统权限检查的方式实现。
查看系统源码: /frameworks/base/core/java/android/app/StatusBarManager.java
/**
* Disable some features in the status bar. Pass the bitwise-or of the DISABLE_flags.
* To re-enable everything, pass {@link #DISABLE_NONE}.
*/
public void disable(int what) {
try {
final IStatusBarService svc = getService();
if (svc != null) {
svc.disable(what, mToken, mContext.getPackageName());
}
} catch (RemoteException ex) {
// system process is dead anyway.
throw new RuntimeException(ex);
}
}
根据其中 disable 函数,采用反射方式禁用下拉菜单,源码如下:
public static final int STATUS_BAR_DISABLE_EXPAND = 0x00010000;
public static final void disableStatusBar(Context ctx) {
Object sbservice = ctx.getSystemService("statusbar");
try {
Class> statusBarManager = Class
.forName("android.app.StatusBarManager");
Method collapse;
collapse = statusBarManager.getMethod("disable", int.class);
collapse.invoke(sbservice,
Integer.valueOf(STATUS_BAR_DISABLE_EXPAND));
} catch (Exception e) {
e.printStackTrace();
}
}
但是实际调用时会抛出异常信息,如下图所示:
异常显示必须配置 android.Manifest.permission.STATUS_BAR 权限,但必须系统应用才能配置该权限。怎样绕过系统权限的检测呢,下面将进行介绍。
继续查看源码,查找 StatusBarManager.java 中 disable 实现方式
发现 /frameworks/base/services/java/com/android/server/StatusBarManagerService.java 中有该函数的具体实现。
public void disable(int what, IBinder token, String pkg) {
enforceStatusBar();
synchronized (mLock) {
disableLocked(what, token, pkg);
}
}
查看 enforceStatusBar 源码发现该函数,对 android.Manifest.permission.STATUS_BAR 权限进行了检测。
private void enforceStatusBar() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR,
"StatusBarManagerService");
}
那么我们只要修改 disable 函数,取消其中关于 enforceStatusBar 调用,直接调用 disableLocked 函数禁用下拉通知,即可绕过权限的检测了。
此步需要用到的工具:
baksmali、smali。下载地址
1、adb 连接入手机,将 /system/framework/services.jar 文件考出进行修改,此步骤需要 ROOT 权限。
2、使用 baksmali 对 serviec.jar 进行反编译。命令:java -jar baksmali.jar -o OUTDIR services.jar
3、在反编译生成的 smali 文件中找到 StatusBarManagerService.smali 进行修改。此步需要对smali文件格式有一定的了解,可参照网上相关教程这里不再详解。
.method public disable(ILandroid/os/IBinder;Ljava/lang/String;)V
.registers 6
.param p1, "what" # I
.param p2, "token" # Landroid/os/IBinder;
.param p3, "pkg" # Ljava/lang/String;
.prologue
.line 143
invoke-direct {p0}, Lcom/android/server/StatusBarManagerService;->enforceStatusBar()V
.line 145
iget-object v1, p0, Lcom/android/server/StatusBarManagerService;->mLock:Ljava/lang/Object;
monitor-enter v1
.line 146
:try_start_6
invoke-direct {p0, p1, p2, p3}, Lcom/android/server/StatusBarManagerService;->disableLocked(ILandroid/os/IBinder;Ljava/lang/String;)V
.line 147
monitor-exit v1
.line 148
return-void
.line 147
:catchall_b
move-exception v0
monitor-exit v1
:try_end_d
.catchall {:try_start_6 .. :try_end_d} :catchall_b
throw v0
.end method
其中第143行,就是对 enforceStatusBar 函数的调用,这里删除相关的调用即可。
.line 143
invoke-direct {p0}, Lcom/android/server/StatusBarManagerService;->enforceStatusBar()V
修改后的 disable 函数如下:
.method public disable(ILandroid/os/IBinder;Ljava/lang/String;)V
.registers 6
.param p1, "what" # I
.param p2, "token" # Landroid/os/IBinder;
.param p3, "pkg" # Ljava/lang/String;
.prologue
.line 145
iget-object v1, p0, Lcom/android/server/StatusBarManagerService;->mLock:Ljava/lang/Object;
monitor-enter v1
.line 146
:try_start_6
invoke-direct {p0, p1, p2, p3}, Lcom/android/server/StatusBarManagerService;->disableLocked(ILandroid/os/IBinder;Ljava/lang/String;)V
.line 147
monitor-exit v1
.line 148
return-void
.line 147
:catchall_b
move-exception v0
monitor-exit v1
:try_end_d
.catchall {:try_start_6 .. :try_end_d} :catchall_b
throw v0
.end method
4、使用 smali 对修改后的文件进行编译。命令:java -jar smali.jar SRCDIR -o OUTDIR\classes.dex
5、用压缩方式打开 services.jar,将生成的 classes.dex 复制进去替换同名文件。
6、再将完成修改的 services.jar 文件复制到手机 /system/framework/ 目录并替换源文件,整个framework层的修改就完成了,替换后一定记得修改 services.jar 文件权限并重启手机,默认权限为644。
修改后使用之前反射的方式调用 disable 函数就不会再抛出异常,而且下拉栏也被成功禁用。
本文主要提供了一种绕过权限检测,禁用下拉菜单的思路,针对4.0以上的大多数系统都能完美实现。大家也可以根据该思路实现其他需要 framework 层修改的功能,具体实现就需要查阅相关系统源码了。