summaryrefslogtreecommitdiff
path: root/apex
diff options
context:
space:
mode:
author Xin Li <delphij@google.com> 2024-11-13 13:57:20 -0800
committer Xin Li <delphij@google.com> 2024-11-13 14:01:00 -0800
commitde53d9657cd5e352984697fc130159c474e819a9 (patch)
tree79b7d2c74d20c83b9adaddfde7862240e4d3725e /apex
parent70a1e5dcf316f8ddf6d1b3b00341c7a35a8dcc58 (diff)
parent8eecb1a1afe454e3233f27b06f9e5d41b5bec011 (diff)
Merge 24Q4 (ab/12406339) into aosp-main-future
Bug: 370570306 Merged-In: Ic2ffd5a1f4420121b958cadb726c3e805cda1434 Change-Id: If0f7008f8e699bf1fca8d20ee5e05877a61ea002
Diffstat (limited to 'apex')
-rw-r--r--apex/jobscheduler/framework/aconfig/job.aconfig7
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/IJobCallback.aidl8
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/JobParameters.java123
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/JobServiceEngine.java9
-rw-r--r--apex/jobscheduler/service/aconfig/job.aconfig27
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java95
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java7
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/UserWakeupStore.java64
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java22
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java55
-rw-r--r--apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java6
11 files changed, 304 insertions, 119 deletions
diff --git a/apex/jobscheduler/framework/aconfig/job.aconfig b/apex/jobscheduler/framework/aconfig/job.aconfig
index 80db264d0f44..5f5507587f72 100644
--- a/apex/jobscheduler/framework/aconfig/job.aconfig
+++ b/apex/jobscheduler/framework/aconfig/job.aconfig
@@ -23,3 +23,10 @@ flag {
description: "Introduce a new RUN_BACKUP_JOBS permission and exemption logic allowing for longer running jobs for apps whose primary purpose is to backup or sync content."
bug: "318731461"
}
+
+flag {
+ name: "cleanup_empty_jobs"
+ namespace: "backstage_power"
+ description: "Enables automatic cancellation of jobs due to leaked JobParameters, reducing unnecessary battery drain and improving system efficiency. This includes logging and traces for better issue diagnosis."
+ bug: "349688611"
+}
diff --git a/apex/jobscheduler/framework/java/android/app/job/IJobCallback.aidl b/apex/jobscheduler/framework/java/android/app/job/IJobCallback.aidl
index 96494ec28204..11d17ca749b7 100644
--- a/apex/jobscheduler/framework/java/android/app/job/IJobCallback.aidl
+++ b/apex/jobscheduler/framework/java/android/app/job/IJobCallback.aidl
@@ -85,6 +85,14 @@ interface IJobCallback {
*/
@UnsupportedAppUsage
void jobFinished(int jobId, boolean reschedule);
+
+ /*
+ * Inform JobScheduler to force finish this job because the client has lost
+ * the job handle. jobFinished can no longer be called from the client.
+ * @param jobId Unique integer used to identify this job
+ */
+ void forceJobFinished(int jobId);
+
/*
* Inform JobScheduler of a change in the estimated transfer payload.
*
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
index e833bb95a302..52a761f8d486 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
@@ -34,15 +34,21 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
import android.os.RemoteException;
+import android.system.SystemCleaner;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.Cleaner;
/**
* Contains the parameters used to configure/identify your job. You do not create this object
* yourself, instead it is handed in to your application by the System.
*/
public class JobParameters implements Parcelable {
+ private static final String TAG = "JobParameters";
/** @hide */
public static final int INTERNAL_STOP_REASON_UNKNOWN = -1;
@@ -306,6 +312,10 @@ public class JobParameters implements Parcelable {
private int mStopReason = STOP_REASON_UNDEFINED;
private int mInternalStopReason = INTERNAL_STOP_REASON_UNKNOWN;
private String debugStopReason; // Human readable stop reason for debugging.
+ @Nullable
+ private JobCleanupCallback mJobCleanupCallback;
+ @Nullable
+ private Cleaner.Cleanable mCleanable;
/** @hide */
public JobParameters(IBinder callback, String namespace, int jobId, PersistableBundle extras,
@@ -326,6 +336,8 @@ public class JobParameters implements Parcelable {
this.mTriggeredContentAuthorities = triggeredContentAuthorities;
this.mNetwork = network;
this.mJobNamespace = namespace;
+ this.mJobCleanupCallback = null;
+ this.mCleanable = null;
}
/**
@@ -597,6 +609,8 @@ public class JobParameters implements Parcelable {
mStopReason = in.readInt();
mInternalStopReason = in.readInt();
debugStopReason = in.readString();
+ mJobCleanupCallback = null;
+ mCleanable = null;
}
/** @hide */
@@ -612,6 +626,54 @@ public class JobParameters implements Parcelable {
this.debugStopReason = debugStopReason;
}
+ /** @hide */
+ public void initCleaner(JobCleanupCallback jobCleanupCallback) {
+ mJobCleanupCallback = jobCleanupCallback;
+ mCleanable = SystemCleaner.cleaner().register(this, mJobCleanupCallback);
+ }
+
+ /**
+ * Lazy initialize the cleaner and enable it
+ *
+ * @hide
+ */
+ public void enableCleaner() {
+ if (mJobCleanupCallback == null) {
+ initCleaner(new JobCleanupCallback(IJobCallback.Stub.asInterface(callback), jobId));
+ }
+ mJobCleanupCallback.enableCleaner();
+ }
+
+ /**
+ * Disable the cleaner from running and unregister it
+ *
+ * @hide
+ */
+ public void disableCleaner() {
+ if (mJobCleanupCallback != null) {
+ mJobCleanupCallback.disableCleaner();
+ if (mCleanable != null) {
+ mCleanable.clean();
+ mCleanable = null;
+ }
+ mJobCleanupCallback = null;
+ }
+ }
+
+ /** @hide */
+ @VisibleForTesting
+ @Nullable
+ public Cleaner.Cleanable getCleanable() {
+ return mCleanable;
+ }
+
+ /** @hide */
+ @VisibleForTesting
+ @Nullable
+ public JobCleanupCallback getJobCleanupCallback() {
+ return mJobCleanupCallback;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -647,6 +709,67 @@ public class JobParameters implements Parcelable {
dest.writeString(debugStopReason);
}
+ /**
+ * JobCleanupCallback is used track JobParameters leak. If the job is started
+ * and jobFinish is not called at the time of garbage collection of JobParameters
+ * instance, it is considered a job leak. Force finish the job.
+ *
+ * @hide
+ */
+ public static class JobCleanupCallback implements Runnable {
+ private final IJobCallback mCallback;
+ private final int mJobId;
+ private boolean mIsCleanerEnabled;
+
+ public JobCleanupCallback(
+ IJobCallback callback,
+ int jobId) {
+ mCallback = callback;
+ mJobId = jobId;
+ mIsCleanerEnabled = false;
+ }
+
+ /**
+ * Check if the cleaner is enabled
+ *
+ * @hide
+ */
+ public boolean isCleanerEnabled() {
+ return mIsCleanerEnabled;
+ }
+
+ /**
+ * Enable the cleaner to detect JobParameter leak
+ *
+ * @hide
+ */
+ public void enableCleaner() {
+ mIsCleanerEnabled = true;
+ }
+
+ /**
+ * Disable the cleaner from running.
+ *
+ * @hide
+ */
+ public void disableCleaner() {
+ mIsCleanerEnabled = false;
+ }
+
+ /** @hide */
+ @Override
+ public void run() {
+ if (!isCleanerEnabled()) {
+ return;
+ }
+ try {
+ mCallback.forceJobFinished(mJobId);
+ } catch (Exception e) {
+ Log.wtf(TAG, "Could not destroy running job", e);
+ }
+ }
+ }
+
public static final @android.annotation.NonNull Creator<JobParameters> CREATOR = new Creator<JobParameters>() {
@Override
public JobParameters createFromParcel(Parcel in) {
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobServiceEngine.java b/apex/jobscheduler/framework/java/android/app/job/JobServiceEngine.java
index 79d87edff9b2..5f80c52388b4 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobServiceEngine.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobServiceEngine.java
@@ -165,7 +165,13 @@ public abstract class JobServiceEngine {
case MSG_EXECUTE_JOB: {
final JobParameters params = (JobParameters) msg.obj;
try {
+ if (Flags.cleanupEmptyJobs()) {
+ params.enableCleaner();
+ }
boolean workOngoing = JobServiceEngine.this.onStartJob(params);
+ if (Flags.cleanupEmptyJobs() && !workOngoing) {
+ params.disableCleaner();
+ }
ackStartMessage(params, workOngoing);
} catch (Exception e) {
Log.e(TAG, "Error while executing job: " + params.getJobId());
@@ -190,6 +196,9 @@ public abstract class JobServiceEngine {
IJobCallback callback = params.getCallback();
if (callback != null) {
try {
+ if (Flags.cleanupEmptyJobs()) {
+ params.disableCleaner();
+ }
callback.jobFinished(params.getJobId(), needsReschedule);
} catch (RemoteException e) {
Log.e(TAG, "Error reporting job finish to system: binder has gone" +
diff --git a/apex/jobscheduler/service/aconfig/job.aconfig b/apex/jobscheduler/service/aconfig/job.aconfig
index e489c1ad891a..e5389b4f96fb 100644
--- a/apex/jobscheduler/service/aconfig/job.aconfig
+++ b/apex/jobscheduler/service/aconfig/job.aconfig
@@ -48,3 +48,30 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "create_work_chain_by_default"
+ namespace: "backstage_power"
+ description: "Create a workchain by default when acquiring a wakelock"
+ bug: "352676818"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "remove_user_during_user_switch"
+ namespace: "backstage_power"
+ description: "Remove started user if user will be stopped due to user switch"
+ bug: "321598070"
+}
+
+flag {
+ name: "use_correct_process_state_for_logging"
+ namespace: "backstage_power"
+ description: "Use correct process state for statsd logging"
+ bug: "361308212"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index d1f1ac3c1c92..033da2df9bf6 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -116,10 +116,10 @@ import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.SystemClock;
-import android.os.SystemProperties;
import android.os.ThreadLocalWorkSource;
import android.os.Trace;
import android.os.UserHandle;
+import android.os.UserManager;
import android.os.WorkSource;
import android.provider.DeviceConfig;
import android.provider.Settings;
@@ -165,6 +165,7 @@ import com.android.server.SystemService;
import com.android.server.SystemServiceManager;
import com.android.server.SystemTimeZone;
import com.android.server.SystemTimeZone.TimeZoneConfidence;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.pm.permission.PermissionManagerService;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.pm.pkg.AndroidPackage;
@@ -178,9 +179,6 @@ import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.text.SimpleDateFormat;
-import java.time.Instant;
-import java.time.zone.ZoneOffsetTransition;
-import java.time.zone.ZoneRules;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
@@ -192,7 +190,6 @@ import java.util.Set;
import java.util.TimeZone;
import java.util.TreeSet;
import java.util.concurrent.ThreadLocalRandom;
-import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
/**
@@ -232,13 +229,6 @@ public class AlarmManagerService extends SystemService {
private static final long TEMPORARY_QUOTA_DURATION = INTERVAL_DAY;
- // System properties read on some device configurations to initialize time properly and
- // perform DST transitions at the bootloader level.
- private static final String TIMEOFFSET_PROPERTY = "persist.sys.time.offset";
- private static final String DST_TRANSITION_PROPERTY = "persist.sys.time.dst_transition";
- private static final String DST_OFFSET_PROPERTY = "persist.sys.time.dst_offset";
-
-
private final Intent mBackgroundIntent
= new Intent().addFlags(Intent.FLAG_FROM_BACKGROUND);
@@ -329,7 +319,7 @@ public class AlarmManagerService extends SystemService {
*/
int mSystemUiUid;
- static boolean isTimeTickAlarm(Alarm a) {
+ private static boolean isTimeTickAlarm(Alarm a) {
return a.uid == Process.SYSTEM_UID && TIME_TICK_TAG.equals(a.listenerTag);
}
@@ -367,6 +357,7 @@ public class AlarmManagerService extends SystemService {
}
// TODO(b/172085676): Move inside alarm store.
+ @GuardedBy("mLock")
private final SparseArray<AlarmManager.AlarmClockInfo> mNextAlarmClockForUser =
new SparseArray<>();
private final SparseArray<AlarmManager.AlarmClockInfo> mTmpSparseAlarmClockArray =
@@ -1794,7 +1785,8 @@ public class AlarmManagerService extends SystemService {
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
mUseFrozenStateToDropListenerAlarms = Flags.useFrozenStateToDropListenerAlarms();
- mStartUserBeforeScheduledAlarms = Flags.startUserBeforeScheduledAlarms();
+ mStartUserBeforeScheduledAlarms = Flags.startUserBeforeScheduledAlarms()
+ && UserManager.supportsMultipleUsers();
if (mStartUserBeforeScheduledAlarms) {
mUserWakeupStore = new UserWakeupStore();
mUserWakeupStore.init();
@@ -2125,22 +2117,6 @@ public class AlarmManagerService extends SystemService {
// "GMT" if the ID is unrecognized). The parameter ID is used here rather than
// newZone.getId(). It will be rejected if it is invalid.
timeZoneWasChanged = SystemTimeZone.setTimeZoneId(tzId, confidence, logInfo);
-
- final int gmtOffset = newZone.getOffset(mInjector.getCurrentTimeMillis());
- SystemProperties.set(TIMEOFFSET_PROPERTY, String.valueOf(gmtOffset));
-
- final ZoneRules rules = newZone.toZoneId().getRules();
- final ZoneOffsetTransition transition = rules.nextTransition(Instant.now());
- if (null != transition) {
- // Get the offset between the time after the DST transition and before.
- final long transitionOffset = TimeUnit.SECONDS.toMillis((
- transition.getOffsetAfter().getTotalSeconds()
- - transition.getOffsetBefore().getTotalSeconds()));
- // Time when the next DST transition is programmed.
- final long nextTransition = TimeUnit.SECONDS.toMillis(transition.toEpochSecond());
- SystemProperties.set(DST_TRANSITION_PROPERTY, String.valueOf(nextTransition));
- SystemProperties.set(DST_OFFSET_PROPERTY, String.valueOf(transitionOffset));
- }
}
// Clear the default time zone in the system server process. This forces the next call
@@ -2641,6 +2617,13 @@ public class AlarmManagerService extends SystemService {
mInFlightListeners.add(callback);
}
}
+
+ /** @see AlarmManagerInternal#getNextAlarmTriggerTimeForUser(int) */
+ @Override
+ public long getNextAlarmTriggerTimeForUser(@UserIdInt int userId) {
+ final AlarmManager.AlarmClockInfo nextAlarm = getNextAlarmClockImpl(userId);
+ return nextAlarm != null ? nextAlarm.getTriggerTime() : 0;
+ }
}
boolean hasUseExactAlarmInternal(String packageName, int uid) {
@@ -3015,7 +2998,7 @@ public class AlarmManagerService extends SystemService {
mUseFrozenStateToDropListenerAlarms);
pw.println();
pw.print(Flags.FLAG_START_USER_BEFORE_SCHEDULED_ALARMS,
- mStartUserBeforeScheduledAlarms);
+ Flags.startUserBeforeScheduledAlarms());
pw.decreaseIndent();
pw.println();
pw.println();
@@ -3789,8 +3772,10 @@ public class AlarmManagerService extends SystemService {
}
mNextAlarmClockForUser.put(userId, alarmClock);
if (mStartUserBeforeScheduledAlarms) {
- mUserWakeupStore.addUserWakeup(userId, convertToElapsed(
- mNextAlarmClockForUser.get(userId).getTriggerTime(), RTC));
+ if (shouldAddWakeupForUser(userId)) {
+ mUserWakeupStore.addUserWakeup(userId, convertToElapsed(
+ mNextAlarmClockForUser.get(userId).getTriggerTime(), RTC));
+ }
}
} else {
if (DEBUG_ALARM_CLOCK) {
@@ -3810,6 +3795,23 @@ public class AlarmManagerService extends SystemService {
}
/**
+ * Checks whether the user is of type that needs to be started before the alarm.
+ */
+ @VisibleForTesting
+ boolean shouldAddWakeupForUser(@UserIdInt int userId) {
+ final UserManagerInternal umInternal = LocalServices.getService(UserManagerInternal.class);
+ if (umInternal.getUserInfo(userId) == null || umInternal.getUserInfo(userId).isGuest()) {
+ // Guest user should not be started in the background.
+ return false;
+ } else {
+ // SYSTEM user is always running, so no need to schedule wakeup for it.
+ // Profiles are excluded from the wakeup list because users can explicitly stop them and
+ // so starting them in the background would go against the user's intent.
+ return userId != UserHandle.USER_SYSTEM && umInternal.getUserInfo(userId).isFull();
+ }
+ }
+
+ /**
* Updates NEXT_ALARM_FORMATTED and sends NEXT_ALARM_CLOCK_CHANGED_INTENT for all users
* for which alarm clocks have changed since the last call to this.
*
@@ -3953,6 +3955,9 @@ public class AlarmManagerService extends SystemService {
if (!RemovedAlarm.isLoggable(reason)) {
continue;
}
+ if (isTimeTickAlarm(removed)) {
+ Slog.wtf(TAG, "Removed TIME_TICK alarm");
+ }
RingBuffer<RemovedAlarm> bufferForUid = mRemovalHistory.get(removed.uid);
if (bufferForUid == null) {
bufferForUid = new RingBuffer<>(RemovedAlarm.class, REMOVAL_HISTORY_SIZE_PER_UID);
@@ -4447,6 +4452,11 @@ public class AlarmManagerService extends SystemService {
public void run() {
ArrayList<Alarm> triggerList = new ArrayList<Alarm>();
+ synchronized (mLock) {
+ mLastTimeChangeClockTime = mInjector.getCurrentTimeMillis();
+ mLastTimeChangeRealtime = mInjector.getElapsedRealtimeMillis();
+ }
+
while (true) {
int result = mInjector.waitForAlarm();
final long nowRTC = mInjector.getCurrentTimeMillis();
@@ -4470,10 +4480,9 @@ public class AlarmManagerService extends SystemService {
expectedClockTime = lastTimeChangeClockTime
+ (nowELAPSED - mLastTimeChangeRealtime);
}
- if (lastTimeChangeClockTime == 0 || nowRTC < (expectedClockTime - 1000)
+ if (nowRTC < (expectedClockTime - 1000)
|| nowRTC > (expectedClockTime + 1000)) {
- // The change is by at least +/- 1000 ms (or this is the first change),
- // let's do it!
+ // The change is by at least +/- 1000 ms, let's do it!
if (DEBUG_BATCH) {
Slog.v(TAG, "Time changed notification from kernel; rebatching");
}
@@ -4521,8 +4530,9 @@ public class AlarmManagerService extends SystemService {
final int[] userIds =
mUserWakeupStore.getUserIdsToWakeup(nowELAPSED);
for (int i = 0; i < userIds.length; i++) {
- if (!mActivityManagerInternal.startUserInBackground(
- userIds[i])) {
+ if (mActivityManagerInternal.isUserRunning(userIds[i], 0)
+ || !mActivityManagerInternal.startUserInBackground(
+ userIds[i])) {
mUserWakeupStore.removeUserWakeup(userIds[i]);
}
}
@@ -4925,7 +4935,6 @@ public class AlarmManagerService extends SystemService {
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
sdFilter.addAction(Intent.ACTION_USER_STOPPED);
if (mStartUserBeforeScheduledAlarms) {
- sdFilter.addAction(Intent.ACTION_LOCKED_BOOT_COMPLETED);
sdFilter.addAction(Intent.ACTION_USER_REMOVED);
}
sdFilter.addAction(Intent.ACTION_UID_REMOVED);
@@ -4962,14 +4971,6 @@ public class AlarmManagerService extends SystemService {
mTemporaryQuotaReserve.removeForUser(userHandle);
}
return;
- case Intent.ACTION_LOCKED_BOOT_COMPLETED:
- final int handle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
- if (handle >= 0) {
- if (mStartUserBeforeScheduledAlarms) {
- mUserWakeupStore.onUserStarted(handle);
- }
- }
- return;
case Intent.ACTION_USER_REMOVED:
final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
if (user >= 0) {
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java b/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java
index 0073335a1332..020b510269f8 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java
@@ -17,11 +17,9 @@
package com.android.server.alarm;
import static com.android.server.alarm.AlarmManagerService.dumpAlarmList;
-import static com.android.server.alarm.AlarmManagerService.isTimeTickAlarm;
import android.app.AlarmManager;
import android.util.IndentingPrintWriter;
-import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
@@ -88,11 +86,6 @@ public class LazyAlarmStore implements AlarmStore {
if (removed.alarmClock != null && mOnAlarmClockRemoved != null) {
mOnAlarmClockRemoved.run();
}
- if (isTimeTickAlarm(removed)) {
- // This code path is not invoked when delivering alarms, only when removing
- // alarms due to the caller cancelling it or getting uninstalled, etc.
- Slog.wtf(TAG, "Removed TIME_TICK alarm");
- }
removedAlarms.add(removed);
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/UserWakeupStore.java b/apex/jobscheduler/service/java/com/android/server/alarm/UserWakeupStore.java
index 7fc630c964e5..9fe197d69ce5 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/UserWakeupStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/UserWakeupStore.java
@@ -98,12 +98,7 @@ public class UserWakeupStore {
*/
@GuardedBy("mUserWakeupLock")
private final SparseLongArray mUserStarts = new SparseLongArray();
- /**
- * A list of users that are in a phase after they have been started but before alarms were
- * initialized.
- */
- @GuardedBy("mUserWakeupLock")
- private final SparseLongArray mStartingUsers = new SparseLongArray();
+
private Executor mBackgroundExecutor;
private static final File USER_WAKEUP_DIR = new File(Environment.getDataSystemDirectory(),
ROOT_DIR_NAME);
@@ -118,15 +113,12 @@ public class UserWakeupStore {
}
/**
- * Add user wakeup for the alarm.
+ * Add user wakeup for the alarm if needed.
* @param userId Id of the user that scheduled alarm.
* @param alarmTime time when alarm is expected to trigger.
*/
public void addUserWakeup(int userId, long alarmTime) {
synchronized (mUserWakeupLock) {
- // This should not be needed, but if an app in the user is scheduling an alarm clock, we
- // consider the user start complete. There is a dedicated removal when user is started.
- mStartingUsers.delete(userId);
mUserStarts.put(userId, alarmTime - BUFFER_TIME_MS + getUserWakeupOffset());
}
updateUserListFile();
@@ -192,23 +184,10 @@ public class UserWakeupStore {
}
/**
- * Move user from wakeup list to starting user list.
+ * Remove scheduled user wakeup from the list when it is started.
*/
public void onUserStarting(int userId) {
- synchronized (mUserWakeupLock) {
- final long wakeup = getWakeupTimeForUser(userId);
- if (wakeup >= 0) {
- mStartingUsers.put(userId, wakeup);
- mUserStarts.delete(userId);
- }
- }
- }
-
- /**
- * Remove userId from starting user list once start is complete.
- */
- public void onUserStarted(int userId) {
- if (deleteWakeupFromStartingUsers(userId)) {
+ if (deleteWakeupFromUserStarts(userId)) {
updateUserListFile();
}
}
@@ -217,7 +196,7 @@ public class UserWakeupStore {
* Remove userId from the store when the user is removed.
*/
public void onUserRemoved(int userId) {
- if (deleteWakeupFromUserStarts(userId) || deleteWakeupFromStartingUsers(userId)) {
+ if (deleteWakeupFromUserStarts(userId)) {
updateUserListFile();
}
}
@@ -227,29 +206,14 @@ public class UserWakeupStore {
* @return true if an entry is found and the list of wakeups changes.
*/
private boolean deleteWakeupFromUserStarts(int userId) {
- int index;
synchronized (mUserWakeupLock) {
- index = mUserStarts.indexOfKey(userId);
+ final int index = mUserStarts.indexOfKey(userId);
if (index >= 0) {
mUserStarts.removeAt(index);
+ return true;
}
+ return false;
}
- return index >= 0;
- }
-
- /**
- * Remove wakeup for a given userId from mStartingUsers.
- * @return true if an entry is found and the list of wakeups changes.
- */
- private boolean deleteWakeupFromStartingUsers(int userId) {
- int index;
- synchronized (mUserWakeupLock) {
- index = mStartingUsers.indexOfKey(userId);
- if (index >= 0) {
- mStartingUsers.removeAt(index);
- }
- }
- return index >= 0;
}
/**
@@ -299,9 +263,6 @@ public class UserWakeupStore {
for (int i = 0; i < mUserStarts.size(); i++) {
listOfUsers.add(new Pair<>(mUserStarts.keyAt(i), mUserStarts.valueAt(i)));
}
- for (int i = 0; i < mStartingUsers.size(); i++) {
- listOfUsers.add(new Pair<>(mStartingUsers.keyAt(i), mStartingUsers.valueAt(i)));
- }
}
Collections.sort(listOfUsers, Comparator.comparingLong(pair -> pair.second));
for (int i = 0; i < listOfUsers.size(); i++) {
@@ -329,7 +290,6 @@ public class UserWakeupStore {
}
synchronized (mUserWakeupLock) {
mUserStarts.clear();
- mStartingUsers.clear();
}
try (FileInputStream fis = userWakeupFile.openRead()) {
final TypedXmlPullParser parser = Xml.resolvePullParser(fis);
@@ -396,14 +356,6 @@ public class UserWakeupStore {
TimeUtils.formatDuration(mUserStarts.valueAt(i), nowELAPSED, pw);
pw.println();
}
- pw.println(mStartingUsers.size() + " starting users: ");
- for (int i = 0; i < mStartingUsers.size(); i++) {
- pw.print("UserId: ");
- pw.print(mStartingUsers.keyAt(i));
- pw.print(", userStartTime: ");
- TimeUtils.formatDuration(mStartingUsers.valueAt(i), nowELAPSED, pw);
- pw.println();
- }
pw.decreaseIndent();
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index ff73a4922977..97c6e25eeb75 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1617,10 +1617,11 @@ public class JobSchedulerService extends com.android.server.SystemService
@NonNull
public WorkSource deriveWorkSource(int sourceUid, @Nullable String sourcePackageName) {
- if (WorkSource.isChainedBatteryAttributionEnabled(getContext())) {
+ if (Flags.createWorkChainByDefault()
+ || WorkSource.isChainedBatteryAttributionEnabled(getContext())) {
WorkSource ws = new WorkSource();
ws.createWorkChain()
- .addNode(sourceUid, sourcePackageName)
+ .addNode(sourceUid, null)
.addNode(Process.SYSTEM_UID, "JobScheduler");
return ws;
} else {
@@ -1668,6 +1669,20 @@ public class JobSchedulerService extends com.android.server.SystemService
}
@Override
+ public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) {
+ if (!Flags.removeUserDuringUserSwitch()
+ || from == null
+ || !mActivityManagerInternal.isEarlyPackageKillEnabledForUserSwitch(
+ from.getUserIdentifier(),
+ to.getUserIdentifier())) {
+ return;
+ }
+ synchronized (mLock) {
+ mStartedUsers = ArrayUtils.removeInt(mStartedUsers, from.getUserIdentifier());
+ }
+ }
+
+ @Override
public void onUserStopping(@NonNull TargetUser user) {
synchronized (mLock) {
mStartedUsers = ArrayUtils.removeInt(mStartedUsers, user.getUserIdentifier());
@@ -5793,9 +5808,10 @@ public class JobSchedulerService extends com.android.server.SystemService
static void dumpHelp(PrintWriter pw) {
pw.println("Job Scheduler (jobscheduler) dump options:");
- pw.println(" [-h] [package] ...");
+ pw.println(" [-h] [package] [--proto] ...");
pw.println(" -h: print this help");
pw.println(" [package] is an optional package name to limit the output to.");
+ pw.println(" --proto: output dump in protocol buffer format.");
}
/** Sort jobs by caller UID, then by Job ID. */
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index d65a66c83fcc..ee246d84997f 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -129,6 +129,8 @@ public final class JobServiceContext implements ServiceConnection {
private static final String[] VERB_STRINGS = {
"VERB_BINDING", "VERB_STARTING", "VERB_EXECUTING", "VERB_STOPPING", "VERB_FINISHED"
};
+ private static final String TRACE_JOB_FORCE_FINISHED_PREFIX = "forceJobFinished:";
+ private static final String TRACE_JOB_FORCE_FINISHED_DELIMITER = "#";
// States that a job occupies while interacting with the client.
static final int VERB_BINDING = 0;
@@ -292,6 +294,11 @@ public final class JobServiceContext implements ServiceConnection {
}
@Override
+ public void forceJobFinished(int jobId) {
+ doForceJobFinished(this, jobId);
+ }
+
+ @Override
public void updateEstimatedNetworkBytes(int jobId, JobWorkItem item,
long downloadBytes, long uploadBytes) {
doUpdateEstimatedNetworkBytes(this, jobId, item, downloadBytes, uploadBytes);
@@ -473,6 +480,14 @@ public final class JobServiceContext implements ServiceConnection {
mInitialDownloadedBytesFromCalling = TrafficStats.getUidRxBytes(job.getUid());
mInitialUploadedBytesFromCalling = TrafficStats.getUidTxBytes(job.getUid());
+ int procState = mService.getUidProcState(job.getUid());
+ if (Flags.useCorrectProcessStateForLogging()
+ && procState > ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND) {
+ // Try to get the latest proc state from AMS, there might be some delay
+ // for the proc states worse than TRANSIENT_BACKGROUND.
+ procState = mActivityManagerInternal.getUidProcessState(job.getUid());
+ }
+
FrameworkStatsLog.write(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
job.isProxyJob() ? new int[]{sourceUid, job.getUid()} : new int[]{sourceUid},
// Given that the source tag is set by the calling app, it should be connected
@@ -517,7 +532,7 @@ public final class JobServiceContext implements ServiceConnection {
job.getEstimatedNetworkDownloadBytes(),
job.getEstimatedNetworkUploadBytes(),
job.getWorkCount(),
- ActivityManager.processStateAmToProto(mService.getUidProcState(job.getUid())),
+ ActivityManager.processStateAmToProto(procState),
job.getNamespaceHash(),
/* system_measured_source_download_bytes */ 0,
/* system_measured_source_upload_bytes */ 0,
@@ -754,6 +769,35 @@ public final class JobServiceContext implements ServiceConnection {
}
}
+ /**
+ * This method just adds traces to evaluate jobs that leak jobparameters at the client.
+ * It does not stop the job.
+ */
+ void doForceJobFinished(JobCallback cb, int jobId) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ final JobStatus executing;
+ synchronized (mLock) {
+ // not the current job, presumably it has finished in some way already
+ if (!verifyCallerLocked(cb)) {
+ return;
+ }
+
+ executing = getRunningJobLocked();
+ }
+ if (executing != null && jobId == executing.getJobId()) {
+ final StringBuilder stateSuffix = new StringBuilder();
+ stateSuffix.append(TRACE_JOB_FORCE_FINISHED_PREFIX);
+ stateSuffix.append(executing.getBatteryName());
+ stateSuffix.append(TRACE_JOB_FORCE_FINISHED_DELIMITER);
+ stateSuffix.append(executing.getJobId());
+ Trace.instant(Trace.TRACE_TAG_POWER, stateSuffix.toString());
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
private void doAcknowledgeGetTransferredDownloadBytesMessage(JobCallback cb, int jobId,
int workId, @BytesLong long transferredBytes) {
// TODO(255393346): Make sure apps call this appropriately and monitor for abuse
@@ -1528,6 +1572,13 @@ public final class JobServiceContext implements ServiceConnection {
mJobPackageTracker.noteInactive(completedJob,
loggingInternalStopReason, loggingDebugReason);
final int sourceUid = completedJob.getSourceUid();
+ int procState = mService.getUidProcState(completedJob.getUid());
+ if (Flags.useCorrectProcessStateForLogging()
+ && procState > ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND) {
+ // Try to get the latest proc state from AMS, there might be some delay
+ // for the proc states worse than TRANSIENT_BACKGROUND.
+ procState = mActivityManagerInternal.getUidProcessState(completedJob.getUid());
+ }
FrameworkStatsLog.write(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
completedJob.isProxyJob()
? new int[]{sourceUid, completedJob.getUid()} : new int[]{sourceUid},
@@ -1573,7 +1624,7 @@ public final class JobServiceContext implements ServiceConnection {
completedJob.getEstimatedNetworkUploadBytes(),
completedJob.getWorkCount(),
ActivityManager
- .processStateAmToProto(mService.getUidProcState(completedJob.getUid())),
+ .processStateAmToProto(procState),
completedJob.getNamespaceHash(),
TrafficStats.getUidRxBytes(completedJob.getSourceUid())
- mInitialDownloadedBytesFromSource,
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index c3fe0314636e..c9d340757c6b 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -1989,10 +1989,8 @@ public class AppStandbyController
mAdminProtectedPackages.put(userId, packageNames);
}
}
- if (android.app.admin.flags.Flags.disallowUserControlBgUsageFix()) {
- if (!Flags.avoidIdleCheck()) {
- postCheckIdleStates(userId);
- }
+ if (!Flags.avoidIdleCheck() || mInjector.getBootPhase() >= PHASE_BOOT_COMPLETED) {
+ postCheckIdleStates(userId);
}
}