import android.content.ContentResolver;
import android.content.Context;
import android.content.IContentService;
+import android.content.Intent;
import android.content.ISyncStatusObserver;
import android.content.PeriodicSync;
-import android.content.pm.PackageManager;
import android.content.SyncAdapterType;
import android.content.SyncInfo;
import android.content.SyncRequest;
import android.content.SyncStatusInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.database.IContentObserver;
import android.database.sqlite.SQLiteException;
import android.net.Uri;
import android.util.Log;
import android.util.Slog;
import android.util.SparseIntArray;
+import com.android.server.LocalServices;
import java.io.FileDescriptor;
import java.io.PrintWriter;
/*package*/ ContentService(Context context, boolean factoryTest) {
mContext = context;
mFactoryTest = factoryTest;
+
+ // Let the package manager query for the sync adapters for a given authority
+ // as we grant default permissions to sync adapters for specific authorities.
+ PackageManagerInternal packageManagerInternal = LocalServices.getService(
+ PackageManagerInternal.class);
+ packageManagerInternal.setSyncAdapterPackagesprovider(
+ new PackageManagerInternal.SyncAdapterPackagesProvider() {
+ @Override
+ public String[] getPackages(String authority, int userId) {
+ return getSyncAdapterPackagesForAuthorityAsUser(authority, userId);
+ }
+ });
}
public void systemReady() {
* 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.
+ * hold the INTERACT_ACROSS_USERS_FULL permission or hold a read uri grant to the uri.
+ * Pseudousers USER_ALL and USER_CURRENT are properly handled; all other pseudousers
+ * are forbidden.
*/
@Override
public void registerContentObserver(Uri uri, boolean notifyForDescendants,
throw new IllegalArgumentException("You must pass a valid uri and observer");
}
- enforceCrossUserPermission(userHandle,
- "no permission to observe other users' provider view");
+ final int uid = Binder.getCallingUid();
+ final int pid = Binder.getCallingPid();
+ final int callingUserHandle = UserHandle.getCallingUserId();
+ // Registering an observer for any user other than the calling user requires uri grant or
+ // cross user permission
+ if (callingUserHandle != userHandle &&
+ mContext.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ != PackageManager.PERMISSION_GRANTED) {
+ enforceCrossUserPermission(userHandle,
+ "no permission to observe other users' provider view");
+ }
if (userHandle < 0) {
if (userHandle == UserHandle.USER_CURRENT) {
synchronized (mRootNode) {
mRootNode.addObserverLocked(uri, observer, notifyForDescendants, mRootNode,
- Binder.getCallingUid(), Binder.getCallingPid(), userHandle);
+ uid, pid, userHandle);
if (false) Log.v(TAG, "Registered observer " + observer + " at " + uri +
" with notifyForDescendants " + notifyForDescendants);
}
* 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.
+ * hold the INTERACT_ACROSS_USERS_FULL permission or hold a write uri grant to the uri.
+ * Pseudousers USER_ALL and USER_CURRENT are properly interpreted; no other pseudousers are
+ * allowed.
*/
@Override
public void notifyChange(Uri uri, IContentObserver observer,
+ " from observer " + observer + ", syncToNetwork " + syncToNetwork);
}
- // Notify for any user other than the caller's own requires permission.
+ final int uid = Binder.getCallingUid();
+ final int pid = Binder.getCallingPid();
final int callingUserHandle = UserHandle.getCallingUserId();
- if (userHandle != callingUserHandle) {
- mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS,
- "no permission to notify other users");
+ // Notify for any user other than the caller requires uri grant or cross user permission
+ if (callingUserHandle != userHandle &&
+ mContext.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
+ != PackageManager.PERMISSION_GRANTED) {
+ enforceCrossUserPermission(userHandle, "no permission to notify other users");
}
// We passed the permission check; resolve pseudouser targets as appropriate
}
}
- final int uid = Binder.getCallingUid();
// 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();
}
@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());
}
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
"no permission to read the sync stats");
+ final boolean canAccessAccounts =
+ mContext.checkCallingOrSelfPermission(Manifest.permission.GET_ACCOUNTS)
+ == PackageManager.PERMISSION_GRANTED;
long identityToken = clearCallingIdentity();
try {
- return getSyncManager().getSyncStorageEngine().getCurrentSyncsCopy(userId);
+ return getSyncManager().getSyncStorageEngine()
+ .getCurrentSyncsCopy(userId, canAccessAccounts);
} finally {
restoreCallingIdentity(identityToken);
}