From 7a96c39c510923ef73bbb06ab20109f0168b8eb1 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Thu, 15 Nov 2012 14:01:46 -0800 Subject: Move lingering services to services.jar. This helps reduce the pressure on framework.jar, and makes it clear that it should only be used by the system_server. Bug: 7333397 Change-Id: I0858904239535380fbf30562b793e277d8c3f054 --- .../com/android/server/content/ContentService.java | 848 +++++++++++++++++++++ 1 file changed, 848 insertions(+) create mode 100644 services/java/com/android/server/content/ContentService.java (limited to 'services/java/com/android/server/content/ContentService.java') diff --git a/services/java/com/android/server/content/ContentService.java b/services/java/com/android/server/content/ContentService.java new file mode 100644 index 0000000..3b92338 --- /dev/null +++ b/services/java/com/android/server/content/ContentService.java @@ -0,0 +1,848 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.content; + +import android.Manifest; +import android.accounts.Account; +import android.app.ActivityManager; +import android.content.ContentResolver; +import android.content.Context; +import android.content.IContentService; +import android.content.ISyncStatusObserver; +import android.content.PeriodicSync; +import android.content.SyncAdapterType; +import android.content.SyncInfo; +import android.content.SyncStatusInfo; +import android.database.IContentObserver; +import android.database.sqlite.SQLiteException; +import android.net.Uri; +import android.os.Binder; +import android.os.Bundle; +import android.os.IBinder; +import android.os.Parcel; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.UserHandle; +import android.util.Log; +import android.util.SparseIntArray; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.security.InvalidParameterException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +/** + * {@hide} + */ +public final class ContentService extends IContentService.Stub { + private static final String TAG = "ContentService"; + private Context mContext; + private boolean mFactoryTest; + private final ObserverNode mRootNode = new ObserverNode(""); + private SyncManager mSyncManager = null; + private final Object mSyncManagerLock = new Object(); + + private SyncManager getSyncManager() { + synchronized(mSyncManagerLock) { + try { + // Try to create the SyncManager, return null if it fails (e.g. the disk is full). + if (mSyncManager == null) mSyncManager = new SyncManager(mContext, mFactoryTest); + } catch (SQLiteException e) { + Log.e(TAG, "Can't create SyncManager", e); + } + return mSyncManager; + } + } + + @Override + protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + mContext.enforceCallingOrSelfPermission(Manifest.permission.DUMP, + "caller doesn't have the DUMP permission"); + + // 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(); + try { + if (mSyncManager == null) { + pw.println("No SyncManager created! (Disk full?)"); + } else { + mSyncManager.dump(fd, pw); + } + pw.println(); + pw.println("Observer tree:"); + synchronized (mRootNode) { + int[] counts = new int[2]; + final SparseIntArray pidCounts = new SparseIntArray(); + mRootNode.dumpLocked(fd, pw, args, "", " ", counts, pidCounts); + pw.println(); + ArrayList sorted = new ArrayList(); + for (int i=0; i() { + @Override + public int compare(Integer lhs, Integer rhs) { + int lc = pidCounts.get(lhs); + int rc = pidCounts.get(rhs); + if (lc < rc) { + return 1; + } else if (lc > rc) { + return -1; + } + return 0; + } + + }); + for (int i=0; i calls = new ArrayList(); + synchronized (mRootNode) { + mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications, + userHandle, calls); + } + final int numCalls = calls.size(); + for (int i=0; i list + = oc.mNode.mObservers; + int numList = list.size(); + for (int j=0; j getPeriodicSyncs(Account account, String providerName) { + mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS, + "no permission to read the sync settings"); + int userId = UserHandle.getCallingUserId(); + + long identityToken = clearCallingIdentity(); + try { + return getSyncManager().getSyncStorageEngine().getPeriodicSyncs( + account, userId, providerName); + } finally { + restoreCallingIdentity(identityToken); + } + } + + public int getIsSyncable(Account account, String providerName) { + mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS, + "no permission to read the sync settings"); + int userId = UserHandle.getCallingUserId(); + + long identityToken = clearCallingIdentity(); + try { + SyncManager syncManager = getSyncManager(); + if (syncManager != null) { + return syncManager.getSyncStorageEngine().getIsSyncable( + account, userId, providerName); + } + } finally { + restoreCallingIdentity(identityToken); + } + return -1; + } + + public void setIsSyncable(Account account, String providerName, int syncable) { + mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, + "no permission to write the sync settings"); + int userId = UserHandle.getCallingUserId(); + + long identityToken = clearCallingIdentity(); + try { + SyncManager syncManager = getSyncManager(); + if (syncManager != null) { + syncManager.getSyncStorageEngine().setIsSyncable( + account, userId, providerName, syncable); + } + } finally { + restoreCallingIdentity(identityToken); + } + } + + public boolean getMasterSyncAutomatically() { + mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS, + "no permission to read the sync settings"); + int userId = UserHandle.getCallingUserId(); + + long identityToken = clearCallingIdentity(); + try { + SyncManager syncManager = getSyncManager(); + if (syncManager != null) { + return syncManager.getSyncStorageEngine().getMasterSyncAutomatically(userId); + } + } finally { + restoreCallingIdentity(identityToken); + } + return false; + } + + public void setMasterSyncAutomatically(boolean flag) { + mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS, + "no permission to write the sync settings"); + int userId = UserHandle.getCallingUserId(); + + long identityToken = clearCallingIdentity(); + try { + SyncManager syncManager = getSyncManager(); + if (syncManager != null) { + syncManager.getSyncStorageEngine().setMasterSyncAutomatically(flag, userId); + } + } finally { + restoreCallingIdentity(identityToken); + } + } + + public boolean isSyncActive(Account account, String authority) { + mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS, + "no permission to read the sync stats"); + int userId = UserHandle.getCallingUserId(); + + long identityToken = clearCallingIdentity(); + try { + SyncManager syncManager = getSyncManager(); + if (syncManager != null) { + return syncManager.getSyncStorageEngine().isSyncActive( + account, userId, authority); + } + } finally { + restoreCallingIdentity(identityToken); + } + return false; + } + + public List getCurrentSyncs() { + mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS, + "no permission to read the sync stats"); + int userId = UserHandle.getCallingUserId(); + + long identityToken = clearCallingIdentity(); + try { + return getSyncManager().getSyncStorageEngine().getCurrentSyncs(userId); + } finally { + restoreCallingIdentity(identityToken); + } + } + + public SyncStatusInfo getSyncStatus(Account account, String authority) { + mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS, + "no permission to read the sync stats"); + int userId = UserHandle.getCallingUserId(); + + long identityToken = clearCallingIdentity(); + try { + SyncManager syncManager = getSyncManager(); + if (syncManager != null) { + return syncManager.getSyncStorageEngine().getStatusByAccountAndAuthority( + account, userId, authority); + } + } finally { + restoreCallingIdentity(identityToken); + } + return null; + } + + public boolean isSyncPending(Account account, String authority) { + mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS, + "no permission to read the sync stats"); + int userId = UserHandle.getCallingUserId(); + + long identityToken = clearCallingIdentity(); + try { + SyncManager syncManager = getSyncManager(); + if (syncManager != null) { + return syncManager.getSyncStorageEngine().isSyncPending(account, userId, authority); + } + } finally { + restoreCallingIdentity(identityToken); + } + return false; + } + + public void addStatusChangeListener(int mask, ISyncStatusObserver callback) { + long identityToken = clearCallingIdentity(); + try { + SyncManager syncManager = getSyncManager(); + if (syncManager != null && callback != null) { + syncManager.getSyncStorageEngine().addStatusChangeListener(mask, callback); + } + } finally { + restoreCallingIdentity(identityToken); + } + } + + public void removeStatusChangeListener(ISyncStatusObserver callback) { + long identityToken = clearCallingIdentity(); + try { + SyncManager syncManager = getSyncManager(); + if (syncManager != null && callback != null) { + syncManager.getSyncStorageEngine().removeStatusChangeListener(callback); + } + } finally { + restoreCallingIdentity(identityToken); + } + } + + public static ContentService main(Context context, boolean factoryTest) { + ContentService service = new ContentService(context, factoryTest); + ServiceManager.addService(ContentResolver.CONTENT_SERVICE_NAME, service); + return service; + } + + /** + * Hide this class since it is not part of api, + * but current unittest framework requires it to be public + * @hide + */ + public static final class ObserverNode { + private class ObserverEntry implements IBinder.DeathRecipient { + public final IContentObserver observer; + public final int uid; + public final int pid; + 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 _userHandle) { + this.observersLock = observersLock; + observer = o; + uid = _uid; + pid = _pid; + userHandle = _userHandle; + notifyForDescendants = n; + try { + observer.asBinder().linkToDeath(this, 0); + } catch (RemoteException e) { + binderDied(); + } + } + + public void binderDied() { + synchronized (observersLock) { + removeObserverLocked(observer); + } + } + + public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args, + String name, String prefix, SparseIntArray pidCounts) { + 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(" user="); + pw.print(userHandle); pw.print(" target="); + pw.println(Integer.toHexString(System.identityHashCode( + observer != null ? observer.asBinder() : null))); + } + } + + public static final int INSERT_TYPE = 0; + public static final int UPDATE_TYPE = 1; + public static final int DELETE_TYPE = 2; + + private String mName; + private ArrayList mChildren = new ArrayList(); + private ArrayList mObservers = new ArrayList(); + + public ObserverNode(String name) { + mName = name; + } + + public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args, + String name, String prefix, int[] counts, SparseIntArray pidCounts) { + String innerName = null; + if (mObservers.size() > 0) { + if ("".equals(name)) { + innerName = mName; + } else { + innerName = name + "/" + mName; + } + for (int i=0; i 0) { + if (innerName == null) { + if ("".equals(name)) { + innerName = mName; + } else { + innerName = name + "/" + mName; + } + } + for (int i=0; i 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 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.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, int targetUserHandle, + ArrayList calls) { + String segment = null; + int segmentCount = countUriSegments(uri); + if (index >= segmentCount) { + // This is the leaf node, notify all observers + collectMyObserversLocked(true, observer, observerWantsSelfNotifications, + targetUserHandle, calls); + } else if (index < segmentCount){ + segment = getUriSegment(uri, index); + // Notify any observers at this level who are interested in descendants + collectMyObserversLocked(false, observer, observerWantsSelfNotifications, + targetUserHandle, calls); + } + + int N = mChildren.size(); + for (int i = 0; i < N; i++) { + ObserverNode node = mChildren.get(i); + if (segment == null || node.mName.equals(segment)) { + // We found the child, + node.collectObserversLocked(uri, index + 1, + observer, observerWantsSelfNotifications, targetUserHandle, calls); + if (segment != null) { + break; + } + } + } + } + } +} -- cgit v1.1