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
This commit is contained in:
Kweku Adams
2023-05-11 19:50:05 +00:00
parent 09c9df3f4c
commit 7a03040040
3 changed files with 78 additions and 4 deletions

View File

@@ -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) {

View File

@@ -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());

View File

@@ -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;
}