Skip to content

Commit f1aa107

Browse files
Christopher TateAndroid (Google) Code Review
authored andcommitted
Merge "Per-user content observer APIs" into jb-mr1-dev
2 parents ca28410 + 16aa973 commit f1aa107

File tree

5 files changed

+173
-47
lines changed

5 files changed

+173
-47
lines changed

core/java/android/content/ContentResolver.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1218,10 +1218,17 @@ public final ContentProviderClient acquireUnstableContentProviderClient(String n
12181218
*/
12191219
public final void registerContentObserver(Uri uri, boolean notifyForDescendents,
12201220
ContentObserver observer)
1221+
{
1222+
registerContentObserver(uri, notifyForDescendents, observer, UserHandle.myUserId());
1223+
}
1224+
1225+
/** @hide - designated user version */
1226+
public final void registerContentObserver(Uri uri, boolean notifyForDescendents,
1227+
ContentObserver observer, int userHandle)
12211228
{
12221229
try {
12231230
getContentService().registerContentObserver(uri, notifyForDescendents,
1224-
observer.getContentObserver());
1231+
observer.getContentObserver(), userHandle);
12251232
} catch (RemoteException e) {
12261233
}
12271234
}
@@ -1276,10 +1283,21 @@ public void notifyChange(Uri uri, ContentObserver observer) {
12761283
* @see #requestSync(android.accounts.Account, String, android.os.Bundle)
12771284
*/
12781285
public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
1286+
notifyChange(uri, observer, syncToNetwork, UserHandle.myUserId());
1287+
}
1288+
1289+
/**
1290+
* Notify registered observers within the designated user(s) that a row was updated.
1291+
*
1292+
* @hide
1293+
*/
1294+
public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork,
1295+
int userHandle) {
12791296
try {
12801297
getContentService().notifyChange(
12811298
uri, observer == null ? null : observer.getContentObserver(),
1282-
observer != null && observer.deliverSelfNotifications(), syncToNetwork);
1299+
observer != null && observer.deliverSelfNotifications(), syncToNetwork,
1300+
userHandle);
12831301
} catch (RemoteException e) {
12841302
}
12851303
}

core/java/android/content/ContentService.java

Lines changed: 116 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package android.content;
1818

1919
import android.accounts.Account;
20+
import android.app.ActivityManager;
2021
import android.database.IContentObserver;
2122
import android.database.sqlite.SQLiteException;
2223
import android.net.Uri;
@@ -33,6 +34,7 @@
3334

3435
import java.io.FileDescriptor;
3536
import java.io.PrintWriter;
37+
import java.security.InvalidParameterException;
3638
import java.util.ArrayList;
3739
import java.util.Collections;
3840
import 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
}

core/java/android/content/IContentService.aidl

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,28 @@ import android.database.IContentObserver;
3030
* @hide
3131
*/
3232
interface IContentService {
33-
void registerContentObserver(in Uri uri, boolean notifyForDescendentsn,
34-
IContentObserver observer);
3533
void unregisterContentObserver(IContentObserver observer);
3634

35+
/**
36+
* Register a content observer tied to a specific user's view of the provider.
37+
* @param userHandle the user whose view of the provider is to be observed. May be
38+
* the calling user without requiring any permission, otherwise the caller needs to
39+
* hold the INTERACT_ACROSS_USERS_FULL permission. Pseudousers USER_ALL and
40+
* USER_CURRENT are properly handled.
41+
*/
42+
void registerContentObserver(in Uri uri, boolean notifyForDescendants,
43+
IContentObserver observer, int userHandle);
44+
45+
/**
46+
* Notify observers of a particular user's view of the provider.
47+
* @param userHandle the user whose view of the provider is to be notified. May be
48+
* the calling user without requiring any permission, otherwise the caller needs to
49+
* hold the INTERACT_ACROSS_USERS_FULL permission. Pseudousers USER_ALL
50+
* USER_CURRENT are properly interpreted.
51+
*/
3752
void notifyChange(in Uri uri, IContentObserver observer,
38-
boolean observerWantsSelfNotifications, boolean syncToNetwork);
53+
boolean observerWantsSelfNotifications, boolean syncToNetwork,
54+
int userHandle);
3955

4056
void requestSync(in Account account, String authority, in Bundle extras);
4157
void cancelSync(in Account account, String authority);

0 commit comments

Comments
 (0)