Merge "Delay syncs for idle apps"
This commit is contained in:
committed by
Android (Google) Code Review
commit
c1c31d09cd
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.content;
|
||||
|
||||
import android.app.usage.UsageStatsManagerInternal;
|
||||
import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.BatteryManager;
|
||||
import android.os.UserHandle;
|
||||
|
||||
import com.android.server.LocalServices;
|
||||
|
||||
/**
|
||||
* Helper to listen for app idle and charging status changes and restart backed off
|
||||
* sync operations.
|
||||
*/
|
||||
class AppIdleMonitor implements AppIdleStateChangeListener {
|
||||
|
||||
private final SyncManager mSyncManager;
|
||||
private final UsageStatsManagerInternal mUsageStats;
|
||||
final BatteryManager mBatteryManager;
|
||||
/** Is the device currently plugged into power. */
|
||||
private boolean mPluggedIn;
|
||||
|
||||
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
onPluggedIn(mBatteryManager.isCharging());
|
||||
}
|
||||
};
|
||||
|
||||
AppIdleMonitor(SyncManager syncManager, Context context) {
|
||||
mSyncManager = syncManager;
|
||||
mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
|
||||
mUsageStats.addAppIdleStateChangeListener(this);
|
||||
mBatteryManager = context.getSystemService(BatteryManager.class);
|
||||
mPluggedIn = isPowered();
|
||||
registerReceivers(context);
|
||||
}
|
||||
|
||||
private void registerReceivers(Context context) {
|
||||
// Monitor battery charging state
|
||||
IntentFilter filter = new IntentFilter(BatteryManager.ACTION_CHARGING);
|
||||
filter.addAction(BatteryManager.ACTION_DISCHARGING);
|
||||
context.registerReceiver(mReceiver, filter);
|
||||
}
|
||||
|
||||
private boolean isPowered() {
|
||||
return mBatteryManager.isCharging();
|
||||
}
|
||||
|
||||
void onPluggedIn(boolean pluggedIn) {
|
||||
if (mPluggedIn == pluggedIn) {
|
||||
return;
|
||||
}
|
||||
mPluggedIn = pluggedIn;
|
||||
if (mPluggedIn) {
|
||||
mSyncManager.onAppNotIdle(null, UserHandle.USER_ALL);
|
||||
}
|
||||
}
|
||||
|
||||
boolean isAppIdle(String packageName, int userId) {
|
||||
return !mPluggedIn && mUsageStats.isAppIdle(packageName, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAppIdleStateChanged(String packageName, int userId, boolean idle) {
|
||||
// Don't care if the app is becoming idle
|
||||
if (idle) return;
|
||||
mSyncManager.onAppNotIdle(packageName, userId);
|
||||
}
|
||||
}
|
||||
@@ -83,6 +83,7 @@ import com.android.internal.os.BackgroundThread;
|
||||
import com.android.internal.util.IndentingPrintWriter;
|
||||
import com.android.server.accounts.AccountManagerService;
|
||||
import com.android.server.content.SyncStorageEngine.AuthorityInfo;
|
||||
import com.android.server.content.SyncStorageEngine.EndPoint;
|
||||
import com.android.server.content.SyncStorageEngine.OnSyncRequestListener;
|
||||
import com.google.android.collect.Lists;
|
||||
import com.google.android.collect.Maps;
|
||||
@@ -107,7 +108,7 @@ import java.util.Set;
|
||||
* @hide
|
||||
*/
|
||||
public class SyncManager {
|
||||
private static final String TAG = "SyncManager";
|
||||
static final String TAG = "SyncManager";
|
||||
|
||||
/** Delay a sync due to local changes this long. In milliseconds */
|
||||
private static final long LOCAL_SYNC_DELAY;
|
||||
@@ -199,6 +200,8 @@ public class SyncManager {
|
||||
|
||||
protected SyncAdaptersCache mSyncAdapters;
|
||||
|
||||
private final AppIdleMonitor mAppIdleMonitor;
|
||||
|
||||
private BroadcastReceiver mStorageIntentReceiver =
|
||||
new BroadcastReceiver() {
|
||||
@Override
|
||||
@@ -427,6 +430,8 @@ public class SyncManager {
|
||||
mSyncAlarmIntent = PendingIntent.getBroadcast(
|
||||
mContext, 0 /* ignored */, new Intent(ACTION_SYNC_ALARM), 0);
|
||||
|
||||
mAppIdleMonitor = new AppIdleMonitor(this, mContext);
|
||||
|
||||
IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
|
||||
context.registerReceiver(mConnectivityIntentReceiver, intentFilter);
|
||||
|
||||
@@ -1168,6 +1173,36 @@ public class SyncManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear backoff on operations in the sync queue that match the packageName and userId.
|
||||
* @param packageName The package that just became active. Can be null to indicate that all
|
||||
* packages are now considered active due to being plugged in.
|
||||
* @param userId The user for which the package has become active. Can be USER_ALL if
|
||||
* the device just plugged in.
|
||||
*/
|
||||
void onAppNotIdle(String packageName, int userId) {
|
||||
synchronized (mSyncQueue) {
|
||||
// For all sync operations in sync queue, if marked as idle, compare with package name
|
||||
// and unmark. And clear backoff for the operation.
|
||||
final Iterator<SyncOperation> operationIterator =
|
||||
mSyncQueue.getOperations().iterator();
|
||||
boolean changed = false;
|
||||
while (operationIterator.hasNext()) {
|
||||
final SyncOperation op = operationIterator.next();
|
||||
if (op.appIdle
|
||||
&& getPackageName(op.target).equals(packageName)
|
||||
&& (userId == UserHandle.USER_ALL || op.target.userId == userId)) {
|
||||
op.appIdle = false;
|
||||
clearBackoffSetting(op);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
sendCheckAlarmsMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@@ -2447,6 +2482,19 @@ public class SyncManager {
|
||||
}
|
||||
continue;
|
||||
}
|
||||
String packageName = getPackageName(op.target);
|
||||
// If app is considered idle, then skip for now and backoff
|
||||
if (packageName != null
|
||||
&& mAppIdleMonitor.isAppIdle(packageName, op.target.userId)) {
|
||||
increaseBackoffSetting(op);
|
||||
op.appIdle = true;
|
||||
if (isLoggable) {
|
||||
Log.v(TAG, "Sync backing off idle app " + packageName);
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
op.appIdle = false;
|
||||
}
|
||||
// Add this sync to be run.
|
||||
operations.add(op);
|
||||
}
|
||||
@@ -3194,6 +3242,21 @@ public class SyncManager {
|
||||
}
|
||||
}
|
||||
|
||||
String getPackageName(EndPoint endpoint) {
|
||||
if (endpoint.target_service) {
|
||||
return endpoint.service.getPackageName();
|
||||
} else {
|
||||
SyncAdapterType syncAdapterType =
|
||||
SyncAdapterType.newKey(endpoint.provider, endpoint.account.type);
|
||||
final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo;
|
||||
syncAdapterInfo = mSyncAdapters.getServiceInfo(syncAdapterType, endpoint.userId);
|
||||
if (syncAdapterInfo == null) {
|
||||
return null;
|
||||
}
|
||||
return syncAdapterInfo.componentName.getPackageName();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isSyncStillActive(ActiveSyncContext activeSyncContext) {
|
||||
for (ActiveSyncContext sync : mActiveSyncContexts) {
|
||||
if (sync == activeSyncContext) {
|
||||
|
||||
@@ -90,6 +90,9 @@ public class SyncOperation implements Comparable {
|
||||
/** Descriptive string key for this operation */
|
||||
public String wakeLockName;
|
||||
|
||||
/** Whether this sync op was recently skipped due to the app being idle */
|
||||
public boolean appIdle;
|
||||
|
||||
public SyncOperation(Account account, int userId, int reason, int source, String provider,
|
||||
Bundle extras, long runTimeFromNow, long flexTime, long backoff,
|
||||
long delayUntil, boolean allowParallelSyncs) {
|
||||
|
||||
@@ -53,6 +53,7 @@ import android.util.SparseArray;
|
||||
|
||||
import com.android.internal.os.BackgroundThread;
|
||||
import com.android.internal.util.IndentingPrintWriter;
|
||||
import com.android.server.SystemConfig;
|
||||
import com.android.server.SystemService;
|
||||
|
||||
import java.io.File;
|
||||
@@ -383,6 +384,9 @@ public class UsageStatsService extends SystemService implements
|
||||
}
|
||||
|
||||
boolean isAppIdle(String packageName, int userId) {
|
||||
if (SystemConfig.getInstance().getAllowInPowerSave().contains(packageName)) {
|
||||
return false;
|
||||
}
|
||||
final long lastUsed = getLastPackageAccessTime(packageName, userId);
|
||||
return hasPassedIdleDuration(lastUsed);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user