diff options
| author | 2014-06-06 19:06:26 -0700 | |
|---|---|---|
| committer | 2014-06-09 17:49:50 -0700 | |
| commit | 115afdadb5863a02f0b0daefcc0511bfd35b531e (patch) | |
| tree | 63fe6ade57e00de390c3d490b6a61875e5c161aa | |
| parent | 5553aeb2aeee88929b11ee3bbf8e02da1e9368bd (diff) | |
Switch framework to using new scheduled-work API
Also add the intended permission-use enforcement to said API.
Bug 14994893
Bug 14993295
Change-Id: I5a3ffd32d0702c68f4ef6da68f7fa6e9de674380
| -rw-r--r-- | core/res/AndroidManifest.xml | 14 | ||||
| -rw-r--r-- | core/res/res/values/strings.xml | 6 | ||||
| -rw-r--r-- | services/core/java/com/android/server/MountService.java | 5 | ||||
| -rw-r--r-- | services/core/java/com/android/server/MountServiceIdler.java | 70 | ||||
| -rw-r--r-- | services/core/java/com/android/server/task/TaskManagerService.java | 46 |
5 files changed, 115 insertions, 26 deletions
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 90d499688af0..d34181e58faf 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -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> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 241526ebabc3..10edcc88ccd1 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -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> diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java index d31fb607e566..39410c267109 100644 --- a/services/core/java/com/android/server/MountService.java +++ b/services/core/java/com/android/server/MountService.java @@ -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() { diff --git a/services/core/java/com/android/server/MountServiceIdler.java b/services/core/java/com/android/server/MountServiceIdler.java index 8b1932143eab..61790826f796 100644 --- a/services/core/java/com/android/server/MountServiceIdler.java +++ b/services/core/java/com/android/server/MountServiceIdler.java @@ -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; } } diff --git a/services/core/java/com/android/server/task/TaskManagerService.java b/services/core/java/com/android/server/task/TaskManagerService.java index a6b68d921d10..0c55a1d9888a 100644 --- a/services/core/java/com/android/server/task/TaskManagerService.java +++ b/services/core/java/com/android/server/task/TaskManagerService.java @@ -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); |