summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
authorAmith Yamasani <yamasani@google.com>2015-04-10 16:16:30 -0700
committerAmith Yamasani <yamasani@google.com>2015-04-13 15:36:32 -0700
commit96a0fd65e18e5b9a0eaed3c24fd8a60a1fac1c3a (patch)
tree497996afd2d57bc9b65602bd4bcd595c60c4917e /services
parentfb1e9b79782580acabf0dd4dda6a74349fafc978 (diff)
downloadframeworks_base-96a0fd65e18e5b9a0eaed3c24fd8a60a1fac1c3a.zip
frameworks_base-96a0fd65e18e5b9a0eaed3c24fd8a60a1fac1c3a.tar.gz
frameworks_base-96a0fd65e18e5b9a0eaed3c24fd8a60a1fac1c3a.tar.bz2
Delay syncs for idle apps
Apps that haven't been in use for a while and are considered idle are not synced until the device is charging or the app is used. Bug: 20066058 Change-Id: I3471e3a11edae04777163b0dbd74e86495743caa
Diffstat (limited to 'services')
-rw-r--r--services/core/java/com/android/server/content/AppIdleMonitor.java89
-rw-r--r--services/core/java/com/android/server/content/SyncManager.java65
-rw-r--r--services/core/java/com/android/server/content/SyncOperation.java3
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java4
4 files changed, 160 insertions, 1 deletions
diff --git a/services/core/java/com/android/server/content/AppIdleMonitor.java b/services/core/java/com/android/server/content/AppIdleMonitor.java
new file mode 100644
index 0000000..9598de8
--- /dev/null
+++ b/services/core/java/com/android/server/content/AppIdleMonitor.java
@@ -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);
+ }
+}
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 7866ddc..4173b78 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -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);
@@ -1169,6 +1174,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
*/
class ActiveSyncContext extends ISyncContext.Stub
@@ -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) {
diff --git a/services/core/java/com/android/server/content/SyncOperation.java b/services/core/java/com/android/server/content/SyncOperation.java
index 35827cc..10efe81 100644
--- a/services/core/java/com/android/server/content/SyncOperation.java
+++ b/services/core/java/com/android/server/content/SyncOperation.java
@@ -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) {
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index cc0ab81..3d54dfb 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -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);
}