From 37a40c24deb02bca3868a8085069afae112f22e4 Mon Sep 17 00:00:00 2001 From: Amith Yamasani Date: Wed, 17 Jun 2015 13:25:42 -0700 Subject: [PATCH] App Standby : Association between content providers and their sync adapter Set sync adapters to active if the associated content providers are used at foreground process state. Minimize how frequently published content providers are reported by keeping track of last reported time. Also cache sync adapters associated with an authority in SyncManager. Bug: 21785111 Change-Id: Ic2c8cb6a27f005d1a1d0aad21d36b1510160753a --- .../app/usage/UsageStatsManagerInternal.java | 8 ++ .../java/android/content/ContentResolver.java | 15 +++ .../java/android/content/IContentService.aidl | 2 + .../android/content/SyncAdaptersCache.java | 62 +++++++++ .../content/pm/RegisteredServicesCache.java | 12 +- .../server/am/ActivityManagerService.java | 121 ++++++++++-------- .../server/am/ActivityStackSupervisor.java | 12 +- .../{UserStartedState.java => UserState.java} | 11 +- .../server/content/ContentService.java | 15 +++ .../android/server/content/SyncManager.java | 4 + .../server/usage/UsageStatsService.java | 48 ++++++- 11 files changed, 248 insertions(+), 62 deletions(-) rename services/core/java/com/android/server/am/{UserStartedState.java => UserState.java} (85%) diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java index 7bcc0385c13ac..8a31390dbf260 100644 --- a/core/java/android/app/usage/UsageStatsManagerInternal.java +++ b/core/java/android/app/usage/UsageStatsManagerInternal.java @@ -53,6 +53,14 @@ public abstract class UsageStatsManagerInternal { */ public abstract void reportConfigurationChange(Configuration config, int userId); + /** + * Reports that a content provider has been accessed by a foreground app. + * @param name The authority of the content provider + * @param pkgName The package name of the content provider + * @param userId The user in which the content provider was accessed. + */ + public abstract void reportContentProviderUsage(String name, String pkgName, int userId); + /** * Prepares the UsageStatsService for shutdown. */ diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index 057001ca3ff30..d37dda049b6cf 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -48,6 +48,7 @@ import android.util.Log; import dalvik.system.CloseGuard; +import com.android.internal.util.ArrayUtils; import com.android.internal.util.Preconditions; import java.io.File; @@ -57,6 +58,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Random; @@ -1935,6 +1937,19 @@ public abstract class ContentResolver { } } + /** + * @hide + * Returns the package names of syncadapters that match a given user and authority. + */ + public static String[] getSyncAdapterPackagesForAuthorityAsUser(String authority, + int userId) { + try { + return getContentService().getSyncAdapterPackagesForAuthorityAsUser(authority, userId); + } catch (RemoteException e) { + } + return ArrayUtils.emptyArray(String.class); + } + /** * Check if the provider should be synced when a network tickle is received *

This method requires the caller to hold the permission diff --git a/core/java/android/content/IContentService.aidl b/core/java/android/content/IContentService.aidl index 9998f08ff54cb..8b471a04f25b5 100644 --- a/core/java/android/content/IContentService.aidl +++ b/core/java/android/content/IContentService.aidl @@ -143,6 +143,8 @@ interface IContentService { SyncAdapterType[] getSyncAdapterTypes(); SyncAdapterType[] getSyncAdapterTypesAsUser(int userId); + String[] getSyncAdapterPackagesForAuthorityAsUser(String authority, int userId); + /** * Returns true if there is currently a operation for the given account/authority or service * actively being processed. diff --git a/core/java/android/content/SyncAdaptersCache.java b/core/java/android/content/SyncAdaptersCache.java index 8bb3ee7f178bf..6704b75dff7f3 100644 --- a/core/java/android/content/SyncAdaptersCache.java +++ b/core/java/android/content/SyncAdaptersCache.java @@ -20,12 +20,19 @@ import android.content.pm.RegisteredServicesCache; import android.content.pm.XmlSerializerAndParser; import android.content.res.Resources; import android.content.res.TypedArray; +import android.util.ArrayMap; import android.util.AttributeSet; +import android.util.SparseArray; + +import com.android.internal.annotations.GuardedBy; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlSerializer; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; /** * A cache of services that export the {@link android.content.ISyncAdapter} interface. @@ -39,6 +46,10 @@ public class SyncAdaptersCache extends RegisteredServicesCache private static final String ATTRIBUTES_NAME = "sync-adapter"; private static final MySerializer sSerializer = new MySerializer(); + @GuardedBy("mServicesLock") + private SparseArray> mAuthorityToSyncAdapters + = new SparseArray<>(); + public SyncAdaptersCache(Context context) { super(context, SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME, sSerializer); } @@ -76,6 +87,57 @@ public class SyncAdaptersCache extends RegisteredServicesCache } } + @Override + protected void onServicesChangedLocked(int userId) { + synchronized (mServicesLock) { + ArrayMap adapterMap = mAuthorityToSyncAdapters.get(userId); + if (adapterMap != null) { + adapterMap.clear(); + } + } + + super.onServicesChangedLocked(userId); + } + + public String[] getSyncAdapterPackagesForAuthority(String authority, int userId) { + synchronized (mServicesLock) { + ArrayMap adapterMap = mAuthorityToSyncAdapters.get(userId); + if (adapterMap == null) { + adapterMap = new ArrayMap<>(); + mAuthorityToSyncAdapters.put(userId, adapterMap); + } + // If the mapping exists, return it + if (adapterMap.containsKey(authority)) { + return adapterMap.get(authority); + } + // Create the mapping and cache it + String[] syncAdapterPackages; + final Collection> serviceInfos; + serviceInfos = getAllServices(userId); + ArrayList packages = new ArrayList<>(); + for (RegisteredServicesCache.ServiceInfo serviceInfo : serviceInfos) { + if (authority.equals(serviceInfo.type.authority) + && serviceInfo.componentName != null) { + packages.add(serviceInfo.componentName.getPackageName()); + } + } + syncAdapterPackages = new String[packages.size()]; + packages.toArray(syncAdapterPackages); + adapterMap.put(authority, syncAdapterPackages); + + return syncAdapterPackages; + } + } + + @Override + protected void onUserRemoved(int userId) { + synchronized (mServicesLock) { + mAuthorityToSyncAdapters.remove(userId); + } + + super.onUserRemoved(userId); + } + static class MySerializer implements XmlSerializerAndParser { public void writeAsXml(SyncAdapterType item, XmlSerializer out) throws IOException { out.attribute(null, "authority", item.authority); diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java index 0bd2a3b84f067..b293e2a834000 100644 --- a/core/java/android/content/pm/RegisteredServicesCache.java +++ b/core/java/android/content/pm/RegisteredServicesCache.java @@ -84,7 +84,7 @@ public abstract class RegisteredServicesCache { private final String mAttributesName; private final XmlSerializerAndParser mSerializerAndParser; - private final Object mServicesLock = new Object(); + protected final Object mServicesLock = new Object(); @GuardedBy("mServicesLock") private final SparseArray> mUserServices = new SparseArray>(2); @@ -232,6 +232,7 @@ public abstract class RegisteredServicesCache { synchronized (mServicesLock) { final UserServices user = findOrCreateUserLocked(userId); user.services = null; + onServicesChangedLocked(userId); } } @@ -489,11 +490,16 @@ public abstract class RegisteredServicesCache { } } if (changed) { + onServicesChangedLocked(userId); writePersistentServicesLocked(user, userId); } } } + protected void onServicesChangedLocked(int userId) { + // Feel free to override + } + /** * Returns true if the list of changed uids is null (wildcard) or the specified uid * is contained in the list of changed uids. @@ -687,7 +693,9 @@ public abstract class RegisteredServicesCache { @VisibleForTesting protected void onUserRemoved(int userId) { - mUserServices.remove(userId); + synchronized (mServicesLock) { + mUserServices.remove(userId); + } } @VisibleForTesting diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index c4f460e66febd..1120b6bca03a4 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -676,9 +676,9 @@ public final class ActivityManagerService extends ActivityManagerNative final SparseArray mActiveUids = new SparseArray<>(); /** - * Which uses have been started, so are allowed to run code. + * Which users have been started, so are allowed to run code. */ - final SparseArray mStartedUsers = new SparseArray<>(); + final SparseArray mStartedUsers = new SparseArray<>(); /** * LRU list of history of current users. Most recently current is at the end. @@ -1781,15 +1781,15 @@ public final class ActivityManagerService extends ActivityManagerNative break; } case REPORT_USER_SWITCH_MSG: { - dispatchUserSwitch((UserStartedState) msg.obj, msg.arg1, msg.arg2); + dispatchUserSwitch((UserState) msg.obj, msg.arg1, msg.arg2); break; } case CONTINUE_USER_SWITCH_MSG: { - continueUserSwitch((UserStartedState) msg.obj, msg.arg1, msg.arg2); + continueUserSwitch((UserState) msg.obj, msg.arg1, msg.arg2); break; } case USER_SWITCH_TIMEOUT_MSG: { - timeoutUserSwitch((UserStartedState) msg.obj, msg.arg1, msg.arg2); + timeoutUserSwitch((UserState) msg.obj, msg.arg1, msg.arg2); break; } case IMMERSIVE_MODE_LOCK_MSG: { @@ -2309,7 +2309,7 @@ public final class ActivityManagerService extends ActivityManagerNative mGrantFile = new AtomicFile(new File(systemDir, "urigrants.xml")); // User 0 is the first and only user that runs at boot. - mStartedUsers.put(UserHandle.USER_OWNER, new UserStartedState(UserHandle.OWNER, true)); + mStartedUsers.put(UserHandle.USER_OWNER, new UserState(UserHandle.OWNER, true)); mUserLru.add(UserHandle.USER_OWNER); updateStartedUserArrayLocked(); @@ -6313,9 +6313,9 @@ public final class ActivityManagerService extends ActivityManagerNative SystemProperties.set("dev.bootcomplete", "1"); } for (int i=0; i 0) { @@ -19821,21 +19840,21 @@ public final class ActivityManagerService extends ActivityManagerNative mUserSwitchObservers.finishBroadcast(); } - void timeoutUserSwitch(UserStartedState uss, int oldUserId, int newUserId) { + void timeoutUserSwitch(UserState uss, int oldUserId, int newUserId) { synchronized (this) { Slog.w(TAG, "User switch timeout: from " + oldUserId + " to " + newUserId); sendContinueUserSwitchLocked(uss, oldUserId, newUserId); } } - void sendContinueUserSwitchLocked(UserStartedState uss, int oldUserId, int newUserId) { + void sendContinueUserSwitchLocked(UserState uss, int oldUserId, int newUserId) { mCurUserSwitchCallback = null; mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG); mHandler.sendMessage(mHandler.obtainMessage(CONTINUE_USER_SWITCH_MSG, oldUserId, newUserId, uss)); } - void onUserInitialized(UserStartedState uss, boolean foreground, int oldUserId, int newUserId) { + void onUserInitialized(UserState uss, boolean foreground, int oldUserId, int newUserId) { synchronized (this) { if (foreground) { moveUserToForeground(uss, oldUserId, newUserId); @@ -19845,7 +19864,7 @@ public final class ActivityManagerService extends ActivityManagerNative completeSwitchAndInitalize(uss, newUserId, true, false); } - void moveUserToForeground(UserStartedState uss, int oldUserId, int newUserId) { + void moveUserToForeground(UserState uss, int oldUserId, int newUserId) { boolean homeInFront = mStackSupervisor.switchUserLocked(newUserId, uss); if (homeInFront) { startHomeActivityLocked(newUserId, "moveUserToFroreground"); @@ -19857,11 +19876,11 @@ public final class ActivityManagerService extends ActivityManagerNative sendUserSwitchBroadcastsLocked(oldUserId, newUserId); } - void continueUserSwitch(UserStartedState uss, int oldUserId, int newUserId) { + void continueUserSwitch(UserState uss, int oldUserId, int newUserId) { completeSwitchAndInitalize(uss, newUserId, false, true); } - void completeSwitchAndInitalize(UserStartedState uss, int newUserId, + void completeSwitchAndInitalize(UserState uss, int newUserId, boolean clearInitializing, boolean clearSwitching) { boolean unfrozen = false; synchronized (this) { @@ -19898,10 +19917,10 @@ public final class ActivityManagerService extends ActivityManagerNative final int num = mUserLru.size(); for (int i = 0; i < num; i++) { Integer oldUserId = mUserLru.get(i); - UserStartedState oldUss = mStartedUsers.get(oldUserId); + UserState oldUss = mStartedUsers.get(oldUserId); if (oldUserId == UserHandle.USER_OWNER || oldUserId == mCurrentUserId - || oldUss.mState == UserStartedState.STATE_STOPPING - || oldUss.mState == UserStartedState.STATE_SHUTDOWN) { + || oldUss.mState == UserState.STATE_STOPPING + || oldUss.mState == UserState.STATE_SHUTDOWN) { continue; } UserInfo userInfo = mUserManager.getUserInfo(oldUserId); @@ -19942,11 +19961,11 @@ public final class ActivityManagerService extends ActivityManagerNative } } - void finishUserBoot(UserStartedState uss) { + void finishUserBoot(UserState uss) { synchronized (this) { - if (uss.mState == UserStartedState.STATE_BOOTING + if (uss.mState == UserState.STATE_BOOTING && mStartedUsers.get(uss.mHandle.getIdentifier()) == uss) { - uss.mState = UserStartedState.STATE_RUNNING; + uss.mState = UserState.STATE_RUNNING; final int userId = uss.mHandle.getIdentifier(); Intent intent = new Intent(Intent.ACTION_BOOT_COMPLETED, null); intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); @@ -19959,7 +19978,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } - void finishUserSwitch(UserStartedState uss) { + void finishUserSwitch(UserState uss) { synchronized (this) { finishUserBoot(uss); @@ -19969,15 +19988,15 @@ public final class ActivityManagerService extends ActivityManagerNative int i = 0; while (num > MAX_RUNNING_USERS && i < mUserLru.size()) { Integer oldUserId = mUserLru.get(i); - UserStartedState oldUss = mStartedUsers.get(oldUserId); + UserState oldUss = mStartedUsers.get(oldUserId); if (oldUss == null) { // Shouldn't happen, but be sane if it does. mUserLru.remove(i); num--; continue; } - if (oldUss.mState == UserStartedState.STATE_STOPPING - || oldUss.mState == UserStartedState.STATE_SHUTDOWN) { + if (oldUss.mState == UserState.STATE_STOPPING + || oldUss.mState == UserState.STATE_SHUTDOWN) { // This user is already stopping, doesn't count. num--; i++; @@ -20022,7 +20041,7 @@ public final class ActivityManagerService extends ActivityManagerNative return ActivityManager.USER_OP_IS_CURRENT; } - final UserStartedState uss = mStartedUsers.get(userId); + final UserState uss = mStartedUsers.get(userId); if (uss == null) { // User is not started, nothing to do... but we do need to // callback if requested. @@ -20044,9 +20063,9 @@ public final class ActivityManagerService extends ActivityManagerNative uss.mStopCallbacks.add(callback); } - if (uss.mState != UserStartedState.STATE_STOPPING - && uss.mState != UserStartedState.STATE_SHUTDOWN) { - uss.mState = UserStartedState.STATE_STOPPING; + if (uss.mState != UserState.STATE_STOPPING + && uss.mState != UserState.STATE_SHUTDOWN) { + uss.mState = UserState.STATE_STOPPING; updateStartedUserArrayLocked(); long ident = Binder.clearCallingIdentity(); @@ -20074,11 +20093,11 @@ public final class ActivityManagerService extends ActivityManagerNative Bundle extras, boolean ordered, boolean sticky, int sendingUser) { // On to the next. synchronized (ActivityManagerService.this) { - if (uss.mState != UserStartedState.STATE_STOPPING) { + if (uss.mState != UserState.STATE_STOPPING) { // Whoops, we are being started back up. Abort, abort! return; } - uss.mState = UserStartedState.STATE_SHUTDOWN; + uss.mState = UserState.STATE_SHUTDOWN; } mBatteryStatsService.noteEvent( BatteryStats.HistoryItem.EVENT_USER_RUNNING_FINISH, @@ -20102,7 +20121,7 @@ public final class ActivityManagerService extends ActivityManagerNative return ActivityManager.USER_OP_SUCCESS; } - void finishUserStop(UserStartedState uss) { + void finishUserStop(UserState uss) { final int userId = uss.mHandle.getIdentifier(); boolean stopped; ArrayList callbacks; @@ -20110,7 +20129,7 @@ public final class ActivityManagerService extends ActivityManagerNative callbacks = new ArrayList(uss.mStopCallbacks); if (mStartedUsers.get(userId) != uss) { stopped = false; - } else if (uss.mState != UserStartedState.STATE_SHUTDOWN) { + } else if (uss.mState != UserState.STATE_SHUTDOWN) { stopped = false; } else { stopped = true; @@ -20184,15 +20203,15 @@ public final class ActivityManagerService extends ActivityManagerNative } boolean isUserRunningLocked(int userId, boolean orStopped) { - UserStartedState state = mStartedUsers.get(userId); + UserState state = mStartedUsers.get(userId); if (state == null) { return false; } if (orStopped) { return true; } - return state.mState != UserStartedState.STATE_STOPPING - && state.mState != UserStartedState.STATE_SHUTDOWN; + return state.mState != UserState.STATE_STOPPING + && state.mState != UserState.STATE_SHUTDOWN; } @Override @@ -20214,19 +20233,19 @@ public final class ActivityManagerService extends ActivityManagerNative private void updateStartedUserArrayLocked() { int num = 0; for (int i=0; i mGoingToSleepActivities = new ArrayList<>(); /** Used on user changes */ - final ArrayList mStartingUsers = new ArrayList<>(); + final ArrayList mStartingUsers = new ArrayList<>(); /** Used to queue up any background users being started */ - final ArrayList mStartingBackgroundUsers = new ArrayList<>(); + final ArrayList mStartingBackgroundUsers = new ArrayList<>(); /** Set to indicate whether to issue an onUserLeaving callback when a newly launched activity * is being brought in front of us. */ @@ -2371,7 +2371,7 @@ public final class ActivityStackSupervisor implements DisplayListener { ArrayList stops = null; ArrayList finishes = null; - ArrayList startingUsers = null; + ArrayList startingUsers = null; int NS = 0; int NF = 0; boolean booting = false; @@ -2468,7 +2468,7 @@ public final class ActivityStackSupervisor implements DisplayListener { } // Complete starting up of background users if (mStartingBackgroundUsers.size() > 0) { - startingUsers = new ArrayList(mStartingBackgroundUsers); + startingUsers = new ArrayList(mStartingBackgroundUsers); mStartingBackgroundUsers.clear(); for (int i = 0; i < startingUsers.size(); i++) { mService.finishUserBoot(startingUsers.get(i)); @@ -3230,7 +3230,7 @@ public final class ActivityStackSupervisor implements DisplayListener { } } - boolean switchUserLocked(int userId, UserStartedState uss) { + boolean switchUserLocked(int userId, UserState uss) { mUserStackInFront.put(mCurrentUser, mFocusedStack.getStackId()); final int restoreStackId = mUserStackInFront.get(userId, HOME_STACK_ID); mCurrentUser = userId; @@ -3271,7 +3271,7 @@ public final class ActivityStackSupervisor implements DisplayListener { * @param userId The user being started in the background * @param uss The state object for the user. */ - public void startBackgroundUserLocked(int userId, UserStartedState uss) { + public void startBackgroundUserLocked(int userId, UserState uss) { mStartingBackgroundUsers.add(uss); } diff --git a/services/core/java/com/android/server/am/UserStartedState.java b/services/core/java/com/android/server/am/UserState.java similarity index 85% rename from services/core/java/com/android/server/am/UserStartedState.java rename to services/core/java/com/android/server/am/UserState.java index d3e73d5222a45..b3d82bca2de8c 100644 --- a/services/core/java/com/android/server/am/UserStartedState.java +++ b/services/core/java/com/android/server/am/UserState.java @@ -21,8 +21,9 @@ import java.util.ArrayList; import android.app.IStopUserCallback; import android.os.UserHandle; +import android.util.ArrayMap; -public final class UserStartedState { +public final class UserState { // User is first coming up. public final static int STATE_BOOTING = 0; // User is in the normal running state. @@ -40,7 +41,13 @@ public final class UserStartedState { public boolean switching; public boolean initializing; - public UserStartedState(UserHandle handle, boolean initial) { + /** + * The last time that a provider was reported to usage stats as being brought to important + * foreground procstate. + */ + public final ArrayMap mProviderLastReportedFg = new ArrayMap<>(); + + public UserState(UserHandle handle, boolean initial) { mHandle = handle; } diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java index ea461e52947ff..93ed2ee0c7a2a 100644 --- a/services/core/java/com/android/server/content/ContentService.java +++ b/services/core/java/com/android/server/content/ContentService.java @@ -505,6 +505,21 @@ public final class ContentService extends IContentService.Stub { } } + @Override + public String[] getSyncAdapterPackagesForAuthorityAsUser(String authority, int userId) { + enforceCrossUserPermission(userId, + "no permission to read sync settings for user " + userId); + // This makes it so that future permission checks will be in the context of this + // process rather than the caller's process. We will restore this before returning. + final long identityToken = clearCallingIdentity(); + try { + SyncManager syncManager = getSyncManager(); + return syncManager.getSyncAdapterPackagesForAuthorityAsUser(authority, userId); + } finally { + restoreCallingIdentity(identityToken); + } + } + @Override public boolean getSyncAutomatically(Account account, String providerName) { return getSyncAutomaticallyAsUser(account, providerName, UserHandle.getCallingUserId()); diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index 2eb8797916679..cd9c7fe0b9bb1 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -908,6 +908,10 @@ public class SyncManager { return types; } + public String[] getSyncAdapterPackagesForAuthorityAsUser(String authority, int userId) { + return mSyncAdapters.getSyncAdapterPackagesForAuthority(authority, userId); + } + private void sendSyncAlarmMessage() { if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_SYNC_ALARM"); mSyncHandler.sendEmptyMessage(SyncHandler.MESSAGE_SYNC_ALARM); diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 3767fce183d9e..3b7ed91306053 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -31,9 +31,12 @@ import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener; import android.appwidget.AppWidgetManager; import android.content.BroadcastReceiver; import android.content.ComponentName; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.SyncAdapterType; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; @@ -69,6 +72,7 @@ import android.view.Display; import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IBatteryStats; import com.android.internal.os.BackgroundThread; +import com.android.internal.os.SomeArgs; import com.android.internal.util.IndentingPrintWriter; import com.android.server.DeviceIdleController; import com.android.server.SystemService; @@ -117,6 +121,7 @@ public class UsageStatsService extends SystemService implements static final int MSG_CHECK_IDLE_STATES = 5; static final int MSG_CHECK_PAROLE_TIMEOUT = 6; static final int MSG_PAROLE_END_TIMEOUT = 7; + static final int MSG_REPORT_CONTENT_PROVIDER_USAGE = 8; private final Object mLock = new Object(); Handler mHandler; @@ -583,6 +588,29 @@ public class UsageStatsService extends SystemService implements } } + void reportContentProviderUsage(String authority, String providerPkgName, int userId) { + // Get sync adapters for the authority + String[] packages = ContentResolver.getSyncAdapterPackagesForAuthorityAsUser( + authority, userId); + for (String packageName: packages) { + // Only force the sync adapters to active if the provider is not in the same package and + // the sync adapter is a system package. + try { + PackageInfo pi = AppGlobals.getPackageManager().getPackageInfo( + packageName, 0, userId); + if (pi == null || pi.applicationInfo == null + || !pi.applicationInfo.isSystemApp()) { + continue; + } + if (!packageName.equals(providerPkgName)) { + forceIdleState(packageName, userId, false); + } + } catch (RemoteException re) { + // Shouldn't happen + } + } + } + /** * Forces the app's beginIdleTime and lastUsedTime to reflect idle or active. If idle, * then it rolls back the beginIdleTime and lastUsedTime to a point in time that's behind @@ -605,7 +633,7 @@ public class UsageStatsService extends SystemService implements timeNow - (idle ? mAppIdleWallclockThresholdMillis : 0) - 5000); // Inform listeners if necessary if (previouslyIdle != idle) { - // Slog.d(TAG, "Informing listeners of out-of-idle " + event.mPackage); + // Slog.d(TAG, "Informing listeners of out-of-idle " + packageName); mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId, /* idle = */ idle ? 1 : 0, packageName)); if (!idle) { @@ -916,6 +944,14 @@ public class UsageStatsService extends SystemService implements setAppIdleParoled(false); break; + case MSG_REPORT_CONTENT_PROVIDER_USAGE: + SomeArgs args = (SomeArgs) msg.obj; + reportContentProviderUsage((String) args.arg1, // authority name + (String) args.arg2, // package name + (int) args.arg3); // userId + args.recycle(); + break; + default: super.handleMessage(msg); break; @@ -1176,6 +1212,16 @@ public class UsageStatsService extends SystemService implements mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget(); } + @Override + public void reportContentProviderUsage(String name, String packageName, int userId) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = name; + args.arg2 = packageName; + args.arg3 = userId; + mHandler.obtainMessage(MSG_REPORT_CONTENT_PROVIDER_USAGE, args) + .sendToTarget(); + } + @Override public boolean isAppIdle(String packageName, int userId) { return UsageStatsService.this.isAppIdleFiltered(packageName, userId, -1);