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
This commit is contained in:
Christopher Tate
2014-06-16 15:51:39 -07:00
parent 6b2df21eca
commit cf1a2f73fc
16 changed files with 54 additions and 1434 deletions

View File

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

View File

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

View File

@@ -1,53 +0,0 @@
/**
* 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);
}

View File

@@ -1,34 +0,0 @@
/**
* 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);
}

View File

@@ -1,228 +0,0 @@
/*
* 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();
}
}

View File

@@ -1,5 +0,0 @@
<html>
<body>
{@hide}
</body>
</html>

View File

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

View File

@@ -1,818 +0,0 @@
/*
* Copyright (C) 2013 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.server;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.maintenance.IIdleCallback;
import android.app.maintenance.IIdleService;
import android.app.maintenance.IdleService;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Handler;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.WorkSource;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
/**
* This service observes the device state and when applicable sends
* broadcasts at the beginning and at the end of a period during which
* observers can perform idle maintenance tasks. Typical use of the
* idle maintenance is to perform somehow expensive tasks that can be
* postponed to a moment when they will not degrade user experience.
*
* The current implementation is very simple. The start of a maintenance
* window is announced if: the screen is off or showing a dream AND the
* battery level is more than twenty percent AND at least one hour passed
* activity).
*
* The end of a maintenance window is announced only if: a start was
* announced AND the screen turned on or a dream was stopped.
*
* Method naming note:
* Methods whose name ends with "Tm" must only be called from the main thread.
*/
public class IdleMaintenanceService extends BroadcastReceiver {
private static final boolean DEBUG = false;
private static final String TAG = IdleMaintenanceService.class.getSimpleName();
private static final int LAST_USER_ACTIVITY_TIME_INVALID = -1;
private static final long MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS = 24 * 60 * 60 * 1000; // 1 day
private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_CHARGING = 30; // percent
private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_NOT_CHARGING = 80; // percent
private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_RUNNING = 20; // percent
private static final long MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START = 71 * 60 * 1000; // 71 min
private static final long MAX_IDLE_MAINTENANCE_DURATION = 71 * 60 * 1000; // 71 min
private static final String ACTION_UPDATE_IDLE_MAINTENANCE_STATE =
"com.android.server.IdleMaintenanceService.action.UPDATE_IDLE_MAINTENANCE_STATE";
private static final String ACTION_FORCE_IDLE_MAINTENANCE =
"com.android.server.IdleMaintenanceService.action.FORCE_IDLE_MAINTENANCE";
static final int MSG_OP_COMPLETE = 1;
static final int MSG_IDLE_FINISHED = 2;
static final int MSG_TIMEOUT = 3;
// when a timeout happened, what were we expecting?
static final int VERB_BINDING = 1;
static final int VERB_IDLING = 2;
static final int VERB_ENDING = 3;
// What are our relevant timeouts / allocated slices?
static final long OP_TIMEOUT = 8 * 1000; // 8 seconds to bind or ack the start
static final long IDLE_TIMESLICE = 10 * 60 * 1000; // ten minutes for each idler
private final AlarmManager mAlarmService;
private final BatteryService mBatteryService;
private final PendingIntent mUpdateIdleMaintenanceStatePendingIntent;
private final Context mContext;
private final WakeLock mWakeLock;
private final WorkSource mSystemWorkSource = new WorkSource(Process.myUid());
private long mLastIdleMaintenanceStartTimeMillis;
private long mLastUserActivityElapsedTimeMillis = LAST_USER_ACTIVITY_TIME_INVALID;
private boolean mIdleMaintenanceStarted;
final IdleCallback mCallback;
final Handler mHandler;
final Random mTokenGenerator = new Random();
int makeToken() {
int token;
do {
token = mTokenGenerator.nextInt(Integer.MAX_VALUE);
} while (token == 0);
return token;
}
class ActiveTask {
public IdleServiceInfo who;
public int verb;
public int token;
ActiveTask(IdleServiceInfo target, int action) {
who = target;
verb = action;
token = makeToken();
}
@Override
public String toString() {
return "ActiveTask{" + Integer.toHexString(this.hashCode())
+ " : verb=" + verb
+ " : token=" + token
+ " : "+ who + "}";
}
}
// What operations are in flight?
final SparseArray<ActiveTask> mPendingOperations = new SparseArray<ActiveTask>();
// Idle service queue management
class IdleServiceInfo {
public final ComponentName componentName;
public final int uid;
public IIdleService service;
IdleServiceInfo(ResolveInfo info, ComponentName cname) {
componentName = cname; // derived from 'info' but this avoids an extra object
uid = info.serviceInfo.applicationInfo.uid;
service = null;
}
@Override
public int hashCode() {
return componentName.hashCode();
}
@Override
public String toString() {
return "IdleServiceInfo{" + componentName
+ " / " + (service == null ? "null" : service.asBinder()) + "}";
}
}
final ArrayMap<ComponentName, IdleServiceInfo> mIdleServices =
new ArrayMap<ComponentName, IdleServiceInfo>();
final LinkedList<IdleServiceInfo> mIdleServiceQueue = new LinkedList<IdleServiceInfo>();
IdleServiceInfo mCurrentIdler; // set when we've committed to launching an idler
IdleServiceInfo mLastIdler; // end of queue when idling begins
void reportNoTimeout(int token, boolean result) {
final Message msg = mHandler.obtainMessage(MSG_OP_COMPLETE, result ? 1 : 0, token);
mHandler.sendMessage(msg);
}
// Binder acknowledgment trampoline
class IdleCallback extends IIdleCallback.Stub {
@Override
public void acknowledgeStart(int token, boolean result) throws RemoteException {
reportNoTimeout(token, result);
}
@Override
public void acknowledgeStop(int token) throws RemoteException {
reportNoTimeout(token, false);
}
@Override
public void idleFinished(int token) throws RemoteException {
if (DEBUG) {
Slog.v(TAG, "idleFinished: " + token);
}
final Message msg = mHandler.obtainMessage(MSG_IDLE_FINISHED, 0, token);
mHandler.sendMessage(msg);
}
}
// Stuff that we run on a Handler
class IdleHandler extends Handler {
public IdleHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
final int token = msg.arg2;
switch (msg.what) {
case MSG_OP_COMPLETE: {
if (DEBUG) {
Slog.i(TAG, "MSG_OP_COMPLETE of " + token);
}
ActiveTask task = mPendingOperations.get(token);
if (task != null) {
mPendingOperations.remove(token);
removeMessages(MSG_TIMEOUT);
handleOpCompleteTm(task, msg.arg1);
} else {
// Can happen in a race between timeout and actual
// (belated) completion of a "begin idling" or similar
// operation. In that state we've already processed the
// timeout, so we intentionally no-op here.
if (DEBUG) {
Slog.w(TAG, "Belated op-complete of " + token);
}
}
break;
}
case MSG_IDLE_FINISHED: {
if (DEBUG) {
Slog.i(TAG, "MSG_IDLE_FINISHED of " + token);
}
ActiveTask task = mPendingOperations.get(token);
if (task != null) {
if (DEBUG) {
Slog.i(TAG, "... removing task " + token);
}
mPendingOperations.remove(token);
removeMessages(MSG_TIMEOUT);
handleIdleFinishedTm(task);
} else {
// Can happen "legitimately" from an app explicitly calling
// idleFinished() after already having been told that its slice
// has ended.
if (DEBUG) {
Slog.w(TAG, "Belated idle-finished of " + token);
}
}
break;
}
case MSG_TIMEOUT: {
if (DEBUG) {
Slog.i(TAG, "MSG_TIMEOUT of " + token);
}
ActiveTask task = mPendingOperations.get(token);
if (task != null) {
mPendingOperations.remove(token);
removeMessages(MSG_OP_COMPLETE);
handleTimeoutTm(task);
} else {
// This one should not happen; we flushed timeout messages
// whenever we entered a state after which we have established
// that they are not appropriate.
Slog.w(TAG, "Unexpected timeout of " + token);
}
break;
}
default:
Slog.w(TAG, "Unknown message: " + msg.what);
}
}
}
void handleTimeoutTm(ActiveTask task) {
switch (task.verb) {
case VERB_BINDING: {
// We were trying to bind to this service, but it wedged or otherwise
// failed to respond in time. Let it stay in the queue for the next
// time around, but just give up on it for now and go on to the next.
startNextIdleServiceTm();
break;
}
case VERB_IDLING: {
// The service has reached the end of its designated idle timeslice.
// This is not considered an error.
if (DEBUG) {
Slog.i(TAG, "Idler reached end of timeslice: " + task.who);
}
sendEndIdleTm(task.who);
break;
}
case VERB_ENDING: {
if (mCurrentIdler == task.who) {
if (DEBUG) {
Slog.i(TAG, "Task timed out when ending; unbind needed");
}
handleIdleFinishedTm(task);
} else {
if (DEBUG) {
Slog.w(TAG, "Ending timeout for non-current idle service!");
}
}
break;
}
default: {
Slog.w(TAG, "Unknown timeout state " + task.verb);
break;
}
}
}
void handleOpCompleteTm(ActiveTask task, int result) {
if (DEBUG) {
Slog.i(TAG, "handleOpComplete : task=" + task + " result=" + result);
}
if (task.verb == VERB_IDLING) {
// If the service was told to begin idling and responded positively, then
// it has begun idling and will eventually either explicitly finish, or
// reach the end of its allotted timeslice. It's running free now, so we
// just schedule the idle-expiration timeout under the token it's already been
// given and let it keep going.
if (result != 0) {
scheduleOpTimeoutTm(task);
} else {
// The idle service has indicated that it does not, in fact,
// need to run at present, so we immediately indicate that it's
// to finish idling, and go on to the next idler.
if (DEBUG) {
Slog.i(TAG, "Idler declined idling; moving along");
}
sendEndIdleTm(task.who);
}
} else {
// In the idling case, the task will be cleared either as the result of a timeout
// or of an explicit idleFinished(). For all other operations (binding, ending) we
// are done with the task as such, so we remove it from our bookkeeping.
if (DEBUG) {
Slog.i(TAG, "Clearing task " + task);
}
mPendingOperations.remove(task.token);
if (task.verb == VERB_ENDING) {
// The last bit of handshaking around idle cessation for this target
handleIdleFinishedTm(task);
}
}
}
void handleIdleFinishedTm(ActiveTask task) {
final IdleServiceInfo who = task.who;
if (who == mCurrentIdler) {
if (DEBUG) {
Slog.i(TAG, "Current idler has finished: " + who);
Slog.i(TAG, "Attributing wakelock to system work source");
}
mContext.unbindService(mConnection);
startNextIdleServiceTm();
} else {
Slog.w(TAG, "finish from non-current idle service? " + who);
}
}
void updateIdleServiceQueueTm() {
if (DEBUG) {
Slog.i(TAG, "Updating idle service queue");
}
PackageManager pm = mContext.getPackageManager();
Intent idleIntent = new Intent(IdleService.SERVICE_INTERFACE);
List<ResolveInfo> services = pm.queryIntentServices(idleIntent, 0);
for (ResolveInfo info : services) {
if (info.serviceInfo != null) {
if (IdleService.PERMISSION_BIND.equals(info.serviceInfo.permission)) {
final ComponentName componentName = new ComponentName(
info.serviceInfo.packageName,
info.serviceInfo.name);
if (DEBUG) {
Slog.i(TAG, " - " + componentName);
}
if (!mIdleServices.containsKey(componentName)) {
if (DEBUG) {
Slog.i(TAG, " + not known; adding");
}
IdleServiceInfo serviceInfo = new IdleServiceInfo(info, componentName);
mIdleServices.put(componentName, serviceInfo);
mIdleServiceQueue.add(serviceInfo);
}
} else {
if (DEBUG) {
Slog.i(TAG, "Idle service " + info.serviceInfo
+ " does not have required permission; ignoring");
}
}
}
}
}
void startNextIdleServiceTm() {
mWakeLock.setWorkSource(mSystemWorkSource);
if (mLastIdler == null) {
// we've run the queue; nothing more to do until the next idle interval.
if (DEBUG) {
Slog.i(TAG, "Queue already drained; nothing more to do");
}
return;
}
if (DEBUG) {
Slog.i(TAG, "startNextIdleService : last=" + mLastIdler + " cur=" + mCurrentIdler);
if (mIdleServiceQueue.size() > 0) {
int i = 0;
Slog.i(TAG, "Queue (" + mIdleServiceQueue.size() + "):");
for (IdleServiceInfo info : mIdleServiceQueue) {
Slog.i(TAG, " " + i + " : " + info);
i++;
}
}
}
if (mCurrentIdler != mLastIdler) {
if (mIdleServiceQueue.size() > 0) {
IdleServiceInfo target = mIdleServiceQueue.pop();
if (DEBUG) {
Slog.i(TAG, "starting next idle service " + target);
}
Intent idleIntent = new Intent(IdleService.SERVICE_INTERFACE);
idleIntent.setComponent(target.componentName);
mCurrentIdler = target;
ActiveTask task = new ActiveTask(target, VERB_BINDING);
scheduleOpTimeoutTm(task);
boolean bindOk = mContext.bindServiceAsUser(idleIntent, mConnection,
Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY, UserHandle.OWNER);
if (!bindOk) {
if (DEBUG) {
Slog.w(TAG, "bindService() to " + target.componentName
+ " failed");
}
} else {
mIdleServiceQueue.add(target); // at the end for next time
if (DEBUG) { Slog.i(TAG, "Attributing wakelock to target uid " + target.uid); }
mWakeLock.setWorkSource(new WorkSource(target.uid));
}
} else {
// Queue is empty but mLastIdler is non-null -- eeep. Clear *everything*
// and wind up until the next time around.
Slog.e(TAG, "Queue unexpectedly empty; resetting. last="
+ mLastIdler + " cur=" + mCurrentIdler);
mHandler.removeMessages(MSG_TIMEOUT);
mPendingOperations.clear();
stopIdleMaintenanceTm();
}
} else {
// we've reached the place we started, so mark the queue as drained
if (DEBUG) {
Slog.i(TAG, "Reached end of queue.");
}
stopIdleMaintenanceTm();
}
}
void sendStartIdleTm(IdleServiceInfo who) {
ActiveTask task = new ActiveTask(who, VERB_IDLING);
scheduleOpTimeoutTm(task);
try {
who.service.startIdleMaintenance(mCallback, task.token);
} catch (RemoteException e) {
// We bound to it, but now we can't reach it. Bail and go on to the
// next service.
mContext.unbindService(mConnection);
if (DEBUG) { Slog.i(TAG, "Attributing wakelock to system work source"); }
mHandler.removeMessages(MSG_TIMEOUT);
startNextIdleServiceTm();
}
}
void sendEndIdleTm(IdleServiceInfo who) {
ActiveTask task = new ActiveTask(who, VERB_ENDING);
scheduleOpTimeoutTm(task);
if (DEBUG) {
Slog.i(TAG, "Sending end-idle to " + who);
}
try {
who.service.stopIdleMaintenance(mCallback, task.token);
} catch (RemoteException e) {
// We bound to it, but now we can't reach it. Bail and go on to the
// next service.
mContext.unbindService(mConnection);
if (DEBUG) { Slog.i(TAG, "Attributing wakelock to system work source"); }
mHandler.removeMessages(MSG_TIMEOUT);
startNextIdleServiceTm();
}
}
ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
if (DEBUG) {
Slog.i(TAG, "onServiceConnected(" + name + ")");
}
IdleServiceInfo info = mIdleServices.get(name);
if (info != null) {
// Bound! Cancel the bind timeout
mHandler.removeMessages(MSG_TIMEOUT);
// Now tell it to start its idle work
info.service = IIdleService.Stub.asInterface(service);
sendStartIdleTm(info);
} else {
// We bound to a service we don't know about. That's ungood.
Slog.e(TAG, "Connected to unexpected component " + name);
mContext.unbindService(this);
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
if (DEBUG) {
Slog.i(TAG, "onServiceDisconnected(" + name + ")");
}
IdleServiceInfo who = mIdleServices.get(name);
if (who == mCurrentIdler) {
// Hm, okay; they didn't tell us they were finished but they
// went away. Crashed, probably. Oh well. They're gone, so
// we can't finish them cleanly; just force things along.
Slog.w(TAG, "Idler unexpectedly vanished: " + mCurrentIdler);
mContext.unbindService(this);
mHandler.removeMessages(MSG_TIMEOUT);
startNextIdleServiceTm();
} else {
// Not the current idler, so we don't interrupt our process...
if (DEBUG) {
Slog.w(TAG, "Disconnect of abandoned or unexpected service " + name);
}
}
}
};
// Schedules a timeout / end-of-work based on the task verb
void scheduleOpTimeoutTm(ActiveTask task) {
final long timeoutMillis = (task.verb == VERB_IDLING) ? IDLE_TIMESLICE : OP_TIMEOUT;
if (DEBUG) {
Slog.i(TAG, "Scheduling timeout (token " + task.token
+ " : verb " + task.verb + ") for " + task + " in " + timeoutMillis);
}
mPendingOperations.put(task.token, task);
mHandler.removeMessages(MSG_TIMEOUT);
final Message msg = mHandler.obtainMessage(MSG_TIMEOUT, 0, task.token);
mHandler.sendMessageDelayed(msg, timeoutMillis);
}
// -------------------------------------------------------------------------------
public IdleMaintenanceService(Context context, BatteryService batteryService) {
mContext = context;
mBatteryService = batteryService;
mAlarmService = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
mHandler = new IdleHandler(mContext.getMainLooper());
mCallback = new IdleCallback();
Intent intent = new Intent(ACTION_UPDATE_IDLE_MAINTENANCE_STATE);
intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
mUpdateIdleMaintenanceStatePendingIntent = PendingIntent.getBroadcast(mContext, 0,
intent, PendingIntent.FLAG_UPDATE_CURRENT);
register(mHandler);
}
public void register(Handler handler) {
IntentFilter intentFilter = new IntentFilter();
// Alarm actions.
intentFilter.addAction(ACTION_UPDATE_IDLE_MAINTENANCE_STATE);
// Battery actions.
intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
// Screen actions.
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
// Dream actions.
intentFilter.addAction(Intent.ACTION_DREAMING_STARTED);
intentFilter.addAction(Intent.ACTION_DREAMING_STOPPED);
mContext.registerReceiverAsUser(this, UserHandle.ALL,
intentFilter, null, mHandler);
intentFilter = new IntentFilter();
intentFilter.addAction(ACTION_FORCE_IDLE_MAINTENANCE);
mContext.registerReceiverAsUser(this, UserHandle.ALL,
intentFilter, android.Manifest.permission.SET_ACTIVITY_WATCHER, mHandler);
}
private void scheduleUpdateIdleMaintenanceState(long delayMillis) {
final long triggetRealTimeMillis = SystemClock.elapsedRealtime() + delayMillis;
mAlarmService.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggetRealTimeMillis,
mUpdateIdleMaintenanceStatePendingIntent);
}
private void unscheduleUpdateIdleMaintenanceState() {
mAlarmService.cancel(mUpdateIdleMaintenanceStatePendingIntent);
}
private void updateIdleMaintenanceStateTm(boolean noisy) {
if (mIdleMaintenanceStarted) {
// Idle maintenance can be interrupted by user activity, or duration
// time out, or low battery.
final boolean batteryOk
= batteryLevelAndMaintenanceTimeoutPermitsIdleMaintenanceRunning();
if (!lastUserActivityPermitsIdleMaintenanceRunning() || !batteryOk) {
unscheduleUpdateIdleMaintenanceState();
mIdleMaintenanceStarted = false;
// We stopped since we don't have enough battery or timed out but the
// user is not using the device, so we should be able to run maintenance
// in the next maintenance window since the battery may be charged
// without interaction and the min interval between maintenances passed.
if (!batteryOk) {
scheduleUpdateIdleMaintenanceState(
getNextIdleMaintenanceIntervalStartFromNow());
}
EventLogTags.writeIdleMaintenanceWindowFinish(SystemClock.elapsedRealtime(),
mLastUserActivityElapsedTimeMillis, mBatteryService.getBatteryLevel(),
isBatteryCharging() ? 1 : 0);
scheduleIdleFinishTm();
}
} else if (deviceStatePermitsIdleMaintenanceStart(noisy)
&& lastUserActivityPermitsIdleMaintenanceStart(noisy)
&& lastRunPermitsIdleMaintenanceStart(noisy)) {
// Now that we started idle maintenance, we should schedule another
// update for the moment when the idle maintenance times out.
scheduleUpdateIdleMaintenanceState(MAX_IDLE_MAINTENANCE_DURATION);
mIdleMaintenanceStarted = true;
EventLogTags.writeIdleMaintenanceWindowStart(SystemClock.elapsedRealtime(),
mLastUserActivityElapsedTimeMillis, mBatteryService.getBatteryLevel(),
isBatteryCharging() ? 1 : 0);
mLastIdleMaintenanceStartTimeMillis = SystemClock.elapsedRealtime();
startIdleMaintenanceTm();
} else if (lastUserActivityPermitsIdleMaintenanceStart(noisy)) {
if (lastRunPermitsIdleMaintenanceStart(noisy)) {
// The user does not use the device and we did not run maintenance in more
// than the min interval between runs, so schedule an update - maybe the
// battery will be charged latter.
scheduleUpdateIdleMaintenanceState(MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);
} else {
// The user does not use the device but we have run maintenance in the min
// interval between runs, so schedule an update after the min interval ends.
scheduleUpdateIdleMaintenanceState(
getNextIdleMaintenanceIntervalStartFromNow());
}
}
}
void startIdleMaintenanceTm() {
if (DEBUG) {
Slog.i(TAG, "*** Starting idle maintenance ***");
}
if (DEBUG) { Slog.i(TAG, "Attributing wakelock to system work source"); }
mWakeLock.setWorkSource(mSystemWorkSource);
mWakeLock.acquire();
updateIdleServiceQueueTm();
mCurrentIdler = null;
mLastIdler = (mIdleServiceQueue.size() > 0) ? mIdleServiceQueue.peekLast() : null;
startNextIdleServiceTm();
}
// Start a graceful wind-down of the idle maintenance state: end the current idler
// and pretend that we've finished running the queue. If there's no current idler,
// this is a no-op.
void scheduleIdleFinishTm() {
if (mCurrentIdler != null) {
if (DEBUG) {
Slog.i(TAG, "*** Finishing idle maintenance ***");
}
mLastIdler = mCurrentIdler;
sendEndIdleTm(mCurrentIdler);
} else {
if (DEBUG) {
Slog.w(TAG, "Asked to finish idle maintenance but we're done already");
}
}
}
// Actual finalization of the idle maintenance sequence
void stopIdleMaintenanceTm() {
if (mLastIdler != null) {
if (DEBUG) {
Slog.i(TAG, "*** Idle maintenance shutdown ***");
}
mWakeLock.setWorkSource(mSystemWorkSource);
mLastIdler = mCurrentIdler = null;
updateIdleMaintenanceStateTm(false); // resets 'started' and schedules next window
mWakeLock.release();
} else {
Slog.e(TAG, "ERROR: idle shutdown but invariants not held. last=" + mLastIdler
+ " cur=" + mCurrentIdler + " size=" + mIdleServiceQueue.size());
}
}
private long getNextIdleMaintenanceIntervalStartFromNow() {
return mLastIdleMaintenanceStartTimeMillis + MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS
- SystemClock.elapsedRealtime();
}
private boolean deviceStatePermitsIdleMaintenanceStart(boolean noisy) {
final int minBatteryLevel = isBatteryCharging()
? MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_CHARGING
: MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_NOT_CHARGING;
boolean allowed = (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID
&& mBatteryService.getBatteryLevel() > minBatteryLevel);
if (!allowed && noisy) {
Slog.i("IdleMaintenance", "Idle maintenance not allowed due to power");
}
return allowed;
}
private boolean lastUserActivityPermitsIdleMaintenanceStart(boolean noisy) {
// The last time the user poked the device is above the threshold.
boolean allowed = (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID
&& SystemClock.elapsedRealtime() - mLastUserActivityElapsedTimeMillis
> MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);
if (!allowed && noisy) {
Slog.i("IdleMaintenance", "Idle maintenance not allowed due to last user activity");
}
return allowed;
}
private boolean lastRunPermitsIdleMaintenanceStart(boolean noisy) {
// Enough time passed since the last maintenance run.
boolean allowed = SystemClock.elapsedRealtime() - mLastIdleMaintenanceStartTimeMillis
> MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS;
if (!allowed && noisy) {
Slog.i("IdleMaintenance", "Idle maintenance not allowed due time since last");
}
return allowed;
}
private boolean lastUserActivityPermitsIdleMaintenanceRunning() {
// The user is not using the device.
return (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID);
}
private boolean batteryLevelAndMaintenanceTimeoutPermitsIdleMaintenanceRunning() {
// Battery not too low and the maintenance duration did not timeout.
return (mBatteryService.getBatteryLevel() > MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_RUNNING
&& mLastIdleMaintenanceStartTimeMillis + MAX_IDLE_MAINTENANCE_DURATION
> SystemClock.elapsedRealtime());
}
private boolean isBatteryCharging() {
return mBatteryService.getPlugType() > 0
&& mBatteryService.getInvalidCharger() == 0;
}
@Override
public void onReceive(Context context, Intent intent) {
if (DEBUG) {
Log.i(TAG, intent.getAction());
}
String action = intent.getAction();
if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
// We care about battery only if maintenance is in progress so we can
// stop it if battery is too low. Note that here we assume that the
// maintenance clients are properly holding a wake lock. We will
// refactor the maintenance to use services instead of intents for the
// next release. The only client for this for now is internal an holds
// a wake lock correctly.
if (mIdleMaintenanceStarted) {
updateIdleMaintenanceStateTm(false);
}
} else if (Intent.ACTION_SCREEN_ON.equals(action)
|| Intent.ACTION_DREAMING_STOPPED.equals(action)) {
mLastUserActivityElapsedTimeMillis = LAST_USER_ACTIVITY_TIME_INVALID;
// Unschedule any future updates since we already know that maintenance
// cannot be performed since the user is back.
unscheduleUpdateIdleMaintenanceState();
// If the screen went on/stopped dreaming, we know the user is using the
// device which means that idle maintenance should be stopped if running.
updateIdleMaintenanceStateTm(false);
} else if (Intent.ACTION_SCREEN_OFF.equals(action)
|| Intent.ACTION_DREAMING_STARTED.equals(action)) {
mLastUserActivityElapsedTimeMillis = SystemClock.elapsedRealtime();
// If screen went off/started dreaming, we may be able to start idle maintenance
// after the minimal user inactivity elapses. We schedule an alarm for when
// this timeout elapses since the device may go to sleep by then.
scheduleUpdateIdleMaintenanceState(MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);
} else if (ACTION_UPDATE_IDLE_MAINTENANCE_STATE.equals(action)) {
updateIdleMaintenanceStateTm(false);
} else if (ACTION_FORCE_IDLE_MAINTENANCE.equals(action)) {
long now = SystemClock.elapsedRealtime() - 1;
mLastUserActivityElapsedTimeMillis = now - MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START;
mLastIdleMaintenanceStartTimeMillis = now - MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS;
updateIdleMaintenanceStateTm(true);
}
}
}

View File

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

View File

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

View File

@@ -1,13 +0,0 @@
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)

View File

@@ -1,59 +0,0 @@
<?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>

View File

@@ -1,52 +0,0 @@
/*
* 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()");
}
}

View File

@@ -1,48 +0,0 @@
/*
* 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()");
}
}

View File

@@ -1,36 +0,0 @@
/*
* 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.util.Log;
public class TimeoutTestService extends IdleService {
private static final String TAG = "TimeoutTestService";
@Override
public boolean onIdleStart() {
Log.i(TAG, "onIdleStart() but anticipating time-slice timeout");
return true;
}
@Override
public void onIdleStop() {
Log.i(TAG, "onIdleStop() so we're done");
}
}

View File

@@ -1,38 +0,0 @@
/*
* 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.util.Log;
// Should never be invoked because its manifest declaration does not
// require the necessary permission.
public class UnpermissionedTestService extends IdleService {
private static final String TAG = "UnpermissionedTestService";
@Override
public boolean onIdleStart() {
Log.e(TAG, "onIdleStart() for this service should never be called!");
return false;
}
@Override
public void onIdleStop() {
Log.e(TAG, "onIdleStop() for this service should never be called!");
}
}