Loading core/java/android/content/ContentResolver.java +20 −2 Original line number Diff line number Diff line Loading @@ -1218,10 +1218,17 @@ public abstract class ContentResolver { */ public final void registerContentObserver(Uri uri, boolean notifyForDescendents, ContentObserver observer) { registerContentObserver(uri, notifyForDescendents, observer, UserHandle.myUserId()); } /** @hide - designated user version */ public final void registerContentObserver(Uri uri, boolean notifyForDescendents, ContentObserver observer, int userHandle) { try { getContentService().registerContentObserver(uri, notifyForDescendents, observer.getContentObserver()); observer.getContentObserver(), userHandle); } catch (RemoteException e) { } } Loading Loading @@ -1276,10 +1283,21 @@ public abstract class ContentResolver { * @see #requestSync(android.accounts.Account, String, android.os.Bundle) */ public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) { notifyChange(uri, observer, syncToNetwork, UserHandle.myUserId()); } /** * Notify registered observers within the designated user(s) that a row was updated. * * @hide */ public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork, int userHandle) { try { getContentService().notifyChange( uri, observer == null ? null : observer.getContentObserver(), observer != null && observer.deliverSelfNotifications(), syncToNetwork); observer != null && observer.deliverSelfNotifications(), syncToNetwork, userHandle); } catch (RemoteException e) { } } Loading core/java/android/content/ContentService.java +116 −34 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package android.content; import android.accounts.Account; import android.app.ActivityManager; import android.database.IContentObserver; import android.database.sqlite.SQLiteException; import android.net.Uri; Loading @@ -33,6 +34,7 @@ import android.Manifest; import java.io.FileDescriptor; import java.io.PrintWriter; import java.security.InvalidParameterException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; Loading Loading @@ -138,19 +140,49 @@ public final class ContentService extends IContentService.Stub { getSyncManager(); } public void registerContentObserver(Uri uri, boolean notifyForDescendents, IContentObserver observer) { /** * Register a content observer tied to a specific user's view of the provider. * @param userHandle the user whose view of the provider is to be observed. May be * the calling user without requiring any permission, otherwise the caller needs to * hold the INTERACT_ACROSS_USERS_FULL permission. Pseudousers USER_ALL and * USER_CURRENT are properly handled; all other pseudousers are forbidden. */ @Override public void registerContentObserver(Uri uri, boolean notifyForDescendants, IContentObserver observer, int userHandle) { if (observer == null || uri == null) { throw new IllegalArgumentException("You must pass a valid uri and observer"); } final int callingUser = UserHandle.getCallingUserId(); if (callingUser != userHandle) { mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, "no permission to observe other users' provider view"); } if (userHandle < 0) { if (userHandle == UserHandle.USER_CURRENT) { userHandle = ActivityManager.getCurrentUser(); } else if (userHandle != UserHandle.USER_ALL) { throw new InvalidParameterException("Bad user handle for registerContentObserver: " + userHandle); } } synchronized (mRootNode) { mRootNode.addObserverLocked(uri, observer, notifyForDescendents, mRootNode, Binder.getCallingUid(), Binder.getCallingPid()); mRootNode.addObserverLocked(uri, observer, notifyForDescendants, mRootNode, Binder.getCallingUid(), Binder.getCallingPid(), userHandle); if (false) Log.v(TAG, "Registered observer " + observer + " at " + uri + " with notifyForDescendents " + notifyForDescendents); " with notifyForDescendants " + notifyForDescendants); } } public void registerContentObserver(Uri uri, boolean notifyForDescendants, IContentObserver observer) { registerContentObserver(uri, notifyForDescendants, observer, UserHandle.getCallingUserId()); } public void unregisterContentObserver(IContentObserver observer) { if (observer == null) { throw new IllegalArgumentException("You must pass a valid observer"); Loading @@ -161,14 +193,39 @@ public final class ContentService extends IContentService.Stub { } } /** * Notify observers of a particular user's view of the provider. * @param userHandle the user whose view of the provider is to be notified. May be * the calling user without requiring any permission, otherwise the caller needs to * hold the INTERACT_ACROSS_USERS_FULL permission. Pseudousers USER_ALL and * USER_CURRENT are properly interpreted; no other pseudousers are allowed. */ @Override public void notifyChange(Uri uri, IContentObserver observer, boolean observerWantsSelfNotifications, boolean syncToNetwork) { boolean observerWantsSelfNotifications, boolean syncToNetwork, int userHandle) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "Notifying update of " + uri + " from observer " + observer + ", syncToNetwork " + syncToNetwork); Log.v(TAG, "Notifying update of " + uri + " for user " + userHandle + " from observer " + observer + ", syncToNetwork " + syncToNetwork); } // Notify for any user other than the caller's own requires permission. final int callingUserHandle = UserHandle.getCallingUserId(); if (userHandle != callingUserHandle) { mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, "no permission to notify other users"); } // We passed the permission check; resolve pseudouser targets as appropriate if (userHandle < 0) { if (userHandle == UserHandle.USER_CURRENT) { userHandle = ActivityManager.getCurrentUser(); } else if (userHandle != UserHandle.USER_ALL) { throw new InvalidParameterException("Bad user handle for notifyChange: " + userHandle); } } int userId = UserHandle.getCallingUserId(); // 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. long identityToken = clearCallingIdentity(); Loading @@ -176,7 +233,7 @@ public final class ContentService extends IContentService.Stub { ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>(); synchronized (mRootNode) { mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications, calls); userHandle, calls); } final int numCalls = calls.size(); for (int i=0; i<numCalls; i++) { Loading Loading @@ -207,7 +264,7 @@ public final class ContentService extends IContentService.Stub { if (syncToNetwork) { SyncManager syncManager = getSyncManager(); if (syncManager != null) { syncManager.scheduleLocalSync(null /* all accounts */, userId, syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle, uri.getAuthority()); } } Loading @@ -216,6 +273,12 @@ public final class ContentService extends IContentService.Stub { } } public void notifyChange(Uri uri, IContentObserver observer, boolean observerWantsSelfNotifications, boolean syncToNetwork) { notifyChange(uri, observer, observerWantsSelfNotifications, syncToNetwork, UserHandle.getCallingUserId()); } /** * Hide this class since it is not part of api, * but current unittest framework requires it to be public Loading Loading @@ -543,16 +606,18 @@ public final class ContentService extends IContentService.Stub { public final IContentObserver observer; public final int uid; public final int pid; public final boolean notifyForDescendents; public final boolean notifyForDescendants; private final int userHandle; private final Object observersLock; public ObserverEntry(IContentObserver o, boolean n, Object observersLock, int _uid, int _pid) { int _uid, int _pid, int _userHandle) { this.observersLock = observersLock; observer = o; uid = _uid; pid = _pid; notifyForDescendents = n; userHandle = _userHandle; notifyForDescendants = n; try { observer.asBinder().linkToDeath(this, 0); } catch (RemoteException e) { Loading @@ -571,7 +636,8 @@ public final class ContentService extends IContentService.Stub { pidCounts.put(pid, pidCounts.get(pid)+1); pw.print(prefix); pw.print(name); pw.print(": pid="); pw.print(pid); pw.print(" uid="); pw.print(uid); pw.print(" target="); pw.print(uid); pw.print(" user="); pw.print(userHandle); pw.print(" target="); pw.println(Integer.toHexString(System.identityHashCode( observer != null ? observer.asBinder() : null))); } Loading Loading @@ -639,17 +705,21 @@ public final class ContentService extends IContentService.Stub { return uri.getPathSegments().size() + 1; } // Invariant: userHandle is either a hard user number or is USER_ALL public void addObserverLocked(Uri uri, IContentObserver observer, boolean notifyForDescendents, Object observersLock, int uid, int pid) { addObserverLocked(uri, 0, observer, notifyForDescendents, observersLock, uid, pid); boolean notifyForDescendants, Object observersLock, int uid, int pid, int userHandle) { addObserverLocked(uri, 0, observer, notifyForDescendants, observersLock, uid, pid, userHandle); } private void addObserverLocked(Uri uri, int index, IContentObserver observer, boolean notifyForDescendents, Object observersLock, int uid, int pid) { boolean notifyForDescendants, Object observersLock, int uid, int pid, int userHandle) { // If this is the leaf node add the observer if (index == countUriSegments(uri)) { mObservers.add(new ObserverEntry(observer, notifyForDescendents, observersLock, uid, pid)); mObservers.add(new ObserverEntry(observer, notifyForDescendants, observersLock, uid, pid, userHandle)); return; } Loading @@ -662,8 +732,8 @@ public final class ContentService extends IContentService.Stub { for (int i = 0; i < N; i++) { ObserverNode node = mChildren.get(i); if (node.mName.equals(segment)) { node.addObserverLocked(uri, index + 1, observer, notifyForDescendents, observersLock, uid, pid); node.addObserverLocked(uri, index + 1, observer, notifyForDescendants, observersLock, uid, pid, userHandle); return; } } Loading @@ -671,8 +741,8 @@ public final class ContentService extends IContentService.Stub { // No child found, create one ObserverNode node = new ObserverNode(segment); mChildren.add(node); node.addObserverLocked(uri, index + 1, observer, notifyForDescendents, observersLock, uid, pid); node.addObserverLocked(uri, index + 1, observer, notifyForDescendants, observersLock, uid, pid, userHandle); } public boolean removeObserverLocked(IContentObserver observer) { Loading Loading @@ -705,37 +775,49 @@ public final class ContentService extends IContentService.Stub { } private void collectMyObserversLocked(boolean leaf, IContentObserver observer, boolean observerWantsSelfNotifications, ArrayList<ObserverCall> calls) { boolean observerWantsSelfNotifications, int targetUserHandle, ArrayList<ObserverCall> calls) { int N = mObservers.size(); IBinder observerBinder = observer == null ? null : observer.asBinder(); for (int i = 0; i < N; i++) { ObserverEntry entry = mObservers.get(i); // Don't notify the observer if it sent the notification and isn't interesed // Don't notify the observer if it sent the notification and isn't interested // in self notifications boolean selfChange = (entry.observer.asBinder() == observerBinder); if (selfChange && !observerWantsSelfNotifications) { continue; } // Does this observer match the target user? if (targetUserHandle == UserHandle.USER_ALL || entry.userHandle == UserHandle.USER_ALL || targetUserHandle == entry.userHandle) { // Make sure the observer is interested in the notification if (leaf || (!leaf && entry.notifyForDescendents)) { if (leaf || (!leaf && entry.notifyForDescendants)) { calls.add(new ObserverCall(this, entry.observer, selfChange)); } } } } /** * targetUserHandle is either a hard user handle or is USER_ALL */ public void collectObserversLocked(Uri uri, int index, IContentObserver observer, boolean observerWantsSelfNotifications, ArrayList<ObserverCall> calls) { boolean observerWantsSelfNotifications, int targetUserHandle, ArrayList<ObserverCall> calls) { String segment = null; int segmentCount = countUriSegments(uri); if (index >= segmentCount) { // This is the leaf node, notify all observers collectMyObserversLocked(true, observer, observerWantsSelfNotifications, calls); collectMyObserversLocked(true, observer, observerWantsSelfNotifications, targetUserHandle, calls); } else if (index < segmentCount){ segment = getUriSegment(uri, index); // Notify any observers at this level who are interested in descendents collectMyObserversLocked(false, observer, observerWantsSelfNotifications, calls); // Notify any observers at this level who are interested in descendants collectMyObserversLocked(false, observer, observerWantsSelfNotifications, targetUserHandle, calls); } int N = mChildren.size(); Loading @@ -744,7 +826,7 @@ public final class ContentService extends IContentService.Stub { if (segment == null || node.mName.equals(segment)) { // We found the child, node.collectObserversLocked(uri, index + 1, observer, observerWantsSelfNotifications, calls); observer, observerWantsSelfNotifications, targetUserHandle, calls); if (segment != null) { break; } Loading core/java/android/content/IContentService.aidl +19 −3 Original line number Diff line number Diff line Loading @@ -30,12 +30,28 @@ import android.database.IContentObserver; * @hide */ interface IContentService { void registerContentObserver(in Uri uri, boolean notifyForDescendentsn, IContentObserver observer); void unregisterContentObserver(IContentObserver observer); /** * Register a content observer tied to a specific user's view of the provider. * @param userHandle the user whose view of the provider is to be observed. May be * the calling user without requiring any permission, otherwise the caller needs to * hold the INTERACT_ACROSS_USERS_FULL permission. Pseudousers USER_ALL and * USER_CURRENT are properly handled. */ void registerContentObserver(in Uri uri, boolean notifyForDescendants, IContentObserver observer, int userHandle); /** * Notify observers of a particular user's view of the provider. * @param userHandle the user whose view of the provider is to be notified. May be * the calling user without requiring any permission, otherwise the caller needs to * hold the INTERACT_ACROSS_USERS_FULL permission. Pseudousers USER_ALL * USER_CURRENT are properly interpreted. */ void notifyChange(in Uri uri, IContentObserver observer, boolean observerWantsSelfNotifications, boolean syncToNetwork); boolean observerWantsSelfNotifications, boolean syncToNetwork, int userHandle); void requestSync(in Account account, String authority, in Bundle extras); void cancelSync(in Account account, String authority); Loading core/tests/coretests/src/android/content/ObserverNodeTest.java +13 −5 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.content.ContentService.ObserverNode; import android.database.ContentObserver; import android.net.Uri; import android.os.Handler; import android.os.UserHandle; import android.test.AndroidTestCase; public class ObserverNodeTest extends AndroidTestCase { Loading @@ -33,6 +34,8 @@ public class ObserverNodeTest extends AndroidTestCase { } public void testUri() { final int myUserHandle = UserHandle.myUserId(); ObserverNode root = new ObserverNode(""); Uri[] uris = new Uri[] { Uri.parse("content://c/a/"), Loading @@ -48,21 +51,25 @@ public class ObserverNodeTest extends AndroidTestCase { int[] nums = new int[] {4, 7, 1, 4, 2, 2, 3, 3}; // special case root.addObserverLocked(uris[0], new TestObserver().getContentObserver(), false, root, 0, 0); root.addObserverLocked(uris[0], new TestObserver().getContentObserver(), false, root, 0, 0, myUserHandle); for(int i = 1; i < uris.length; i++) { root.addObserverLocked(uris[i], new TestObserver().getContentObserver(), true, root, 0, 0); root.addObserverLocked(uris[i], new TestObserver().getContentObserver(), true, root, 0, 0, myUserHandle); } ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>(); for (int i = nums.length - 1; i >=0; --i) { root.collectObserversLocked(uris[i], 0, null, false, calls); root.collectObserversLocked(uris[i], 0, null, false, myUserHandle, calls); assertEquals(nums[i], calls.size()); calls.clear(); } } public void testUriNotNotify() { final int myUserHandle = UserHandle.myUserId(); ObserverNode root = new ObserverNode(""); Uri[] uris = new Uri[] { Uri.parse("content://c/"), Loading @@ -77,13 +84,14 @@ public class ObserverNodeTest extends AndroidTestCase { int[] nums = new int[] {7, 1, 3, 3, 1, 1, 1, 1}; for(int i = 0; i < uris.length; i++) { root.addObserverLocked(uris[i], new TestObserver().getContentObserver(), false, root, 0, 0); root.addObserverLocked(uris[i], new TestObserver().getContentObserver(), false, root, 0, 0, myUserHandle); } ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>(); for (int i = uris.length - 1; i >=0; --i) { root.collectObserversLocked(uris[i], 0, null, false, calls); root.collectObserversLocked(uris[i], 0, null, false, myUserHandle, calls); assertEquals(nums[i], calls.size()); calls.clear(); } Loading packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +5 −3 Original line number Diff line number Diff line Loading @@ -317,13 +317,14 @@ public class SettingsProvider extends ContentProvider { boolean backedUpDataChanged = false; String property = null, table = uri.getPathSegments().get(0); final boolean isGlobal = table.equals(TABLE_GLOBAL); if (table.equals(TABLE_SYSTEM)) { property = Settings.System.SYS_PROP_SETTING_VERSION + '_' + userHandle; backedUpDataChanged = true; } else if (table.equals(TABLE_SECURE)) { property = Settings.Secure.SYS_PROP_SETTING_VERSION + '_' + userHandle; backedUpDataChanged = true; } else if (table.equals(TABLE_GLOBAL)) { } else if (isGlobal) { property = Settings.Global.SYS_PROP_SETTING_VERSION; // this one is global backedUpDataChanged = true; } Loading @@ -342,8 +343,9 @@ public class SettingsProvider extends ContentProvider { String notify = uri.getQueryParameter("notify"); if (notify == null || "true".equals(notify)) { getContext().getContentResolver().notifyChange(uri, null); if (LOCAL_LOGV) Log.v(TAG, "notifying: " + uri); final int notifyTarget = isGlobal ? UserHandle.USER_ALL : userHandle; getContext().getContentResolver().notifyChange(uri, null, true, notifyTarget); if (LOCAL_LOGV) Log.v(TAG, "notifying for " + notifyTarget + ": " + uri); } else { if (LOCAL_LOGV) Log.v(TAG, "notification suppressed: " + uri); } Loading Loading
core/java/android/content/ContentResolver.java +20 −2 Original line number Diff line number Diff line Loading @@ -1218,10 +1218,17 @@ public abstract class ContentResolver { */ public final void registerContentObserver(Uri uri, boolean notifyForDescendents, ContentObserver observer) { registerContentObserver(uri, notifyForDescendents, observer, UserHandle.myUserId()); } /** @hide - designated user version */ public final void registerContentObserver(Uri uri, boolean notifyForDescendents, ContentObserver observer, int userHandle) { try { getContentService().registerContentObserver(uri, notifyForDescendents, observer.getContentObserver()); observer.getContentObserver(), userHandle); } catch (RemoteException e) { } } Loading Loading @@ -1276,10 +1283,21 @@ public abstract class ContentResolver { * @see #requestSync(android.accounts.Account, String, android.os.Bundle) */ public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) { notifyChange(uri, observer, syncToNetwork, UserHandle.myUserId()); } /** * Notify registered observers within the designated user(s) that a row was updated. * * @hide */ public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork, int userHandle) { try { getContentService().notifyChange( uri, observer == null ? null : observer.getContentObserver(), observer != null && observer.deliverSelfNotifications(), syncToNetwork); observer != null && observer.deliverSelfNotifications(), syncToNetwork, userHandle); } catch (RemoteException e) { } } Loading
core/java/android/content/ContentService.java +116 −34 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package android.content; import android.accounts.Account; import android.app.ActivityManager; import android.database.IContentObserver; import android.database.sqlite.SQLiteException; import android.net.Uri; Loading @@ -33,6 +34,7 @@ import android.Manifest; import java.io.FileDescriptor; import java.io.PrintWriter; import java.security.InvalidParameterException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; Loading Loading @@ -138,19 +140,49 @@ public final class ContentService extends IContentService.Stub { getSyncManager(); } public void registerContentObserver(Uri uri, boolean notifyForDescendents, IContentObserver observer) { /** * Register a content observer tied to a specific user's view of the provider. * @param userHandle the user whose view of the provider is to be observed. May be * the calling user without requiring any permission, otherwise the caller needs to * hold the INTERACT_ACROSS_USERS_FULL permission. Pseudousers USER_ALL and * USER_CURRENT are properly handled; all other pseudousers are forbidden. */ @Override public void registerContentObserver(Uri uri, boolean notifyForDescendants, IContentObserver observer, int userHandle) { if (observer == null || uri == null) { throw new IllegalArgumentException("You must pass a valid uri and observer"); } final int callingUser = UserHandle.getCallingUserId(); if (callingUser != userHandle) { mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, "no permission to observe other users' provider view"); } if (userHandle < 0) { if (userHandle == UserHandle.USER_CURRENT) { userHandle = ActivityManager.getCurrentUser(); } else if (userHandle != UserHandle.USER_ALL) { throw new InvalidParameterException("Bad user handle for registerContentObserver: " + userHandle); } } synchronized (mRootNode) { mRootNode.addObserverLocked(uri, observer, notifyForDescendents, mRootNode, Binder.getCallingUid(), Binder.getCallingPid()); mRootNode.addObserverLocked(uri, observer, notifyForDescendants, mRootNode, Binder.getCallingUid(), Binder.getCallingPid(), userHandle); if (false) Log.v(TAG, "Registered observer " + observer + " at " + uri + " with notifyForDescendents " + notifyForDescendents); " with notifyForDescendants " + notifyForDescendants); } } public void registerContentObserver(Uri uri, boolean notifyForDescendants, IContentObserver observer) { registerContentObserver(uri, notifyForDescendants, observer, UserHandle.getCallingUserId()); } public void unregisterContentObserver(IContentObserver observer) { if (observer == null) { throw new IllegalArgumentException("You must pass a valid observer"); Loading @@ -161,14 +193,39 @@ public final class ContentService extends IContentService.Stub { } } /** * Notify observers of a particular user's view of the provider. * @param userHandle the user whose view of the provider is to be notified. May be * the calling user without requiring any permission, otherwise the caller needs to * hold the INTERACT_ACROSS_USERS_FULL permission. Pseudousers USER_ALL and * USER_CURRENT are properly interpreted; no other pseudousers are allowed. */ @Override public void notifyChange(Uri uri, IContentObserver observer, boolean observerWantsSelfNotifications, boolean syncToNetwork) { boolean observerWantsSelfNotifications, boolean syncToNetwork, int userHandle) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "Notifying update of " + uri + " from observer " + observer + ", syncToNetwork " + syncToNetwork); Log.v(TAG, "Notifying update of " + uri + " for user " + userHandle + " from observer " + observer + ", syncToNetwork " + syncToNetwork); } // Notify for any user other than the caller's own requires permission. final int callingUserHandle = UserHandle.getCallingUserId(); if (userHandle != callingUserHandle) { mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, "no permission to notify other users"); } // We passed the permission check; resolve pseudouser targets as appropriate if (userHandle < 0) { if (userHandle == UserHandle.USER_CURRENT) { userHandle = ActivityManager.getCurrentUser(); } else if (userHandle != UserHandle.USER_ALL) { throw new InvalidParameterException("Bad user handle for notifyChange: " + userHandle); } } int userId = UserHandle.getCallingUserId(); // 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. long identityToken = clearCallingIdentity(); Loading @@ -176,7 +233,7 @@ public final class ContentService extends IContentService.Stub { ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>(); synchronized (mRootNode) { mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications, calls); userHandle, calls); } final int numCalls = calls.size(); for (int i=0; i<numCalls; i++) { Loading Loading @@ -207,7 +264,7 @@ public final class ContentService extends IContentService.Stub { if (syncToNetwork) { SyncManager syncManager = getSyncManager(); if (syncManager != null) { syncManager.scheduleLocalSync(null /* all accounts */, userId, syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle, uri.getAuthority()); } } Loading @@ -216,6 +273,12 @@ public final class ContentService extends IContentService.Stub { } } public void notifyChange(Uri uri, IContentObserver observer, boolean observerWantsSelfNotifications, boolean syncToNetwork) { notifyChange(uri, observer, observerWantsSelfNotifications, syncToNetwork, UserHandle.getCallingUserId()); } /** * Hide this class since it is not part of api, * but current unittest framework requires it to be public Loading Loading @@ -543,16 +606,18 @@ public final class ContentService extends IContentService.Stub { public final IContentObserver observer; public final int uid; public final int pid; public final boolean notifyForDescendents; public final boolean notifyForDescendants; private final int userHandle; private final Object observersLock; public ObserverEntry(IContentObserver o, boolean n, Object observersLock, int _uid, int _pid) { int _uid, int _pid, int _userHandle) { this.observersLock = observersLock; observer = o; uid = _uid; pid = _pid; notifyForDescendents = n; userHandle = _userHandle; notifyForDescendants = n; try { observer.asBinder().linkToDeath(this, 0); } catch (RemoteException e) { Loading @@ -571,7 +636,8 @@ public final class ContentService extends IContentService.Stub { pidCounts.put(pid, pidCounts.get(pid)+1); pw.print(prefix); pw.print(name); pw.print(": pid="); pw.print(pid); pw.print(" uid="); pw.print(uid); pw.print(" target="); pw.print(uid); pw.print(" user="); pw.print(userHandle); pw.print(" target="); pw.println(Integer.toHexString(System.identityHashCode( observer != null ? observer.asBinder() : null))); } Loading Loading @@ -639,17 +705,21 @@ public final class ContentService extends IContentService.Stub { return uri.getPathSegments().size() + 1; } // Invariant: userHandle is either a hard user number or is USER_ALL public void addObserverLocked(Uri uri, IContentObserver observer, boolean notifyForDescendents, Object observersLock, int uid, int pid) { addObserverLocked(uri, 0, observer, notifyForDescendents, observersLock, uid, pid); boolean notifyForDescendants, Object observersLock, int uid, int pid, int userHandle) { addObserverLocked(uri, 0, observer, notifyForDescendants, observersLock, uid, pid, userHandle); } private void addObserverLocked(Uri uri, int index, IContentObserver observer, boolean notifyForDescendents, Object observersLock, int uid, int pid) { boolean notifyForDescendants, Object observersLock, int uid, int pid, int userHandle) { // If this is the leaf node add the observer if (index == countUriSegments(uri)) { mObservers.add(new ObserverEntry(observer, notifyForDescendents, observersLock, uid, pid)); mObservers.add(new ObserverEntry(observer, notifyForDescendants, observersLock, uid, pid, userHandle)); return; } Loading @@ -662,8 +732,8 @@ public final class ContentService extends IContentService.Stub { for (int i = 0; i < N; i++) { ObserverNode node = mChildren.get(i); if (node.mName.equals(segment)) { node.addObserverLocked(uri, index + 1, observer, notifyForDescendents, observersLock, uid, pid); node.addObserverLocked(uri, index + 1, observer, notifyForDescendants, observersLock, uid, pid, userHandle); return; } } Loading @@ -671,8 +741,8 @@ public final class ContentService extends IContentService.Stub { // No child found, create one ObserverNode node = new ObserverNode(segment); mChildren.add(node); node.addObserverLocked(uri, index + 1, observer, notifyForDescendents, observersLock, uid, pid); node.addObserverLocked(uri, index + 1, observer, notifyForDescendants, observersLock, uid, pid, userHandle); } public boolean removeObserverLocked(IContentObserver observer) { Loading Loading @@ -705,37 +775,49 @@ public final class ContentService extends IContentService.Stub { } private void collectMyObserversLocked(boolean leaf, IContentObserver observer, boolean observerWantsSelfNotifications, ArrayList<ObserverCall> calls) { boolean observerWantsSelfNotifications, int targetUserHandle, ArrayList<ObserverCall> calls) { int N = mObservers.size(); IBinder observerBinder = observer == null ? null : observer.asBinder(); for (int i = 0; i < N; i++) { ObserverEntry entry = mObservers.get(i); // Don't notify the observer if it sent the notification and isn't interesed // Don't notify the observer if it sent the notification and isn't interested // in self notifications boolean selfChange = (entry.observer.asBinder() == observerBinder); if (selfChange && !observerWantsSelfNotifications) { continue; } // Does this observer match the target user? if (targetUserHandle == UserHandle.USER_ALL || entry.userHandle == UserHandle.USER_ALL || targetUserHandle == entry.userHandle) { // Make sure the observer is interested in the notification if (leaf || (!leaf && entry.notifyForDescendents)) { if (leaf || (!leaf && entry.notifyForDescendants)) { calls.add(new ObserverCall(this, entry.observer, selfChange)); } } } } /** * targetUserHandle is either a hard user handle or is USER_ALL */ public void collectObserversLocked(Uri uri, int index, IContentObserver observer, boolean observerWantsSelfNotifications, ArrayList<ObserverCall> calls) { boolean observerWantsSelfNotifications, int targetUserHandle, ArrayList<ObserverCall> calls) { String segment = null; int segmentCount = countUriSegments(uri); if (index >= segmentCount) { // This is the leaf node, notify all observers collectMyObserversLocked(true, observer, observerWantsSelfNotifications, calls); collectMyObserversLocked(true, observer, observerWantsSelfNotifications, targetUserHandle, calls); } else if (index < segmentCount){ segment = getUriSegment(uri, index); // Notify any observers at this level who are interested in descendents collectMyObserversLocked(false, observer, observerWantsSelfNotifications, calls); // Notify any observers at this level who are interested in descendants collectMyObserversLocked(false, observer, observerWantsSelfNotifications, targetUserHandle, calls); } int N = mChildren.size(); Loading @@ -744,7 +826,7 @@ public final class ContentService extends IContentService.Stub { if (segment == null || node.mName.equals(segment)) { // We found the child, node.collectObserversLocked(uri, index + 1, observer, observerWantsSelfNotifications, calls); observer, observerWantsSelfNotifications, targetUserHandle, calls); if (segment != null) { break; } Loading
core/java/android/content/IContentService.aidl +19 −3 Original line number Diff line number Diff line Loading @@ -30,12 +30,28 @@ import android.database.IContentObserver; * @hide */ interface IContentService { void registerContentObserver(in Uri uri, boolean notifyForDescendentsn, IContentObserver observer); void unregisterContentObserver(IContentObserver observer); /** * Register a content observer tied to a specific user's view of the provider. * @param userHandle the user whose view of the provider is to be observed. May be * the calling user without requiring any permission, otherwise the caller needs to * hold the INTERACT_ACROSS_USERS_FULL permission. Pseudousers USER_ALL and * USER_CURRENT are properly handled. */ void registerContentObserver(in Uri uri, boolean notifyForDescendants, IContentObserver observer, int userHandle); /** * Notify observers of a particular user's view of the provider. * @param userHandle the user whose view of the provider is to be notified. May be * the calling user without requiring any permission, otherwise the caller needs to * hold the INTERACT_ACROSS_USERS_FULL permission. Pseudousers USER_ALL * USER_CURRENT are properly interpreted. */ void notifyChange(in Uri uri, IContentObserver observer, boolean observerWantsSelfNotifications, boolean syncToNetwork); boolean observerWantsSelfNotifications, boolean syncToNetwork, int userHandle); void requestSync(in Account account, String authority, in Bundle extras); void cancelSync(in Account account, String authority); Loading
core/tests/coretests/src/android/content/ObserverNodeTest.java +13 −5 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.content.ContentService.ObserverNode; import android.database.ContentObserver; import android.net.Uri; import android.os.Handler; import android.os.UserHandle; import android.test.AndroidTestCase; public class ObserverNodeTest extends AndroidTestCase { Loading @@ -33,6 +34,8 @@ public class ObserverNodeTest extends AndroidTestCase { } public void testUri() { final int myUserHandle = UserHandle.myUserId(); ObserverNode root = new ObserverNode(""); Uri[] uris = new Uri[] { Uri.parse("content://c/a/"), Loading @@ -48,21 +51,25 @@ public class ObserverNodeTest extends AndroidTestCase { int[] nums = new int[] {4, 7, 1, 4, 2, 2, 3, 3}; // special case root.addObserverLocked(uris[0], new TestObserver().getContentObserver(), false, root, 0, 0); root.addObserverLocked(uris[0], new TestObserver().getContentObserver(), false, root, 0, 0, myUserHandle); for(int i = 1; i < uris.length; i++) { root.addObserverLocked(uris[i], new TestObserver().getContentObserver(), true, root, 0, 0); root.addObserverLocked(uris[i], new TestObserver().getContentObserver(), true, root, 0, 0, myUserHandle); } ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>(); for (int i = nums.length - 1; i >=0; --i) { root.collectObserversLocked(uris[i], 0, null, false, calls); root.collectObserversLocked(uris[i], 0, null, false, myUserHandle, calls); assertEquals(nums[i], calls.size()); calls.clear(); } } public void testUriNotNotify() { final int myUserHandle = UserHandle.myUserId(); ObserverNode root = new ObserverNode(""); Uri[] uris = new Uri[] { Uri.parse("content://c/"), Loading @@ -77,13 +84,14 @@ public class ObserverNodeTest extends AndroidTestCase { int[] nums = new int[] {7, 1, 3, 3, 1, 1, 1, 1}; for(int i = 0; i < uris.length; i++) { root.addObserverLocked(uris[i], new TestObserver().getContentObserver(), false, root, 0, 0); root.addObserverLocked(uris[i], new TestObserver().getContentObserver(), false, root, 0, 0, myUserHandle); } ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>(); for (int i = uris.length - 1; i >=0; --i) { root.collectObserversLocked(uris[i], 0, null, false, calls); root.collectObserversLocked(uris[i], 0, null, false, myUserHandle, calls); assertEquals(nums[i], calls.size()); calls.clear(); } Loading
packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +5 −3 Original line number Diff line number Diff line Loading @@ -317,13 +317,14 @@ public class SettingsProvider extends ContentProvider { boolean backedUpDataChanged = false; String property = null, table = uri.getPathSegments().get(0); final boolean isGlobal = table.equals(TABLE_GLOBAL); if (table.equals(TABLE_SYSTEM)) { property = Settings.System.SYS_PROP_SETTING_VERSION + '_' + userHandle; backedUpDataChanged = true; } else if (table.equals(TABLE_SECURE)) { property = Settings.Secure.SYS_PROP_SETTING_VERSION + '_' + userHandle; backedUpDataChanged = true; } else if (table.equals(TABLE_GLOBAL)) { } else if (isGlobal) { property = Settings.Global.SYS_PROP_SETTING_VERSION; // this one is global backedUpDataChanged = true; } Loading @@ -342,8 +343,9 @@ public class SettingsProvider extends ContentProvider { String notify = uri.getQueryParameter("notify"); if (notify == null || "true".equals(notify)) { getContext().getContentResolver().notifyChange(uri, null); if (LOCAL_LOGV) Log.v(TAG, "notifying: " + uri); final int notifyTarget = isGlobal ? UserHandle.USER_ALL : userHandle; getContext().getContentResolver().notifyChange(uri, null, true, notifyTarget); if (LOCAL_LOGV) Log.v(TAG, "notifying for " + notifyTarget + ": " + uri); } else { if (LOCAL_LOGV) Log.v(TAG, "notification suppressed: " + uri); } Loading