Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6ce7413f authored by Kweku Adams's avatar Kweku Adams Committed by Android (Google) Code Review
Browse files

Merge "Implement JS <-> TaskManager user-visible job link."

parents 48740cd2 b3feb9d6
Loading
Loading
Loading
Loading
+12 −3
Original line number Diff line number Diff line
@@ -158,7 +158,10 @@ public class JobSchedulerImpl extends JobScheduler {
            android.Manifest.permission.INTERACT_ACROSS_USERS_FULL})
    @Override
    public void registerUserVisibleJobObserver(@NonNull IUserVisibleJobObserver observer) {
        // TODO(255767350): implement
        try {
            mBinder.registerUserVisibleJobObserver(observer);
        } catch (RemoteException e) {
        }
    }

    @RequiresPermission(allOf = {
@@ -166,7 +169,10 @@ public class JobSchedulerImpl extends JobScheduler {
            android.Manifest.permission.INTERACT_ACROSS_USERS_FULL})
    @Override
    public void unregisterUserVisibleJobObserver(@NonNull IUserVisibleJobObserver observer) {
        // TODO(255767350): implement
        try {
            mBinder.unregisterUserVisibleJobObserver(observer);
        } catch (RemoteException e) {
        }
    }

    @RequiresPermission(allOf = {
@@ -174,6 +180,9 @@ public class JobSchedulerImpl extends JobScheduler {
            android.Manifest.permission.INTERACT_ACROSS_USERS_FULL})
    @Override
    public void stopUserVisibleJobsForUser(@NonNull String packageName, int userId) {
        // TODO(255767350): implement
        try {
            mBinder.stopUserVisibleJobsForUser(packageName, userId);
        } catch (RemoteException e) {
        }
    }
}
+7 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.app.job;

import android.app.job.IUserVisibleJobObserver;
import android.app.job.JobInfo;
import android.app.job.JobSnapshot;
import android.app.job.JobWorkItem;
@@ -38,4 +39,10 @@ interface IJobScheduler {
    boolean hasRunLongJobsPermission(String packageName, int userId);
    List<JobInfo> getStartedJobs();
    ParceledListSlice getAllJobSnapshots();
    @EnforcePermission(allOf={"MANAGE_ACTIVITY_TASKS", "INTERACT_ACROSS_USERS_FULL"})
    void registerUserVisibleJobObserver(in IUserVisibleJobObserver observer);
    @EnforcePermission(allOf={"MANAGE_ACTIVITY_TASKS", "INTERACT_ACROSS_USERS_FULL"})
    void unregisterUserVisibleJobObserver(in IUserVisibleJobObserver observer);
    @EnforcePermission(allOf={"MANAGE_ACTIVITY_TASKS", "INTERACT_ACROSS_USERS_FULL"})
    void stopUserVisibleJobsForUser(String packageName, int userId);
}
+9 −1
Original line number Diff line number Diff line
@@ -98,6 +98,12 @@ public class JobParameters implements Parcelable {
     */
    public static final int INTERNAL_STOP_REASON_SUCCESSFUL_FINISH =
            JobProtoEnums.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH; // 10.
    /**
     * The user stopped the job via some UI (eg. Task Manager).
     * @hide
     */
    public static final int INTERNAL_STOP_REASON_USER_UI_STOP =
            JobProtoEnums.INTERNAL_STOP_REASON_USER_UI_STOP; // 11.

    /**
     * All the stop reason codes. This should be regarded as an immutable array at runtime.
@@ -121,6 +127,7 @@ public class JobParameters implements Parcelable {
            INTERNAL_STOP_REASON_DATA_CLEARED,
            INTERNAL_STOP_REASON_RTC_UPDATED,
            INTERNAL_STOP_REASON_SUCCESSFUL_FINISH,
            INTERNAL_STOP_REASON_USER_UI_STOP,
    };

    /**
@@ -141,6 +148,7 @@ public class JobParameters implements Parcelable {
            case INTERNAL_STOP_REASON_DATA_CLEARED: return "data_cleared";
            case INTERNAL_STOP_REASON_RTC_UPDATED: return "rtc_updated";
            case INTERNAL_STOP_REASON_SUCCESSFUL_FINISH: return "successful_finish";
            case INTERNAL_STOP_REASON_USER_UI_STOP: return "user_ui_stop";
            default: return "unknown:" + reasonCode;
        }
    }
@@ -230,7 +238,7 @@ public class JobParameters implements Parcelable {
    public static final int STOP_REASON_APP_STANDBY = 12;
    /**
     * The user stopped the job. This can happen either through force-stop, adb shell commands,
     * or uninstalling.
     * uninstalling, or some other UI.
     */
    public static final int STOP_REASON_USER = 13;
    /** The system is doing some processing that requires stopping this job. */
+16 −0
Original line number Diff line number Diff line
@@ -1183,6 +1183,22 @@ class JobConcurrencyManager {
        }
    }

    @GuardedBy("mLock")
    void stopUserVisibleJobsLocked(int userId, @NonNull String packageName,
            @JobParameters.StopReason int reason, int internalReasonCode) {
        for (int i = mActiveServices.size() - 1; i >= 0; --i) {
            final JobServiceContext jsc = mActiveServices.get(i);
            final JobStatus jobStatus = jsc.getRunningJobLocked();

            if (jobStatus != null && userId == jobStatus.getSourceUserId()
                    && jobStatus.getSourcePackageName().equals(packageName)
                    && jobStatus.isUserVisibleJob()) {
                jsc.cancelExecutingJobLocked(reason, internalReasonCode,
                        JobParameters.getInternalReasonCodeDescription(internalReasonCode));
            }
        }
    }

    @GuardedBy("mLock")
    void stopNonReadyActiveJobsLocked() {
        for (int i = 0; i < mActiveServices.size(); i++) {
+105 −0
Original line number Diff line number Diff line
@@ -16,11 +16,14 @@

package com.android.server.job;

import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.Manifest.permission.MANAGE_ACTIVITY_TASKS;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;

import android.annotation.EnforcePermission;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -31,6 +34,7 @@ import android.app.AppGlobals;
import android.app.IUidObserver;
import android.app.compat.CompatChanges;
import android.app.job.IJobScheduler;
import android.app.job.IUserVisibleJobObserver;
import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.app.job.JobProtoEnums;
@@ -38,6 +42,7 @@ import android.app.job.JobScheduler;
import android.app.job.JobService;
import android.app.job.JobSnapshot;
import android.app.job.JobWorkItem;
import android.app.job.UserVisibleJobSummary;
import android.app.usage.UsageStatsManager;
import android.app.usage.UsageStatsManagerInternal;
import android.compat.annotation.ChangeId;
@@ -68,6 +73,7 @@ import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -248,6 +254,8 @@ public class JobSchedulerService extends com.android.server.SystemService
    static final int MSG_UID_IDLE = 7;
    static final int MSG_CHECK_CHANGED_JOB_LIST = 8;
    static final int MSG_CHECK_MEDIA_EXEMPTION = 9;
    static final int MSG_INFORM_OBSERVER_OF_ALL_USER_VISIBLE_JOBS = 10;
    static final int MSG_INFORM_OBSERVERS_OF_USER_VISIBLE_JOB_CHANGE = 11;

    /** List of controllers that will notify this service of updates to jobs. */
    final List<StateController> mControllers;
@@ -279,6 +287,9 @@ public class JobSchedulerService extends com.android.server.SystemService
    @GuardedBy("mLock")
    private final SparseArray<String> mCloudMediaProviderPackages = new SparseArray<>();

    private final RemoteCallbackList<IUserVisibleJobObserver> mUserVisibleJobObservers =
            new RemoteCallbackList<>();

    private final CountQuotaTracker mQuotaTracker;
    private static final String QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG = ".schedulePersisted()";
    private static final String QUOTA_TRACKER_SCHEDULE_LOGGED =
@@ -1501,6 +1512,14 @@ public class JobSchedulerService extends com.android.server.SystemService
        }
    }

    private void stopUserVisibleJobsInternal(@NonNull String packageName, int userId) {
        synchronized (mLock) {
            mConcurrencyManager.stopUserVisibleJobsLocked(userId, packageName,
                    JobParameters.STOP_REASON_USER,
                    JobParameters.INTERNAL_STOP_REASON_USER_UI_STOP);
        }
    }

    private final Consumer<JobStatus> mCancelJobDueToUserRemovalConsumer = (toRemove) -> {
        // There's no guarantee that the process has been stopped by the time we get
        // here, but since this is a user-initiated action, it should be fine to just
@@ -2159,6 +2178,7 @@ public class JobSchedulerService extends com.android.server.SystemService
        }
        delayMillis =
                Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);
        // TODO(255767350): demote all jobs to regular for user stops so they don't keep privileges
        JobStatus newJob = new JobStatus(failureToReschedule,
                elapsedNowMillis + delayMillis,
                JobStatus.NO_LATEST_RUNTIME, numFailures, numSystemStops,
@@ -2509,6 +2529,52 @@ public class JobSchedulerService extends com.android.server.SystemService
                        args.recycle();
                        break;
                    }

                    case MSG_INFORM_OBSERVER_OF_ALL_USER_VISIBLE_JOBS: {
                        final IUserVisibleJobObserver observer =
                                (IUserVisibleJobObserver) message.obj;
                        synchronized (mLock) {
                            for (int i = mConcurrencyManager.mActiveServices.size() - 1; i >= 0;
                                    --i) {
                                JobServiceContext context =
                                        mConcurrencyManager.mActiveServices.get(i);
                                final JobStatus jobStatus = context.getRunningJobLocked();
                                if (jobStatus != null && jobStatus.isUserVisibleJob()) {
                                    try {
                                        observer.onUserVisibleJobStateChanged(
                                                jobStatus.getUserVisibleJobSummary(),
                                                /* isRunning */ true);
                                    } catch (RemoteException e) {
                                        // Will be unregistered automatically by
                                        // RemoteCallbackList's dead-object tracking,
                                        // so don't need to remove it here.
                                        break;
                                    }
                                }
                            }
                        }
                        break;
                    }

                    case MSG_INFORM_OBSERVERS_OF_USER_VISIBLE_JOB_CHANGE: {
                        final SomeArgs args = (SomeArgs) message.obj;
                        final JobServiceContext context = (JobServiceContext) args.arg1;
                        final JobStatus jobStatus = (JobStatus) args.arg2;
                        final UserVisibleJobSummary summary = jobStatus.getUserVisibleJobSummary();
                        final boolean isRunning = args.argi1 == 1;
                        for (int i = mUserVisibleJobObservers.beginBroadcast() - 1; i >= 0; --i) {
                            try {
                                mUserVisibleJobObservers.getBroadcastItem(i)
                                        .onUserVisibleJobStateChanged(summary, isRunning);
                            } catch (RemoteException e) {
                                // Will be unregistered automatically by RemoteCallbackList's
                                // dead-object tracking, so nothing we need to do here.
                            }
                        }
                        mUserVisibleJobObservers.finishBroadcast();
                        args.recycle();
                        break;
                    }
                }
                maybeRunPendingJobsLocked();
            }
@@ -3022,6 +3088,16 @@ public class JobSchedulerService extends com.android.server.SystemService
        return adjustJobBias(bias, job);
    }

    void informObserversOfUserVisibleJobChange(JobServiceContext context, JobStatus jobStatus,
            boolean isRunning) {
        SomeArgs args = SomeArgs.obtain();
        args.arg1 = context;
        args.arg2 = jobStatus;
        args.argi1 = isRunning ? 1 : 0;
        mHandler.obtainMessage(MSG_INFORM_OBSERVERS_OF_USER_VISIBLE_JOB_CHANGE, args)
                .sendToTarget();
    }

    private final class BatteryStateTracker extends BroadcastReceiver {
        /**
         * Track whether we're "charging", where charging means that we're ready to commit to
@@ -3744,6 +3820,35 @@ public class JobSchedulerService extends com.android.server.SystemService
                return new ParceledListSlice<>(snapshots);
            }
        }

        @Override
        @EnforcePermission(allOf = {MANAGE_ACTIVITY_TASKS, INTERACT_ACROSS_USERS_FULL})
        public void registerUserVisibleJobObserver(@NonNull IUserVisibleJobObserver observer) {
            if (observer == null) {
                throw new NullPointerException("observer");
            }
            mUserVisibleJobObservers.register(observer);
            mHandler.obtainMessage(MSG_INFORM_OBSERVER_OF_ALL_USER_VISIBLE_JOBS, observer)
                    .sendToTarget();
        }

        @Override
        @EnforcePermission(allOf = {MANAGE_ACTIVITY_TASKS, INTERACT_ACROSS_USERS_FULL})
        public void unregisterUserVisibleJobObserver(@NonNull IUserVisibleJobObserver observer) {
            if (observer == null) {
                throw new NullPointerException("observer");
            }
            mUserVisibleJobObservers.unregister(observer);
        }

        @Override
        @EnforcePermission(allOf = {"MANAGE_ACTIVITY_TASKS", "INTERACT_ACROSS_USERS_FULL"})
        public void stopUserVisibleJobsForUser(@NonNull String packageName, int userId) {
            if (packageName == null) {
                throw new NullPointerException("packageName");
            }
            JobSchedulerService.this.stopUserVisibleJobsInternal(packageName, userId);
        }
    }

    // Shell command infrastructure: run the given job immediately
Loading