Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5918429f authored by Tej Singh's avatar Tej Singh
Browse files

Java API for pulled atoms

This creates a java API for registering pullers. Will implement the
statsd side in a follow up CL.

Test: builds, boots
Change-Id: Ib6735984297ce3148839a6370a3c15b2a585baf5
parent 75aa043d
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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",
+113 −3
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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;
@@ -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;
@@ -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"));
@@ -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);
                }
@@ -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) {
+7 −0
Original line number Diff line number Diff line
@@ -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);

+7 −0
Original line number Diff line number Diff line
@@ -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.
     */
+96 −0
Original line number Diff line number Diff line
@@ -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.
@@ -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.
     */
@@ -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 {
@@ -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() {
@@ -481,6 +566,7 @@ public final class StatsManager {
        }
    }

    @GuardedBy("this")
    private IStatsManager getIStatsManagerLocked() throws StatsUnavailableException {
        if (mService != null) {
            return mService;
@@ -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