1717package android .content ;
1818
1919import android .accounts .Account ;
20+ import android .app .ActivityManager ;
2021import android .database .IContentObserver ;
2122import android .database .sqlite .SQLiteException ;
2223import android .net .Uri ;
3334
3435import java .io .FileDescriptor ;
3536import java .io .PrintWriter ;
37+ import java .security .InvalidParameterException ;
3638import java .util .ArrayList ;
3739import java .util .Collections ;
3840import java .util .Comparator ;
@@ -138,19 +140,49 @@ public void systemReady() {
138140 getSyncManager ();
139141 }
140142
141- public void registerContentObserver (Uri uri , boolean notifyForDescendents ,
142- IContentObserver observer ) {
143+ /**
144+ * Register a content observer tied to a specific user's view of the provider.
145+ * @param userHandle the user whose view of the provider is to be observed. May be
146+ * the calling user without requiring any permission, otherwise the caller needs to
147+ * hold the INTERACT_ACROSS_USERS_FULL permission. Pseudousers USER_ALL and
148+ * USER_CURRENT are properly handled; all other pseudousers are forbidden.
149+ */
150+ @ Override
151+ public void registerContentObserver (Uri uri , boolean notifyForDescendants ,
152+ IContentObserver observer , int userHandle ) {
143153 if (observer == null || uri == null ) {
144154 throw new IllegalArgumentException ("You must pass a valid uri and observer" );
145155 }
156+
157+ final int callingUser = UserHandle .getCallingUserId ();
158+ if (callingUser != userHandle ) {
159+ mContext .enforceCallingOrSelfPermission (Manifest .permission .INTERACT_ACROSS_USERS_FULL ,
160+ "no permission to observe other users' provider view" );
161+ }
162+
163+ if (userHandle < 0 ) {
164+ if (userHandle == UserHandle .USER_CURRENT ) {
165+ userHandle = ActivityManager .getCurrentUser ();
166+ } else if (userHandle != UserHandle .USER_ALL ) {
167+ throw new InvalidParameterException ("Bad user handle for registerContentObserver: "
168+ + userHandle );
169+ }
170+ }
171+
146172 synchronized (mRootNode ) {
147- mRootNode .addObserverLocked (uri , observer , notifyForDescendents , mRootNode ,
148- Binder .getCallingUid (), Binder .getCallingPid ());
173+ mRootNode .addObserverLocked (uri , observer , notifyForDescendants , mRootNode ,
174+ Binder .getCallingUid (), Binder .getCallingPid (), userHandle );
149175 if (false ) Log .v (TAG , "Registered observer " + observer + " at " + uri +
150- " with notifyForDescendents " + notifyForDescendents );
176+ " with notifyForDescendants " + notifyForDescendants );
151177 }
152178 }
153179
180+ public void registerContentObserver (Uri uri , boolean notifyForDescendants ,
181+ IContentObserver observer ) {
182+ registerContentObserver (uri , notifyForDescendants , observer ,
183+ UserHandle .getCallingUserId ());
184+ }
185+
154186 public void unregisterContentObserver (IContentObserver observer ) {
155187 if (observer == null ) {
156188 throw new IllegalArgumentException ("You must pass a valid observer" );
@@ -161,22 +193,47 @@ public void unregisterContentObserver(IContentObserver observer) {
161193 }
162194 }
163195
196+ /**
197+ * Notify observers of a particular user's view of the provider.
198+ * @param userHandle the user whose view of the provider is to be notified. May be
199+ * the calling user without requiring any permission, otherwise the caller needs to
200+ * hold the INTERACT_ACROSS_USERS_FULL permission. Pseudousers USER_ALL and
201+ * USER_CURRENT are properly interpreted; no other pseudousers are allowed.
202+ */
203+ @ Override
164204 public void notifyChange (Uri uri , IContentObserver observer ,
165- boolean observerWantsSelfNotifications , boolean syncToNetwork ) {
205+ boolean observerWantsSelfNotifications , boolean syncToNetwork ,
206+ int userHandle ) {
166207 if (Log .isLoggable (TAG , Log .VERBOSE )) {
167- Log .v (TAG , "Notifying update of " + uri + " from observer " + observer
168- + ", syncToNetwork " + syncToNetwork );
208+ Log .v (TAG , "Notifying update of " + uri + " for user " + userHandle
209+ + " from observer " + observer + ", syncToNetwork " + syncToNetwork );
210+ }
211+
212+ // Notify for any user other than the caller's own requires permission.
213+ final int callingUserHandle = UserHandle .getCallingUserId ();
214+ if (userHandle != callingUserHandle ) {
215+ mContext .enforceCallingOrSelfPermission (Manifest .permission .INTERACT_ACROSS_USERS_FULL ,
216+ "no permission to notify other users" );
217+ }
218+
219+ // We passed the permission check; resolve pseudouser targets as appropriate
220+ if (userHandle < 0 ) {
221+ if (userHandle == UserHandle .USER_CURRENT ) {
222+ userHandle = ActivityManager .getCurrentUser ();
223+ } else if (userHandle != UserHandle .USER_ALL ) {
224+ throw new InvalidParameterException ("Bad user handle for notifyChange: "
225+ + userHandle );
226+ }
169227 }
170228
171- int userId = UserHandle .getCallingUserId ();
172229 // This makes it so that future permission checks will be in the context of this
173230 // process rather than the caller's process. We will restore this before returning.
174231 long identityToken = clearCallingIdentity ();
175232 try {
176233 ArrayList <ObserverCall > calls = new ArrayList <ObserverCall >();
177234 synchronized (mRootNode ) {
178235 mRootNode .collectObserversLocked (uri , 0 , observer , observerWantsSelfNotifications ,
179- calls );
236+ userHandle , calls );
180237 }
181238 final int numCalls = calls .size ();
182239 for (int i =0 ; i <numCalls ; i ++) {
@@ -207,7 +264,7 @@ public void notifyChange(Uri uri, IContentObserver observer,
207264 if (syncToNetwork ) {
208265 SyncManager syncManager = getSyncManager ();
209266 if (syncManager != null ) {
210- syncManager .scheduleLocalSync (null /* all accounts */ , userId ,
267+ syncManager .scheduleLocalSync (null /* all accounts */ , callingUserHandle ,
211268 uri .getAuthority ());
212269 }
213270 }
@@ -216,6 +273,12 @@ public void notifyChange(Uri uri, IContentObserver observer,
216273 }
217274 }
218275
276+ public void notifyChange (Uri uri , IContentObserver observer ,
277+ boolean observerWantsSelfNotifications , boolean syncToNetwork ) {
278+ notifyChange (uri , observer , observerWantsSelfNotifications , syncToNetwork ,
279+ UserHandle .getCallingUserId ());
280+ }
281+
219282 /**
220283 * Hide this class since it is not part of api,
221284 * but current unittest framework requires it to be public
@@ -543,16 +606,18 @@ private class ObserverEntry implements IBinder.DeathRecipient {
543606 public final IContentObserver observer ;
544607 public final int uid ;
545608 public final int pid ;
546- public final boolean notifyForDescendents ;
609+ public final boolean notifyForDescendants ;
610+ private final int userHandle ;
547611 private final Object observersLock ;
548612
549613 public ObserverEntry (IContentObserver o , boolean n , Object observersLock ,
550- int _uid , int _pid ) {
614+ int _uid , int _pid , int _userHandle ) {
551615 this .observersLock = observersLock ;
552616 observer = o ;
553617 uid = _uid ;
554618 pid = _pid ;
555- notifyForDescendents = n ;
619+ userHandle = _userHandle ;
620+ notifyForDescendants = n ;
556621 try {
557622 observer .asBinder ().linkToDeath (this , 0 );
558623 } catch (RemoteException e ) {
@@ -571,7 +636,8 @@ public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
571636 pidCounts .put (pid , pidCounts .get (pid )+1 );
572637 pw .print (prefix ); pw .print (name ); pw .print (": pid=" );
573638 pw .print (pid ); pw .print (" uid=" );
574- pw .print (uid ); pw .print (" target=" );
639+ pw .print (uid ); pw .print (" user=" );
640+ pw .print (userHandle ); pw .print (" target=" );
575641 pw .println (Integer .toHexString (System .identityHashCode (
576642 observer != null ? observer .asBinder () : null )));
577643 }
@@ -639,17 +705,21 @@ private int countUriSegments(Uri uri) {
639705 return uri .getPathSegments ().size () + 1 ;
640706 }
641707
708+ // Invariant: userHandle is either a hard user number or is USER_ALL
642709 public void addObserverLocked (Uri uri , IContentObserver observer ,
643- boolean notifyForDescendents , Object observersLock , int uid , int pid ) {
644- addObserverLocked (uri , 0 , observer , notifyForDescendents , observersLock , uid , pid );
710+ boolean notifyForDescendants , Object observersLock ,
711+ int uid , int pid , int userHandle ) {
712+ addObserverLocked (uri , 0 , observer , notifyForDescendants , observersLock ,
713+ uid , pid , userHandle );
645714 }
646715
647716 private void addObserverLocked (Uri uri , int index , IContentObserver observer ,
648- boolean notifyForDescendents , Object observersLock , int uid , int pid ) {
717+ boolean notifyForDescendants , Object observersLock ,
718+ int uid , int pid , int userHandle ) {
649719 // If this is the leaf node add the observer
650720 if (index == countUriSegments (uri )) {
651- mObservers .add (new ObserverEntry (observer , notifyForDescendents , observersLock ,
652- uid , pid ));
721+ mObservers .add (new ObserverEntry (observer , notifyForDescendants , observersLock ,
722+ uid , pid , userHandle ));
653723 return ;
654724 }
655725
@@ -662,17 +732,17 @@ private void addObserverLocked(Uri uri, int index, IContentObserver observer,
662732 for (int i = 0 ; i < N ; i ++) {
663733 ObserverNode node = mChildren .get (i );
664734 if (node .mName .equals (segment )) {
665- node .addObserverLocked (uri , index + 1 , observer , notifyForDescendents ,
666- observersLock , uid , pid );
735+ node .addObserverLocked (uri , index + 1 , observer , notifyForDescendants ,
736+ observersLock , uid , pid , userHandle );
667737 return ;
668738 }
669739 }
670740
671741 // No child found, create one
672742 ObserverNode node = new ObserverNode (segment );
673743 mChildren .add (node );
674- node .addObserverLocked (uri , index + 1 , observer , notifyForDescendents ,
675- observersLock , uid , pid );
744+ node .addObserverLocked (uri , index + 1 , observer , notifyForDescendants ,
745+ observersLock , uid , pid , userHandle );
676746 }
677747
678748 public boolean removeObserverLocked (IContentObserver observer ) {
@@ -705,37 +775,49 @@ public boolean removeObserverLocked(IContentObserver observer) {
705775 }
706776
707777 private void collectMyObserversLocked (boolean leaf , IContentObserver observer ,
708- boolean observerWantsSelfNotifications , ArrayList <ObserverCall > calls ) {
778+ boolean observerWantsSelfNotifications , int targetUserHandle ,
779+ ArrayList <ObserverCall > calls ) {
709780 int N = mObservers .size ();
710781 IBinder observerBinder = observer == null ? null : observer .asBinder ();
711782 for (int i = 0 ; i < N ; i ++) {
712783 ObserverEntry entry = mObservers .get (i );
713784
714- // Don't notify the observer if it sent the notification and isn't interesed
785+ // Don't notify the observer if it sent the notification and isn't interested
715786 // in self notifications
716787 boolean selfChange = (entry .observer .asBinder () == observerBinder );
717788 if (selfChange && !observerWantsSelfNotifications ) {
718789 continue ;
719790 }
720791
721- // Make sure the observer is interested in the notification
722- if (leaf || (!leaf && entry .notifyForDescendents )) {
723- calls .add (new ObserverCall (this , entry .observer , selfChange ));
792+ // Does this observer match the target user?
793+ if (targetUserHandle == UserHandle .USER_ALL
794+ || entry .userHandle == UserHandle .USER_ALL
795+ || targetUserHandle == entry .userHandle ) {
796+ // Make sure the observer is interested in the notification
797+ if (leaf || (!leaf && entry .notifyForDescendants )) {
798+ calls .add (new ObserverCall (this , entry .observer , selfChange ));
799+ }
724800 }
725801 }
726802 }
727803
804+ /**
805+ * targetUserHandle is either a hard user handle or is USER_ALL
806+ */
728807 public void collectObserversLocked (Uri uri , int index , IContentObserver observer ,
729- boolean observerWantsSelfNotifications , ArrayList <ObserverCall > calls ) {
808+ boolean observerWantsSelfNotifications , int targetUserHandle ,
809+ ArrayList <ObserverCall > calls ) {
730810 String segment = null ;
731811 int segmentCount = countUriSegments (uri );
732812 if (index >= segmentCount ) {
733813 // This is the leaf node, notify all observers
734- collectMyObserversLocked (true , observer , observerWantsSelfNotifications , calls );
814+ collectMyObserversLocked (true , observer , observerWantsSelfNotifications ,
815+ targetUserHandle , calls );
735816 } else if (index < segmentCount ){
736817 segment = getUriSegment (uri , index );
737- // Notify any observers at this level who are interested in descendents
738- collectMyObserversLocked (false , observer , observerWantsSelfNotifications , calls );
818+ // Notify any observers at this level who are interested in descendants
819+ collectMyObserversLocked (false , observer , observerWantsSelfNotifications ,
820+ targetUserHandle , calls );
739821 }
740822
741823 int N = mChildren .size ();
@@ -744,7 +826,7 @@ public void collectObserversLocked(Uri uri, int index, IContentObserver observer
744826 if (segment == null || node .mName .equals (segment )) {
745827 // We found the child,
746828 node .collectObserversLocked (uri , index + 1 ,
747- observer , observerWantsSelfNotifications , calls );
829+ observer , observerWantsSelfNotifications , targetUserHandle , calls );
748830 if (segment != null ) {
749831 break ;
750832 }
0 commit comments