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

Commit d478a3a0 authored by Christopher Tate's avatar Christopher Tate Committed by Android (Google) Code Review
Browse files

Merge "Switch framework to using new scheduled-work API" into lmp-preview-dev

parents 5eba4c56 115afdad
Loading
Loading
Loading
Loading
+6 −8
Original line number Diff line number Diff line
@@ -1754,12 +1754,13 @@
        android:label="@string/permlab_recovery"
        android:description="@string/permdesc_recovery" />

    <!-- Allows the system to bind to an application's idle services
    <!-- Allows the system to bind to an application's task services
         @hide -->
    <permission android:name="android.permission.BIND_IDLE_SERVICE"
    <permission android:name="android.permission.BIND_TASK_SERVICE"
        android:protectionLevel="signature"
        android:label="@string/permlab_bindIdleService"
        android:description="@string/permdesc_bindIdleService" />
        android:label="@string/permlab_bindTaskService"
        android:description="@string/permdesc_bindTaskService" />
    <uses-permission android:name="android.permission.BIND_TASK_SERVICE"/>

    <!-- ========================================= -->
    <!-- Permissions for special development tools -->
@@ -2875,10 +2876,7 @@

        <service android:name="com.android.server.MountServiceIdler"
                 android:exported="false"
                 android:permission="android.permission.BIND_IDLE_SERVICE" >
            <intent-filter>
                <action android:name="android.service.idle.IdleService" />
            </intent-filter>
                 android:permission="android.permission.BIND_TASK_SERVICE" >
        </service>

    </application>
+3 −3
Original line number Diff line number Diff line
@@ -1260,11 +1260,11 @@
    <!-- Title of a permission that is never presented to the user.  This is not a
         permission that an application must be granted by the user.  Instead, it
         is part of a mechanism that applications use to indicate to the system
         that they want to do occasional work while the device is idle.  -->
    <string name="permlab_bindIdleService">run application during idle time</string>
         that they want to do scheduled background work.  -->
    <string name="permlab_bindTaskService">run the application\'s scheduled background work</string>
    <!-- Description of an application permission, so that the user can understand
         what is being done if they are curious. -->
    <string name="permdesc_bindIdleService">This permission allows the Android system to run the application in the background while the device is not in use.</string>
    <string name="permdesc_bindTaskService">This permission allows the Android system to run the application in the background when requested.</string>

    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
    <string name="permlab_diagnostic">read/write to resources owned by diag</string>
+5 −0
Original line number Diff line number Diff line
@@ -629,6 +629,11 @@ class MountService extends IMountService.Stub
            sendUmsIntent(true);
            mSendUmsConnectedOnBoot = false;
        }

        /*
         * Start scheduling nominally-daily fstrim operations
         */
        MountServiceIdler.scheduleIdlePass(mContext);
    }

    private final BroadcastReceiver mUserReceiver = new BroadcastReceiver() {
+65 −5
Original line number Diff line number Diff line
@@ -16,34 +16,94 @@

package com.android.server;

import android.app.maintenance.IdleService;
import java.util.Calendar;

import android.app.task.Task;
import android.app.task.TaskManager;
import android.app.task.TaskParams;
import android.app.task.TaskService;
import android.content.ComponentName;
import android.content.Context;
import android.util.Slog;

public class MountServiceIdler extends IdleService {
public class MountServiceIdler extends TaskService {
    private static final String TAG = "MountServiceIdler";

    private static ComponentName sIdleService =
            new ComponentName(MountServiceIdler.class.getPackage().getName(),
                    MountServiceIdler.class.getName());

    private static int MOUNT_TASK_ID = 808;

    private boolean mStarted;
    private TaskParams mTaskParams;
    private Runnable mFinishCallback = new Runnable() {
        @Override
        public void run() {
            Slog.i(TAG, "Got mount service completion callback");
            finishIdle();
            synchronized (mFinishCallback) {
                if (mStarted) {
                    taskFinished(mTaskParams, false);
                    mStarted = false;
                }
            }
            // ... and try again tomorrow
            scheduleIdlePass(MountServiceIdler.this);
        }
    };

    @Override
    public boolean onIdleStart() {
    public boolean onStartTask(TaskParams params) {
        // The mount service will run an fstrim operation asynchronously
        // on a designated separate thread, so we provide it with a callback
        // that lets us cleanly end our idle timeslice.  It's safe to call
        // finishIdle() from any thread.
        mTaskParams = params;
        MountService ms = MountService.sSelf;
        if (ms != null) {
            synchronized (mFinishCallback) {
                mStarted = true;
            }
            ms.runIdleMaintenance(mFinishCallback);
        }
        return ms != null;
    }

    @Override
    public void onIdleStop() {
    public boolean onStopTask(TaskParams params) {
        // Once we kick off the fstrim we aren't actually interruptible; just note
        // that we don't need to call taskFinished(), and let everything happen in
        // the callback from the mount service.
        synchronized (mFinishCallback) {
            mStarted = false;
        }
        return false;
    }

    /**
     * Schedule the idle job that will ping the mount service
     */
    public static void scheduleIdlePass(Context context) {
        TaskManager tm = (TaskManager) context.getSystemService(Context.TASK_SERVICE);

        Calendar calendar = tomorrowMidnight();
        final long timeToMidnight = calendar.getTimeInMillis() - System.currentTimeMillis();

        Task.Builder builder = new Task.Builder(MOUNT_TASK_ID, sIdleService);
        builder.setRequiresDeviceIdle(true);
        builder.setRequiresCharging(true);
        builder.setMinimumLatency(timeToMidnight);
        tm.schedule(builder.build());
    }

    private static Calendar tomorrowMidnight() {
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(System.currentTimeMillis());
        calendar.set(Calendar.HOUR, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        calendar.add(Calendar.DAY_OF_MONTH, 1);
        return calendar;
    }
}
+36 −10
Original line number Diff line number Diff line
@@ -26,10 +26,14 @@ import android.app.task.ITaskManager;
import android.app.task.Task;
import android.app.task.TaskManager;
import android.content.BroadcastReceiver;
import android.app.task.TaskService;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ServiceInfo;
import android.os.Binder;
import android.os.Handler;
import android.os.Looper;
@@ -608,24 +612,43 @@ public class TaskManagerService extends com.android.server.SystemService
         */
        private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();

        // Determine whether the caller is allowed to persist tasks, with a small cache
        // because the lookup is expensive enough that we'd like to avoid repeating it.
        // This must be called from within the calling app's binder identity!
        private boolean canCallerPersistTasks() {
        // Enforce that only the app itself (or shared uid participant) can schedule a
        // task that runs one of the app's services, as well as verifying that the
        // named service properly requires the BIND_TASK_SERVICE permission
        private void enforceValidJobRequest(int uid, Task job) {
            final PackageManager pm = getContext().getPackageManager();
            final ComponentName service = job.getService();
            try {
                ServiceInfo si = pm.getServiceInfo(service, 0);
                if (si.applicationInfo.uid != uid) {
                    throw new IllegalArgumentException("uid " + uid +
                            " cannot schedule job in " + service.getPackageName());
                }
                if (!TaskService.PERMISSION_BIND.equals(si.permission)) {
                    throw new IllegalArgumentException("Scheduled service " + service
                            + " does not require android.permission.BIND_TASK_SERVICE permission");
                }
            } catch (NameNotFoundException e) {
                throw new IllegalArgumentException("No such service: " + service);
            }
        }

        private boolean canPersistJobs(int pid, int uid) {
            // If we get this far we're good to go; all we need to do now is check
            // whether the app is allowed to persist its scheduled work.
            final boolean canPersist;
            final int callingUid = Binder.getCallingUid();
            synchronized (mPersistCache) {
                Boolean cached = mPersistCache.get(callingUid);
                Boolean cached = mPersistCache.get(uid);
                if (cached != null) {
                    canPersist = cached.booleanValue();
                } else {
                    // Persisting tasks is tantamount to running at boot, so we permit
                    // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED
                    // permission
                    int result = getContext().checkCallingPermission(
                            android.Manifest.permission.RECEIVE_BOOT_COMPLETED);
                    int result = getContext().checkPermission(
                            android.Manifest.permission.RECEIVE_BOOT_COMPLETED, pid, uid);
                    canPersist = (result == PackageManager.PERMISSION_GRANTED);
                    mPersistCache.put(callingUid, canPersist);
                    mPersistCache.put(uid, canPersist);
                }
            }
            return canPersist;
@@ -637,9 +660,12 @@ public class TaskManagerService extends com.android.server.SystemService
            if (DEBUG) {
                Slog.d(TAG, "Scheduling task: " + task);
            }
            final boolean canPersist = canCallerPersistTasks();
            final int pid = Binder.getCallingPid();
            final int uid = Binder.getCallingUid();

            enforceValidJobRequest(uid, task);
            final boolean canPersist = canPersistJobs(pid, uid);

            long ident = Binder.clearCallingIdentity();
            try {
                return TaskManagerService.this.schedule(task, uid, canPersist);