Android 高版本引入了一些限制和变化,不像以前7.1以下的方便,对我们客制化搬砖效率和用户都有一定的影响。例如,Android 8+限制了后台应用启动活动的能力,增加了后台位置访问权限的要求,以及禁止了手动安装的应用在未打开的情况下接收开机广播。
本文将介绍如何通过修改Android源码来实现以下三个目标:
- 自定义开机广播:在系统启动完成后发送一个自定义的广播,让自定义的应用可以接收并执行相应的操作。
- 禁止后台服务:取消对后台应用启动服务的限制,让任何应用都可以在后台运行服务。
- 运行手动安装应用接收开机广播:允许手动安装的应用在未打开的情况下接收系统的开机广播,让它们可以在开机后自动启动。
自定义开机广播
背景知识
开机广播是一种特殊的系统广播,它在系统启动完成后发送给所有注册了android.intent.action.BOOT_COMPLETED
过滤器的应用。开机广播可以让应用在开机后执行一些初始化或定期的任务,例如更新数据,启动服务,设置闹钟等。
修改方法
要实现自定义开机广播,我们需要修改两个文件:
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
:这个文件是ActivityManagerService类的源码,它负责管理系统中所有活动、进程、服务和广播等组件。我们需要修改它的broadcastIntentLocked
方法,这个方法是用来发送广播的核心方法。frameworks/base/services/core/java/com/android/server/am/UserController.java
:这个文件是UserController类的源码,它负责管理系统中所有用户和用户相关的操作。我们需要修改它的finishUserUnlockedCompleted
方法,这个方法是在用户解锁完成后调用的。
具体的修改步骤如下:
- 在
ActivityManagerService.java
中找到broadcastIntentLocked
方法,在第15846行添加一行代码:
|| "com.ln28.intent.action.BOOT_COMPLETED".equals(action)
这样就可以让我们自定义的开机广播通过系统的保护检查,不被拒绝发送。
- 在同一个文件中,在第15906行注释掉以下代码:
if (callerApp != null) { Log.wtf(TAG, "Sending non-protected broadcast " + action + " from system " + callerApp.toShortString() + " pkg " + callerPackage, new Throwable()); } else { Log.wtf(TAG, "Sending non-protected broadcast " + action + " from system uid " + UserHandle.formatUid(callingUid) + " pkg " + callerPackage, new Throwable()); }
这样就可以避免发送我们自定义的开机广播时产生不必要的警告日志。
- 在
UserController.java
中找到finishUserUnlockedCompleted
方法,在第729行添加以下代码:
///ln28 add custom broadcast cast final Intent custombootIntent = new Intent("com.ln28.intent.action.BOOT_COMPLETED", null); custombootIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId); custombootIntent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND | Intent.FLAG_RECEIVER_OFFLOAD); final int callinguid = Binder.getCallingUid(); final int callingpid = Binder.getCallingPid(); FgThread.getHandler().post(() -> { mInjector.loadUserRecents(userId); }); FgThread.getHandler().post(() -> { mInjector.broadcastIntent(custombootIntent, null, null, 0, null, null, null, AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, callinguid, callingpid, userId); }); ///ln28 add custom broadcast cast
这样就可以在用户解锁完成后发送我们自定义的开机广播,其中com.ln28.intent.action.BOOT_COMPLETED
是我们自定义的广播的动作,userId
是当前用户的ID,callinguid
和callingpid
是调用者的UID和PID,MY_PID
和SYSTEM_UID
是系统的PID和UID,其他参数可以参考broadcastIntentLocked
方法的注释。
修改效果
修改完成后,我们需要重新编译并刷入系统。可以在任何应用中注册一个广播接收器,用来接收我们自定义的开机广播。例如,我们可以在AndroidManifest.xml中添加以下代码:
<receiver android:name=".CustomBootReceiver"> <intent-filter> <action android:name="com.ln28.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver>
然后,在CustomBootReceiver类中实现onReceive方法,用来处理我们自定义的开机广播。例如,我们可以在onReceive方法中打印一条日志:
public class CustomBootReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Log.d("CustomBootReceiver", "Received custom boot completed broadcast"); } }
这样,当我们开机并解锁后,就可以在日志中看到以下信息:
08-07 17:00:11.062 1229 1229 D CustomBootReceiver: Received custom boot completed broadcast
这说明我们成功地实现了自定义开机广播的功能。
注意:
测试发现自定义开机广播比系统自带的开机广播 要早1分钟!!
禁止后台服务和
背景知识
后台服务是一种在后台运行的组件,它可以执行一些不需要用户交互的长时间运行的任务。后台服务通常通过调用startService
或bindService
方法来启动。
但是,从Android 8.0(API级别26)开始,系统对后台服务的启动做了一些限制。具体来说,当应用处于后台时(即没有任何可见的活动),它不能直接启动服务。如果它尝试这样做,系统会抛出一个IllegalStateException
异常,并显示以下错误信息:
java.lang.IllegalStateException: Not allowed to start service Intent { cmp=com.xxx/.MyService }: app is in background uid UidRecord{...}
为了避免这个异常,应用需要使用以下两种方式之一来启动服务:
- 使用前台服务:前台服务是一种显示一个通知栏图标的服务,它表明该服务正在执行一些用户关心的操作。要使用前台服务,应用需要在服务启动后调用
startForeground
方法,并传递一个有效的通知对象。 - 使用作业调度器:作业调度器是一种让应用在满足一定条件时执行一些任务的机制。要使用作业调度器,应用需要创建一个继承自
JobService
的类,并在其中实现任务的逻辑。然后,应用需要使用JobScheduler
类来创建一个JobInfo
对象,并指定任务的触发条件和执行策略。最后,应用需要使用schedule
方法来提交任务给系统
修改方法
要实现禁止后台服务的功能,我们只需要修改一个文件:
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
:这个文件是ActivityManagerService类的源码,它负责管理系统中所有活动、进程、服务和广播等组件。我们需要修改它的appRestrictedInBackgroundLocked
方法,这个方法是用来检查应用是否可以在后台启动服务的。
具体的修改步骤如下:
- 在
ActivityManagerService.java
中找到appRestrictedInBackgroundLocked
方法,在第6275行注释掉以下代码:
if (packageTargetSdk >= Build.VERSION_CODES.O) { if (DEBUG_BACKGROUND_CHECK) { Slog.i(TAG, "App " + uid + "/" + packageName + " targets O+, restricted"); } return ActivityManager.APP_START_MODE_DELAYED_RIGID; }
这样就可以取消对目标SDK为O+的应用的后台服务启动限制。
修改效果
修改完成后,我们需要重新编译并刷入系统。然后,我们可以在任何应用中创建一个后台服务,并在AndroidManifest.xml中声明它。例如,我们可以创建一个MyService类,继承自Service,并在其中打印一条日志:
public class MyService extends Service { @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.d("MyService", "Service started"); return START_STICKY; } }
然后,在AndroidManifest.xml中添加以下代码:
<service android:name=".MyService" />
这样,当我们在应用中调用以下代码时:
startService(new Intent(this, MyService.class));
就可以在日志中看到以下信息:
08-07 17:41:14.125 1229 1229 D MyService: Service started
这说明我们成功地实现了禁止后台服务的功能。
运行手动安装应用接收开机广播
背景知识
手动安装应用是指通过非官方渠道(例如APK文件)安装的应用,它们通常不受系统的信任和管理。手动安装应用在Android 11中受到了一些限制,例如不能在后台访问剪贴板,不能请求敏感权限,以及不能在未打开的情况下接收开机广播。
开机广播是一种特殊的系统广播,它在系统启动完成后发送给所有注册了android.intent.action.BOOT_COMPLETED
过滤器的应用。开机广播可以让应用在开机后执行一些初始化或定期的任务,例如更新数据,启动服务,设置闹钟等。
修改方法
要实现运行手动安装应用接收开机广播的功能,我们需要修改一个文件:
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
:这个文件是ActivityManagerService类的源码,它负责管理系统中所有活动、进程、服务和广播等组件。
具体的修改步骤如下:
- 在同一个文件中,在第15957行注释掉以下代码:
intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
并在下一行添加以下代码:
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
这样就可以让后台服务启动时发送的广播包含已停止的应用,让它们可以接收到广播并启动服务。
这样就可以让所有应用都不受安装来源的限制,无论它们是不是即时应用。
修改效果
修改完成后,我们需要重新编译并刷入系统。可以在任何手动安装的应用中注册一个广播接收器,用来接收系统的开机广播。例如,我们可以在AndroidManifest.xml中添加以下代码:
<receiver android:name=".BootReceiver"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver>
然后,在BootReceiver类中实现onReceive方法,用来处理系统的开机广播。例如,我们可以在onReceive方法中打印一条日志:
public class BootReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Log.d("SystemBootReceiver", "Received boot completed broadcast"); } }
这样,当我们开机并解锁后,就可以在日志中看到以下信息:
08-07 17:01:14.175 1229 1229 D SystemBootReceiver: Received system boot completed broadcast
这说明我们成功地实现了运行手动安装应用接收开机广播的功能。
总结
本文介绍了如何通过修改Android源码来实现三个目标:
- 自定义开机广播:在系统启动完成后发送一个自定义的广播,让感兴趣的应用可以接收并执行相应的操作。
- 禁止后台服务:取消对后台应用启动服务的限制,让任何应用都可以在后台运行服务。
- 运行手动安装应用接收开机广播:允许手动安装的应用在未打开的情况下接收系统的开机广播,让它们可以在开机后自动启动。