Android 8+ Not allowed to start service: app is in background #2
Comments
The problem: Basically it is no longer possible for a background app to start a service while it is in the background. Awake is in the background after the app restarts, and the TileService is created. This correctly registers the PowerConnectionReceiver, but it does not allow the service to be started. Only when the user interacts with the tile icon or the MainActivity will the process be marked as "foreground" and thus will be able to start the service. This link has more information on what has changed: Here's more info on the problem: |
DOES NOT WORK Having tried this, it doesn't work. The JobScheduler ends up getting into the same issue that MyTileService got into as well (when I scheduled a job from the PowerConnectionReceiver). Also, the BOOT_COMPLETED receiver never runs! One solution might be to use the JobScheduler & BOOT_COMPLETED BroadcastReceiver in order to replicate the functionality that I desire (which is to have the app in the background start a service, when the device is connected to power). Tutorials on this: This requires the creation of the following
Here's an example. AndroidManifest.xml <!-- JobScheduler service that queues the job to start when the device is charging. -->
<!-- More info: https://developer.android.com/reference/android/app/job/JobService -->
<service
android:name=".MyJobService"
android:label="Stay Awake Service that runs when device charging"
android:permission="android.permission.BIND_JOB_SERVICE"
/>
<!-- BroadcastReceiver that starts schedules the job above when device boots. -->
<receiver android:name=".MyBootCompletedBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver> MyJobService.java public class MyJobService extends JobService {
public static void scheduleJob(Context context) {
d(TAG, "scheduleJob: create JobInfo and hand it to the JobScheduler");
ComponentName serviceComponent = new ComponentName(context, MyJobService.class);
JobInfo.Builder jobInfoBuilder = new JobInfo.Builder(0, serviceComponent);
jobInfoBuilder.setRequiresCharging(true);
JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
jobScheduler.schedule(jobInfoBuilder.build());
}
/**
* https://developer.android.com/reference/android/app/job/JobService#onStartJob(android.app.job.JobParameters)
*/
@Override public boolean onStartJob(JobParameters params) {
d(TAG, "onStartJob: Start Service");
MyTileService.startService(getApplicationContext());
return true;
}
/**
* https://developer.android.com/reference/android/app/job/JobService#onStartJob(android.app.job.JobParameters)
*/
@Override public boolean onStopJob(JobParameters params) {
d(TAG, "onStopJob: Stop Service");
MyTileService.stopService(getApplicationContext());
return false;
}
} MyBootCompletedBroadcastReceiver.java public class MyJobService extends JobService {
public static void scheduleJob(Context context) {
d(TAG, "scheduleJob: create JobInfo and hand it to the JobScheduler");
ComponentName serviceComponent = new ComponentName(context, MyJobService.class);
JobInfo.Builder jobInfoBuilder = new JobInfo.Builder(0, serviceComponent);
jobInfoBuilder.setRequiresCharging(true);
JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
jobScheduler.schedule(jobInfoBuilder.build());
}
/**
* https://developer.android.com/reference/android/app/job/JobService#onStartJob(android.app.job.JobParameters)
*/
@Override public boolean onStartJob(JobParameters params) {
d(TAG, "onStartJob: Start Service");
MyTileService.startService(getApplicationContext());
return true;
}
/**
* https://developer.android.com/reference/android/app/job/JobService#onStartJob(android.app.job.JobParameters)
*/
@Override public boolean onStopJob(JobParameters params) {
d(TAG, "onStopJob: Stop Service");
MyTileService.stopService(getApplicationContext());
return false;
}
} |
DOES NOT WORK The BOOT_COMPLETED broadcast receiver does not run! Even when I enqueue the work from the PowerConnectionReceiver it ends up running into the same issue as MyTimeService and the failed solution above. https://medium.com/@berriz_/service-and-boot-completed-on-android-o-6a389eae50f1 AndroidManifest.xml <!-- JobScheduler service that queues the job to start when the device is charging. -->
<!-- More info: https://developer.android.com/reference/android/app/job/JobService -->
<service
android:name=".MyJobService"
android:icon="@drawable/ic_stat_visibility"
android:label="@string/tile_uninstalled_text"
android:permission="android.permission.BIND_JOB_SERVICE"
/>
<!-- BroadcastReceiver that starts schedules the job above when device boots. -->
<receiver android:name=".MyBootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver> MyJobService.java public class MyJobService extends JobIntentService {
// JobIntentService
// More info: https://developer.android.com/reference/androidx/core/app/JobIntentService
// More info: https://medium.com/@berriz_/service-and-boot-completed-on-android-o-6a389eae50f1
public static final int JOB_ID = 0x01;
public static void enqueueWork(Context context) {
d(TAG, "enqueueWork: hand work over to JobIntentService");
enqueueWork(context, MyJobService.class, JOB_ID, new Intent());
}
@Override protected void onHandleWork(@NonNull Intent intent) {
d(TAG, "onHandleWork: Start Service");
MyTileService.startService(getApplicationContext());
}
} MyBootReceiver.java public class MyBootReceiver extends BroadcastReceiver {
@Override public void onReceive(Context context, Intent intent) {
d(TAG, "onReceive: MyBootCompletedBroadcastReceiver " + intent.getAction());
if (intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
MyJobService.enqueueWork(context.getApplicationContext());
}
d(TAG, "onReceive: scheduleJob()");
}
} |
After
If the
Then
The only thing that I can think of is showing the user a Toast asking them to either launch the app, or activate the Awake quick tile. |
The following line of code fixes this issue. Turns out that the exception was arising because I was trying to call context.startService(), instead, in onCreate(), which is already a created service, I can simply call the following: if (isCharging(this)) {
commandStart();
} And this resolves the issue! |
Repro steps:
The text was updated successfully, but these errors were encountered: