Commit cf1a2f73 authored by Christopher Tate's avatar Christopher Tate

Switch everything to scheduled jobs

Everything that used the IdleMaintenance APIs/broadcasts gets to use the
spiffy new JobScheduler instead.  Hooray!

On top of that, the now-obsolete "idle maintenance" APIs are now gone
entirely.  Double hooray!

Bug 14993295

Change-Id: I5fb67c296ca8cd0ba8a2c8760a0f0d9d962d813b
parent 6b2df21e
......@@ -94,8 +94,6 @@ LOCAL_SRC_FILES += \
core/java/android/app/backup/IFullBackupRestoreObserver.aidl \
core/java/android/app/backup/IRestoreObserver.aidl \
core/java/android/app/backup/IRestoreSession.aidl \
core/java/android/app/maintenance/IIdleCallback.aidl \
core/java/android/app/maintenance/IIdleService.aidl \
core/java/android/bluetooth/IBluetooth.aidl \
core/java/android/bluetooth/IBluetoothA2dp.aidl \
core/java/android/bluetooth/IBluetoothA2dpSink.aidl \
......
......@@ -34,9 +34,9 @@
# made today requires touching the same file, just copy the old
# touch step and add it to the end of the list.
#
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************
# *****************************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THE BANNER
# *****************************************************************
# For example:
#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
......@@ -194,10 +194,15 @@ $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framew
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/src/android/app/wearable)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/tv/ITv*)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/classes/android/app/task)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/app/task)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/classes/android/app/TaskManager)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/app/maintenance)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/classes/android/app/maintenance)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes/android/app/maintenance)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/classes/android/app/maintenance)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/src/android/app/maintenance)
# ******************************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
# ******************************************************************
/**
* Copyright 2014, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.maintenance;
import android.app.maintenance.IIdleService;
/**
* The server side of the idle maintenance IPC protocols. The app-side implementation
* invokes on this interface to indicate completion of the (asynchronous) instructions
* issued by the server.
*
* In all cases, the 'who' parameter is the caller's service binder, used to track
* which idle service instance is reporting.
*
* {@hide}
*/
interface IIdleCallback {
/**
* Acknowledge receipt and processing of the asynchronous "start idle work" incall.
* 'result' is true if the app wants some time to perform ongoing background
* idle-time work; or false if the app declares that it does not need any time
* for such work.
*/
void acknowledgeStart(int token, boolean result);
/**
* Acknowledge receipt and processing of the asynchronous "stop idle work" incall.
*/
void acknowledgeStop(int token);
/*
* Tell the idle service manager that we're done with our idle maintenance, so that
* it can go on to the next one and stop attributing wakelock time to us etc.
*
* @param opToken The identifier passed in the startIdleMaintenance() call that
* indicated the beginning of this service's idle timeslice.
*/
void idleFinished(int token);
}
/**
* Copyright 2014, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.maintenance;
import android.app.maintenance.IIdleCallback;
/**
* Interface that the framework uses to communicate with application code
* that implements an idle-time "maintenance" service. End user code does
* not implement this interface directly; instead, the app's idle service
* implementation will extend android.app.maintenance.IdleService.
* {@hide}
*/
oneway interface IIdleService {
/**
* Begin your idle-time work.
*/
void startIdleMaintenance(IIdleCallback callbackBinder, int token);
void stopIdleMaintenance(IIdleCallback callbackBinder, int token);
}
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.maintenance;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
import android.util.Slog;
/**
* Idle maintenance API. Full docs TBW (to be written).
*/
public abstract class IdleService extends Service {
private static final String TAG = "IdleService";
static final int MSG_START = 1;
static final int MSG_STOP = 2;
static final int MSG_FINISH = 3;
IdleHandler mHandler;
IIdleCallback mCallbackBinder;
int mToken;
final Object mHandlerLock = new Object();
void ensureHandler() {
synchronized (mHandlerLock) {
if (mHandler == null) {
mHandler = new IdleHandler(getMainLooper());
}
}
}
/**
* TBW: the idle service should supply an intent-filter handling this intent
* <p>
* <p class="note">The application must also protect the idle service with the
* {@code "android.permission.BIND_IDLE_SERVICE"} permission to ensure that other
* applications cannot maliciously bind to it. If an idle service's manifest
* declaration does not require that permission, it will never be invoked.
* </p>
*/
@SdkConstant(SdkConstantType.SERVICE_ACTION)
public static final String SERVICE_INTERFACE =
"android.service.idle.IdleService";
/**
* Idle services must be protected with this permission:
*
* <pre class="prettyprint">
* <service android:name="MyIdleService"
* android:permission="android.permission.BIND_IDLE_SERVICE" >
* ...
* </service>
* </pre>
*
* <p>If an idle service is declared in the manifest but not protected with this
* permission, that service will be ignored by the OS.
*/
public static final String PERMISSION_BIND =
"android.permission.BIND_IDLE_SERVICE";
// Trampoline: the callbacks are always run on the main thread
IIdleService mBinder = new IIdleService.Stub() {
@Override
public void startIdleMaintenance(IIdleCallback callbackBinder, int token)
throws RemoteException {
ensureHandler();
Message msg = mHandler.obtainMessage(MSG_START, token, 0, callbackBinder);
mHandler.sendMessage(msg);
}
@Override
public void stopIdleMaintenance(IIdleCallback callbackBinder, int token)
throws RemoteException {
ensureHandler();
Message msg = mHandler.obtainMessage(MSG_STOP, token, 0, callbackBinder);
mHandler.sendMessage(msg);
}
};
/**
* Your application may begin doing "idle" maintenance work in the background.
* <p>
* Your application may continue to run in the background until it receives a call
* to {@link #onIdleStop()}, at which point you <i>must</i> cease doing work. The
* OS will hold a wakelock on your application's behalf from the time this method is
* called until after the following call to {@link #onIdleStop()} returns.
* </p>
* <p>
* Returning {@code false} from this method indicates that you have no ongoing work
* to do at present. The OS will respond by immediately calling {@link #onIdleStop()}
* and returning your application to its normal stopped state. Returning {@code true}
* indicates that the application is indeed performing ongoing work, so the OS will
* let your application run in this state until it's no longer appropriate.
* </p>
* <p>
* You will always receive a matching call to {@link #onIdleStop()} even if your
* application returns {@code false} from this method.
*
* @return {@code true} to indicate that the application wishes to perform some ongoing
* background work; {@code false} to indicate that it does not need to perform such
* work at present.
*/
public abstract boolean onIdleStart();
/**
* Your app's maintenance opportunity is over. Once the application returns from
* this method, the wakelock held by the OS on its behalf will be released.
*/
public abstract void onIdleStop();
/**
* Tell the OS that you have finished your idle work. Calling this more than once,
* or calling it when you have not received an {@link #onIdleStart()} callback, is
* an error.
*
* <p>It is safe to call {@link #finishIdle()} from any thread.
*/
public final void finishIdle() {
ensureHandler();
mHandler.sendEmptyMessage(MSG_FINISH);
}
class IdleHandler extends Handler {
IdleHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_START: {
// Call the concrete onIdleStart(), reporting its return value back to
// the OS. If onIdleStart() throws, report it as a 'false' return but
// rethrow the exception at the offending app.
boolean result = false;
IIdleCallback callbackBinder = (IIdleCallback) msg.obj;
mCallbackBinder = callbackBinder;
final int token = mToken = msg.arg1;
try {
result = IdleService.this.onIdleStart();
} catch (Exception e) {
Log.e(TAG, "Unable to start idle workload", e);
throw new RuntimeException(e);
} finally {
// don't bother if the service already called finishIdle()
if (mCallbackBinder != null) {
try {
callbackBinder.acknowledgeStart(token, result);
} catch (RemoteException re) {
Log.e(TAG, "System unreachable to start idle workload");
}
}
}
break;
}
case MSG_STOP: {
// Structured just like MSG_START for the stop-idle bookend call.
IIdleCallback callbackBinder = (IIdleCallback) msg.obj;
final int token = msg.arg1;
try {
IdleService.this.onIdleStop();
} catch (Exception e) {
Log.e(TAG, "Unable to stop idle workload", e);
throw new RuntimeException(e);
} finally {
if (mCallbackBinder != null) {
try {
callbackBinder.acknowledgeStop(token);
} catch (RemoteException re) {
Log.e(TAG, "System unreachable to stop idle workload");
}
}
}
break;
}
case MSG_FINISH: {
if (mCallbackBinder != null) {
try {
mCallbackBinder.idleFinished(mToken);
} catch (RemoteException e) {
Log.e(TAG, "System unreachable to finish idling");
} finally {
mCallbackBinder = null;
}
} else {
Log.e(TAG, "finishIdle() called but the idle service is not started");
}
break;
}
default: {
Slog.w(TAG, "Unknown message " + msg.what);
}
}
}
}
/** @hide */
@Override
public final IBinder onBind(Intent intent) {
return mBinder.asBinder();
}
}
......@@ -2928,6 +2928,12 @@
android:permission="android.permission.BIND_JOB_SERVICE" >
</service>
<service
android:name="com.android.server.pm.BackgroundDexOptService"
android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE">
</service>
</application>
</manifest>
......@@ -16,12 +16,13 @@
package com.android.server.pm;
import android.content.BroadcastReceiver;
import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.app.job.JobScheduler;
import android.app.job.JobService;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.Log;
import java.util.HashSet;
......@@ -30,62 +31,63 @@ import java.util.concurrent.atomic.AtomicBoolean;
/**
* {@hide}
*/
public class BackgroundDexOptService {
public class BackgroundDexOptService extends JobService {
static final String TAG = "BackgroundDexOptService";
private final BroadcastReceiver mIdleMaintenanceReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (Intent.ACTION_IDLE_MAINTENANCE_START.equals(action)) {
onIdleStart();
} else if (Intent.ACTION_IDLE_MAINTENANCE_END.equals(action)) {
onIdleStop();
}
}
};
final PackageManagerService mPackageManager;
static final int BACKGROUND_DEXOPT_JOB = 808;
private static ComponentName sDexoptServiceName = new ComponentName(
BackgroundDexOptService.class.getPackage().getName(),
BackgroundDexOptService.class.getName());
final AtomicBoolean mIdleTime = new AtomicBoolean(false);
public BackgroundDexOptService(Context context) {
mPackageManager = (PackageManagerService)ServiceManager.getService("package");
IntentFilter idleMaintenanceFilter = new IntentFilter();
idleMaintenanceFilter.addAction(Intent.ACTION_IDLE_MAINTENANCE_START);
idleMaintenanceFilter.addAction(Intent.ACTION_IDLE_MAINTENANCE_END);
context.registerReceiverAsUser(mIdleMaintenanceReceiver, UserHandle.ALL,
idleMaintenanceFilter, null, null);
public static void schedule(Context context) {
JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
JobInfo job = new JobInfo.Builder(BACKGROUND_DEXOPT_JOB, sDexoptServiceName)
.setRequiresDeviceIdle(true)
.setRequiresCharging(true)
.build();
js.schedule(job);
}
public boolean onIdleStart() {
@Override
public boolean onStartJob(JobParameters params) {
Log.i(TAG, "onIdleStart");
if (mPackageManager.isStorageLow()) {
final PackageManagerService pm =
(PackageManagerService)ServiceManager.getService("package");
if (pm.isStorageLow()) {
return false;
}
final HashSet<String> pkgs = mPackageManager.getPackagesThatNeedDexOpt();
final HashSet<String> pkgs = pm.getPackagesThatNeedDexOpt();
if (pkgs == null) {
return false;
}
final JobParameters jobParams = params;
mIdleTime.set(true);
new Thread("BackgroundDexOptService_DexOpter") {
@Override
public void run() {
for (String pkg : pkgs) {
if (!mIdleTime.get()) {
break;
// stopped while still working, so we need to reschedule
schedule(BackgroundDexOptService.this);
return;
}
mPackageManager.performDexOpt(pkg, false);
pm.performDexOpt(pkg, false);
}
// ran to completion, so we abandon our timeslice and do not reschedule
jobFinished(jobParams, false);
}
}.start();
return true;
}
public void onIdleStop() {
@Override
public boolean onStopJob(JobParameters params) {
Log.i(TAG, "onIdleStop");
mIdleTime.set(false);
return false;
}
}
......@@ -932,13 +932,6 @@ public final class SystemServer {
}
}
try {
Slog.i(TAG, "IdleMaintenanceService");
new IdleMaintenanceService(context, battery);
} catch (Throwable e) {
reportWtf("starting IdleMaintenanceService", e);
}
try {
if (pm.hasSystemFeature(PackageManager.FEATURE_PRINTING)) {
mSystemServiceManager.startService(PRINT_MANAGER_SERVICE_CLASS);
......@@ -990,7 +983,7 @@ public final class SystemServer {
try {
Slog.i(TAG, "BackgroundDexOptService");
new BackgroundDexOptService(context);
BackgroundDexOptService.schedule(context);
} catch (Throwable e) {
reportWtf("starting BackgroundDexOptService", e);
}
......
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := IdleServiceTest
LOCAL_CERTIFICATE := platform
LOCAL_PROGUARD_ENABLED := disabled
include $(BUILD_PACKAGE)
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.idleservicetest">
<application>
<service android:name="TestService"
android:exported="true"
android:enabled="true"
android:permission="android.permission.BIND_IDLE_SERVICE" >
<intent-filter>
<action android:name="android.service.idle.IdleService" />
</intent-filter>
</service>
<service android:name="CrashingTestService"
android:exported="true"
android:enabled="true"
android:permission="android.permission.BIND_IDLE_SERVICE" >
<intent-filter>
<action android:name="android.service.idle.IdleService" />
</intent-filter>
</service>
<service android:name="TimeoutTestService"
android:exported="true"
android:enabled="true"
android:permission="android.permission.BIND_IDLE_SERVICE" >
<intent-filter>
<action android:name="android.service.idle.IdleService" />
</intent-filter>
</service>
<!-- UnpermissionedTestService should never run because it does
not require the necessary permission in its <service> block -->
<service android:name="UnpermissionedTestService"
android:exported="true"
android:enabled="true" >
<intent-filter>
<action android:name="android.service.idle.IdleService" />
</intent-filter>
</service>
</application>
</manifest>
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.idleservicetest;
import android.app.maintenance.IdleService;
import android.os.Handler;
import android.util.Log;
public class CrashingTestService extends IdleService {
static final String TAG = "CrashingTestService";
String mNull = null;
@Override
public boolean onIdleStart() {
Log.i(TAG, "Idle maintenance: onIdleStart()");
Handler h = new Handler();
Runnable r = new Runnable() {
@Override
public void run() {
Log.i(TAG, "Explicitly crashing");
if (mNull.equals("")) {
Log.i(TAG, "won't happen");
}
}
};
Log.i(TAG, "Posting explicit crash in 15 seconds");
h.postDelayed(r, 15 * 1000);
return true;
}
@Override
public void onIdleStop() {
Log.i(TAG, "Idle maintenance: onIdleStop()");
}
}
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.idleservicetest;
import android.app.maintenance.IdleService;
import android.os.Handler;
import android.util.Log;
public class TestService extends IdleService {
static final String TAG = "TestService";
@Override
public boolean onIdleStart() {
Log.i(TAG, "Idle maintenance: onIdleStart()");
Handler h = new Handler();
Runnable r = new Runnable() {
@Override
public void run() {
Log.i(TAG, "Explicitly finishing idle");
finishIdle();
}
};
Log.i(TAG, "Posting explicit finish in 15 seconds");
h.postDelayed(r, 15 * 1000);
return true;
}
@Override
public void onIdleStop() {
Log.i(TAG, "Idle maintenance: onIdleStop()");
}
}
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.