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:
@@ -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 \
|
||||
|
||||
19
CleanSpec.mk
19
CleanSpec.mk
@@ -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
|
||||
# ******************************************************************
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
<html>
|
||||
<body>
|
||||
{@hide}
|
||||
</body>
|
||||
</html>
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
@@ -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>
|
||||
@@ -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()");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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()");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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!");
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user