1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.jobscheduler.cts.jobtestapp; 18 19 import android.app.AlarmManager; 20 import android.app.Notification; 21 import android.app.NotificationChannel; 22 import android.app.NotificationManager; 23 import android.app.PendingIntent; 24 import android.app.job.JobInfo; 25 import android.app.job.JobScheduler; 26 import android.app.job.JobService; 27 import android.content.BroadcastReceiver; 28 import android.content.ComponentName; 29 import android.content.Context; 30 import android.content.Intent; 31 import android.os.Bundle; 32 import android.os.SystemClock; 33 import android.util.Log; 34 35 /** 36 * Schedules jobs for this package but does not, by itself, occupy a foreground uid state 37 * while doing so. 38 */ 39 public class TestJobSchedulerReceiver extends BroadcastReceiver { 40 private static final String TAG = TestJobSchedulerReceiver.class.getSimpleName(); 41 static final String PACKAGE_NAME = "android.jobscheduler.cts.jobtestapp"; 42 private static final String NOTIFICATION_CHANNEL_ID = TAG + "_channel"; 43 44 public static final long NO_DEADLINE = -1L; 45 // Don't use JobInfo.PRIORITY_DEFAULT because that may cause issues if the job is meant to be 46 // expedited or user-initiated. 47 public static final int NO_PRIORITY = -1; 48 49 public static final String ACTION_JOB_SCHEDULE_RESULT = 50 PACKAGE_NAME + ".action.SCHEDULE_RESULT"; 51 public static final String EXTRA_SCHEDULE_RESULT = PACKAGE_NAME + ".extra.SCHEDULE_RESULT"; 52 53 public static final String EXTRA_JOB_ID_KEY = PACKAGE_NAME + ".extra.JOB_ID"; 54 public static final String EXTRA_ALLOW_IN_IDLE = PACKAGE_NAME + ".extra.ALLOW_IN_IDLE"; 55 public static final String EXTRA_DEADLINE = PACKAGE_NAME + ".extra.DEADLINE"; 56 public static final String EXTRA_REQUIRED_NETWORK_TYPE = 57 PACKAGE_NAME + ".extra.REQUIRED_NETWORK_TYPE"; 58 public static final String EXTRA_REQUIRES_STORAGE_NOT_LOW = 59 PACKAGE_NAME + ".extra.REQUIRES_STORAGE_NOT_LOW"; 60 public static final String EXTRA_AS_EXPEDITED = PACKAGE_NAME + ".extra.AS_EXPEDITED"; 61 public static final String EXTRA_AS_USER_INITIATED = PACKAGE_NAME + ".extra.AS_USER_INITIATED"; 62 public static final String EXTRA_PRIORITY = ".extra.PRIORITY"; 63 public static final String EXTRA_REQUEST_JOB_UID_STATE = 64 PACKAGE_NAME + ".extra.REQUEST_JOB_UID_STATE"; 65 public static final String EXTRA_SET_NOTIFICATION = PACKAGE_NAME + ".extra.SET_NOTIFICATION"; 66 public static final String EXTRA_SET_NOTIFICATION_JOB_END_POLICY = 67 PACKAGE_NAME + ".extra.SET_NOTIFICATION_JOB_END_POLICY"; 68 public static final String EXTRA_SLOW_START = PACKAGE_NAME + ".extra.SLOW_START"; 69 public static final String EXTRA_SLOW_STOP = PACKAGE_NAME + ".extra.SLOW_STOP"; 70 public static final String ACTION_SCHEDULE_JOB = PACKAGE_NAME + ".action.SCHEDULE_JOB"; 71 public static final String ACTION_CANCEL_JOBS = PACKAGE_NAME + ".action.CANCEL_JOBS"; 72 public static final String ACTION_POST_UI_INITIATING_NOTIFICATION = 73 PACKAGE_NAME + ".action.POST_UI_INITIATING_NOTIFICATION"; 74 public static final String ACTION_SCHEDULE_FGS_START_ALARM = 75 PACKAGE_NAME + ".action.SCHEDULE_FGS_START_ALARM"; 76 public static final String ACTION_START_FGS = PACKAGE_NAME + ".action.START_FGS"; 77 public static final String ACTION_NOTIFICATION_POSTED = 78 PACKAGE_NAME + ".action.NOTIFICATION_POSTED"; 79 public static final String ACTION_ALARM_SCHEDULED = 80 PACKAGE_NAME + ".action.ALARM_SCHEDULED"; 81 public static final int JOB_INITIAL_BACKOFF = 10_000; 82 83 @Override onReceive(Context context, Intent intent)84 public void onReceive(Context context, Intent intent) { 85 Log.d(TAG, "Processing intent: " + intent.getAction()); 86 final ComponentName jobServiceComponent = new ComponentName(context, TestJobService.class); 87 final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class); 88 switch (intent.getAction()) { 89 case ACTION_CANCEL_JOBS: 90 jobScheduler.cancelAll(); 91 Log.d(TAG, "Cancelled all jobs for " + context.getPackageName()); 92 break; 93 case ACTION_SCHEDULE_JOB: 94 final int jobId = intent.getIntExtra(EXTRA_JOB_ID_KEY, hashCode()); 95 final boolean allowInIdle = intent.getBooleanExtra(EXTRA_ALLOW_IN_IDLE, false); 96 final int networkType = 97 intent.getIntExtra(EXTRA_REQUIRED_NETWORK_TYPE, JobInfo.NETWORK_TYPE_NONE); 98 final boolean expedited = intent.getBooleanExtra(EXTRA_AS_EXPEDITED, false); 99 final boolean storageNotLow = 100 intent.getBooleanExtra(EXTRA_REQUIRES_STORAGE_NOT_LOW, false); 101 final long deadline = intent.getLongExtra(EXTRA_DEADLINE, NO_DEADLINE); 102 final int priority = intent.getIntExtra(EXTRA_PRIORITY, NO_PRIORITY); 103 final boolean userInitiated = 104 intent.getBooleanExtra(EXTRA_AS_USER_INITIATED, false); 105 final int backoffPolicy = userInitiated ? JobInfo.BACKOFF_POLICY_EXPONENTIAL 106 : JobInfo.BACKOFF_POLICY_LINEAR; 107 final boolean requestJobUidState = intent.getBooleanExtra( 108 EXTRA_REQUEST_JOB_UID_STATE, false); 109 final Bundle extras = new Bundle(); 110 extras.putBoolean(EXTRA_REQUEST_JOB_UID_STATE, requestJobUidState); 111 extras.putBoolean(EXTRA_SET_NOTIFICATION, 112 intent.getBooleanExtra(EXTRA_SET_NOTIFICATION, false)); 113 extras.putInt(EXTRA_SET_NOTIFICATION_JOB_END_POLICY, 114 intent.getIntExtra(EXTRA_SET_NOTIFICATION_JOB_END_POLICY, 115 JobService.JOB_END_NOTIFICATION_POLICY_REMOVE)); 116 extras.putBoolean(EXTRA_SLOW_START, 117 intent.getBooleanExtra(EXTRA_SLOW_START, false)); 118 extras.putBoolean(EXTRA_SLOW_STOP, intent.getBooleanExtra(EXTRA_SLOW_STOP, false)); 119 JobInfo.Builder jobBuilder = new JobInfo.Builder(jobId, jobServiceComponent) 120 .setBackoffCriteria(JOB_INITIAL_BACKOFF, backoffPolicy) 121 .setTransientExtras(extras) 122 .setImportantWhileForeground(allowInIdle) 123 .setExpedited(expedited) 124 .setUserInitiated(userInitiated) 125 .setRequiredNetworkType(networkType) 126 .setRequiresStorageNotLow(storageNotLow); 127 if (deadline != NO_DEADLINE) { 128 jobBuilder.setOverrideDeadline(deadline); 129 } 130 if (priority != NO_PRIORITY) { 131 jobBuilder.setPriority(priority); 132 } 133 final int result = jobScheduler.schedule(jobBuilder.build()); 134 if (result != JobScheduler.RESULT_SUCCESS) { 135 Log.e(TAG, "Could not schedule job " + jobId); 136 } else { 137 Log.d(TAG, "Successfully scheduled job with id " + jobId); 138 } 139 140 final Intent scheduleJobResultIntent = new Intent(ACTION_JOB_SCHEDULE_RESULT); 141 scheduleJobResultIntent.putExtra(EXTRA_JOB_ID_KEY, jobId); 142 scheduleJobResultIntent.putExtra(EXTRA_SCHEDULE_RESULT, result); 143 context.sendBroadcast(scheduleJobResultIntent); 144 break; 145 case ACTION_POST_UI_INITIATING_NOTIFICATION: 146 NotificationManager notificationManager = 147 context.getSystemService(NotificationManager.class); 148 NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, 149 TestJobSchedulerReceiver.class.getSimpleName(), 150 NotificationManager.IMPORTANCE_DEFAULT); 151 notificationManager.createNotificationChannel(channel); 152 153 final Intent scheduleJobIntent = new Intent(intent); 154 scheduleJobIntent.setAction(ACTION_SCHEDULE_JOB); 155 156 final PendingIntent scheduleJobPendingIntent = PendingIntent.getBroadcast( 157 context, 1, scheduleJobIntent, 158 PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT); 159 Notification notification = 160 new Notification.Builder(context, NOTIFICATION_CHANNEL_ID) 161 .setContentTitle("Test title") 162 .setSmallIcon(android.R.mipmap.sym_def_app_icon) 163 .setContentText("Click to start UI job!") 164 .setContentIntent(scheduleJobPendingIntent) 165 .build(); 166 notificationManager.notify(0, notification); 167 168 final Intent completionIntent = new Intent(ACTION_NOTIFICATION_POSTED); 169 context.sendBroadcast(completionIntent); 170 break; 171 case ACTION_SCHEDULE_FGS_START_ALARM: { 172 final Intent startFgsIntent = new Intent(ACTION_START_FGS); 173 ComponentName testComponentName = new ComponentName(context, TestFgsService.class); 174 startFgsIntent.setComponent(testComponentName); 175 176 PendingIntent startFgsPendingIntent = PendingIntent.getForegroundService( 177 context, 2, startFgsIntent, 178 PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT); 179 180 AlarmManager alarmManager = context.getSystemService(AlarmManager.class); 181 alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, 182 SystemClock.elapsedRealtime(), // Get the alarm to fire immediately 183 startFgsPendingIntent); 184 185 final Intent alarmScheduledIntent = new Intent(ACTION_ALARM_SCHEDULED); 186 context.sendBroadcast(alarmScheduledIntent); 187 break; 188 } 189 case ACTION_START_FGS: { 190 final Intent startFgsIntent = new Intent(); 191 ComponentName testComponentName = new ComponentName(context, TestFgsService.class); 192 startFgsIntent.setComponent(testComponentName); 193 194 context.startForegroundService(startFgsIntent); 195 break; 196 } 197 default: 198 Log.e(TAG, "Unknown action " + intent.getAction()); 199 } 200 } 201 } 202