Merge "App Standby : Association between content providers and their sync adapter" into mnc-dev
diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java
index 7bcc038..8a31390 100644
--- a/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -54,6 +54,14 @@
     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.
      */
     public abstract void prepareShutdown();
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 057001c..d37dda0 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -48,6 +48,7 @@
 
 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.InputStream;
 import java.io.OutputStream;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Random;
 
@@ -1936,6 +1938,19 @@
     }
 
     /**
+     * @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
      * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#READ_SYNC_SETTINGS}.
diff --git a/core/java/android/content/IContentService.aidl b/core/java/android/content/IContentService.aidl
index 9998f08..8b471a0 100644
--- a/core/java/android/content/IContentService.aidl
+++ b/core/java/android/content/IContentService.aidl
@@ -143,6 +143,8 @@
     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 8bb3ee7..6704b75 100644
--- a/core/java/android/content/SyncAdaptersCache.java
+++ b/core/java/android/content/SyncAdaptersCache.java
@@ -20,12 +20,19 @@
 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 @@
     private static final String ATTRIBUTES_NAME = "sync-adapter";
     private static final MySerializer sSerializer = new MySerializer();
 
+    @GuardedBy("mServicesLock")
+    private SparseArray<ArrayMap<String,String[]>> mAuthorityToSyncAdapters
+            = new SparseArray<>();
+
     public SyncAdaptersCache(Context context) {
         super(context, SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME, sSerializer);
     }
@@ -76,6 +87,57 @@
         }
     }
 
+    @Override
+    protected void onServicesChangedLocked(int userId) {
+        synchronized (mServicesLock) {
+            ArrayMap<String,String[]> adapterMap = mAuthorityToSyncAdapters.get(userId);
+            if (adapterMap != null) {
+                adapterMap.clear();
+            }
+        }
+
+        super.onServicesChangedLocked(userId);
+    }
+
+    public String[] getSyncAdapterPackagesForAuthority(String authority, int userId) {
+        synchronized (mServicesLock) {
+            ArrayMap<String,String[]> 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<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> serviceInfos;
+            serviceInfos = getAllServices(userId);
+            ArrayList<String> packages = new ArrayList<>();
+            for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> 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<SyncAdapterType> {
         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 0bd2a3b..b293e2a 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -84,7 +84,7 @@
     private final String mAttributesName;
     private final XmlSerializerAndParser<V> mSerializerAndParser;
 
-    private final Object mServicesLock = new Object();
+    protected final Object mServicesLock = new Object();
 
     @GuardedBy("mServicesLock")
     private final SparseArray<UserServices<V>> mUserServices = new SparseArray<UserServices<V>>(2);
@@ -232,6 +232,7 @@
         synchronized (mServicesLock) {
             final UserServices<V> user = findOrCreateUserLocked(userId);
             user.services = null;
+            onServicesChangedLocked(userId);
         }
     }
 
@@ -489,11 +490,16 @@
                 }
             }
             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 @@
 
     @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 1134556c..c0c401d 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 @@
     final SparseArray<UidRecord> 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<UserStartedState> mStartedUsers = new SparseArray<>();
+    final SparseArray<UserState> mStartedUsers = new SparseArray<>();
 
     /**
      * LRU list of history of current users.  Most recently current is at the end.
@@ -1781,15 +1781,15 @@
                 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 @@
         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();
 
@@ -6305,9 +6305,9 @@
                     SystemProperties.set("dev.bootcomplete", "1");
                 }
                 for (int i=0; i<mStartedUsers.size(); i++) {
-                    UserStartedState uss = mStartedUsers.valueAt(i);
-                    if (uss.mState == UserStartedState.STATE_BOOTING) {
-                        uss.mState = UserStartedState.STATE_RUNNING;
+                    UserState uss = mStartedUsers.valueAt(i);
+                    if (uss.mState == UserState.STATE_BOOTING) {
+                        uss.mState = UserState.STATE_RUNNING;
                         final int userId = mStartedUsers.keyAt(i);
                         Intent intent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
                         intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
@@ -9434,6 +9434,7 @@
                     }
                     checkTime(startTime, "getContentProviderImpl: before updateOomAdj");
                     boolean success = updateOomAdjLocked(cpr.proc);
+                    maybeUpdateProviderUsageStatsLocked(r, cpr.info.packageName, name);
                     checkTime(startTime, "getContentProviderImpl: after updateOomAdj");
                     if (DEBUG_PROVIDER) Slog.i(TAG_PROVIDER, "Adjust success: " + success);
                     // NOTE: there is still a race here where a signal could be
@@ -9823,6 +9824,8 @@
                         dst.notifyAll();
                     }
                     updateOomAdjLocked(r);
+                    maybeUpdateProviderUsageStatsLocked(r, src.info.packageName,
+                            src.info.authority);
                 }
             }
 
@@ -13335,7 +13338,7 @@
             needSep = false;
             pw.println("  mStartedUsers:");
             for (int i=0; i<mStartedUsers.size(); i++) {
-                UserStartedState uss = mStartedUsers.valueAt(i);
+                UserState uss = mStartedUsers.valueAt(i);
                 pw.print("    User #"); pw.print(uss.mHandle.getIdentifier());
                         pw.print(": "); uss.dump("", pw);
             }
@@ -18607,6 +18610,22 @@
         uidRec.pendingChange.processState = uidRec.setProcState;
     }
 
+    private void maybeUpdateProviderUsageStatsLocked(ProcessRecord app, String providerPkgName,
+            String authority) {
+        if (app == null) return;
+        if (app.curProcState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
+            UserState userState = mStartedUsers.get(app.userId);
+            if (userState == null) return;
+            final long now = SystemClock.elapsedRealtime();
+            Long lastReported = userState.mProviderLastReportedFg.get(authority);
+            if (lastReported == null || lastReported < now - 60 * 1000L) {
+                mUsageStatsService.reportContentProviderUsage(
+                        authority, providerPkgName, app.userId);
+                userState.mProviderLastReportedFg.put(authority, now);
+            }
+        }
+    }
+
     private void maybeUpdateUsageStatsLocked(ProcessRecord app) {
         if (DEBUG_USAGE_STATS) {
             Slog.d(TAG, "Checking proc [" + Arrays.toString(app.getPackageList())
@@ -19594,7 +19613,7 @@
                 // If the user we are switching to is not currently started, then
                 // we need to start it now.
                 if (mStartedUsers.get(userId) == null) {
-                    mStartedUsers.put(userId, new UserStartedState(new UserHandle(userId), false));
+                    mStartedUsers.put(userId, new UserState(new UserHandle(userId), false));
                     updateStartedUserArrayLocked();
                     needStart = true;
                 }
@@ -19619,26 +19638,26 @@
                     mUserLru.add(currentUserIdInt);
                 }
 
-                final UserStartedState uss = mStartedUsers.get(userId);
+                final UserState uss = mStartedUsers.get(userId);
 
                 // Make sure user is in the started state.  If it is currently
                 // stopping, we need to knock that off.
-                if (uss.mState == UserStartedState.STATE_STOPPING) {
+                if (uss.mState == UserState.STATE_STOPPING) {
                     // If we are stopping, we haven't sent ACTION_SHUTDOWN,
                     // so we can just fairly silently bring the user back from
                     // the almost-dead.
-                    uss.mState = UserStartedState.STATE_RUNNING;
+                    uss.mState = UserState.STATE_RUNNING;
                     updateStartedUserArrayLocked();
                     needStart = true;
-                } else if (uss.mState == UserStartedState.STATE_SHUTDOWN) {
+                } else if (uss.mState == UserState.STATE_SHUTDOWN) {
                     // This means ACTION_SHUTDOWN has been sent, so we will
                     // need to treat this as a new boot of the user.
-                    uss.mState = UserStartedState.STATE_BOOTING;
+                    uss.mState = UserState.STATE_BOOTING;
                     updateStartedUserArrayLocked();
                     needStart = true;
                 }
 
-                if (uss.mState == UserStartedState.STATE_BOOTING) {
+                if (uss.mState == UserState.STATE_BOOTING) {
                     // Booting up a new user, need to tell system services about it.
                     // Note that this is on the same handler as scheduling of broadcasts,
                     // which is important because it needs to go first.
@@ -19776,7 +19795,7 @@
         }
     }
 
-    void dispatchUserSwitch(final UserStartedState uss, final int oldUserId,
+    void dispatchUserSwitch(final UserState uss, final int oldUserId,
             final int newUserId) {
         final int N = mUserSwitchObservers.beginBroadcast();
         if (N > 0) {
@@ -19813,21 +19832,21 @@
         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);
@@ -19837,7 +19856,7 @@
         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");
@@ -19849,11 +19868,11 @@
         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) {
@@ -19890,10 +19909,10 @@
             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);
@@ -19934,11 +19953,11 @@
         }
     }
 
-    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);
@@ -19951,7 +19970,7 @@
         }
     }
 
-    void finishUserSwitch(UserStartedState uss) {
+    void finishUserSwitch(UserState uss) {
         synchronized (this) {
             finishUserBoot(uss);
 
@@ -19961,15 +19980,15 @@
             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++;
@@ -20014,7 +20033,7 @@
             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.
@@ -20036,9 +20055,9 @@
             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();
@@ -20066,11 +20085,11 @@
                             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,
@@ -20094,7 +20113,7 @@
         return ActivityManager.USER_OP_SUCCESS;
     }
 
-    void finishUserStop(UserStartedState uss) {
+    void finishUserStop(UserState uss) {
         final int userId = uss.mHandle.getIdentifier();
         boolean stopped;
         ArrayList<IStopUserCallback> callbacks;
@@ -20102,7 +20121,7 @@
             callbacks = new ArrayList<IStopUserCallback>(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;
@@ -20176,15 +20195,15 @@
     }
 
     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
@@ -20206,19 +20225,19 @@
     private void updateStartedUserArrayLocked() {
         int num = 0;
         for (int i=0; i<mStartedUsers.size();  i++) {
-            UserStartedState uss = mStartedUsers.valueAt(i);
+            UserState uss = mStartedUsers.valueAt(i);
             // This list does not include stopping users.
-            if (uss.mState != UserStartedState.STATE_STOPPING
-                    && uss.mState != UserStartedState.STATE_SHUTDOWN) {
+            if (uss.mState != UserState.STATE_STOPPING
+                    && uss.mState != UserState.STATE_SHUTDOWN) {
                 num++;
             }
         }
         mStartedUserArray = new int[num];
         num = 0;
         for (int i=0; i<mStartedUsers.size();  i++) {
-            UserStartedState uss = mStartedUsers.valueAt(i);
-            if (uss.mState != UserStartedState.STATE_STOPPING
-                    && uss.mState != UserStartedState.STATE_SHUTDOWN) {
+            UserState uss = mStartedUsers.valueAt(i);
+            if (uss.mState != UserState.STATE_STOPPING
+                    && uss.mState != UserState.STATE_SHUTDOWN) {
                 mStartedUserArray[num] = mStartedUsers.keyAt(i);
                 num++;
             }
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 4e98576..c54a96d 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -233,10 +233,10 @@
     final ArrayList<ActivityRecord> mGoingToSleepActivities = new ArrayList<>();
 
     /** Used on user changes */
-    final ArrayList<UserStartedState> mStartingUsers = new ArrayList<>();
+    final ArrayList<UserState> mStartingUsers = new ArrayList<>();
 
     /** Used to queue up any background users being started */
-    final ArrayList<UserStartedState> mStartingBackgroundUsers = new ArrayList<>();
+    final ArrayList<UserState> mStartingBackgroundUsers = new ArrayList<>();
 
     /** Set to indicate whether to issue an onUserLeaving callback when a newly launched activity
      * is being brought in front of us. */
@@ -2376,7 +2376,7 @@
 
         ArrayList<ActivityRecord> stops = null;
         ArrayList<ActivityRecord> finishes = null;
-        ArrayList<UserStartedState> startingUsers = null;
+        ArrayList<UserState> startingUsers = null;
         int NS = 0;
         int NF = 0;
         boolean booting = false;
@@ -2473,7 +2473,7 @@
             }
             // Complete starting up of background users
             if (mStartingBackgroundUsers.size() > 0) {
-                startingUsers = new ArrayList<UserStartedState>(mStartingBackgroundUsers);
+                startingUsers = new ArrayList<UserState>(mStartingBackgroundUsers);
                 mStartingBackgroundUsers.clear();
                 for (int i = 0; i < startingUsers.size(); i++) {
                     mService.finishUserBoot(startingUsers.get(i));
@@ -3235,7 +3235,7 @@
         }
     }
 
-    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;
@@ -3276,7 +3276,7 @@
      * @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 d3e73d5..b3d82bc 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 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 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<String,Long> 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 ea461e5..93ed2ee 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -506,6 +506,21 @@
     }
 
     @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 2eb8797..cd9c7fe 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 @@
         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 3767fce..3b7ed91 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.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 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 @@
     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 @@
         }
     }
 
+    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 @@
                     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 @@
                     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;
@@ -1177,6 +1213,16 @@
         }
 
         @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);
         }