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

Commit 7a030400 authored by Kweku Adams's avatar Kweku Adams
Browse files

Log namespace hash.

Log the hash of the namespace to reduce privacy concerns while still
being able to distinguish between namespaces.

Bug: 138239687
Test: statsd_testdrive 8
Change-Id: I94d0e927e287339d5e74fa3db2f0e226f2fe3048
parent 09c9df3f
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -1631,7 +1631,8 @@ public class JobSchedulerService extends com.android.server.SystemService
                    jobStatus.getEstimatedNetworkDownloadBytes(),
                    jobStatus.getEstimatedNetworkUploadBytes(),
                    jobStatus.getWorkCount(),
                    ActivityManager.processStateAmToProto(mUidProcStates.get(jobStatus.getUid())));
                    ActivityManager.processStateAmToProto(mUidProcStates.get(jobStatus.getUid())),
                    jobStatus.getNamespaceHash());

            // If the job is immediately ready to run, then we can just immediately
            // put it in the pending list and try to schedule it.  This is especially
@@ -2059,7 +2060,8 @@ public class JobSchedulerService extends com.android.server.SystemService
                    cancelled.getEstimatedNetworkDownloadBytes(),
                    cancelled.getEstimatedNetworkUploadBytes(),
                    cancelled.getWorkCount(),
                    ActivityManager.processStateAmToProto(mUidProcStates.get(cancelled.getUid())));
                    ActivityManager.processStateAmToProto(mUidProcStates.get(cancelled.getUid())),
                    cancelled.getNamespaceHash());
        }
        // If this is a replacement, bring in the new version of the job
        if (incomingJob != null) {
+4 −2
Original line number Diff line number Diff line
@@ -499,7 +499,8 @@ public final class JobServiceContext implements ServiceConnection {
                    job.getEstimatedNetworkDownloadBytes(),
                    job.getEstimatedNetworkUploadBytes(),
                    job.getWorkCount(),
                    ActivityManager.processStateAmToProto(mService.getUidProcState(job.getUid())));
                    ActivityManager.processStateAmToProto(mService.getUidProcState(job.getUid())),
                    job.getNamespaceHash());
            sEnqueuedJwiAtJobStart.logSampleWithUid(job.getUid(), job.getWorkCount());
            final String sourcePackage = job.getSourcePackageName();
            if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
@@ -1557,7 +1558,8 @@ public final class JobServiceContext implements ServiceConnection {
                completedJob.getEstimatedNetworkUploadBytes(),
                completedJob.getWorkCount(),
                ActivityManager
                        .processStateAmToProto(mService.getUidProcState(completedJob.getUid())));
                        .processStateAmToProto(mService.getUidProcState(completedJob.getUid())),
                completedJob.getNamespaceHash());
        if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
            Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_SYSTEM_SERVER, "JobScheduler",
                    getId());
+70 −0
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.MediaStore;
import android.text.format.DateFormat;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import android.util.Pair;
@@ -51,6 +52,7 @@ import android.util.Slog;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
@@ -65,10 +67,12 @@ import com.android.server.job.JobStatusShortInfoProto;
import dalvik.annotation.optimization.NeverCompile;

import java.io.PrintWriter;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Objects;
import java.util.Random;
import java.util.function.Predicate;

/**
@@ -88,6 +92,13 @@ public final class JobStatus {
    private static final String TAG = "JobScheduler.JobStatus";
    static final boolean DEBUG = JobSchedulerService.DEBUG;

    private static MessageDigest sMessageDigest;
    /** Cache of namespace to hash to reduce how often we need to generate the namespace hash. */
    @GuardedBy("sNamespaceHashCache")
    private static final ArrayMap<String, String> sNamespaceHashCache = new ArrayMap<>();
    /** Maximum size of {@link #sNamespaceHashCache}. */
    private static final int MAX_NAMESPACE_CACHE_SIZE = 128;

    private static final int NUM_CONSTRAINT_CHANGE_HISTORY = 10;

    public static final long NO_LATEST_RUNTIME = Long.MAX_VALUE;
@@ -231,6 +242,8 @@ public final class JobStatus {
    final String sourceTag;
    @Nullable
    private final String mNamespace;
    @Nullable
    private final String mNamespaceHash;
    /** An ID that can be used to uniquely identify the job when logging statsd metrics. */
    private final long mLoggingJobId;

@@ -570,6 +583,7 @@ public final class JobStatus {
        this.callingUid = callingUid;
        this.standbyBucket = standbyBucket;
        mNamespace = namespace;
        mNamespaceHash = generateNamespaceHash(namespace);
        mLoggingJobId = generateLoggingId(namespace, job.getId());

        int tempSourceUid = -1;
@@ -814,6 +828,56 @@ public final class JobStatus {
        return ((long) namespace.hashCode()) << 31 | jobId;
    }

    @Nullable
    private static String generateNamespaceHash(@Nullable String namespace) {
        if (namespace == null) {
            return null;
        }
        if (namespace.trim().isEmpty()) {
            // Input is composed of all spaces (or nothing at all).
            return namespace;
        }
        synchronized (sNamespaceHashCache) {
            final int idx = sNamespaceHashCache.indexOfKey(namespace);
            if (idx >= 0) {
                return sNamespaceHashCache.valueAt(idx);
            }
        }
        String hash = null;
        try {
            // .hashCode() can result in conflicts that would make distinguishing between
            // namespaces hard and reduce the accuracy of certain metrics. Use SHA-256
            // to generate the hash since the probability of collision is extremely low.
            if (sMessageDigest == null) {
                sMessageDigest = MessageDigest.getInstance("SHA-256");
            }
            final byte[] digest = sMessageDigest.digest(namespace.getBytes());
            // Convert to hexadecimal representation
            StringBuilder hexBuilder = new StringBuilder(digest.length);
            for (byte byteChar : digest) {
                hexBuilder.append(String.format("%02X", byteChar));
            }
            hash = hexBuilder.toString();
        } catch (Exception e) {
            Slog.wtf(TAG, "Couldn't hash input", e);
        }
        if (hash == null) {
            // If we get to this point, something went wrong with the MessageDigest above.
            // Don't return the raw input value (which would defeat the purpose of hashing).
            return "failed_namespace_hash";
        }
        hash = hash.intern();
        synchronized (sNamespaceHashCache) {
            if (sNamespaceHashCache.size() >= MAX_NAMESPACE_CACHE_SIZE) {
                // Drop a random mapping instead of dropping at a predefined index to avoid
                // potentially always dropping the same mapping.
                sNamespaceHashCache.removeAt((new Random()).nextInt(MAX_NAMESPACE_CACHE_SIZE));
            }
            sNamespaceHashCache.put(namespace, hash);
        }
        return hash;
    }

    public void enqueueWorkLocked(JobWorkItem work) {
        if (pendingWork == null) {
            pendingWork = new ArrayList<>();
@@ -1117,10 +1181,16 @@ public final class JobStatus {
        return true;
    }

    @Nullable
    public String getNamespace() {
        return mNamespace;
    }

    @Nullable
    public String getNamespaceHash() {
        return mNamespaceHash;
    }

    public String getSourceTag() {
        return sourceTag;
    }