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

This commit is contained in:
Christopher Tate
2014-06-10 01:03:51 +00:00
committed by Android (Google) Code Review
5 changed files with 115 additions and 26 deletions

View File

@@ -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>

View File

@@ -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>

View File

@@ -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() {

View File

@@ -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;
}
}

View File

@@ -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);