Android Q实现app开机自启
  8km7sx0kvVPb 2023年11月02日 35 0

开机自启动

​ 开机自启,那系统开机成功会发生什么情况,借助某些信息的产生我们可以实现开机自启吗?

​ 似乎原理就是这样子的,但是总需要考虑现实情况。现实就是首先这个app是系统app,所以这一步就排除了一大波的app,不过你要问难道不是系统应用就不行了吗,当然Android 10 以下可以一试。

BOOT_COMPLETED广播

​ 系统加载成功会发送 BOOT_COMPLETED广播,既然是广播我们自然便可以接收,注册一个广播接收器。

<receiver
    android:name=".BootCompletedReceiver"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>

​ 同时需要申请接收广播权限:

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
public class BootCompletedReceiver extends BroadcastReceiver {

    final String TAG = BootCompletedReceiver.class.getSimpleName();

    @Override
    public void onReceive(Context context, Intent intent) {
        // TODO: This method is called when the BroadcastReceiver is receiving
        // an Intent broadcast.
        Log.d(TAG, "onReceive: " + intent.getAction());
    }
}

​ 以上的步骤实现了在系统启动成功时接收广播并在广播接收器中处理需要的逻辑。而我们所需要的逻辑便是在这其中启动MainActivity,所以简单的startActivity似乎就能满足需求,但实际上并非如此。

直接启动Activity

​ 直接在onReceive中添加如下代码

 Intent intentOfMain = new Intent(context,MainActivity.class);
 intentOfMain.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 context.startActivity(intentOfMain);

​ 但在查阅log时可以发现以下问题:

08-30 16:27:21.928  1009  1467 W ActivityTaskManager: Background activity start [callingPackage: com.lzwml.selfstart; callingUid: 10110; isCallingUidForeground: false; isCallingUidPersistentSystemProcess: false; realCallingUid: 10110; isRealCallingUidForeground: false; isRealCallingUidPersistentSystemProcess: false; originatingPendingIntent: null; isBgStartWhitelisted: false; intent: Intent { flg=0x10000000 cmp=com.lzwml.selfstart/.MainActivity }; callerApp: ProcessRecord{dee5bb2 3345:com.lzwml.selfstart/u0a110}]
08-30 16:27:21.937  1009  1467 I ActivityManager: Finished processing BOOT_COMPLETED for u0
08-30 16:27:21.942  1009  1467 I ActivityManager: Killing 2773:com.android.contacts/u0a90 (adj 985): empty #17
  • callingPackage: 调用包名,即发起启动活动的应用程序包名为com.lzwml.selfstart。
  • callingUid: 调用应用程序的用户ID为10110。
  • isCallingUidForeground: 调用应用程序的用户ID是否在前台运行为false。
  • isCallingUidPersistentSystemProcess: 调用应用程序的用户ID是否是持久的系统进程为false。
  • realCallingUid: 实际调用应用程序的用户ID为10110。
  • isRealCallingUidForeground: 实际调用应用程序的用户ID是否在前台运行为false。
  • isRealCallingUidPersistentSystemProcess: 实际调用应用程序的用户ID是否是持久的系统进程为false。
  • originatingPendingIntent: 源PendingIntent,即启动活动的原始PendingIntent为null。
  • isBgStartWhitelisted: 后台启动白名单,即启动活动是否在后台启动白名单中为false。
  • intent: 意图,即启动活动的Intent为Intent { flg=0x10000000 cmp=com.lzwml.selfstart/.MainActivity },表示要启动的是com.lzwml.selfstart应用程序的MainActivity活动。

按照Google要求,在Android Q上运行的应用只有在满足以下一个或多个条件时才能启动Activity:常见的有如下几种

具有可见窗口,例如:

  1. 在前台运行的Activity。(前台服务不会将应用限定为在前台运行。)

  2. 该应用在前台任务的返回栈中具有一项 Activity。(必须同前台Activity位于同一个Task返回栈,如果两个Task栈不行。)

  3. 该应用已获得用户授予的 SYSTEM_ALERT_WINDOW 权限。

  4. pendingIntent临时白名单机制,不拦截通过通知拉起的应用。

    • 通过通知,利用pendingIntent启动 Activity。
    • 通过通知,在 PendingIntent中发送广播,接收广播后启动 Activity。
    • 通过通知,在 PendingIntent中启动 Service(一定可以启动Service),在 Service 中启动 Activity。

可以暂时的得出这样一个结论,无法开机直接拉起一个Activity,不过看一下前两个关键点,在前台运行的持久化进程。熟悉吗,非常熟悉,这不就是前台服务吗!所以我们来试着拉起一个前台服务。

直接启动前台服务

申请前台服务的权限及弹窗权限

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"
    android:protectionLevel="appop|development|installer|pre23|setup|signature" />

在广播接收器中启动前台服务

ContextCompat.startForegroundService(context, new Intent(context, ForegroundService.class));

在前台服务中启动Activity

public class ForegroundService extends Service {

    private static final int NOTIFICATION_ID = 1;
    final static String TAG = ForegroundService.class.getSimpleName();

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        Log.d(TAG, "onStartCommand: " + "start");
        if (Build.VERSION.SDK_INT >= 26) {
            NotificationChannel channel = new NotificationChannel("channel_id", "Channel Name", 3);
            NotificationManager notificationManager = (NotificationManager) getSystemService(NotificationManager.class);
            if (notificationManager != null) {
                notificationManager.createNotificationChannel(channel);
            }
        }
        Intent newActivityIntent = new Intent(this, MainActivity.class);
        newActivityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startForeground(NOTIFICATION_ID, new NotificationCompat.Builder(this, "channel_id")
                .setContentTitle("New Activity")
                .setContentText("Click to open update activity")
                .setSmallIcon(R.drawable.ic_notification)
                .setContentIntent(PendingIntent.getActivity(this, 0, newActivityIntent, PendingIntent.FLAG_NO_CREATE | PendingIntent.FLAG_IMMUTABLE))
                .setAutoCancel(true)
                .build());
        Log.d("RebootForegroundService", "====> start");
        startActivity(newActivityIntent);
        return START_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        return null;
    }
}

只不过现实并不如我们所预料的这般,在接收到广播之时,无法创建前台服务

ContextImpl: Calling a method in the system process without a qualified user: android.app.ContextImpl.startForegroundService:1577 android.content.ContextWrapper.startForegroundService:674 android.content.ContextWrapper.startForegroundService:674 com.mediatek.omacp.message.OmacpReceiver.beginStartingService:104 com.mediatek.omacp.message.OmacpReceiver.onReceive:77 

所以意味着无法启动服务或者活动吗?除了修改底层源码逻辑还有其他办法吗?

创建线程

创建线程,在哪里创建线程呢,在onReceive中new一个线程,然后将相关的启动代码放进去,神奇的是这样子是可行的,具体的原理还有待钻研,总之,通过这种方式,可以暂时的实现开机自启活动。

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
  3I1N9ysrcSyk   2023年12月08日   31   0   0 javahapi数据交换
  DF5J4hb0hcmT   2023年12月07日   50   0   0 javaArthas
8km7sx0kvVPb
作者其他文章 更多