Loading Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -405,6 +405,8 @@ filegroup { filegroup { name: "statsd_aidl", srcs: [ "core/java/android/os/IPullAtomCallback.aidl", "core/java/android/os/IPullAtomResultReceiver.aidl", "core/java/android/os/IStatsCompanionService.aidl", "core/java/android/os/IStatsManager.aidl", "core/java/android/os/IStatsPullerCallback.aidl", Loading apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java +113 −3 Original line number Diff line number Diff line Loading @@ -76,6 +76,7 @@ import android.os.FileUtils; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.os.IPullAtomCallback; import android.os.IStatsCompanionService; import android.os.IStatsManager; import android.os.IStoraged; Loading Loading @@ -165,6 +166,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Set; import java.util.UUID; import java.util.concurrent.CompletableFuture; Loading Loading @@ -272,6 +274,72 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { private final BroadcastReceiver mAppUpdateReceiver; private final BroadcastReceiver mUserUpdateReceiver; private final ShutdownEventReceiver mShutdownEventReceiver; private static final class PullerKey { private final int mUid; private final int mAtomTag; PullerKey(int uid, int atom) { mUid = uid; mAtomTag = atom; } public int getUid() { return mUid; } public int getAtom() { return mAtomTag; } @Override public int hashCode() { return Objects.hash(mUid, mAtomTag); } @Override public boolean equals(Object obj) { if (obj instanceof PullerKey) { PullerKey other = (PullerKey) obj; return this.mUid == other.getUid() && this.mAtomTag == other.getAtom(); } return false; } } private static final class PullerValue { private final long mCoolDownNs; private final long mTimeoutNs; private int[] mAdditiveFields; private IPullAtomCallback mCallback; PullerValue(long coolDownNs, long timeoutNs, int[] additiveFields, IPullAtomCallback callback) { mCoolDownNs = coolDownNs; mTimeoutNs = timeoutNs; mAdditiveFields = additiveFields; mCallback = callback; } public long getCoolDownNs() { return mCoolDownNs; } public long getTimeoutNs() { return mTimeoutNs; } public int[] getAdditiveFields() { return mAdditiveFields; } public IPullAtomCallback getCallback() { return mCallback; } } private final HashMap<PullerKey, PullerValue> mPullers = new HashMap<>(); private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader(); private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats(); private IWifiManager mWifiManager = null; Loading Loading @@ -323,7 +391,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { @Override public void onReceive(Context context, Intent intent) { synchronized (sStatsdLock) { sStatsd = fetchStatsdService(); if (sStatsd == null) { Slog.w(TAG, "Could not access statsd for UserUpdateReceiver"); return; Loading Loading @@ -2553,10 +2620,40 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { mContext.enforceCallingPermission(android.Manifest.permission.STATSCOMPANION, null); } @Override public void registerPullAtomCallback(int atomTag, long coolDownNs, long timeoutNs, int[] additiveFields, IPullAtomCallback pullerCallback) { synchronized (sStatsdLock) { // Always cache the puller in SCS. // If statsd is down, we will register it when it comes back up. int callingUid = Binder.getCallingUid(); final long token = Binder.clearCallingIdentity(); PullerKey key = new PullerKey(callingUid, atomTag); PullerValue val = new PullerValue( coolDownNs, timeoutNs, additiveFields, pullerCallback); mPullers.put(key, val); if (sStatsd == null) { Slog.w(TAG, "Could not access statsd for registering puller for atom " + atomTag); return; } try { sStatsd.registerPullAtomCallback( callingUid, atomTag, coolDownNs, timeoutNs, additiveFields, pullerCallback); } catch (RemoteException e) { Slog.e(TAG, "Failed to access statsd to register puller for atom " + atomTag); } finally { Binder.restoreCallingIdentity(token); } } } // Lifecycle and related code /** * Fetches the statsd IBinder service * Fetches the statsd IBinder service. * Note: This should only be called from sayHiToStatsd. All other clients should use the cached * sStatsd with a null check. */ private static IStatsManager fetchStatsdService() { return IStatsManager.Stub.asInterface(ServiceManager.getService("stats")); Loading Loading @@ -2654,6 +2751,8 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { // Pull the latest state of UID->app name, version mapping when // statsd starts. informAllUidsLocked(mContext); // Register all pullers. If SCS has just started, this should be empty. registerAllPullersLocked(); } finally { restoreCallingIdentity(token); } Loading @@ -2665,10 +2764,21 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } } @GuardedBy("sStatsdLock") private void registerAllPullersLocked() throws RemoteException { // TODO: pass in one call, using a file descriptor (similar to uidmap). for (Map.Entry<PullerKey, PullerValue> entry : mPullers.entrySet()) { PullerKey key = entry.getKey(); PullerValue val = entry.getValue(); sStatsd.registerPullAtomCallback(key.getUid(), key.getAtom(), val.getCoolDownNs(), val.getTimeoutNs(), val.getAdditiveFields(), val.getCallback()); } } private class StatsdDeathRecipient implements IBinder.DeathRecipient { @Override public void binderDied() { Slog.i(TAG, "Statsd is dead - erase all my knowledge."); Slog.i(TAG, "Statsd is dead - erase all my knowledge, except pullers"); synchronized (sStatsdLock) { long now = SystemClock.elapsedRealtime(); for (Long timeMillis : mDeathTimeMillis) { Loading cmds/statsd/src/StatsService.cpp +7 −0 Original line number Diff line number Diff line Loading @@ -1289,6 +1289,13 @@ Status StatsService::registerPullerCallback(int32_t atomTag, return Status::ok(); } Status StatsService::registerPullAtomCallback(int32_t uid, int32_t atomTag, int64_t coolDownNs, int64_t timeoutNs, const std::vector<int32_t>& additiveFields, const sp<android::os::IPullAtomCallback>& pullerCallback) { VLOG("StatsService::registerPuller called."); return Status::ok(); } Status StatsService::unregisterPullerCallback(int32_t atomTag, const String16& packageName) { ENFORCE_DUMP_AND_USAGE_STATS(packageName); Loading cmds/statsd/src/StatsService.h +7 −0 Original line number Diff line number Diff line Loading @@ -179,6 +179,13 @@ public: const sp<android::os::IStatsPullerCallback>& pullerCallback, const String16& packageName) override; /** * Binder call to register a callback function for a pulled atom. */ virtual Status registerPullAtomCallback(int32_t uid, int32_t atomTag, int64_t coolDownNs, int64_t timeoutNs, const std::vector<int32_t>& additiveFields, const sp<android::os::IPullAtomCallback>& pullerCallback) override; /** * Binder call to unregister any existing callback function for a vendor pulled atom. */ Loading core/java/android/app/StatsManager.java +96 −0 Original line number Diff line number Diff line Loading @@ -24,12 +24,22 @@ import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.content.Context; import android.os.IBinder; import android.os.IPullAtomCallback; import android.os.IPullAtomResultReceiver; import android.os.IStatsCompanionService; import android.os.IStatsManager; import android.os.IStatsPullerCallback; import android.os.RemoteException; import android.os.ServiceManager; import android.util.AndroidException; import android.util.Slog; import android.util.StatsEvent; import com.android.internal.annotations.GuardedBy; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executor; /** * API for statsd clients to send configurations and retrieve data. Loading @@ -43,8 +53,12 @@ public final class StatsManager { private final Context mContext; @GuardedBy("this") private IStatsManager mService; @GuardedBy("this") private IStatsCompanionService mStatsCompanion; /** * Long extra of uid that added the relevant stats config. */ Loading Loading @@ -449,7 +463,9 @@ public final class StatsManager { * @throws StatsUnavailableException if unsuccessful due to failing to connect to stats service * * @hide * @deprecated Please use registerPullAtomCallback */ @Deprecated @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS }) public void setPullerCallback(int atomTag, IStatsPullerCallback callback) throws StatsUnavailableException { Loading @@ -472,6 +488,75 @@ public final class StatsManager { } } /** * Registers a callback for an atom when that atom is to be pulled. The stats service will * invoke pullData in the callback when the stats service determines that this atom needs to be * pulled. * * @param atomTag The tag of the atom for this puller callback. * @param coolDownNs The minimum time between successive pulls. A cache of the previous * pull will be used if the time between pulls is less than coolDownNs. * @param timeoutNs The maximum time a pull should take. Statsd will wait timeoutNs for * the pull to complete before timing out and marking the pull as * failed. * @param additiveFields Fields that are added when mapping isolated uids to host uids. * @param callback The callback to be invoked when the stats service pulls the atom. * @throws RemoteException if unsuccessful due to failing to connect to system server. * * @hide */ public void registerPullAtomCallback(int atomTag, long coolDownNs, long timeoutNs, int[] additiveFields, @NonNull StatsPullAtomCallback callback, @NonNull Executor executor) throws RemoteException, SecurityException { synchronized (this) { IStatsCompanionService service = getIStatsCompanionServiceLocked(); PullAtomCallbackInternal rec = new PullAtomCallbackInternal(atomTag, callback, executor); service.registerPullAtomCallback(atomTag, coolDownNs, timeoutNs, additiveFields, rec); } } private static class PullAtomCallbackInternal extends IPullAtomCallback.Stub { public final int mAtomId; public final StatsPullAtomCallback mCallback; public final Executor mExecutor; PullAtomCallbackInternal(int atomId, StatsPullAtomCallback callback, Executor executor) { mAtomId = atomId; mCallback = callback; mExecutor = executor; } @Override public void onPullAtom(int atomTag, IPullAtomResultReceiver resultReceiver) { mExecutor.execute(() -> { List<StatsEvent> data = new ArrayList<>(); boolean success = mCallback.onPullAtom(atomTag, data); StatsEvent[] arr = new StatsEvent[data.size()]; arr = data.toArray(arr); try { resultReceiver.pullFinished(atomTag, success, arr); } catch (RemoteException e) { Slog.w(TAG, "StatsPullResultReceiver failed for tag " + mAtomId); } }); } } /** * Callback interface for pulling atoms requested by the stats service. * * @hide */ public interface StatsPullAtomCallback { /** * Pull data for the specified atom tag, filling in the provided list of StatsEvent data. * @return if the pull was successful */ boolean onPullAtom(int atomTag, List<StatsEvent> data); } private class StatsdDeathRecipient implements IBinder.DeathRecipient { @Override public void binderDied() { Loading @@ -481,6 +566,7 @@ public final class StatsManager { } } @GuardedBy("this") private IStatsManager getIStatsManagerLocked() throws StatsUnavailableException { if (mService != null) { return mService; Loading @@ -497,6 +583,16 @@ public final class StatsManager { return mService; } @GuardedBy("this") private IStatsCompanionService getIStatsCompanionServiceLocked() { if (mStatsCompanion != null) { return mStatsCompanion; } mStatsCompanion = IStatsCompanionService.Stub.asInterface( ServiceManager.getService("statscompanion")); return mStatsCompanion; } /** * Exception thrown when communication with the stats service fails (eg if it is not available). * This might be thrown early during boot before the stats service has started or if it crashed. Loading Loading
Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -405,6 +405,8 @@ filegroup { filegroup { name: "statsd_aidl", srcs: [ "core/java/android/os/IPullAtomCallback.aidl", "core/java/android/os/IPullAtomResultReceiver.aidl", "core/java/android/os/IStatsCompanionService.aidl", "core/java/android/os/IStatsManager.aidl", "core/java/android/os/IStatsPullerCallback.aidl", Loading
apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java +113 −3 Original line number Diff line number Diff line Loading @@ -76,6 +76,7 @@ import android.os.FileUtils; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.os.IPullAtomCallback; import android.os.IStatsCompanionService; import android.os.IStatsManager; import android.os.IStoraged; Loading Loading @@ -165,6 +166,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Set; import java.util.UUID; import java.util.concurrent.CompletableFuture; Loading Loading @@ -272,6 +274,72 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { private final BroadcastReceiver mAppUpdateReceiver; private final BroadcastReceiver mUserUpdateReceiver; private final ShutdownEventReceiver mShutdownEventReceiver; private static final class PullerKey { private final int mUid; private final int mAtomTag; PullerKey(int uid, int atom) { mUid = uid; mAtomTag = atom; } public int getUid() { return mUid; } public int getAtom() { return mAtomTag; } @Override public int hashCode() { return Objects.hash(mUid, mAtomTag); } @Override public boolean equals(Object obj) { if (obj instanceof PullerKey) { PullerKey other = (PullerKey) obj; return this.mUid == other.getUid() && this.mAtomTag == other.getAtom(); } return false; } } private static final class PullerValue { private final long mCoolDownNs; private final long mTimeoutNs; private int[] mAdditiveFields; private IPullAtomCallback mCallback; PullerValue(long coolDownNs, long timeoutNs, int[] additiveFields, IPullAtomCallback callback) { mCoolDownNs = coolDownNs; mTimeoutNs = timeoutNs; mAdditiveFields = additiveFields; mCallback = callback; } public long getCoolDownNs() { return mCoolDownNs; } public long getTimeoutNs() { return mTimeoutNs; } public int[] getAdditiveFields() { return mAdditiveFields; } public IPullAtomCallback getCallback() { return mCallback; } } private final HashMap<PullerKey, PullerValue> mPullers = new HashMap<>(); private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader(); private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats(); private IWifiManager mWifiManager = null; Loading Loading @@ -323,7 +391,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { @Override public void onReceive(Context context, Intent intent) { synchronized (sStatsdLock) { sStatsd = fetchStatsdService(); if (sStatsd == null) { Slog.w(TAG, "Could not access statsd for UserUpdateReceiver"); return; Loading Loading @@ -2553,10 +2620,40 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { mContext.enforceCallingPermission(android.Manifest.permission.STATSCOMPANION, null); } @Override public void registerPullAtomCallback(int atomTag, long coolDownNs, long timeoutNs, int[] additiveFields, IPullAtomCallback pullerCallback) { synchronized (sStatsdLock) { // Always cache the puller in SCS. // If statsd is down, we will register it when it comes back up. int callingUid = Binder.getCallingUid(); final long token = Binder.clearCallingIdentity(); PullerKey key = new PullerKey(callingUid, atomTag); PullerValue val = new PullerValue( coolDownNs, timeoutNs, additiveFields, pullerCallback); mPullers.put(key, val); if (sStatsd == null) { Slog.w(TAG, "Could not access statsd for registering puller for atom " + atomTag); return; } try { sStatsd.registerPullAtomCallback( callingUid, atomTag, coolDownNs, timeoutNs, additiveFields, pullerCallback); } catch (RemoteException e) { Slog.e(TAG, "Failed to access statsd to register puller for atom " + atomTag); } finally { Binder.restoreCallingIdentity(token); } } } // Lifecycle and related code /** * Fetches the statsd IBinder service * Fetches the statsd IBinder service. * Note: This should only be called from sayHiToStatsd. All other clients should use the cached * sStatsd with a null check. */ private static IStatsManager fetchStatsdService() { return IStatsManager.Stub.asInterface(ServiceManager.getService("stats")); Loading Loading @@ -2654,6 +2751,8 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { // Pull the latest state of UID->app name, version mapping when // statsd starts. informAllUidsLocked(mContext); // Register all pullers. If SCS has just started, this should be empty. registerAllPullersLocked(); } finally { restoreCallingIdentity(token); } Loading @@ -2665,10 +2764,21 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } } @GuardedBy("sStatsdLock") private void registerAllPullersLocked() throws RemoteException { // TODO: pass in one call, using a file descriptor (similar to uidmap). for (Map.Entry<PullerKey, PullerValue> entry : mPullers.entrySet()) { PullerKey key = entry.getKey(); PullerValue val = entry.getValue(); sStatsd.registerPullAtomCallback(key.getUid(), key.getAtom(), val.getCoolDownNs(), val.getTimeoutNs(), val.getAdditiveFields(), val.getCallback()); } } private class StatsdDeathRecipient implements IBinder.DeathRecipient { @Override public void binderDied() { Slog.i(TAG, "Statsd is dead - erase all my knowledge."); Slog.i(TAG, "Statsd is dead - erase all my knowledge, except pullers"); synchronized (sStatsdLock) { long now = SystemClock.elapsedRealtime(); for (Long timeMillis : mDeathTimeMillis) { Loading
cmds/statsd/src/StatsService.cpp +7 −0 Original line number Diff line number Diff line Loading @@ -1289,6 +1289,13 @@ Status StatsService::registerPullerCallback(int32_t atomTag, return Status::ok(); } Status StatsService::registerPullAtomCallback(int32_t uid, int32_t atomTag, int64_t coolDownNs, int64_t timeoutNs, const std::vector<int32_t>& additiveFields, const sp<android::os::IPullAtomCallback>& pullerCallback) { VLOG("StatsService::registerPuller called."); return Status::ok(); } Status StatsService::unregisterPullerCallback(int32_t atomTag, const String16& packageName) { ENFORCE_DUMP_AND_USAGE_STATS(packageName); Loading
cmds/statsd/src/StatsService.h +7 −0 Original line number Diff line number Diff line Loading @@ -179,6 +179,13 @@ public: const sp<android::os::IStatsPullerCallback>& pullerCallback, const String16& packageName) override; /** * Binder call to register a callback function for a pulled atom. */ virtual Status registerPullAtomCallback(int32_t uid, int32_t atomTag, int64_t coolDownNs, int64_t timeoutNs, const std::vector<int32_t>& additiveFields, const sp<android::os::IPullAtomCallback>& pullerCallback) override; /** * Binder call to unregister any existing callback function for a vendor pulled atom. */ Loading
core/java/android/app/StatsManager.java +96 −0 Original line number Diff line number Diff line Loading @@ -24,12 +24,22 @@ import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.content.Context; import android.os.IBinder; import android.os.IPullAtomCallback; import android.os.IPullAtomResultReceiver; import android.os.IStatsCompanionService; import android.os.IStatsManager; import android.os.IStatsPullerCallback; import android.os.RemoteException; import android.os.ServiceManager; import android.util.AndroidException; import android.util.Slog; import android.util.StatsEvent; import com.android.internal.annotations.GuardedBy; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executor; /** * API for statsd clients to send configurations and retrieve data. Loading @@ -43,8 +53,12 @@ public final class StatsManager { private final Context mContext; @GuardedBy("this") private IStatsManager mService; @GuardedBy("this") private IStatsCompanionService mStatsCompanion; /** * Long extra of uid that added the relevant stats config. */ Loading Loading @@ -449,7 +463,9 @@ public final class StatsManager { * @throws StatsUnavailableException if unsuccessful due to failing to connect to stats service * * @hide * @deprecated Please use registerPullAtomCallback */ @Deprecated @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS }) public void setPullerCallback(int atomTag, IStatsPullerCallback callback) throws StatsUnavailableException { Loading @@ -472,6 +488,75 @@ public final class StatsManager { } } /** * Registers a callback for an atom when that atom is to be pulled. The stats service will * invoke pullData in the callback when the stats service determines that this atom needs to be * pulled. * * @param atomTag The tag of the atom for this puller callback. * @param coolDownNs The minimum time between successive pulls. A cache of the previous * pull will be used if the time between pulls is less than coolDownNs. * @param timeoutNs The maximum time a pull should take. Statsd will wait timeoutNs for * the pull to complete before timing out and marking the pull as * failed. * @param additiveFields Fields that are added when mapping isolated uids to host uids. * @param callback The callback to be invoked when the stats service pulls the atom. * @throws RemoteException if unsuccessful due to failing to connect to system server. * * @hide */ public void registerPullAtomCallback(int atomTag, long coolDownNs, long timeoutNs, int[] additiveFields, @NonNull StatsPullAtomCallback callback, @NonNull Executor executor) throws RemoteException, SecurityException { synchronized (this) { IStatsCompanionService service = getIStatsCompanionServiceLocked(); PullAtomCallbackInternal rec = new PullAtomCallbackInternal(atomTag, callback, executor); service.registerPullAtomCallback(atomTag, coolDownNs, timeoutNs, additiveFields, rec); } } private static class PullAtomCallbackInternal extends IPullAtomCallback.Stub { public final int mAtomId; public final StatsPullAtomCallback mCallback; public final Executor mExecutor; PullAtomCallbackInternal(int atomId, StatsPullAtomCallback callback, Executor executor) { mAtomId = atomId; mCallback = callback; mExecutor = executor; } @Override public void onPullAtom(int atomTag, IPullAtomResultReceiver resultReceiver) { mExecutor.execute(() -> { List<StatsEvent> data = new ArrayList<>(); boolean success = mCallback.onPullAtom(atomTag, data); StatsEvent[] arr = new StatsEvent[data.size()]; arr = data.toArray(arr); try { resultReceiver.pullFinished(atomTag, success, arr); } catch (RemoteException e) { Slog.w(TAG, "StatsPullResultReceiver failed for tag " + mAtomId); } }); } } /** * Callback interface for pulling atoms requested by the stats service. * * @hide */ public interface StatsPullAtomCallback { /** * Pull data for the specified atom tag, filling in the provided list of StatsEvent data. * @return if the pull was successful */ boolean onPullAtom(int atomTag, List<StatsEvent> data); } private class StatsdDeathRecipient implements IBinder.DeathRecipient { @Override public void binderDied() { Loading @@ -481,6 +566,7 @@ public final class StatsManager { } } @GuardedBy("this") private IStatsManager getIStatsManagerLocked() throws StatsUnavailableException { if (mService != null) { return mService; Loading @@ -497,6 +583,16 @@ public final class StatsManager { return mService; } @GuardedBy("this") private IStatsCompanionService getIStatsCompanionServiceLocked() { if (mStatsCompanion != null) { return mStatsCompanion; } mStatsCompanion = IStatsCompanionService.Stub.asInterface( ServiceManager.getService("statscompanion")); return mStatsCompanion; } /** * Exception thrown when communication with the stats service fails (eg if it is not available). * This might be thrown early during boot before the stats service has started or if it crashed. Loading