blob: 68ff0229d5c56a4acb91b11b502fb682b7218071 [file] [log] [blame]
/*
* Copyright (C) 2020 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.stats.pull;
import static android.app.AppOpsManager.OP_FLAG_SELF;
import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXIED;
import static android.app.usage.NetworkStatsManager.FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN;
import static android.app.usage.NetworkStatsManager.FLAG_POLL_FORCE;
import static android.app.usage.NetworkStatsManager.FLAG_POLL_ON_OPEN;
import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.NetworkTemplate.NETWORK_TYPE_ALL;
import static android.net.NetworkTemplate.buildTemplateMobileWildcard;
import static android.net.NetworkTemplate.buildTemplateMobileWithRatType;
import static android.net.NetworkTemplate.buildTemplateWifiWildcard;
import static android.net.NetworkTemplate.getAllCollapsedRatTypes;
import static android.os.Debug.getIonHeapsSizeKb;
import static android.os.Process.LAST_SHARED_APPLICATION_GID;
import static android.os.Process.getUidForPid;
import static android.os.storage.VolumeInfo.TYPE_PRIVATE;
import static android.os.storage.VolumeInfo.TYPE_PUBLIC;
import static android.provider.Settings.Global.NETSTATS_UID_BUCKET_DURATION;
import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID;
import static android.util.MathUtils.constrain;
import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_IS_UID;
import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_TRUNCATE_TIMESTAMP;
import static com.android.internal.util.FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER__OPPORTUNISTIC_DATA_SUB__NOT_OPPORTUNISTIC;
import static com.android.internal.util.FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER__OPPORTUNISTIC_DATA_SUB__OPPORTUNISTIC;
import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
import static com.android.server.stats.pull.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs;
import static com.android.server.stats.pull.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs;
import static com.android.server.stats.pull.ProcfsMemoryUtil.getProcessCmdlines;
import static com.android.server.stats.pull.ProcfsMemoryUtil.readCmdlineFromProcfs;
import static com.android.server.stats.pull.ProcfsMemoryUtil.readMemorySnapshotFromProcfs;
import static java.lang.Math.min;
import static java.util.concurrent.TimeUnit.HOURS;
import static java.util.concurrent.TimeUnit.MICROSECONDS;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
import android.app.AppOpsManager.HistoricalOp;
import android.app.AppOpsManager.HistoricalOps;
import android.app.AppOpsManager.HistoricalOpsRequest;
import android.app.AppOpsManager.HistoricalPackageOps;
import android.app.AppOpsManager.HistoricalUidOps;
import android.app.INotificationManager;
import android.app.ProcessMemoryState;
import android.app.RuntimeAppOpAccessMessage;
import android.app.StatsManager;
import android.app.StatsManager.PullAtomMetadata;
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.UidTraffic;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PermissionInfo;
import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricFaceConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.face.FaceManager;
import android.hardware.face.FaceManager.GetFeatureCallback;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.health.V2_0.IHealth;
import android.net.ConnectivityManager;
import android.net.INetworkStatsService;
import android.net.INetworkStatsSession;
import android.net.Network;
import android.net.NetworkRequest;
import android.net.NetworkStats;
import android.net.NetworkTemplate;
import android.net.wifi.WifiManager;
import android.os.AsyncTask;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.CoolingDevice;
import android.os.Environment;
import android.os.IStoraged;
import android.os.IThermalEventListener;
import android.os.IThermalService;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.StatFs;
import android.os.SynchronousResultReceiver;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.Temperature;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.connectivity.WifiActivityEnergyInfo;
import android.os.storage.DiskInfo;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.stats.storage.StorageEnums;
import android.telephony.ModemActivityInfo;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.util.StatsEvent;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.procstats.IProcessStats;
import com.android.internal.app.procstats.ProcessStats;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatteryStatsHelper;
import com.android.internal.os.BinderCallsStats.ExportedCallStat;
import com.android.internal.os.KernelCpuSpeedReader;
import com.android.internal.os.KernelCpuThreadReader;
import com.android.internal.os.KernelCpuThreadReaderDiff;
import com.android.internal.os.KernelCpuThreadReaderSettingsObserver;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader;
import com.android.internal.os.KernelWakelockReader;
import com.android.internal.os.KernelWakelockStats;
import com.android.internal.os.LooperStats;
import com.android.internal.os.PowerProfile;
import com.android.internal.os.ProcessCpuTracker;
import com.android.internal.os.StoragedUidIoStatsReader;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.BatteryService;
import com.android.server.BinderCallsStatsService;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.SystemServiceManager;
import com.android.server.am.MemoryStatUtil.MemoryStat;
import com.android.server.notification.NotificationManagerService;
import com.android.server.role.RoleManagerInternal;
import com.android.server.stats.pull.IonMemoryUtil.IonAllocations;
import com.android.server.stats.pull.ProcfsMemoryUtil.MemorySnapshot;
import com.android.server.stats.pull.netstats.NetworkStatsExt;
import com.android.server.stats.pull.netstats.SubInfo;
import com.android.server.storage.DiskStatsFileLogger;
import com.android.server.storage.DiskStatsLoggingService;
import libcore.io.IoUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.Random;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.BiConsumer;
/**
* SystemService containing PullAtomCallbacks that are registered with statsd.
*
* @hide
*/
public class StatsPullAtomService extends SystemService {
private static final String TAG = "StatsPullAtomService";
private static final boolean DEBUG = true;
// Random seed stable for StatsPullAtomService life cycle - can be used for stable sampling
private static final int RANDOM_SEED = new Random().nextInt();
private static final int DIMENSION_KEY_SIZE_HARD_LIMIT = 800;
private static final int DIMENSION_KEY_SIZE_SOFT_LIMIT = 500;
private static final long APP_OPS_SAMPLING_INITIALIZATION_DELAY_MILLIS = 45000;
private static final int APP_OPS_SIZE_ESTIMATE = 2000;
private static final String RESULT_RECEIVER_CONTROLLER_KEY = "controller_activity";
/**
* How long to wait on an individual subsystem to return its stats.
*/
private static final long EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS = 2000;
private static final long MILLIS_PER_SEC = 1000;
private static final long MILLI_AMP_HR_TO_NANO_AMP_SECS = 1_000_000L * 3600L;
/**
* The default bucket duration used when query a snapshot from NetworkStatsService.
* The value should be sync with NetworkStatsService#DefaultNetworkStatsSettings#getUidConfig.
*/
private static final long NETSTATS_UID_DEFAULT_BUCKET_DURATION_MS = HOURS.toMillis(2);
private static final int MAX_BATTERY_STATS_HELPER_FREQUENCY_MS = 1000;
private static final int CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES = 8;
private static final int OP_FLAGS_PULLED = OP_FLAG_SELF | OP_FLAG_TRUSTED_PROXIED;
private static final String COMMON_PERMISSION_PREFIX = "android.permission.";
private static final String APP_OPS_TARGET_COLLECTION_SIZE = "app_ops_target_collection_size";
private static final String DANGEROUS_PERMISSION_STATE_SAMPLE_RATE =
"dangerous_permission_state_sample_rate";
/** Parameters relating to ProcStats data upload. */
// Maximum shards to use when generating StatsEvent objects from ProcStats.
private static final int MAX_PROCSTATS_SHARDS = 5;
// Should match MAX_PAYLOAD_SIZE in StatsEvent, minus a small amount for overhead/metadata.
private static final int MAX_PROCSTATS_SHARD_SIZE = 48 * 1024; // 48 KB
// In ProcessStats, we measure the size of a raw ProtoOutputStream, before compaction. This
// typically runs 35-45% larger than the compacted size that will be written to StatsEvent.
// Hence, we can allow a little more room in each shard before moving to the next. Make this
// 20% as a conservative estimate.
private static final int MAX_PROCSTATS_RAW_SHARD_SIZE = (int) (MAX_PROCSTATS_SHARD_SIZE * 1.20);
/**
* Threshold to filter out small CPU times at frequency per UID. Those small values appear
* because of more precise accounting in a BPF program. Discarding them reduces the data by at
* least 20% with negligible error.
*/
private static final int MIN_CPU_TIME_PER_UID_FREQ = 10;
private final Object mThermalLock = new Object();
@GuardedBy("mThermalLock")
private IThermalService mThermalService;
private final Object mStoragedLock = new Object();
@GuardedBy("mStoragedLock")
private IStoraged mStorageService;
private final Object mNotificationStatsLock = new Object();
@GuardedBy("mNotificationStatsLock")
private INotificationManager mNotificationManagerService;
@GuardedBy("mProcStatsLock")
private IProcessStats mProcessStatsService;
@GuardedBy("mProcessCpuTimeLock")
private ProcessCpuTracker mProcessCpuTracker;
@GuardedBy("mDebugElapsedClockLock")
private long mDebugElapsedClockPreviousValue = 0;
@GuardedBy("mDebugElapsedClockLock")
private long mDebugElapsedClockPullCount = 0;
@GuardedBy("mDebugFailingElapsedClockLock")
private long mDebugFailingElapsedClockPreviousValue = 0;
@GuardedBy("mDebugFailingElapsedClockLock")
private long mDebugFailingElapsedClockPullCount = 0;
private final Context mContext;
private StatsManager mStatsManager;
private StorageManager mStorageManager;
private WifiManager mWifiManager;
private TelephonyManager mTelephony;
private SubscriptionManager mSubscriptionManager;
@GuardedBy("mKernelWakelockLock")
private KernelWakelockReader mKernelWakelockReader;
@GuardedBy("mKernelWakelockLock")
private KernelWakelockStats mTmpWakelockStats;
@GuardedBy("mDiskIoLock")
private StoragedUidIoStatsReader mStoragedUidIoStatsReader;
@GuardedBy("mCpuTimePerFreqLock")
private KernelCpuSpeedReader[] mKernelCpuSpeedReaders;
// Disables throttler on CPU time readers.
@GuardedBy("mCpuTimePerUidLock")
private KernelCpuUidUserSysTimeReader mCpuUidUserSysTimeReader;
@GuardedBy("mCpuTimePerUidFreqLock")
private KernelCpuUidFreqTimeReader mCpuUidFreqTimeReader;
@GuardedBy("mCpuActiveTimeLock")
private KernelCpuUidActiveTimeReader mCpuUidActiveTimeReader;
@GuardedBy("mClusterTimeLock")
private KernelCpuUidClusterTimeReader mCpuUidClusterTimeReader;
@GuardedBy("mProcStatsLock")
private File mBaseDir;
@GuardedBy("mHealthHalLock")
private BatteryService.HealthServiceWrapper mHealthService;
@Nullable
@GuardedBy("mCpuTimePerThreadFreqLock")
private KernelCpuThreadReaderDiff mKernelCpuThreadReader;
private final Object mBatteryStatsHelperLock = new Object();
@GuardedBy("mBatteryStatsHelperLock")
private BatteryStatsHelper mBatteryStatsHelper = null;
@GuardedBy("mBatteryStatsHelperLock")
private long mBatteryStatsHelperTimestampMs = -MAX_BATTERY_STATS_HELPER_FREQUENCY_MS;
private StatsPullAtomCallbackImpl mStatsCallbackImpl;
@GuardedBy("mAttributedAppOpsLock")
private int mAppOpsSamplingRate = 0;
private final Object mDangerousAppOpsListLock = new Object();
@GuardedBy("mDangerousAppOpsListLock")
private final ArraySet<Integer> mDangerousAppOpsList = new ArraySet<>();
// Baselines that stores list of NetworkStats right after initializing, with associated
// information. This is used to calculate difference when pulling BytesTransfer atoms.
@NonNull
@GuardedBy("mDataBytesTransferLock")
private final ArrayList<NetworkStatsExt> mNetworkStatsBaselines = new ArrayList<>();
// Listener for monitoring subscriptions changed event.
private StatsSubscriptionsListener mStatsSubscriptionsListener;
// List that stores SubInfo of subscriptions that ever appeared since boot.
@GuardedBy("mDataBytesTransferLock")
private final ArrayList<SubInfo> mHistoricalSubs = new ArrayList<>();
// Puller locks
private final Object mDataBytesTransferLock = new Object();
private final Object mBluetoothBytesTransferLock = new Object();
private final Object mKernelWakelockLock = new Object();
private final Object mCpuTimePerFreqLock = new Object();
private final Object mCpuTimePerUidLock = new Object();
private final Object mCpuTimePerUidFreqLock = new Object();
private final Object mCpuActiveTimeLock = new Object();
private final Object mCpuClusterTimeLock = new Object();
private final Object mWifiActivityInfoLock = new Object();
private final Object mModemActivityInfoLock = new Object();
private final Object mBluetoothActivityInfoLock = new Object();
private final Object mSystemElapsedRealtimeLock = new Object();
private final Object mSystemUptimeLock = new Object();
private final Object mProcessMemoryStateLock = new Object();
private final Object mProcessMemoryHighWaterMarkLock = new Object();
private final Object mProcessMemorySnapshotLock = new Object();
private final Object mSystemIonHeapSizeLock = new Object();
private final Object mIonHeapSizeLock = new Object();
private final Object mProcessSystemIonHeapSizeLock = new Object();
private final Object mTemperatureLock = new Object();
private final Object mCooldownDeviceLock = new Object();
private final Object mBinderCallsStatsLock = new Object();
private final Object mBinderCallsStatsExceptionsLock = new Object();
private final Object mLooperStatsLock = new Object();
private final Object mDiskStatsLock = new Object();
private final Object mDirectoryUsageLock = new Object();
private final Object mAppSizeLock = new Object();
private final Object mCategorySizeLock = new Object();
private final Object mNumBiometricsEnrolledLock = new Object();
private final Object mProcStatsLock = new Object();
private final Object mDiskIoLock = new Object();
private final Object mPowerProfileLock = new Object();
private final Object mProcessCpuTimeLock = new Object();
private final Object mCpuTimePerThreadFreqLock = new Object();
private final Object mDeviceCalculatedPowerUseLock = new Object();
private final Object mDeviceCalculatedPowerBlameUidLock = new Object();
private final Object mDeviceCalculatedPowerBlameOtherLock = new Object();
private final Object mDebugElapsedClockLock = new Object();
private final Object mDebugFailingElapsedClockLock = new Object();
private final Object mBuildInformationLock = new Object();
private final Object mRoleHolderLock = new Object();
private final Object mTimeZoneDataInfoLock = new Object();
private final Object mExternalStorageInfoLock = new Object();
private final Object mAppsOnExternalStorageInfoLock = new Object();
private final Object mFaceSettingsLock = new Object();
private final Object mAppOpsLock = new Object();
private final Object mRuntimeAppOpAccessMessageLock = new Object();
private final Object mNotificationRemoteViewsLock = new Object();
private final Object mDangerousPermissionStateLock = new Object();
private final Object mHealthHalLock = new Object();
private final Object mAttributedAppOpsLock = new Object();
private final Object mSettingsStatsLock = new Object();
public StatsPullAtomService(Context context) {
super(context);
mContext = context;
}
private native void nativeInit();
/**
* Use of this StatsPullAtomCallbackImpl means we avoid one class per tagId, which we would
* get if we used lambdas.
*
* The pull methods are intentionally left to be package private to avoid the creation
* of synthetic methods to save unnecessary bytecode.
*/
private class StatsPullAtomCallbackImpl implements StatsManager.StatsPullAtomCallback {
@Override
public int onPullAtom(int atomTag, List<StatsEvent> data) {
if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StatsPull-" + atomTag);
}
try {
switch (atomTag) {
case FrameworkStatsLog.WIFI_BYTES_TRANSFER:
case FrameworkStatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG:
case FrameworkStatsLog.MOBILE_BYTES_TRANSFER:
case FrameworkStatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG:
case FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED:
case FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER:
synchronized (mDataBytesTransferLock) {
return pullDataBytesTransferLocked(atomTag, data);
}
case FrameworkStatsLog.BLUETOOTH_BYTES_TRANSFER:
synchronized (mBluetoothBytesTransferLock) {
return pullBluetoothBytesTransferLocked(atomTag, data);
}
case FrameworkStatsLog.KERNEL_WAKELOCK:
synchronized (mKernelWakelockLock) {
return pullKernelWakelockLocked(atomTag, data);
}
case FrameworkStatsLog.CPU_TIME_PER_FREQ:
synchronized (mCpuTimePerFreqLock) {
return pullCpuTimePerFreqLocked(atomTag, data);
}
case FrameworkStatsLog.CPU_TIME_PER_UID:
synchronized (mCpuTimePerUidLock) {
return pullCpuTimePerUidLocked(atomTag, data);
}
case FrameworkStatsLog.CPU_TIME_PER_UID_FREQ:
synchronized (mCpuTimePerUidFreqLock) {
return pullCpuTimePerUidFreqLocked(atomTag, data);
}
case FrameworkStatsLog.CPU_ACTIVE_TIME:
synchronized (mCpuActiveTimeLock) {
return pullCpuActiveTimeLocked(atomTag, data);
}
case FrameworkStatsLog.CPU_CLUSTER_TIME:
synchronized (mCpuClusterTimeLock) {
return pullCpuClusterTimeLocked(atomTag, data);
}
case FrameworkStatsLog.WIFI_ACTIVITY_INFO:
synchronized (mWifiActivityInfoLock) {
return pullWifiActivityInfoLocked(atomTag, data);
}
case FrameworkStatsLog.MODEM_ACTIVITY_INFO:
synchronized (mModemActivityInfoLock) {
return pullModemActivityInfoLocked(atomTag, data);
}
case FrameworkStatsLog.BLUETOOTH_ACTIVITY_INFO:
synchronized (mBluetoothActivityInfoLock) {
return pullBluetoothActivityInfoLocked(atomTag, data);
}
case FrameworkStatsLog.SYSTEM_ELAPSED_REALTIME:
synchronized (mSystemElapsedRealtimeLock) {
return pullSystemElapsedRealtimeLocked(atomTag, data);
}
case FrameworkStatsLog.SYSTEM_UPTIME:
synchronized (mSystemUptimeLock) {
return pullSystemUptimeLocked(atomTag, data);
}
case FrameworkStatsLog.PROCESS_MEMORY_STATE:
synchronized (mProcessMemoryStateLock) {
return pullProcessMemoryStateLocked(atomTag, data);
}
case FrameworkStatsLog.PROCESS_MEMORY_HIGH_WATER_MARK:
synchronized (mProcessMemoryHighWaterMarkLock) {
return pullProcessMemoryHighWaterMarkLocked(atomTag, data);
}
case FrameworkStatsLog.PROCESS_MEMORY_SNAPSHOT:
synchronized (mProcessMemorySnapshotLock) {
return pullProcessMemorySnapshotLocked(atomTag, data);
}
case FrameworkStatsLog.SYSTEM_ION_HEAP_SIZE:
synchronized (mSystemIonHeapSizeLock) {
return pullSystemIonHeapSizeLocked(atomTag, data);
}
case FrameworkStatsLog.ION_HEAP_SIZE:
synchronized (mIonHeapSizeLock) {
return pullIonHeapSizeLocked(atomTag, data);
}
case FrameworkStatsLog.PROCESS_SYSTEM_ION_HEAP_SIZE:
synchronized (mProcessSystemIonHeapSizeLock) {
return pullProcessSystemIonHeapSizeLocked(atomTag, data);
}
case FrameworkStatsLog.TEMPERATURE:
synchronized (mTemperatureLock) {
return pullTemperatureLocked(atomTag, data);
}
case FrameworkStatsLog.COOLING_DEVICE:
synchronized (mCooldownDeviceLock) {
return pullCooldownDeviceLocked(atomTag, data);
}
case FrameworkStatsLog.BINDER_CALLS:
synchronized (mBinderCallsStatsLock) {
return pullBinderCallsStatsLocked(atomTag, data);
}
case FrameworkStatsLog.BINDER_CALLS_EXCEPTIONS:
synchronized (mBinderCallsStatsExceptionsLock) {
return pullBinderCallsStatsExceptionsLocked(atomTag, data);
}
case FrameworkStatsLog.LOOPER_STATS:
synchronized (mLooperStatsLock) {
return pullLooperStatsLocked(atomTag, data);
}
case FrameworkStatsLog.DISK_STATS:
synchronized (mDiskStatsLock) {
return pullDiskStatsLocked(atomTag, data);
}
case FrameworkStatsLog.DIRECTORY_USAGE:
synchronized (mDirectoryUsageLock) {
return pullDirectoryUsageLocked(atomTag, data);
}
case FrameworkStatsLog.APP_SIZE:
synchronized (mAppSizeLock) {
return pullAppSizeLocked(atomTag, data);
}
case FrameworkStatsLog.CATEGORY_SIZE:
synchronized (mCategorySizeLock) {
return pullCategorySizeLocked(atomTag, data);
}
case FrameworkStatsLog.NUM_FINGERPRINTS_ENROLLED:
synchronized (mNumBiometricsEnrolledLock) {
return pullNumBiometricsEnrolledLocked(
BiometricsProtoEnums.MODALITY_FINGERPRINT, atomTag, data);
}
case FrameworkStatsLog.NUM_FACES_ENROLLED:
synchronized (mNumBiometricsEnrolledLock) {
return pullNumBiometricsEnrolledLocked(
BiometricsProtoEnums.MODALITY_FACE, atomTag, data);
}
case FrameworkStatsLog.PROC_STATS:
synchronized (mProcStatsLock) {
return pullProcStatsLocked(ProcessStats.REPORT_ALL, atomTag, data);
}
case FrameworkStatsLog.PROC_STATS_PKG_PROC:
synchronized (mProcStatsLock) {
return pullProcStatsLocked(ProcessStats.REPORT_PKG_PROC_STATS, atomTag,
data);
}
case FrameworkStatsLog.DISK_IO:
synchronized (mDiskIoLock) {
return pullDiskIOLocked(atomTag, data);
}
case FrameworkStatsLog.POWER_PROFILE:
synchronized (mPowerProfileLock) {
return pullPowerProfileLocked(atomTag, data);
}
case FrameworkStatsLog.PROCESS_CPU_TIME:
synchronized (mProcessCpuTimeLock) {
return pullProcessCpuTimeLocked(atomTag, data);
}
case FrameworkStatsLog.CPU_TIME_PER_THREAD_FREQ:
synchronized (mCpuTimePerThreadFreqLock) {
return pullCpuTimePerThreadFreqLocked(atomTag, data);
}
case FrameworkStatsLog.DEVICE_CALCULATED_POWER_USE:
synchronized (mDeviceCalculatedPowerUseLock) {
return pullDeviceCalculatedPowerUseLocked(atomTag, data);
}
case FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_UID:
synchronized (mDeviceCalculatedPowerBlameUidLock) {
return pullDeviceCalculatedPowerBlameUidLocked(atomTag, data);
}
case FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER:
synchronized (mDeviceCalculatedPowerBlameOtherLock) {
return pullDeviceCalculatedPowerBlameOtherLocked(atomTag, data);
}
case FrameworkStatsLog.DEBUG_ELAPSED_CLOCK:
synchronized (mDebugElapsedClockLock) {
return pullDebugElapsedClockLocked(atomTag, data);
}
case FrameworkStatsLog.DEBUG_FAILING_ELAPSED_CLOCK:
synchronized (mDebugFailingElapsedClockLock) {
return pullDebugFailingElapsedClockLocked(atomTag, data);
}
case FrameworkStatsLog.BUILD_INFORMATION:
synchronized (mBuildInformationLock) {
return pullBuildInformationLocked(atomTag, data);
}
case FrameworkStatsLog.ROLE_HOLDER:
synchronized (mRoleHolderLock) {
return pullRoleHolderLocked(atomTag, data);
}
case FrameworkStatsLog.DANGEROUS_PERMISSION_STATE:
case FrameworkStatsLog.DANGEROUS_PERMISSION_STATE_SAMPLED:
synchronized (mDangerousPermissionStateLock) {
return pullDangerousPermissionStateLocked(atomTag, data);
}
case FrameworkStatsLog.TIME_ZONE_DATA_INFO:
synchronized (mTimeZoneDataInfoLock) {
return pullTimeZoneDataInfoLocked(atomTag, data);
}
case FrameworkStatsLog.EXTERNAL_STORAGE_INFO:
synchronized (mExternalStorageInfoLock) {
return pullExternalStorageInfoLocked(atomTag, data);
}
case FrameworkStatsLog.APPS_ON_EXTERNAL_STORAGE_INFO:
synchronized (mAppsOnExternalStorageInfoLock) {
return pullAppsOnExternalStorageInfoLocked(atomTag, data);
}
case FrameworkStatsLog.FACE_SETTINGS:
synchronized (mFaceSettingsLock) {
return pullFaceSettingsLocked(atomTag, data);
}
case FrameworkStatsLog.APP_OPS:
synchronized (mAppOpsLock) {
return pullAppOpsLocked(atomTag, data);
}
case FrameworkStatsLog.RUNTIME_APP_OP_ACCESS:
synchronized (mRuntimeAppOpAccessMessageLock) {
return pullRuntimeAppOpAccessMessageLocked(atomTag, data);
}
case FrameworkStatsLog.NOTIFICATION_REMOTE_VIEWS:
synchronized (mNotificationRemoteViewsLock) {
return pullNotificationRemoteViewsLocked(atomTag, data);
}
case FrameworkStatsLog.BATTERY_LEVEL:
case FrameworkStatsLog.REMAINING_BATTERY_CAPACITY:
case FrameworkStatsLog.FULL_BATTERY_CAPACITY:
case FrameworkStatsLog.BATTERY_VOLTAGE:
case FrameworkStatsLog.BATTERY_CYCLE_COUNT:
synchronized (mHealthHalLock) {
return pullHealthHalLocked(atomTag, data);
}
case FrameworkStatsLog.ATTRIBUTED_APP_OPS:
synchronized (mAttributedAppOpsLock) {
return pullAttributedAppOpsLocked(atomTag, data);
}
case FrameworkStatsLog.SETTING_SNAPSHOT:
synchronized (mSettingsStatsLock) {
return pullSettingsStatsLocked(atomTag, data);
}
default:
throw new UnsupportedOperationException("Unknown tagId=" + atomTag);
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
}
}
@Override
public void onStart() {
// no op
}
@Override
public void onBootPhase(int phase) {
super.onBootPhase(phase);
if (phase == PHASE_SYSTEM_SERVICES_READY) {
BackgroundThread.getHandler().post(() -> {
nativeInit();
initializePullersState();
registerPullers();
registerEventListeners();
});
} else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
// Network stats related pullers can only be initialized after service is ready.
BackgroundThread.getHandler().post(() -> initAndRegisterNetworkStatsPullers());
}
}
// We do not hold locks within this function because it is guaranteed to be called before the
// pullers are ever run, as the pullers are not yet registered with statsd.
void initializePullersState() {
// Get Context Managers
mStatsManager = (StatsManager) mContext.getSystemService(Context.STATS_MANAGER);
mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
mTelephony = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
mSubscriptionManager = (SubscriptionManager)
mContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
mStatsSubscriptionsListener = new StatsSubscriptionsListener(mSubscriptionManager);
mStorageManager = (StorageManager) mContext.getSystemService(StorageManager.class);
// Initialize DiskIO
mStoragedUidIoStatsReader = new StoragedUidIoStatsReader();
// Initialize PROC_STATS
mBaseDir = new File(SystemServiceManager.ensureSystemDir(), "stats_pull");
mBaseDir.mkdirs();
// Disables throttler on CPU time readers.
mCpuUidUserSysTimeReader = new KernelCpuUidUserSysTimeReader(false);
mCpuUidFreqTimeReader = new KernelCpuUidFreqTimeReader(false);
mCpuUidActiveTimeReader = new KernelCpuUidActiveTimeReader(false);
mCpuUidClusterTimeReader = new KernelCpuUidClusterTimeReader(false);
// Initialize state for KERNEL_WAKELOCK
mKernelWakelockReader = new KernelWakelockReader();
mTmpWakelockStats = new KernelWakelockStats();
// Initialize state for CPU_TIME_PER_FREQ atom
PowerProfile powerProfile = new PowerProfile(mContext);
final int numClusters = powerProfile.getNumCpuClusters();
mKernelCpuSpeedReaders = new KernelCpuSpeedReader[numClusters];
int firstCpuOfCluster = 0;
for (int i = 0; i < numClusters; i++) {
final int numSpeedSteps = powerProfile.getNumSpeedStepsInCpuCluster(i);
mKernelCpuSpeedReaders[i] = new KernelCpuSpeedReader(firstCpuOfCluster,
numSpeedSteps);
firstCpuOfCluster += powerProfile.getNumCoresInCpuCluster(i);
}
// Used for CPU_TIME_PER_THREAD_FREQ
mKernelCpuThreadReader =
KernelCpuThreadReaderSettingsObserver.getSettingsModifiedReader(mContext);
// Initialize HealthService
mHealthService = new BatteryService.HealthServiceWrapper();
try {
mHealthService.init();
} catch (RemoteException e) {
Slog.e(TAG, "failed to initialize healthHalWrapper");
}
// Initialize list of AppOps related to DangerousPermissions
PackageManager pm = mContext.getPackageManager();
for (int op = 0; op < AppOpsManager._NUM_OP; op++) {
String perm = AppOpsManager.opToPermission(op);
if (perm == null) {
continue;
} else {
PermissionInfo permInfo;
try {
permInfo = pm.getPermissionInfo(perm, 0);
if (permInfo.getProtection() == PROTECTION_DANGEROUS) {
mDangerousAppOpsList.add(op);
}
} catch (PackageManager.NameNotFoundException exception) {
continue;
}
}
}
}
void registerEventListeners() {
final ConnectivityManager connectivityManager =
(ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
// Default NetworkRequest should cover all transport types.
final NetworkRequest request = new NetworkRequest.Builder().build();
connectivityManager.registerNetworkCallback(request, new ConnectivityStatsCallback());
// Enable push notifications of throttling from vendor thermal
// management subsystem via thermalservice.
IThermalService thermalService = getIThermalService();
if (thermalService != null) {
try {
thermalService.registerThermalEventListener(new ThermalEventListener());
Slog.i(TAG, "register thermal listener successfully");
} catch (RemoteException e) {
Slog.i(TAG, "failed to register thermal listener");
}
}
}
void registerPullers() {
if (DEBUG) {
Slog.d(TAG, "Registering pullers with statsd");
}
mStatsCallbackImpl = new StatsPullAtomCallbackImpl();
registerBluetoothBytesTransfer();
registerKernelWakelock();
registerCpuTimePerFreq();
registerCpuTimePerUid();
registerCpuTimePerUidFreq();
registerCpuActiveTime();
registerCpuClusterTime();
registerWifiActivityInfo();
registerModemActivityInfo();
registerBluetoothActivityInfo();
registerSystemElapsedRealtime();
registerSystemUptime();
registerProcessMemoryState();
registerProcessMemoryHighWaterMark();
registerProcessMemorySnapshot();
registerSystemIonHeapSize();
registerIonHeapSize();
registerProcessSystemIonHeapSize();
registerTemperature();
registerCoolingDevice();
registerBinderCallsStats();
registerBinderCallsStatsExceptions();
registerLooperStats();
registerDiskStats();
registerDirectoryUsage();
registerAppSize();
registerCategorySize();
registerNumFingerprintsEnrolled();
registerNumFacesEnrolled();
registerProcStats();
registerProcStatsPkgProc();
registerDiskIO();
registerPowerProfile();
registerProcessCpuTime();
registerCpuTimePerThreadFreq();
registerDeviceCalculatedPowerUse();
registerDeviceCalculatedPowerBlameUid();
registerDeviceCalculatedPowerBlameOther();
registerDebugElapsedClock();
registerDebugFailingElapsedClock();
registerBuildInformation();
registerRoleHolder();
registerTimeZoneDataInfo();
registerExternalStorageInfo();
registerAppsOnExternalStorageInfo();
registerFaceSettings();
registerAppOps();
registerAttributedAppOps();
registerRuntimeAppOpAccessMessage();
registerNotificationRemoteViews();
registerDangerousPermissionState();
registerDangerousPermissionStateSampled();
registerBatteryLevel();
registerRemainingBatteryCapacity();
registerFullBatteryCapacity();
registerBatteryVoltage();
registerBatteryCycleCount();
registerSettingsStats();
}
private void initAndRegisterNetworkStatsPullers() {
if (DEBUG) {
Slog.d(TAG, "Registering NetworkStats pullers with statsd");
}
// Initialize NetworkStats baselines.
mNetworkStatsBaselines.addAll(
collectNetworkStatsSnapshotForAtom(FrameworkStatsLog.WIFI_BYTES_TRANSFER));
mNetworkStatsBaselines.addAll(
collectNetworkStatsSnapshotForAtom(FrameworkStatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG));
mNetworkStatsBaselines.addAll(
collectNetworkStatsSnapshotForAtom(FrameworkStatsLog.MOBILE_BYTES_TRANSFER));
mNetworkStatsBaselines.addAll(collectNetworkStatsSnapshotForAtom(
FrameworkStatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG));
mNetworkStatsBaselines.addAll(collectNetworkStatsSnapshotForAtom(
FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED));
mNetworkStatsBaselines.addAll(
collectNetworkStatsSnapshotForAtom(FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER));
// Listen to subscription changes to record historical subscriptions that activated before
// pulling, this is used by {@code DATA_USAGE_BYTES_TRANSFER}.
mSubscriptionManager.addOnSubscriptionsChangedListener(
BackgroundThread.getExecutor(), mStatsSubscriptionsListener);
registerWifiBytesTransfer();
registerWifiBytesTransferBackground();
registerMobileBytesTransfer();
registerMobileBytesTransferBackground();
registerBytesTransferByTagAndMetered();
registerDataUsageBytesTransfer();
}
/**
* Return the {@code INetworkStatsSession} object that holds the necessary properties needed
* for the subsequent queries to {@link com.android.server.net.NetworkStatsService}. Or
* null if the service or binder cannot be obtained. Calling this method will trigger poll
* in NetworkStatsService with once per 15 seconds rate-limit, unless {@code bypassRateLimit}
* is set to true. This is needed in {@link #getUidNetworkStatsSnapshotForTemplate}, where
* bypassing the limit is necessary for perfd to supply realtime stats to developers looking at
* the network usage of their app.
*/
@Nullable
private INetworkStatsSession getNetworkStatsSession(boolean bypassRateLimit) {
final INetworkStatsService networkStatsService =
INetworkStatsService.Stub.asInterface(
ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
if (networkStatsService == null) return null;
try {
return networkStatsService.openSessionForUsageStats(
FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN | (bypassRateLimit ? FLAG_POLL_FORCE
: FLAG_POLL_ON_OPEN), mContext.getOpPackageName());
} catch (RemoteException e) {
Slog.e(TAG, "Cannot get NetworkStats session", e);
return null;
}
}
private IThermalService getIThermalService() {
synchronized (mThermalLock) {
if (mThermalService == null) {
mThermalService = IThermalService.Stub.asInterface(
ServiceManager.getService(Context.THERMAL_SERVICE));
if (mThermalService != null) {
try {
mThermalService.asBinder().linkToDeath(() -> {
synchronized (mThermalLock) {
mThermalService = null;
}
}, /* flags */ 0);
} catch (RemoteException e) {
Slog.e(TAG, "linkToDeath with thermalService failed", e);
mThermalService = null;
}
}
}
return mThermalService;
}
}
private IStoraged getIStoragedService() {
synchronized (mStoragedLock) {
if (mStorageService == null) {
mStorageService = IStoraged.Stub.asInterface(
ServiceManager.getService("storaged"));
}
if (mStorageService != null) {
try {
mStorageService.asBinder().linkToDeath(() -> {
synchronized (mStoragedLock) {
mStorageService = null;
}
}, /* flags */ 0);
} catch (RemoteException e) {
Slog.e(TAG, "linkToDeath with storagedService failed", e);
mStorageService = null;
}
}
}
return mStorageService;
}
private INotificationManager getINotificationManagerService() {
synchronized (mNotificationStatsLock) {
if (mNotificationManagerService == null) {
mNotificationManagerService = INotificationManager.Stub.asInterface(
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
}
if (mNotificationManagerService != null) {
try {
mNotificationManagerService.asBinder().linkToDeath(() -> {
synchronized (mNotificationStatsLock) {
mNotificationManagerService = null;
}
}, /* flags */ 0);
} catch (RemoteException e) {
Slog.e(TAG, "linkToDeath with notificationManager failed", e);
mNotificationManagerService = null;
}
}
}
return mNotificationManagerService;
}
private IProcessStats getIProcessStatsService() {
synchronized (mProcStatsLock) {
if (mProcessStatsService == null) {
mProcessStatsService = IProcessStats.Stub.asInterface(
ServiceManager.getService(ProcessStats.SERVICE_NAME));
}
if (mProcessStatsService != null) {
try {
mProcessStatsService.asBinder().linkToDeath(() -> {
synchronized (mProcStatsLock) {
mProcessStatsService = null;
}
}, /* flags */ 0);
} catch (RemoteException e) {
Slog.e(TAG, "linkToDeath with ProcessStats failed", e);
mProcessStatsService = null;
}
}
}
return mProcessStatsService;
}
private void registerWifiBytesTransfer() {
int tagId = FrameworkStatsLog.WIFI_BYTES_TRANSFER;
PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setAdditiveFields(new int[] {2, 3, 4, 5})
.build();
mStatsManager.setPullAtomCallback(
tagId,
metadata,
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
@NonNull
private List<NetworkStatsExt> collectNetworkStatsSnapshotForAtom(int atomTag) {
List<NetworkStatsExt> ret = new ArrayList<>();
switch(atomTag) {
case FrameworkStatsLog.WIFI_BYTES_TRANSFER: {
final NetworkStats stats = getUidNetworkStatsSnapshotForTransport(TRANSPORT_WIFI);
if (stats != null) {
ret.add(new NetworkStatsExt(stats.groupedByUid(), new int[] {TRANSPORT_WIFI},
/*slicedByFgbg=*/false));
}
break;
}
case FrameworkStatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG: {
final NetworkStats stats = getUidNetworkStatsSnapshotForTransport(TRANSPORT_WIFI);
if (stats != null) {
ret.add(new NetworkStatsExt(sliceNetworkStatsByUidAndFgbg(stats),
new int[] {TRANSPORT_WIFI}, /*slicedByFgbg=*/true));
}
break;
}
case FrameworkStatsLog.MOBILE_BYTES_TRANSFER: {
final NetworkStats stats =
getUidNetworkStatsSnapshotForTransport(TRANSPORT_CELLULAR);
if (stats != null) {
ret.add(new NetworkStatsExt(stats.groupedByUid(),
new int[] {TRANSPORT_CELLULAR}, /*slicedByFgbg=*/false));
}
break;
}
case FrameworkStatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG: {
final NetworkStats stats =
getUidNetworkStatsSnapshotForTransport(TRANSPORT_CELLULAR);
if (stats != null) {
ret.add(new NetworkStatsExt(sliceNetworkStatsByUidAndFgbg(stats),
new int[] {TRANSPORT_CELLULAR}, /*slicedByFgbg=*/true));
}
break;
}
case FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED: {
final NetworkStats wifiStats = getUidNetworkStatsSnapshotForTemplate(
buildTemplateWifiWildcard(), /*includeTags=*/true);
final NetworkStats cellularStats = getUidNetworkStatsSnapshotForTemplate(
buildTemplateMobileWildcard(), /*includeTags=*/true);
if (wifiStats != null && cellularStats != null) {
final NetworkStats stats = wifiStats.add(cellularStats);
ret.add(new NetworkStatsExt(sliceNetworkStatsByUidTagAndMetered(stats),
new int[] {TRANSPORT_WIFI, TRANSPORT_CELLULAR},
/*slicedByFgbg=*/false, /*slicedByTag=*/true,
/*slicedByMetered=*/true, TelephonyManager.NETWORK_TYPE_UNKNOWN,
/*subInfo=*/null));
}
break;
}
case FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER: {
for (final SubInfo subInfo : mHistoricalSubs) {
ret.addAll(getDataUsageBytesTransferSnapshotForSub(subInfo));
}
break;
}
default:
throw new IllegalArgumentException("Unknown atomTag " + atomTag);
}
return ret;
}
private int pullDataBytesTransferLocked(int atomTag, @NonNull List<StatsEvent> pulledData) {
final List<NetworkStatsExt> current = collectNetworkStatsSnapshotForAtom(atomTag);
if (current == null) {
Slog.e(TAG, "current snapshot is null for " + atomTag + ", return.");
return StatsManager.PULL_SKIP;
}
for (final NetworkStatsExt item : current) {
final NetworkStatsExt baseline = CollectionUtils.find(mNetworkStatsBaselines,
it -> it.hasSameSlicing(item));
// No matched baseline indicates error has occurred during initialization stage,
// skip reporting anything since the snapshot is invalid.
if (baseline == null) {
Slog.e(TAG, "baseline is null for " + atomTag + ", return.");
return StatsManager.PULL_SKIP;
}
final NetworkStatsExt diff = new NetworkStatsExt(
item.stats.subtract(baseline.stats).removeEmptyEntries(), item.transports,
item.slicedByFgbg, item.slicedByTag, item.slicedByMetered, item.ratType,
item.subInfo);
// If no diff, skip.
if (diff.stats.size() == 0) continue;
switch (atomTag) {
case FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED:
addBytesTransferByTagAndMeteredAtoms(diff, pulledData);
break;
case FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER:
addDataUsageBytesTransferAtoms(diff, pulledData);
break;
default:
addNetworkStats(atomTag, pulledData, diff);
}
}
return StatsManager.PULL_SUCCESS;
}
private void addNetworkStats(int atomTag, @NonNull List<StatsEvent> ret,
@NonNull NetworkStatsExt statsExt) {
int size = statsExt.stats.size();
final NetworkStats.Entry entry = new NetworkStats.Entry(); // For recycling
for (int j = 0; j < size; j++) {
statsExt.stats.getValues(j, entry);
StatsEvent.Builder e = StatsEvent.newBuilder();
e.setAtomId(atomTag);
switch (atomTag) {
case FrameworkStatsLog.MOBILE_BYTES_TRANSFER:
case FrameworkStatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG:
e.addBooleanAnnotation(ANNOTATION_ID_TRUNCATE_TIMESTAMP, true);
break;
default:
}
e.writeInt(entry.uid);
e.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
if (statsExt.slicedByFgbg) {
e.writeInt(entry.set);
}
e.writeLong(entry.rxBytes);
e.writeLong(entry.rxPackets);
e.writeLong(entry.txBytes);
e.writeLong(entry.txPackets);
ret.add(e.build());
}
}
private void addBytesTransferByTagAndMeteredAtoms(@NonNull NetworkStatsExt statsExt,
@NonNull List<StatsEvent> pulledData) {
final NetworkStats.Entry entry = new NetworkStats.Entry(); // for recycling
for (int i = 0; i < statsExt.stats.size(); i++) {
statsExt.stats.getValues(i, entry);
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED)
.addBooleanAnnotation(ANNOTATION_ID_TRUNCATE_TIMESTAMP, true)
.writeInt(entry.uid)
.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
.writeBoolean(entry.metered == NetworkStats.METERED_YES)
.writeInt(entry.tag)
.writeLong(entry.rxBytes)
.writeLong(entry.rxPackets)
.writeLong(entry.txBytes)
.writeLong(entry.txPackets)
.build();
pulledData.add(e);
}
}
private void addDataUsageBytesTransferAtoms(@NonNull NetworkStatsExt statsExt,
@NonNull List<StatsEvent> pulledData) {
// Workaround for 5G NSA mode, see {@link NetworkTemplate#NETWORK_TYPE_5G_NSA}.
// 5G NSA mode means the primary cell is LTE with a secondary connection to an
// NR cell. To mitigate risk, NetworkStats is currently storing this state as
// a fake RAT type rather than storing the boolean separately.
final boolean is5GNsa = statsExt.ratType == NetworkTemplate.NETWORK_TYPE_5G_NSA;
// Report NR connected in 5G non-standalone mode, or if the RAT type is NR to begin with.
final boolean isNR = is5GNsa || statsExt.ratType == TelephonyManager.NETWORK_TYPE_NR;
final NetworkStats.Entry entry = new NetworkStats.Entry(); // for recycling
for (int i = 0; i < statsExt.stats.size(); i++) {
statsExt.stats.getValues(i, entry);
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER)
.addBooleanAnnotation(ANNOTATION_ID_TRUNCATE_TIMESTAMP, true)
.writeInt(entry.set)
.writeLong(entry.rxBytes)
.writeLong(entry.rxPackets)
.writeLong(entry.txBytes)
.writeLong(entry.txPackets)
.writeInt(is5GNsa ? TelephonyManager.NETWORK_TYPE_LTE : statsExt.ratType)
// Fill information about subscription, these cannot be null since invalid data
// would be filtered when adding into subInfo list.
.writeString(statsExt.subInfo.mcc)
.writeString(statsExt.subInfo.mnc)
.writeInt(statsExt.subInfo.carrierId)
.writeInt(statsExt.subInfo.isOpportunistic
? DATA_USAGE_BYTES_TRANSFER__OPPORTUNISTIC_DATA_SUB__OPPORTUNISTIC
: DATA_USAGE_BYTES_TRANSFER__OPPORTUNISTIC_DATA_SUB__NOT_OPPORTUNISTIC)
.writeBoolean(isNR)
.build();
pulledData.add(e);
}
}
/**
* Create a snapshot of NetworkStats for a given transport.
*/
@Nullable private NetworkStats getUidNetworkStatsSnapshotForTransport(int transport) {
final NetworkTemplate template = (transport == TRANSPORT_CELLULAR)
? NetworkTemplate.buildTemplateMobileWithRatType(
/*subscriptionId=*/null, NETWORK_TYPE_ALL)
: NetworkTemplate.buildTemplateWifiWildcard();
return getUidNetworkStatsSnapshotForTemplate(template, /*includeTags=*/false);
}
/**
* Create a snapshot of NetworkStats since boot for the given template, but add 1 bucket
* duration before boot as a buffer to ensure at least one full bucket will be included.
* Note that this should be only used to calculate diff since the snapshot might contains
* some traffic before boot.
*/
@Nullable private NetworkStats getUidNetworkStatsSnapshotForTemplate(
@NonNull NetworkTemplate template, boolean includeTags) {
final long elapsedMillisSinceBoot = SystemClock.elapsedRealtime();
final long currentTimeInMillis = MICROSECONDS.toMillis(SystemClock.currentTimeMicro());
final long bucketDuration = Settings.Global.getLong(mContext.getContentResolver(),
NETSTATS_UID_BUCKET_DURATION, NETSTATS_UID_DEFAULT_BUCKET_DURATION_MS);
try {
// TODO (b/156313635): This is short-term hack to allow perfd gets updated networkStats
// history when query in every second in order to show realtime statistics. However,
// this is not a good long-term solution since NetworkStatsService will make frequent
// I/O and also block main thread when polling.
// Consider making perfd queries NetworkStatsService directly.
final NetworkStats stats = getNetworkStatsSession(template.getMatchRule()
== NetworkTemplate.MATCH_WIFI_WILDCARD).getSummaryForAllUid(template,
currentTimeInMillis - elapsedMillisSinceBoot - bucketDuration,
currentTimeInMillis, includeTags);
return stats;
} catch (RemoteException | NullPointerException e) {
Slog.e(TAG, "Pulling netstats for template=" + template + " and includeTags="
+ includeTags + " causes error", e);
}
return null;
}
@NonNull private List<NetworkStatsExt> getDataUsageBytesTransferSnapshotForSub(
@NonNull SubInfo subInfo) {
final List<NetworkStatsExt> ret = new ArrayList<>();
for (final int ratType : getAllCollapsedRatTypes()) {
final NetworkTemplate template =
buildTemplateMobileWithRatType(subInfo.subscriberId, ratType);
final NetworkStats stats =
getUidNetworkStatsSnapshotForTemplate(template, /*includeTags=*/false);
if (stats != null) {
ret.add(new NetworkStatsExt(sliceNetworkStatsByFgbg(stats),
new int[] {TRANSPORT_CELLULAR}, /*slicedByFgbg=*/true,
/*slicedByTag=*/false, /*slicedByMetered=*/false, ratType, subInfo));
}
}
return ret;
}
@NonNull private NetworkStats sliceNetworkStatsByFgbg(@NonNull NetworkStats stats) {
return sliceNetworkStats(stats,
(newEntry, oldEntry) -> {
newEntry.set = oldEntry.set;
});
}
@NonNull private NetworkStats sliceNetworkStatsByUidAndFgbg(@NonNull NetworkStats stats) {
return sliceNetworkStats(stats,
(newEntry, oldEntry) -> {
newEntry.uid = oldEntry.uid;
newEntry.set = oldEntry.set;
});
}
@NonNull private NetworkStats sliceNetworkStatsByUidTagAndMetered(@NonNull NetworkStats stats) {
return sliceNetworkStats(stats,
(newEntry, oldEntry) -> {
newEntry.uid = oldEntry.uid;
newEntry.tag = oldEntry.tag;
newEntry.metered = oldEntry.metered;
});
}
/**
* Slices NetworkStats along the dimensions specified in the slicer lambda and aggregates over
* non-sliced dimensions.
*
* This function iterates through each NetworkStats.Entry, sets its dimensions equal to the
* default state (with the presumption that we don't want to slice on anything), and then
* applies the slicer lambda to allow users to control which dimensions to slice on. This is
* adapted from groupedByUid within NetworkStats.java
*
* @param slicer An operation taking into two parameters, new NetworkStats.Entry and old
* NetworkStats.Entry, that should be used to copy state from the old to the new.
* This is useful for slicing by particular dimensions. For example, if we wished
* to slice by uid and tag, we could write the following lambda:
* (new, old) -> {
* new.uid = old.uid;
* new.tag = old.tag;
* }
* If no slicer is provided, the data is not sliced by any dimensions.
* @return new NeworkStats object appropriately sliced
*/
@NonNull private NetworkStats sliceNetworkStats(@NonNull NetworkStats stats,
@Nullable BiConsumer<NetworkStats.Entry, NetworkStats.Entry> slicer) {
final NetworkStats ret = new NetworkStats(stats.getElapsedRealtime(), 1);
final NetworkStats.Entry entry = new NetworkStats.Entry();
entry.uid = NetworkStats.UID_ALL;
entry.iface = NetworkStats.IFACE_ALL;
entry.set = NetworkStats.SET_ALL;
entry.tag = NetworkStats.TAG_NONE;
entry.metered = NetworkStats.METERED_ALL;
entry.roaming = NetworkStats.ROAMING_ALL;
entry.defaultNetwork = NetworkStats.DEFAULT_NETWORK_ALL;
final NetworkStats.Entry recycle = new NetworkStats.Entry(); // used for retrieving values
for (int i = 0; i < stats.size(); i++) {
stats.getValues(i, recycle);
if (slicer != null) {
slicer.accept(entry, recycle);
}
entry.rxBytes = recycle.rxBytes;
entry.rxPackets = recycle.rxPackets;
entry.txBytes = recycle.txBytes;
entry.txPackets = recycle.txPackets;
// Operations purposefully omitted since we don't use them for statsd.
ret.combineValues(entry);
}
return ret;
}
private void registerWifiBytesTransferBackground() {
int tagId = FrameworkStatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG;
PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setAdditiveFields(new int[] {3, 4, 5, 6})
.build();
mStatsManager.setPullAtomCallback(
tagId,
metadata,
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
private void registerMobileBytesTransfer() {
int tagId = FrameworkStatsLog.MOBILE_BYTES_TRANSFER;
PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setAdditiveFields(new int[] {2, 3, 4, 5})
.build();
mStatsManager.setPullAtomCallback(
tagId,
metadata,
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
private void registerMobileBytesTransferBackground() {
int tagId = FrameworkStatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG;
PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setAdditiveFields(new int[] {3, 4, 5, 6})
.build();
mStatsManager.setPullAtomCallback(
tagId,
metadata,
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
private void registerBytesTransferByTagAndMetered() {
int tagId = FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED;
PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setAdditiveFields(new int[] {4, 5, 6, 7})
.build();
mStatsManager.setPullAtomCallback(
tagId,
metadata,
BackgroundThread.getExecutor(),
mStatsCallbackImpl
);
}
private void registerDataUsageBytesTransfer() {
int tagId = FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER;
PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setAdditiveFields(new int[] {2, 3, 4, 5})
.build();
mStatsManager.setPullAtomCallback(
tagId,
metadata,
BackgroundThread.getExecutor(),
mStatsCallbackImpl
);
}
private void registerBluetoothBytesTransfer() {
int tagId = FrameworkStatsLog.BLUETOOTH_BYTES_TRANSFER;
PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setAdditiveFields(new int[] {2, 3})
.build();
mStatsManager.setPullAtomCallback(
tagId,
metadata,
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
/**
* Helper method to extract the Parcelable controller info from a
* SynchronousResultReceiver.
*/
private static <T extends Parcelable> T awaitControllerInfo(
@Nullable SynchronousResultReceiver receiver) {
if (receiver == null) {
return null;
}
try {
final SynchronousResultReceiver.Result result =
receiver.awaitResult(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS);
if (result.bundle != null) {
// This is the final destination for the Bundle.
result.bundle.setDefusable(true);
final T data = result.bundle.getParcelable(RESULT_RECEIVER_CONTROLLER_KEY);
if (data != null) {
return data;
}
}
} catch (TimeoutException e) {
Slog.w(TAG, "timeout reading " + receiver.getName() + " stats");
}
return null;
}
private BluetoothActivityEnergyInfo fetchBluetoothData() {
final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter != null) {
SynchronousResultReceiver bluetoothReceiver =
new SynchronousResultReceiver("bluetooth");
adapter.requestControllerActivityEnergyInfo(bluetoothReceiver);
return awaitControllerInfo(bluetoothReceiver);
} else {
Slog.e(TAG, "Failed to get bluetooth adapter!");
return null;
}
}
int pullBluetoothBytesTransferLocked(int atomTag, List<StatsEvent> pulledData) {
BluetoothActivityEnergyInfo info = fetchBluetoothData();
if (info == null || info.getUidTraffic() == null) {
return StatsManager.PULL_SKIP;
}
for (UidTraffic traffic : info.getUidTraffic()) {
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeInt(traffic.getUid())
.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
.writeLong(traffic.getRxBytes())
.writeLong(traffic.getTxBytes())
.build();
pulledData.add(e);
}
return StatsManager.PULL_SUCCESS;
}
private void registerKernelWakelock() {
int tagId = FrameworkStatsLog.KERNEL_WAKELOCK;
mStatsManager.setPullAtomCallback(
tagId,
/* PullAtomMetadata */ null,
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullKernelWakelockLocked(int atomTag, List<StatsEvent> pulledData) {
final KernelWakelockStats wakelockStats =
mKernelWakelockReader.readKernelWakelockStats(mTmpWakelockStats);
for (Map.Entry<String, KernelWakelockStats.Entry> ent : wakelockStats.entrySet()) {
String name = ent.getKey();
KernelWakelockStats.Entry kws = ent.getValue();
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeString(name)
.writeInt(kws.mCount)
.writeInt(kws.mVersion)
.writeLong(kws.mTotalTime)
.build();
pulledData.add(e);
}
return StatsManager.PULL_SUCCESS;
}
private void registerCpuTimePerFreq() {
int tagId = FrameworkStatsLog.CPU_TIME_PER_FREQ;
PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setAdditiveFields(new int[] {3})
.build();
mStatsManager.setPullAtomCallback(
tagId,
metadata,
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullCpuTimePerFreqLocked(int atomTag, List<StatsEvent> pulledData) {
for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) {
long[] clusterTimeMs = mKernelCpuSpeedReaders[cluster].readAbsolute();
if (clusterTimeMs != null) {
for (int speed = clusterTimeMs.length - 1; speed >= 0; --speed) {
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeInt(cluster)
.writeInt(speed)
.writeLong(clusterTimeMs[speed])
.build();
pulledData.add(e);
}
}
}
return StatsManager.PULL_SUCCESS;
}
private void registerCpuTimePerUid() {
int tagId = FrameworkStatsLog.CPU_TIME_PER_UID;
PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setAdditiveFields(new int[] {2, 3})
.build();
mStatsManager.setPullAtomCallback(
tagId,
metadata,
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullCpuTimePerUidLocked(int atomTag, List<StatsEvent> pulledData) {
mCpuUidUserSysTimeReader.readAbsolute((uid, timesUs) -> {
long userTimeUs = timesUs[0], systemTimeUs = timesUs[1];
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeInt(uid)
.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
.writeLong(userTimeUs)
.writeLong(systemTimeUs)
.build();
pulledData.add(e);
});
return StatsManager.PULL_SUCCESS;
}
private void registerCpuTimePerUidFreq() {
// the throttling is 3sec, handled in
// frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
int tagId = FrameworkStatsLog.CPU_TIME_PER_UID_FREQ;
PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setAdditiveFields(new int[] {3})
.build();
mStatsManager.setPullAtomCallback(
tagId,
metadata,
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullCpuTimePerUidFreqLocked(int atomTag, List<StatsEvent> pulledData) {
// Aggregate times for the same uids.
SparseArray<long[]> aggregated = new SparseArray<>();
mCpuUidFreqTimeReader.readAbsolute((uid, cpuFreqTimeMs) -> {
if (UserHandle.isIsolated(uid)) {
// Skip individual isolated uids because they are recycled and quickly removed from
// the underlying data source.
return;
} else if (UserHandle.isSharedAppGid(uid)) {
// All shared app gids are accounted together.
uid = LAST_SHARED_APPLICATION_GID;
} else {
// Everything else is accounted under their base uid.
uid = UserHandle.getAppId(uid);
}
long[] aggCpuFreqTimeMs = aggregated.get(uid);
if (aggCpuFreqTimeMs == null) {
aggCpuFreqTimeMs = new long[cpuFreqTimeMs.length];
aggregated.put(uid, aggCpuFreqTimeMs);
}
for (int freqIndex = 0; freqIndex < cpuFreqTimeMs.length; ++freqIndex) {
aggCpuFreqTimeMs[freqIndex] += cpuFreqTimeMs[freqIndex];
}
});
int size = aggregated.size();
for (int i = 0; i < size; ++i) {
int uid = aggregated.keyAt(i);
long[] aggCpuFreqTimeMs = aggregated.valueAt(i);
for (int freqIndex = 0; freqIndex < aggCpuFreqTimeMs.length; ++freqIndex) {
if (aggCpuFreqTimeMs[freqIndex] >= MIN_CPU_TIME_PER_UID_FREQ) {
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeInt(uid)
.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
.writeInt(freqIndex)
.writeLong(aggCpuFreqTimeMs[freqIndex])
.build();
pulledData.add(e);
}
}
}
return StatsManager.PULL_SUCCESS;
}
private void registerCpuActiveTime() {
// the throttling is 3sec, handled in
// frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
int tagId = FrameworkStatsLog.CPU_ACTIVE_TIME;
PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setAdditiveFields(new int[] {2})
.build();
mStatsManager.setPullAtomCallback(
tagId,
metadata,
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullCpuActiveTimeLocked(int atomTag, List<StatsEvent> pulledData) {
mCpuUidActiveTimeReader.readAbsolute((uid, cpuActiveTimesMs) -> {
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeInt(uid)
.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
.writeLong(cpuActiveTimesMs)
.build();
pulledData.add(e);
});
return StatsManager.PULL_SUCCESS;
}
private void registerCpuClusterTime() {
// the throttling is 3sec, handled in
// frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
int tagId = FrameworkStatsLog.CPU_CLUSTER_TIME;
PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setAdditiveFields(new int[] {3})
.build();
mStatsManager.setPullAtomCallback(
tagId,
metadata,
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullCpuClusterTimeLocked(int atomTag, List<StatsEvent> pulledData) {
mCpuUidClusterTimeReader.readAbsolute((uid, cpuClusterTimesMs) -> {
for (int i = 0; i < cpuClusterTimesMs.length; i++) {
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeInt(uid)
.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
.writeInt(i)
.writeLong(cpuClusterTimesMs[i])
.build();
pulledData.add(e);
}
});
return StatsManager.PULL_SUCCESS;
}
private void registerWifiActivityInfo() {
int tagId = FrameworkStatsLog.WIFI_ACTIVITY_INFO;
mStatsManager.setPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullWifiActivityInfoLocked(int atomTag, List<StatsEvent> pulledData) {
long token = Binder.clearCallingIdentity();
try {
SynchronousResultReceiver wifiReceiver = new SynchronousResultReceiver("wifi");
mWifiManager.getWifiActivityEnergyInfoAsync(
new Executor() {
@Override
public void execute(Runnable runnable) {
// run the listener on the binder thread, if it was run on the main
// thread it would deadlock since we would be waiting on ourselves
runnable.run();
}
},
info -> {
Bundle bundle = new Bundle();
bundle.putParcelable(BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY, info);
wifiReceiver.send(0, bundle);
}
);
final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver);
if (wifiInfo == null) {
return StatsManager.PULL_SKIP;
}
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeLong(wifiInfo.getTimeSinceBootMillis())
.writeInt(wifiInfo.getStackState())
.writeLong(wifiInfo.getControllerTxDurationMillis())
.writeLong(wifiInfo.getControllerRxDurationMillis())
.writeLong(wifiInfo.getControllerIdleDurationMillis())
.writeLong(wifiInfo.getControllerEnergyUsedMicroJoules())
.build();
pulledData.add(e);
} catch (RuntimeException e) {
Slog.e(TAG, "failed to getWifiActivityEnergyInfoAsync", e);
return StatsManager.PULL_SKIP;
} finally {
Binder.restoreCallingIdentity(token);
}
return StatsManager.PULL_SUCCESS;
}
private void registerModemActivityInfo() {
int tagId = FrameworkStatsLog.MODEM_ACTIVITY_INFO;
mStatsManager.setPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullModemActivityInfoLocked(int atomTag, List<StatsEvent> pulledData) {
long token = Binder.clearCallingIdentity();
try {
SynchronousResultReceiver modemReceiver = new SynchronousResultReceiver("telephony");
mTelephony.requestModemActivityInfo(modemReceiver);
final ModemActivityInfo modemInfo = awaitControllerInfo(modemReceiver);
if (modemInfo == null) {
return StatsManager.PULL_SKIP;
}
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeLong(modemInfo.getTimestamp())
.writeLong(modemInfo.getSleepTimeMillis())
.writeLong(modemInfo.getIdleTimeMillis())
.writeLong(modemInfo.getTransmitPowerInfo().get(0).getTimeInMillis())
.writeLong(modemInfo.getTransmitPowerInfo().get(1).getTimeInMillis())
.writeLong(modemInfo.getTransmitPowerInfo().get(2).getTimeInMillis())
.writeLong(modemInfo.getTransmitPowerInfo().get(3).getTimeInMillis())
.writeLong(modemInfo.getTransmitPowerInfo().get(4).getTimeInMillis())
.writeLong(modemInfo.getReceiveTimeMillis())
.build();
pulledData.add(e);
} finally {
Binder.restoreCallingIdentity(token);
}
return StatsManager.PULL_SUCCESS;
}
private void registerBluetoothActivityInfo() {
int tagId = FrameworkStatsLog.BLUETOOTH_ACTIVITY_INFO;
mStatsManager.setPullAtomCallback(
tagId,
/* metadata */ null,
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullBluetoothActivityInfoLocked(int atomTag, List<StatsEvent> pulledData) {
BluetoothActivityEnergyInfo info = fetchBluetoothData();
if (info == null) {
return StatsManager.PULL_SKIP;
}
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeLong(info.getTimeStamp())
.writeInt(info.getBluetoothStackState())
.writeLong(info.getControllerTxTimeMillis())
.writeLong(info.getControllerRxTimeMillis())
.writeLong(info.getControllerIdleTimeMillis())
.writeLong(info.getControllerEnergyUsed())
.build();
pulledData.add(e);
return StatsManager.PULL_SUCCESS;
}
private void registerSystemElapsedRealtime() {
int tagId = FrameworkStatsLog.SYSTEM_ELAPSED_REALTIME;
PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setCoolDownMillis(MILLIS_PER_SEC)
.setTimeoutMillis(MILLIS_PER_SEC / 2)
.build();
mStatsManager.setPullAtomCallback(
tagId,
metadata,
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullSystemElapsedRealtimeLocked(int atomTag, List<StatsEvent> pulledData) {
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeLong(SystemClock.elapsedRealtime())
.build();
pulledData.add(e);
return StatsManager.PULL_SUCCESS;
}
private void registerSystemUptime() {
int tagId = FrameworkStatsLog.SYSTEM_UPTIME;
mStatsManager.setPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullSystemUptimeLocked(int atomTag, List<StatsEvent> pulledData) {
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeLong(SystemClock.uptimeMillis())
.build();
pulledData.add(e);
return StatsManager.PULL_SUCCESS;
}
private void registerProcessMemoryState() {
int tagId = FrameworkStatsLog.PROCESS_MEMORY_STATE;
PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setAdditiveFields(new int[] {4, 5, 6, 7, 8})
.build();
mStatsManager.setPullAtomCallback(
tagId,
metadata,
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullProcessMemoryStateLocked(int atomTag, List<StatsEvent> pulledData) {
List<ProcessMemoryState> processMemoryStates =
LocalServices.getService(ActivityManagerInternal.class)
.getMemoryStateForProcesses();
for (ProcessMemoryState processMemoryState : processMemoryStates) {
final MemoryStat memoryStat = readMemoryStatFromFilesystem(processMemoryState.uid,
processMemoryState.pid);
if (memoryStat == null) {
continue;
}
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeInt(processMemoryState.uid)
.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
.writeString(processMemoryState.processName)
.writeInt(processMemoryState.oomScore)
.writeLong(memoryStat.pgfault)
.writeLong(memoryStat.pgmajfault)
.writeLong(memoryStat.rssInBytes)
.writeLong(memoryStat.cacheInBytes)
.writeLong(memoryStat.swapInBytes)
.writeLong(-1) // unused
.writeLong(-1) // unused
.writeInt(-1) // unused
.build();
pulledData.add(e);
}
return StatsManager.PULL_SUCCESS;
}
private void registerProcessMemoryHighWaterMark() {
int tagId = FrameworkStatsLog.PROCESS_MEMORY_HIGH_WATER_MARK;
mStatsManager.setPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullProcessMemoryHighWaterMarkLocked(int atomTag, List<StatsEvent> pulledData) {
List<ProcessMemoryState> managedProcessList =
LocalServices.getService(ActivityManagerInternal.class)
.getMemoryStateForProcesses();
for (ProcessMemoryState managedProcess : managedProcessList) {
final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(managedProcess.pid);
if (snapshot == null) {
continue;
}
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeInt(managedProcess.uid)
.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
.writeString(managedProcess.processName)
// RSS high-water mark in bytes.
.writeLong(snapshot.rssHighWaterMarkInKilobytes * 1024L)
.writeInt(snapshot.rssHighWaterMarkInKilobytes)
.build();
pulledData.add(e);
}
// Complement the data with native system processes
SparseArray<String> processCmdlines = getProcessCmdlines();
managedProcessList.forEach(managedProcess -> processCmdlines.delete(managedProcess.pid));
int size = processCmdlines.size();
for (int i = 0; i < size; ++i) {
final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(processCmdlines.keyAt(i));
if (snapshot == null) {
continue;
}
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeInt(snapshot.uid)
.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
.writeString(processCmdlines.valueAt(i))
// RSS high-water mark in bytes.
.writeLong(snapshot.rssHighWaterMarkInKilobytes * 1024L)
.writeInt(snapshot.rssHighWaterMarkInKilobytes)
.build();
pulledData.add(e);
}
// Invoke rss_hwm_reset binary to reset RSS HWM counters for all processes.
SystemProperties.set("sys.rss_hwm_reset.on", "1");
return StatsManager.PULL_SUCCESS;
}
private void registerProcessMemorySnapshot() {
int tagId = FrameworkStatsLog.PROCESS_MEMORY_SNAPSHOT;
mStatsManager.setPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullProcessMemorySnapshotLocked(int atomTag, List<StatsEvent> pulledData) {
List<ProcessMemoryState> managedProcessList =
LocalServices.getService(ActivityManagerInternal.class)
.getMemoryStateForProcesses();
for (ProcessMemoryState managedProcess : managedProcessList) {
final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(managedProcess.pid);
if (snapshot == null) {
continue;
}
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeInt(managedProcess.uid)
.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
.writeString(managedProcess.processName)
.writeInt(managedProcess.pid)
.writeInt(managedProcess.oomScore)
.writeInt(snapshot.rssInKilobytes)
.writeInt(snapshot.anonRssInKilobytes)
.writeInt(snapshot.swapInKilobytes)
.writeInt(snapshot.anonRssInKilobytes + snapshot.swapInKilobytes)
.build();
pulledData.add(e);
}
// Complement the data with native system processes. Given these measurements can be taken
// in response to LMKs happening, we want to first collect the managed app stats (to
// maximize the probability that a heavyweight process will be sampled before it dies).
SparseArray<String> processCmdlines = getProcessCmdlines();
managedProcessList.forEach(managedProcess -> processCmdlines.delete(managedProcess.pid));
int size = processCmdlines.size();
for (int i = 0; i < size; ++i) {
int pid = processCmdlines.keyAt(i);
final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(pid);
if (snapshot == null) {
continue;
}
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeInt(snapshot.uid)
.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
.writeString(processCmdlines.valueAt(i))
.writeInt(pid)
.writeInt(-1001) // Placeholder for native processes, OOM_SCORE_ADJ_MIN - 1.
.writeInt(snapshot.rssInKilobytes)
.writeInt(snapshot.anonRssInKilobytes)
.writeInt(snapshot.swapInKilobytes)
.writeInt(snapshot.anonRssInKilobytes + snapshot.swapInKilobytes)
.build();
pulledData.add(e);
}
return StatsManager.PULL_SUCCESS;
}
private void registerSystemIonHeapSize() {
int tagId = FrameworkStatsLog.SYSTEM_ION_HEAP_SIZE;
mStatsManager.setPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullSystemIonHeapSizeLocked(int atomTag, List<StatsEvent> pulledData) {
final long systemIonHeapSizeInBytes = readSystemIonHeapSizeFromDebugfs();
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeLong(systemIonHeapSizeInBytes)
.build();
pulledData.add(e);
return StatsManager.PULL_SUCCESS;
}
private void registerIonHeapSize() {
if (!new File("/sys/kernel/ion/total_heaps_kb").exists()) {
return;
}
int tagId = FrameworkStatsLog.ION_HEAP_SIZE;
mStatsManager.setPullAtomCallback(
tagId,
/* PullAtomMetadata */ null,
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullIonHeapSizeLocked(int atomTag, List<StatsEvent> pulledData) {
int ionHeapSizeInKilobytes = (int) getIonHeapsSizeKb();
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeInt(ionHeapSizeInKilobytes)
.build();
pulledData.add(e);
return StatsManager.PULL_SUCCESS;
}
private void registerProcessSystemIonHeapSize() {
int tagId = FrameworkStatsLog.PROCESS_SYSTEM_ION_HEAP_SIZE;
mStatsManager.setPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullProcessSystemIonHeapSizeLocked(int atomTag, List<StatsEvent> pulledData) {
List<IonAllocations> result = readProcessSystemIonHeapSizesFromDebugfs();
for (IonAllocations allocations : result) {
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeInt(getUidForPid(allocations.pid))
.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
.writeString(readCmdlineFromProcfs(allocations.pid))
.writeInt((int) (allocations.totalSizeInBytes / 1024))
.writeInt(allocations.count)
.writeInt((int) (allocations.maxSizeInBytes / 1024))
.build();
pulledData.add(e);
}
return StatsManager.PULL_SUCCESS;
}
private void registerTemperature() {
int tagId = FrameworkStatsLog.TEMPERATURE;
mStatsManager.setPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullTemperatureLocked(int atomTag, List<StatsEvent> pulledData) {
IThermalService thermalService = getIThermalService();
if (thermalService == null) {
return StatsManager.PULL_SKIP;
}
final long callingToken = Binder.clearCallingIdentity();
try {
Temperature temperatures[] = thermalService.getCurrentTemperatures();
for (Temperature temp : temperatures) {
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeInt(temp.getType())
.writeString(temp.getName())
.writeInt((int) (temp.getValue() * 10))
.writeInt(temp.getStatus())
.build();
pulledData.add(e);
}
} catch (RemoteException e) {
// Should not happen.
Slog.e(TAG, "Disconnected from thermal service. Cannot pull temperatures.");
return StatsManager.PULL_SKIP;
} finally {
Binder.restoreCallingIdentity(callingToken);
}
return StatsManager.PULL_SUCCESS;
}
private void registerCoolingDevice() {
int tagId = FrameworkStatsLog.COOLING_DEVICE;
mStatsManager.setPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullCooldownDeviceLocked(int atomTag, List<StatsEvent> pulledData) {
IThermalService thermalService = getIThermalService();
if (thermalService == null) {
return StatsManager.PULL_SKIP;
}
final long callingToken = Binder.clearCallingIdentity();
try {
CoolingDevice devices[] = thermalService.getCurrentCoolingDevices();
for (CoolingDevice device : devices) {
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeInt(device.getType())
.writeString(device.getName())
.writeInt((int) (device.getValue()))
.build();
pulledData.add(e);
}
} catch (RemoteException e) {
// Should not happen.
Slog.e(TAG, "Disconnected from thermal service. Cannot pull temperatures.");
return StatsManager.PULL_SKIP;
} finally {
Binder.restoreCallingIdentity(callingToken);
}
return StatsManager.PULL_SUCCESS;
}
private void registerBinderCallsStats() {
int tagId = FrameworkStatsLog.BINDER_CALLS;
PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setAdditiveFields(new int[] {4, 5, 6, 8, 12})
.build();
mStatsManager.setPullAtomCallback(
tagId,
metadata,
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullBinderCallsStatsLocked(int atomTag, List<StatsEvent> pulledData) {
BinderCallsStatsService.Internal binderStats =
LocalServices.getService(BinderCallsStatsService.Internal.class);
if (binderStats == null) {
Slog.e(TAG, "failed to get binderStats");
return StatsManager.PULL_SKIP;
}
List<ExportedCallStat> callStats = binderStats.getExportedCallStats();
binderStats.reset();
for (ExportedCallStat callStat : callStats) {
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeInt(callStat.workSourceUid)
.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
.writeString(callStat.className)
.writeString(callStat.methodName)
.writeLong(callStat.callCount)
.writeLong(callStat.exceptionCount)
.writeLong(callStat.latencyMicros)
.writeLong(callStat.maxLatencyMicros)
.writeLong(callStat.cpuTimeMicros)
.writeLong(callStat.maxCpuTimeMicros)
.writeLong(callStat.maxReplySizeBytes)
.writeLong(callStat.maxRequestSizeBytes)
.writeLong(callStat.recordedCallCount)
.writeInt(callStat.screenInteractive ? 1 : 0)
.writeInt(callStat.callingUid)
.build();
pulledData.add(e);
}
return StatsManager.PULL_SUCCESS;
}
private void registerBinderCallsStatsExceptions() {
int tagId = FrameworkStatsLog.BINDER_CALLS_EXCEPTIONS;
mStatsManager.setPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullBinderCallsStatsExceptionsLocked(int atomTag, List<StatsEvent> pulledData) {
BinderCallsStatsService.Internal binderStats =
LocalServices.getService(BinderCallsStatsService.Internal.class);
if (binderStats == null) {
Slog.e(TAG, "failed to get binderStats");
return StatsManager.PULL_SKIP;
}
ArrayMap<String, Integer> exceptionStats = binderStats.getExportedExceptionStats();
// TODO: decouple binder calls exceptions with the rest of the binder calls data so that we
// can reset the exception stats.
for (Map.Entry<String, Integer> entry : exceptionStats.entrySet()) {
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeString(entry.getKey())
.writeInt(entry.getValue())
.build();
pulledData.add(e);
}
return StatsManager.PULL_SUCCESS;
}
private void registerLooperStats() {
int tagId = FrameworkStatsLog.LOOPER_STATS;
PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setAdditiveFields(new int[] {5, 6, 7, 8, 9})
.build();
mStatsManager.setPullAtomCallback(
tagId,
metadata,
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullLooperStatsLocked(int atomTag, List<StatsEvent> pulledData) {
LooperStats looperStats = LocalServices.getService(LooperStats.class);
if (looperStats == null) {
return StatsManager.PULL_SKIP;
}
List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
looperStats.reset();
for (LooperStats.ExportedEntry entry : entries) {
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeInt(entry.workSourceUid)
.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
.writeString(entry.handlerClassName)
.writeString(entry.threadName)
.writeString(entry.messageName)
.writeLong(entry.messageCount)
.writeLong(entry.exceptionCount)
.writeLong(entry.recordedMessageCount)
.writeLong(entry.totalLatencyMicros)
.writeLong(entry.cpuUsageMicros)
.writeBoolean(entry.isInteractive)
.writeLong(entry.maxCpuUsageMicros)
.writeLong(entry.maxLatencyMicros)
.writeLong(entry.recordedDelayMessageCount)
.writeLong(entry.delayMillis)
.writeLong(entry.maxDelayMillis)
.build();
pulledData.add(e);
}
return StatsManager.PULL_SUCCESS;
}
private void registerDiskStats() {
int tagId = FrameworkStatsLog.DISK_STATS;
mStatsManager.setPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullDiskStatsLocked(int atomTag, List<StatsEvent> pulledData) {
// Run a quick-and-dirty performance test: write 512 bytes
byte[] junk = new byte[512];
for (int i = 0; i < junk.length; i++) junk[i] = (byte) i; // Write nonzero bytes
File tmp = new File(Environment.getDataDirectory(), "system/statsdperftest.tmp");
FileOutputStream fos = null;
IOException error = null;
long before = SystemClock.elapsedRealtime();
try {
fos = new FileOutputStream(tmp);
fos.write(junk);
} catch (IOException e) {
error = e;
} finally {
try {
if (fos != null) fos.close();
} catch (IOException e) {
// Do nothing.
}
}
long latency = SystemClock.elapsedRealtime() - before;
if (tmp.exists()) tmp.delete();
if (error != null) {
Slog.e(TAG, "Error performing diskstats latency test");
latency = -1;
}
// File based encryption.
boolean fileBased = StorageManager.isFileEncryptedNativeOnly();
//Recent disk write speed. Binder call to storaged.
int writeSpeed = -1;
IStoraged storaged = getIStoragedService();
if (storaged == null) {
return StatsManager.PULL_SKIP;
}
try {
writeSpeed = storaged.getRecentPerf();
} catch (RemoteException e) {
Slog.e(TAG, "storaged not found");
}
// Add info pulledData.
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeLong(latency)
.writeBoolean(fileBased)
.writeInt(writeSpeed)
.build();
pulledData.add(e);
return StatsManager.PULL_SUCCESS;
}
private void registerDirectoryUsage() {
int tagId = FrameworkStatsLog.DIRECTORY_USAGE;
mStatsManager.setPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullDirectoryUsageLocked(int atomTag, List<StatsEvent> pulledData) {
StatFs statFsData = new StatFs(Environment.getDataDirectory().getAbsolutePath());
StatFs statFsSystem = new StatFs(Environment.getRootDirectory().getAbsolutePath());
StatFs statFsCache = new StatFs(Environment.getDownloadCacheDirectory().getAbsolutePath());
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeInt(FrameworkStatsLog.DIRECTORY_USAGE__DIRECTORY__DATA)
.writeLong(statFsData.getAvailableBytes())
.writeLong(statFsData.getTotalBytes())
.build();
pulledData.add(e);
e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeInt(FrameworkStatsLog.DIRECTORY_USAGE__DIRECTORY__CACHE)
.writeLong(statFsCache.getAvailableBytes())
.writeLong(statFsCache.getTotalBytes())
.build();
pulledData.add(e);
e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeInt(FrameworkStatsLog.DIRECTORY_USAGE__DIRECTORY__SYSTEM)
.writeLong(statFsSystem.getAvailableBytes())
.writeLong(statFsSystem.getTotalBytes())
.build();
pulledData.add(e);
return StatsManager.PULL_SUCCESS;
}
private void registerAppSize() {
int tagId = FrameworkStatsLog.APP_SIZE;
mStatsManager.setPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullAppSizeLocked(int atomTag, List<StatsEvent> pulledData) {
try {
String jsonStr = IoUtils.readFileAsString(DiskStatsLoggingService.DUMPSYS_CACHE_PATH);
JSONObject json = new JSONObject(jsonStr);
long cache_time = json.optLong(DiskStatsFileLogger.LAST_QUERY_TIMESTAMP_KEY, -1L);
JSONArray pkg_names = json.getJSONArray(DiskStatsFileLogger.PACKAGE_NAMES_KEY);
JSONArray app_sizes = json.getJSONArray(DiskStatsFileLogger.APP_SIZES_KEY);
JSONArray app_data_sizes = json.getJSONArray(DiskStatsFileLogger.APP_DATA_KEY);
JSONArray app_cache_sizes = json.getJSONArray(DiskStatsFileLogger.APP_CACHES_KEY);
// Sanity check: Ensure all 4 lists have the same length.
int length = pkg_names.length();
if (app_sizes.length() != length || app_data_sizes.length() != length
|| app_cache_sizes.length() != length) {
Slog.e(TAG, "formatting error in diskstats cache file!");
return StatsManager.PULL_SKIP;
}
for (int i = 0; i < length; i++) {
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeString(pkg_names.getString(i))
.writeLong(app_sizes.optLong(i, /* fallback */ -1L))
.writeLong(app_data_sizes.optLong(i, /* fallback */ -1L))
.writeLong(app_cache_sizes.optLong(i, /* fallback */ -1L))
.writeLong(cache_time)
.build();
pulledData.add(e);
}
} catch (IOException | JSONException e) {
Slog.w(TAG, "Unable to read diskstats cache file within pullAppSize");
return StatsManager.PULL_SKIP;
}
return StatsManager.PULL_SUCCESS;
}
private void registerCategorySize() {
int tagId = FrameworkStatsLog.CATEGORY_SIZE;
mStatsManager.setPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullCategorySizeLocked(int atomTag, List<StatsEvent> pulledData) {
try {
String jsonStr = IoUtils.readFileAsString(DiskStatsLoggingService.DUMPSYS_CACHE_PATH);
JSONObject json = new JSONObject(jsonStr);
long cacheTime = json.optLong(
DiskStatsFileLogger.LAST_QUERY_TIMESTAMP_KEY, /* fallback */ -1L);
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeInt(FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__APP_SIZE)
.writeLong(json.optLong(
DiskStatsFileLogger.APP_SIZE_AGG_KEY, /* fallback */ -1L))
.writeLong(cacheTime)
.build();
pulledData.add(e);
e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeInt(FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__APP_DATA_SIZE)
.writeLong(json.optLong(
DiskStatsFileLogger.APP_DATA_SIZE_AGG_KEY, /* fallback */ -1L))
.writeLong(cacheTime)
.build();
pulledData.add(e);
e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeInt(FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__APP_CACHE_SIZE)
.writeLong(json.optLong(
DiskStatsFileLogger.APP_CACHE_AGG_KEY, /* fallback */ -1L))
.writeLong(cacheTime)
.build();
pulledData.add(e);
e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeInt(FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__PHOTOS)
.writeLong(json.optLong(
DiskStatsFileLogger.PHOTOS_KEY, /* fallback */ -1L))
.writeLong(cacheTime)
.build();
pulledData.add(e);
e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeInt(FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__VIDEOS)
.writeLong(
json.optLong(DiskStatsFileLogger.VIDEOS_KEY, /* fallback */ -1L))
.writeLong(cacheTime)
.build();
pulledData.add(e);
e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeInt(FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__AUDIO)
.writeLong(json.optLong(
DiskStatsFileLogger.AUDIO_KEY, /* fallback */ -1L))
.writeLong(cacheTime)
.build();
pulledData.add(e);
e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeInt(FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__DOWNLOADS)
.writeLong(
json.optLong(DiskStatsFileLogger.DOWNLOADS_KEY, /* fallback */ -1L))
.writeLong(cacheTime)
.build();
pulledData.add(e);
e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeInt(FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__SYSTEM)
.writeLong(json.optLong(
DiskStatsFileLogger.SYSTEM_KEY, /* fallback */ -1L))
.writeLong(cacheTime)
.build();
pulledData.add(e);
e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeInt(FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__OTHER)
.writeLong(json.optLong(
DiskStatsFileLogger.MISC_KEY, /* fallback */ -1L))
.writeLong(cacheTime)
.build();
pulledData.add(e);
} catch (IOException | JSONException e) {
Slog.w(TAG, "Unable to read diskstats cache file within pullCategorySize");
return StatsManager.PULL_SKIP;
}
return StatsManager.PULL_SUCCESS;
}
private void registerNumFingerprintsEnrolled() {
int tagId = FrameworkStatsLog.NUM_FINGERPRINTS_ENROLLED;
mStatsManager.setPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
private void registerNumFacesEnrolled() {
int tagId = FrameworkStatsLog.NUM_FACES_ENROLLED;
mStatsManager.setPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
private int pullNumBiometricsEnrolledLocked(int modality, int atomTag,
List<StatsEvent> pulledData) {
final PackageManager pm = mContext.getPackageManager();
FingerprintManager fingerprintManager = null;
FaceManager faceManager = null;
if (pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
fingerprintManager = mContext.getSystemService(FingerprintManager.class);
}
if (pm.hasSystemFeature(PackageManager.FEATURE_FACE)) {
faceManager = mContext.getSystemService(FaceManager.class);
}
if (modality == BiometricsProtoEnums.MODALITY_FINGERPRINT && fingerprintManager == null) {
return StatsManager.PULL_SKIP;
}
if (modality == BiometricsProtoEnums.MODALITY_FACE && faceManager == null) {
return StatsManager.PULL_SKIP;
}
UserManager userManager = mContext.getSystemService(UserManager.class);
if (userManager == null) {
return StatsManager.PULL_SKIP;
}
final long token = Binder.clearCallingIdentity();
try {
for (UserInfo user : userManager.getUsers()) {
final int userId = user.getUserHandle().getIdentifier();
int numEnrolled = 0;
if (modality == BiometricsProtoEnums.MODALITY_FINGERPRINT) {
numEnrolled = fingerprintManager.getEnrolledFingerprints(userId).size();
} else if (modality == BiometricsProtoEnums.MODALITY_FACE) {
numEnrolled = faceManager.getEnrolledFaces(userId).size();
} else {
return StatsManager.PULL_SKIP;
}
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeInt(userId)
.writeInt(numEnrolled)
.build();
pulledData.add(e);
}
} finally {
Binder.restoreCallingIdentity(token);
}
return StatsManager.PULL_SUCCESS;
}
private void registerProcStats() {
int tagId = FrameworkStatsLog.PROC_STATS;
mStatsManager.setPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
private void registerProcStatsPkgProc() {
int tagId = FrameworkStatsLog.PROC_STATS_PKG_PROC;
mStatsManager.setPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
private int pullProcStatsLocked(int section, int atomTag, List<StatsEvent> pulledData) {
IProcessStats processStatsService = getIProcessStatsService();
if (processStatsService == null) {
return StatsManager.PULL_SKIP;
}
final long token = Binder.clearCallingIdentity();
try {
// force procstats to flush & combine old files into one store
long lastHighWaterMark = readProcStatsHighWaterMark(section);
ProtoOutputStream[] protoStreams = new ProtoOutputStream[MAX_PROCSTATS_SHARDS];
for (int i = 0; i < protoStreams.length; i++) {
protoStreams[i] = new ProtoOutputStream();
}
ProcessStats procStats = new ProcessStats(false);
// Force processStatsService to aggregate all in-storage and in-memory data.
long highWaterMark = processStatsService.getCommittedStatsMerged(
lastHighWaterMark, section, true, null, procStats);
procStats.dumpAggregatedProtoForStatsd(protoStreams, MAX_PROCSTATS_RAW_SHARD_SIZE);
for (int i = 0; i < protoStreams.length; i++) {
byte[] bytes = protoStreams[i].getBytes(); // cache the value
if (bytes.length > 0) {
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeByteArray(bytes)
// This is a shard ID, and is specified in the metric definition to be
// a dimension. This will result in statsd using RANDOM_ONE_SAMPLE to
// keep all the shards, as it thinks each shard is a different dimension
// of data.
.writeInt(i)
.build();
pulledData.add(e);
}
}
new File(mBaseDir.getAbsolutePath() + "/" + section + "_" + lastHighWaterMark)
.delete();
new File(mBaseDir.getAbsolutePath() + "/" + section + "_" + highWaterMark)
.createNewFile();
} catch (RemoteException | IOException e) {
Slog.e(TAG, "Getting procstats failed: ", e);
return StatsManager.PULL_SKIP;
} finally {
Binder.restoreCallingIdentity(token);
}
return StatsManager.PULL_SUCCESS;
}
// read high watermark for section
private long readProcStatsHighWaterMark(int section) {
try {
File[] files = mBaseDir.listFiles((d, name) -> {
return name.toLowerCase().startsWith(String.valueOf(section) + '_');
});
if (files == null || files.length == 0) {
return 0;
}
if (files.length > 1) {
Slog.e(TAG, "Only 1 file expected for high water mark. Found " + files.length);
}
return Long.valueOf(files[0].getName().split("_")[1]);
} catch (SecurityException e) {
Slog.e(TAG, "Failed to get procstats high watermark file.", e);
} catch (NumberFormatException e) {
Slog.e(TAG, "Failed to parse file name.", e);
}
return 0;
}
private void registerDiskIO() {
int tagId = FrameworkStatsLog.DISK_IO;
PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setAdditiveFields(new int[] {2, 3, 4, 5, 6, 7, 8, 9, 10, 11})
.setCoolDownMillis(3 * MILLIS_PER_SEC)
.build();
mStatsManager.setPullAtomCallback(
tagId,
metadata,
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullDiskIOLocked(int atomTag, List<StatsEvent> pulledData) {
mStoragedUidIoStatsReader.readAbsolute((uid, fgCharsRead, fgCharsWrite, fgBytesRead,
fgBytesWrite, bgCharsRead, bgCharsWrite, bgBytesRead, bgBytesWrite,
fgFsync, bgFsync) -> {
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeInt(uid)
.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
.writeLong(fgCharsRead)
.writeLong(fgCharsWrite)
.writeLong(fgBytesRead)
.writeLong(fgBytesWrite)
.writeLong(bgCharsRead)
.writeLong(bgCharsWrite)
.writeLong(bgBytesRead)
.writeLong(bgBytesWrite)
.writeLong(fgFsync)
.writeLong(bgFsync)
.build();
pulledData.add(e);
});
return StatsManager.PULL_SUCCESS;
}
private void registerPowerProfile() {
int tagId = FrameworkStatsLog.POWER_PROFILE;
mStatsManager.setPullAtomCallback(
tagId,
/* PullAtomMetadata */ null,
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullPowerProfileLocked(int atomTag, List<StatsEvent> pulledData) {
PowerProfile powerProfile = new PowerProfile(mContext);
ProtoOutputStream proto = new ProtoOutputStream();
powerProfile.dumpDebug(proto);
proto.flush();
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeByteArray(proto.getBytes())
.build();
pulledData.add(e);
return StatsManager.PULL_SUCCESS;
}
private void registerProcessCpuTime() {
int tagId = FrameworkStatsLog.PROCESS_CPU_TIME;
// Min cool-down is 5 sec, in line with what ActivityManagerService uses.
PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setCoolDownMillis(5 * MILLIS_PER_SEC)
.build();
mStatsManager.setPullAtomCallback(
tagId,
metadata,
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullProcessCpuTimeLocked(int atomTag, List<StatsEvent> pulledData) {
if (mProcessCpuTracker == null) {
mProcessCpuTracker = new ProcessCpuTracker(false);
mProcessCpuTracker.init();
}
mProcessCpuTracker.update();
for (int i = 0; i < mProcessCpuTracker.countStats(); i++) {
ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i);
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeInt(st.uid)
.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
.writeString(st.name)
.writeLong(st.base_utime)
.writeLong(st.base_stime)
.build();
pulledData.add(e);
}
return StatsManager.PULL_SUCCESS;
}
private void registerCpuTimePerThreadFreq() {
int tagId = FrameworkStatsLog.CPU_TIME_PER_THREAD_FREQ;
PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setAdditiveFields(new int[] {7, 9, 11, 13, 15, 17, 19, 21})
.build();
mStatsManager.setPullAtomCallback(
tagId,
metadata,
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullCpuTimePerThreadFreqLocked(int atomTag, List<StatsEvent> pulledData) {
if (this.mKernelCpuThreadReader == null) {
Slog.e(TAG, "mKernelCpuThreadReader is null");
return StatsManager.PULL_SKIP;
}
ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsages =
this.mKernelCpuThreadReader.getProcessCpuUsageDiffed();
if (processCpuUsages == null) {
Slog.e(TAG, "processCpuUsages is null");
return StatsManager.PULL_SKIP;
}
int[] cpuFrequencies = mKernelCpuThreadReader.getCpuFrequenciesKhz();
if (cpuFrequencies.length > CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES) {
String message = "Expected maximum " + CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES
+ " frequencies, but got " + cpuFrequencies.length;
Slog.w(TAG, message);
return StatsManager.PULL_SKIP;
}
for (int i = 0; i < processCpuUsages.size(); i++) {
KernelCpuThreadReader.ProcessCpuUsage processCpuUsage = processCpuUsages.get(i);
ArrayList<KernelCpuThreadReader.ThreadCpuUsage> threadCpuUsages =
processCpuUsage.threadCpuUsages;
for (int j = 0; j < threadCpuUsages.size(); j++) {
KernelCpuThreadReader.ThreadCpuUsage threadCpuUsage = threadCpuUsages.get(j);
if (threadCpuUsage.usageTimesMillis.length != cpuFrequencies.length) {
String message = "Unexpected number of usage times,"
+ " expected " + cpuFrequencies.length
+ " but got " + threadCpuUsage.usageTimesMillis.length;
Slog.w(TAG, message);
return StatsManager.PULL_SKIP;
}
StatsEvent.Builder e = StatsEvent.newBuilder();
e.setAtomId(atomTag);
e.writeInt(processCpuUsage.uid);
e.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
e.writeInt(processCpuUsage.processId);
e.writeInt(threadCpuUsage.threadId);
e.writeString(processCpuUsage.processName);
e.writeString(threadCpuUsage.threadName);
for (int k = 0; k < CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES; k++) {
if (k < cpuFrequencies.length) {
e.writeInt(cpuFrequencies[k]);
e.writeInt(threadCpuUsage.usageTimesMillis[k]);
} else {
// If we have no more frequencies to write, we still must write empty data.
// We know that this data is empty (and not just zero) because all
// frequencies are expected to be greater than zero
e.writeInt(0);
e.writeInt(0);
}
}
pulledData.add(e.build());
}
}
return StatsManager.PULL_SUCCESS;
}
private BatteryStatsHelper getBatteryStatsHelper() {
synchronized (mBatteryStatsHelperLock) {
if (mBatteryStatsHelper == null) {
final long callingToken = Binder.clearCallingIdentity();
try {
// clearCallingIdentity required for BatteryStatsHelper.checkWifiOnly().
mBatteryStatsHelper = new BatteryStatsHelper(mContext, false);
} finally {
Binder.restoreCallingIdentity(callingToken);
}
mBatteryStatsHelper.create((Bundle) null);
}
long currentTime = SystemClock.elapsedRealtime();
if (currentTime - mBatteryStatsHelperTimestampMs
>= MAX_BATTERY_STATS_HELPER_FREQUENCY_MS) {
// Load BatteryStats and do all the calculations.
mBatteryStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED,
UserHandle.USER_ALL);
// Calculations are done so we don't need to save the raw BatteryStats data in RAM.
mBatteryStatsHelper.clearStats();
mBatteryStatsHelperTimestampMs = currentTime;
}
}
return mBatteryStatsHelper;
}
private long milliAmpHrsToNanoAmpSecs(double mAh) {
return (long) (mAh * MILLI_AMP_HR_TO_NANO_AMP_SECS + 0.5);
}
private void registerDeviceCalculatedPowerUse() {
int tagId = FrameworkStatsLog.DEVICE_CALCULATED_POWER_USE;
mStatsManager.setPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullDeviceCalculatedPowerUseLocked(int atomTag, List<StatsEvent> pulledData) {
BatteryStatsHelper bsHelper = getBatteryStatsHelper();
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeLong(milliAmpHrsToNanoAmpSecs(bsHelper.getComputedPower()))
.build();
pulledData.add(e);
return StatsManager.PULL_SUCCESS;
}
private void registerDeviceCalculatedPowerBlameUid() {
int tagId = FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_UID;
mStatsManager.setPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullDeviceCalculatedPowerBlameUidLocked(int atomTag, List<StatsEvent> pulledData) {
final List<BatterySipper> sippers = getBatteryStatsHelper().getUsageList();
if (sippers == null) {
return StatsManager.PULL_SKIP;
}
for (BatterySipper bs : sippers) {
if (bs.drainType != bs.drainType.APP) {
continue;
}
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeInt(bs.uidObj.getUid())
.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
.writeLong(milliAmpHrsToNanoAmpSecs(bs.totalPowerMah))
.build();
pulledData.add(e);
}
return StatsManager.PULL_SUCCESS;
}
private void registerDeviceCalculatedPowerBlameOther() {
int tagId = FrameworkStatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER;
mStatsManager.setPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullDeviceCalculatedPowerBlameOtherLocked(int atomTag, List<StatsEvent> pulledData) {
final List<BatterySipper> sippers = getBatteryStatsHelper().getUsageList();
if (sippers == null) {
return StatsManager.PULL_SKIP;
}
for (BatterySipper bs : sippers) {
if (bs.drainType == bs.drainType.APP) {
continue; // This is a separate atom; see pullDeviceCalculatedPowerBlameUid().
}
if (bs.drainType == bs.drainType.USER) {
continue; // This is not supported. We purposefully calculate over USER_ALL.
}
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeInt(bs.drainType.ordinal())
.writeLong(milliAmpHrsToNanoAmpSecs(bs.totalPowerMah))
.build();
pulledData.add(e);
}
return StatsManager.PULL_SUCCESS;
}
private void registerDebugElapsedClock() {
int tagId = FrameworkStatsLog.DEBUG_ELAPSED_CLOCK;
PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setAdditiveFields(new int[] {1, 2, 3, 4})
.build();
mStatsManager.setPullAtomCallback(
tagId,
metadata,
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullDebugElapsedClockLocked(int atomTag, List<StatsEvent> pulledData) {
final long elapsedMillis = SystemClock.elapsedRealtime();
final long clockDiffMillis = mDebugElapsedClockPreviousValue == 0
? 0 : elapsedMillis - mDebugElapsedClockPreviousValue;
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeLong(mDebugElapsedClockPullCount)
.writeLong(elapsedMillis)
// Log it twice to be able to test multi-value aggregation from ValueMetric.
.writeLong(elapsedMillis)
.writeLong(clockDiffMillis)
.writeInt(1 /* always set */)
.build();
pulledData.add(e);
if (mDebugElapsedClockPullCount % 2 == 1) {
StatsEvent e2 = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeLong(mDebugElapsedClockPullCount)
.writeLong(elapsedMillis)
// Log it twice to be able to test multi-value aggregation from ValueMetric.
.writeLong(elapsedMillis)
.writeLong(clockDiffMillis)
.writeInt(2 /* set on odd pulls */)
.build();
pulledData.add(e2);
}
mDebugElapsedClockPullCount++;
mDebugElapsedClockPreviousValue = elapsedMillis;
return StatsManager.PULL_SUCCESS;
}
private void registerDebugFailingElapsedClock() {
int tagId = FrameworkStatsLog.DEBUG_FAILING_ELAPSED_CLOCK;
PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setAdditiveFields(new int[] {1, 2, 3, 4})
.build();
mStatsManager.setPullAtomCallback(
tagId,
metadata,
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullDebugFailingElapsedClockLocked(int atomTag, List<StatsEvent> pulledData) {
final long elapsedMillis = SystemClock.elapsedRealtime();
// Fails every 5 buckets.
if (mDebugFailingElapsedClockPullCount++ % 5 == 0) {
mDebugFailingElapsedClockPreviousValue = elapsedMillis;
Slog.e(TAG, "Failing debug elapsed clock");
return StatsManager.PULL_SKIP;
}
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeLong(mDebugFailingElapsedClockPullCount)
.writeLong(elapsedMillis)
// Log it twice to be able to test multi-value aggregation from ValueMetric.
.writeLong(elapsedMillis)
.writeLong(mDebugFailingElapsedClockPreviousValue == 0
? 0 : elapsedMillis - mDebugFailingElapsedClockPreviousValue)
.build();
pulledData.add(e);
mDebugFailingElapsedClockPreviousValue = elapsedMillis;
return StatsManager.PULL_SUCCESS;
}
private void registerBuildInformation() {
int tagId = FrameworkStatsLog.BUILD_INFORMATION;
mStatsManager.setPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullBuildInformationLocked(int atomTag, List<StatsEvent> pulledData) {
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeString(Build.FINGERPRINT)
.writeString(Build.BRAND)
.writeString(Build.PRODUCT)
.writeString(Build.DEVICE)
.writeString(Build.VERSION.RELEASE_OR_CODENAME)
.writeString(Build.ID)
.writeString(Build.VERSION.INCREMENTAL)
.writeString(Build.TYPE)
.writeString(Build.TAGS)
.build();
pulledData.add(e);
return StatsManager.PULL_SUCCESS;
}
private void registerRoleHolder() {
int tagId = FrameworkStatsLog.ROLE_HOLDER;
mStatsManager.setPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
// Add a RoleHolder atom for each package that holds a role.
int pullRoleHolderLocked(int atomTag, List<StatsEvent> pulledData) {
long callingToken = Binder.clearCallingIdentity();
try {
PackageManager pm = mContext.getPackageManager();
RoleManagerInternal rmi = LocalServices.getService(RoleManagerInternal.class);
List<UserInfo> users = mContext.getSystemService(UserManager.class).getUsers();
int numUsers = users.size();
for (int userNum = 0; userNum < numUsers; userNum++) {
int userId = users.get(userNum).getUserHandle().getIdentifier();
ArrayMap<String, ArraySet<String>> roles = rmi.getRolesAndHolders(userId);
int numRoles = roles.size();
for (int roleNum = 0; roleNum < numRoles; roleNum++) {
String roleName = roles.keyAt(roleNum);
ArraySet<String> holders = roles.valueAt(roleNum);
int numHolders = holders.size();
for (int holderNum = 0; holderNum < numHolders; holderNum++) {
String holderName = holders.valueAt(holderNum);
PackageInfo pkg;
try {
pkg = pm.getPackageInfoAsUser(holderName, 0, userId);
} catch (PackageManager.NameNotFoundException e) {
Slog.w(TAG, "Role holder " + holderName + " not found");
return StatsManager.PULL_SKIP;
}
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeInt(pkg.applicationInfo.uid)
.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
.writeString(holderName)
.writeString(roleName)
.build();
pulledData.add(e);
}
}
}
} finally {
Binder.restoreCallingIdentity(callingToken);
}
return StatsManager.PULL_SUCCESS;
}
private void registerDangerousPermissionState() {
int tagId = FrameworkStatsLog.DANGEROUS_PERMISSION_STATE;
mStatsManager.setPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullDangerousPermissionStateLocked(int atomTag, List<StatsEvent> pulledData) {
final long token = Binder.clearCallingIdentity();
float samplingRate = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_PERMISSIONS,
DANGEROUS_PERMISSION_STATE_SAMPLE_RATE, 0.015f);
Set<Integer> reportedUids = new HashSet<>();
try {
PackageManager pm = mContext.getPackageManager();
List<UserInfo> users = mContext.getSystemService(UserManager.class).getUsers();
int numUsers = users.size();
for (int userNum = 0; userNum < numUsers; userNum++) {
UserHandle user = users.get(userNum).getUserHandle();
List<PackageInfo> pkgs = pm.getInstalledPackagesAsUser(
PackageManager.GET_PERMISSIONS, user.getIdentifier());
int numPkgs = pkgs.size();
for (int pkgNum = 0; pkgNum < numPkgs; pkgNum++) {
PackageInfo pkg = pkgs.get(pkgNum);
if (pkg.requestedPermissions == null) {
continue;
}
if (reportedUids.contains(pkg.applicationInfo.uid)) {
// do not report same uid twice
continue;
}
reportedUids.add(pkg.applicationInfo.uid);
if (atomTag == FrameworkStatsLog.DANGEROUS_PERMISSION_STATE_SAMPLED
&& ThreadLocalRandom.current().nextFloat() > samplingRate) {
continue;
}
int numPerms = pkg.requestedPermissions.length;
for (int permNum = 0; permNum < numPerms; permNum++) {
String permName = pkg.requestedPermissions[permNum];
PermissionInfo permissionInfo;
int permissionFlags = 0;
try {
permissionInfo = pm.getPermissionInfo(permName, 0);
permissionFlags =
pm.getPermissionFlags(permName, pkg.packageName, user);
} catch (PackageManager.NameNotFoundException ignored) {
continue;
}
if (permissionInfo.getProtection() != PROTECTION_DANGEROUS) {
continue;
}
if (permName.startsWith(COMMON_PERMISSION_PREFIX)) {
permName = permName.substring(COMMON_PERMISSION_PREFIX.length());
}
StatsEvent.Builder e = StatsEvent.newBuilder();
e.setAtomId(atomTag);
e.writeString(permName);
e.writeInt(pkg.applicationInfo.uid);
e.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
if (atomTag == FrameworkStatsLog.DANGEROUS_PERMISSION_STATE) {
e.writeString("");
}
e.writeBoolean((pkg.requestedPermissionsFlags[permNum]
& REQUESTED_PERMISSION_GRANTED) != 0);
e.writeInt(permissionFlags);
pulledData.add(e.build());
}
}
}
} catch (Throwable t) {
Log.e(TAG, "Could not read permissions", t);
return StatsManager.PULL_SKIP;
} finally {
Binder.restoreCallingIdentity(token);
}
return StatsManager.PULL_SUCCESS;
}
private void registerTimeZoneDataInfo() {
int tagId = FrameworkStatsLog.TIME_ZONE_DATA_INFO;
mStatsManager.setPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullTimeZoneDataInfoLocked(int atomTag, List<StatsEvent> pulledData) {
String tzDbVersion = "Unknown";
try {
tzDbVersion = android.icu.util.TimeZone.getTZDataVersion();
} catch (MissingResourceException e) {
Slog.e(TAG, "Getting tzdb version failed: ", e);
return StatsManager.PULL_SKIP;
}
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeString(tzDbVersion)
.build();
pulledData.add(e);
return StatsManager.PULL_SUCCESS;
}
private void registerExternalStorageInfo() {
int tagId = FrameworkStatsLog.EXTERNAL_STORAGE_INFO;
mStatsManager.setPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullExternalStorageInfoLocked(int atomTag, List<StatsEvent> pulledData) {
if (mStorageManager == null) {
return StatsManager.PULL_SKIP;
}
List<VolumeInfo> volumes = mStorageManager.getVolumes();
for (VolumeInfo vol : volumes) {
final String envState = VolumeInfo.getEnvironmentForState(vol.getState());
final DiskInfo diskInfo = vol.getDisk();
if (diskInfo != null && envState.equals(Environment.MEDIA_MOUNTED)) {
// Get the type of the volume, if it is adoptable or portable.
int volumeType = FrameworkStatsLog.EXTERNAL_STORAGE_INFO__VOLUME_TYPE__OTHER;
if (vol.getType() == TYPE_PUBLIC) {
volumeType = FrameworkStatsLog.EXTERNAL_STORAGE_INFO__VOLUME_TYPE__PUBLIC;
} else if (vol.getType() == TYPE_PRIVATE) {
volumeType = FrameworkStatsLog.EXTERNAL_STORAGE_INFO__VOLUME_TYPE__PRIVATE;
}
// Get the type of external storage inserted in the device (sd cards, usb, etc.)
int externalStorageType;
if (diskInfo.isSd()) {
externalStorageType = StorageEnums.SD_CARD;
} else if (diskInfo.isUsb()) {
externalStorageType = StorageEnums.USB;
} else {
externalStorageType = StorageEnums.OTHER;
}
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeInt(externalStorageType)
.writeInt(volumeType)
.writeLong(diskInfo.size)
.build();
pulledData.add(e);
}
}
return StatsManager.PULL_SUCCESS;
}
private void registerAppsOnExternalStorageInfo() {
int tagId = FrameworkStatsLog.APPS_ON_EXTERNAL_STORAGE_INFO;
mStatsManager.setPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullAppsOnExternalStorageInfoLocked(int atomTag, List<StatsEvent> pulledData) {
if (mStorageManager == null) {
return StatsManager.PULL_SKIP;
}
PackageManager pm = mContext.getPackageManager();
List<ApplicationInfo> apps = pm.getInstalledApplications(/*flags=*/ 0);
for (ApplicationInfo appInfo : apps) {
UUID storageUuid = appInfo.storageUuid;
if (storageUuid == null) {
continue;
}
VolumeInfo volumeInfo = mStorageManager.findVolumeByUuid(
appInfo.storageUuid.toString());
if (volumeInfo == null) {
continue;
}
DiskInfo diskInfo = volumeInfo.getDisk();
if (diskInfo == null) {
continue;
}
int externalStorageType = -1;
if (diskInfo.isSd()) {
externalStorageType = StorageEnums.SD_CARD;
} else if (diskInfo.isUsb()) {
externalStorageType = StorageEnums.USB;
} else if (appInfo.isExternal()) {
externalStorageType = StorageEnums.OTHER;
}
// App is installed on external storage.
if (externalStorageType != -1) {
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeInt(externalStorageType)
.writeString(appInfo.packageName)
.build();
pulledData.add(e);
}
}
return StatsManager.PULL_SUCCESS;
}
private void registerFaceSettings() {
int tagId = FrameworkStatsLog.FACE_SETTINGS;
mStatsManager.setPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullFaceSettingsLocked(int atomTag, List<StatsEvent> pulledData) {
final long callingToken = Binder.clearCallingIdentity();
try {
UserManager manager = mContext.getSystemService(UserManager.class);
if (manager == null) {
return StatsManager.PULL_SKIP;
}
List<UserInfo> users = manager.getUsers();
int numUsers = users.size();
FaceManager faceManager = mContext.getSystemService(FaceManager.class);
for (int userNum = 0; userNum < numUsers; userNum++) {
int userId = users.get(userNum).getUserHandle().getIdentifier();
if (faceManager != null) {
// Store the current setting from the Face HAL, and upon next upload the value
// reported will be correct (given the user did not modify it).
faceManager.getFeature(userId, BiometricFaceConstants.FEATURE_REQUIRE_ATTENTION,
new GetFeatureCallback() {
@Override
public void onCompleted(boolean success, int feature,
boolean value) {
if (feature == FaceManager.FEATURE_REQUIRE_ATTENTION
&& success) {
Settings.Secure.putIntForUser(mContext.getContentResolver(),
Settings.Secure.FACE_UNLOCK_ATTENTION_REQUIRED,
value ? 1 : 0, userId);
}
}
}
);
}
int unlockKeyguardEnabled = Settings.Secure.getIntForUser(
mContext.getContentResolver(),
Settings.Secure.FACE_UNLOCK_KEYGUARD_ENABLED, 1, userId);
int unlockDismissesKeyguard = Settings.Secure.getIntForUser(
mContext.getContentResolver(),
Settings.Secure.FACE_UNLOCK_DISMISSES_KEYGUARD, 1, userId);
int unlockAttentionRequired = Settings.Secure.getIntForUser(
mContext.getContentResolver(),
Settings.Secure.FACE_UNLOCK_ATTENTION_REQUIRED, 0, userId);
int unlockAppEnabled = Settings.Secure.getIntForUser(
mContext.getContentResolver(),
Settings.Secure.FACE_UNLOCK_APP_ENABLED, 1, userId);
int unlockAlwaysRequireConfirmation = Settings.Secure.getIntForUser(
mContext.getContentResolver(),
Settings.Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION, 0, userId);
int unlockDiversityRequired = Settings.Secure.getIntForUser(
mContext.getContentResolver(),
Settings.Secure.FACE_UNLOCK_DIVERSITY_REQUIRED, 1, userId);
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeBoolean(unlockKeyguardEnabled != 0)
.writeBoolean(unlockDismissesKeyguard != 0)
.writeBoolean(unlockAttentionRequired != 0)
.writeBoolean(unlockAppEnabled != 0)
.writeBoolean(unlockAlwaysRequireConfirmation != 0)
.writeBoolean(unlockDiversityRequired != 0)
.build();
pulledData.add(e);
}
} finally {
Binder.restoreCallingIdentity(callingToken);
}
return StatsManager.PULL_SUCCESS;
}
private void registerAppOps() {
int tagId = FrameworkStatsLog.APP_OPS;
mStatsManager.setPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
private void registerRuntimeAppOpAccessMessage() {
int tagId = FrameworkStatsLog.RUNTIME_APP_OP_ACCESS;
mStatsManager.setPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
private class AppOpEntry {
public final String mPackageName;
public final String mAttributionTag;
public final int mUid;
public final HistoricalOp mOp;
public final int mHash;
AppOpEntry(String packageName, @Nullable String attributionTag, HistoricalOp op, int uid) {
mPackageName = packageName;
mAttributionTag = attributionTag;
mUid = uid;
mOp = op;
mHash = ((op.getOpCode() * 961
+ (attributionTag == null ? 0 : attributionTag.hashCode()) * 31
+ packageName.hashCode() + RANDOM_SEED) & 0x7fffffff) % 100;
}
}
int pullAppOpsLocked(int atomTag, List<StatsEvent> pulledData) {
final long token = Binder.clearCallingIdentity();
try {
AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
CompletableFuture<HistoricalOps> ops = new CompletableFuture<>();
HistoricalOpsRequest histOpsRequest = new HistoricalOpsRequest.Builder(0,
Long.MAX_VALUE).setFlags(OP_FLAGS_PULLED).build();
appOps.getHistoricalOps(histOpsRequest, AsyncTask.THREAD_POOL_EXECUTOR, ops::complete);
HistoricalOps histOps = ops.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS,
TimeUnit.MILLISECONDS);
List<AppOpEntry> opsList = processHistoricalOps(histOps, atomTag, 100);
int samplingRate = sampleAppOps(pulledData, opsList, atomTag, 100);
if (samplingRate != 100) {
Slog.e(TAG, "Atom 10060 downsampled - too many dimensions");
}
} catch (Throwable t) {
// TODO: catch exceptions at a more granular level
Slog.e(TAG, "Could not read appops", t);
return StatsManager.PULL_SKIP;
} finally {
Binder.restoreCallingIdentity(token);
}
return StatsManager.PULL_SUCCESS;
}
private int sampleAppOps(List<StatsEvent> pulledData, List<AppOpEntry> opsList, int atomTag,
int samplingRate) {
int nOps = opsList.size();
for (int i = 0; i < nOps; i++) {
AppOpEntry entry = opsList.get(i);
if (entry.mHash >= samplingRate) {
continue;
}
StatsEvent.Builder e = StatsEvent.newBuilder();
e.setAtomId(atomTag);
e.writeInt(entry.mUid);
e.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
e.writeString(entry.mPackageName);
if (atomTag == FrameworkStatsLog.ATTRIBUTED_APP_OPS) {
e.writeString(entry.mAttributionTag);
}
e.writeInt(entry.mOp.getOpCode());
e.writeLong(entry.mOp.getForegroundAccessCount(OP_FLAGS_PULLED));
e.writeLong(entry.mOp.getBackgroundAccessCount(OP_FLAGS_PULLED));
e.writeLong(entry.mOp.getForegroundRejectCount(OP_FLAGS_PULLED));
e.writeLong(entry.mOp.getBackgroundRejectCount(OP_FLAGS_PULLED));
e.writeLong(entry.mOp.getForegroundAccessDuration(OP_FLAGS_PULLED));
e.writeLong(entry.mOp.getBackgroundAccessDuration(OP_FLAGS_PULLED));
e.writeBoolean(mDangerousAppOpsList.contains(entry.mOp.getOpCode()));
if (atomTag == FrameworkStatsLog.ATTRIBUTED_APP_OPS) {
e.writeInt(samplingRate);
}
pulledData.add(e.build());
}
if (pulledData.size() > DIMENSION_KEY_SIZE_HARD_LIMIT) {
int adjustedSamplingRate = constrain(
samplingRate * DIMENSION_KEY_SIZE_SOFT_LIMIT / pulledData.size(), 0,
samplingRate - 1);
pulledData.clear();
return sampleAppOps(pulledData, opsList, atomTag, adjustedSamplingRate);
}
return samplingRate;
}
private void registerAttributedAppOps() {
int tagId = FrameworkStatsLog.ATTRIBUTED_APP_OPS;
mStatsManager.setPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullAttributedAppOpsLocked(int atomTag, List<StatsEvent> pulledData) {
final long token = Binder.clearCallingIdentity();
try {
AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
CompletableFuture<HistoricalOps> ops = new CompletableFuture<>();
HistoricalOpsRequest histOpsRequest =
new HistoricalOpsRequest.Builder(0, Long.MAX_VALUE).setFlags(
OP_FLAGS_PULLED).build();
appOps.getHistoricalOps(histOpsRequest, AsyncTask.THREAD_POOL_EXECUTOR, ops::complete);
HistoricalOps histOps = ops.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS,
TimeUnit.MILLISECONDS);
if (mAppOpsSamplingRate == 0) {
mContext.getMainThreadHandler().postDelayed(new Runnable() {
@Override
public void run() {
try {
estimateAppOpsSamplingRate();
} catch (Throwable e) {
Slog.e(TAG, "AppOps sampling ratio estimation failed: ", e);
synchronized (mAttributedAppOpsLock) {
mAppOpsSamplingRate = min(mAppOpsSamplingRate, 10);
}
}
}
}, APP_OPS_SAMPLING_INITIALIZATION_DELAY_MILLIS);
mAppOpsSamplingRate = 100;
}
List<AppOpEntry> opsList =
processHistoricalOps(histOps, atomTag, mAppOpsSamplingRate);
int newSamplingRate = sampleAppOps(pulledData, opsList, atomTag, mAppOpsSamplingRate);
mAppOpsSamplingRate = min(mAppOpsSamplingRate, newSamplingRate);
} catch (Throwable t) {
// TODO: catch exceptions at a more granular level
Slog.e(TAG, "Could not read appops", t);
return StatsManager.PULL_SKIP;
} finally {
Binder.restoreCallingIdentity(token);
}
return StatsManager.PULL_SUCCESS;
}
private void estimateAppOpsSamplingRate() throws Exception {
int appOpsTargetCollectionSize = DeviceConfig.getInt(
DeviceConfig.NAMESPACE_PERMISSIONS, APP_OPS_TARGET_COLLECTION_SIZE,
APP_OPS_SIZE_ESTIMATE);
AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
CompletableFuture<HistoricalOps> ops = new CompletableFuture<>();
HistoricalOpsRequest histOpsRequest =
new HistoricalOpsRequest.Builder(
Math.max(Instant.now().minus(1, ChronoUnit.DAYS).toEpochMilli(), 0),
Long.MAX_VALUE).setFlags(
OP_FLAGS_PULLED).build();
appOps.getHistoricalOps(histOpsRequest, AsyncTask.THREAD_POOL_EXECUTOR, ops::complete);
HistoricalOps histOps = ops.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS,
TimeUnit.MILLISECONDS);
List<AppOpEntry> opsList =
processHistoricalOps(histOps, FrameworkStatsLog.ATTRIBUTED_APP_OPS, 100);
long estimatedSize = 0;
int nOps = opsList.size();
for (int i = 0; i < nOps; i++) {
AppOpEntry entry = opsList.get(i);
estimatedSize += 32 + entry.mPackageName.length() + (entry.mAttributionTag == null ? 1
: entry.mAttributionTag.length());
}
int estimatedSamplingRate = (int) constrain(
appOpsTargetCollectionSize * 100 / estimatedSize, 0, 100);
synchronized (mAttributedAppOpsLock) {
mAppOpsSamplingRate = min(mAppOpsSamplingRate, estimatedSamplingRate);
}
}
private List<AppOpEntry> processHistoricalOps(
HistoricalOps histOps, int atomTag, int samplingRatio) {
List<AppOpEntry> opsList = new ArrayList<>();
for (int uidIdx = 0; uidIdx < histOps.getUidCount(); uidIdx++) {
final HistoricalUidOps uidOps = histOps.getUidOpsAt(uidIdx);
final int uid = uidOps.getUid();
for (int pkgIdx = 0; pkgIdx < uidOps.getPackageCount(); pkgIdx++) {
final HistoricalPackageOps packageOps = uidOps.getPackageOpsAt(pkgIdx);
if (atomTag == FrameworkStatsLog.ATTRIBUTED_APP_OPS) {
for (int attributionIdx = 0;
attributionIdx < packageOps.getAttributedOpsCount(); attributionIdx++) {
final AppOpsManager.AttributedHistoricalOps attributedOps =
packageOps.getAttributedOpsAt(attributionIdx);
for (int opIdx = 0; opIdx < attributedOps.getOpCount(); opIdx++) {
final AppOpsManager.HistoricalOp op = attributedOps.getOpAt(opIdx);
processHistoricalOp(op, opsList, uid, samplingRatio,
packageOps.getPackageName(), attributedOps.getTag());
}
}
} else if (atomTag == FrameworkStatsLog.APP_OPS) {
for (int opIdx = 0; opIdx < packageOps.getOpCount(); opIdx++) {
final AppOpsManager.HistoricalOp op = packageOps.getOpAt(opIdx);
processHistoricalOp(op, opsList, uid, samplingRatio,
packageOps.getPackageName(), null);
}
}
}
}
return opsList;
}
private void processHistoricalOp(AppOpsManager.HistoricalOp op,
List<AppOpEntry> opsList, int uid, int samplingRatio, String packageName,
@Nullable String attributionTag) {
int firstChar = 0;
if (attributionTag != null && attributionTag.startsWith(packageName)) {
firstChar = packageName.length();
if (firstChar < attributionTag.length() && attributionTag.charAt(firstChar) == '.') {
firstChar++;
}
}
AppOpEntry entry = new AppOpEntry(packageName,
attributionTag == null ? null : attributionTag.substring(firstChar), op,
uid);
if (entry.mHash < samplingRatio) {
opsList.add(entry);
}
}
int pullRuntimeAppOpAccessMessageLocked(int atomTag, List<StatsEvent> pulledData) {
final long token = Binder.clearCallingIdentity();
try {
AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
RuntimeAppOpAccessMessage message = appOps.collectRuntimeAppOpAccessMessage();
if (message == null) {
Slog.i(TAG, "No runtime appop access message collected");
return StatsManager.PULL_SUCCESS;
}
StatsEvent.Builder e = StatsEvent.newBuilder();
e.setAtomId(atomTag);
e.writeInt(message.getUid());
e.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
e.writeString(message.getPackageName());
e.writeString("");
if (message.getAttributionTag() == null) {
e.writeString("");
} else {
e.writeString(message.getAttributionTag());
}
e.writeString(message.getMessage());
e.writeInt(message.getSamplingStrategy());
e.writeInt(AppOpsManager.strOpToOp(message.getOp()));
pulledData.add(e.build());
} catch (Throwable t) {
// TODO: catch exceptions at a more granular level
Slog.e(TAG, "Could not read runtime appop access message", t);
return StatsManager.PULL_SKIP;
} finally {
Binder.restoreCallingIdentity(token);
}
return StatsManager.PULL_SUCCESS;
}
static void unpackStreamedData(int atomTag, List<StatsEvent> pulledData,
List<ParcelFileDescriptor> statsFiles) throws IOException {
InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(statsFiles.get(0));
int[] len = new int[1];
byte[] stats = readFully(stream, len);
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeByteArray(Arrays.copyOf(stats, len[0]))
.build();
pulledData.add(e);
}
static byte[] readFully(InputStream stream, int[] outLen) throws IOException {
int pos = 0;
final int initialAvail = stream.available();
byte[] data = new byte[initialAvail > 0 ? (initialAvail + 1) : 16384];
while (true) {
int amt = stream.read(data, pos, data.length - pos);
if (DEBUG) {
Slog.i(TAG, "Read " + amt + " bytes at " + pos + " of avail " + data.length);
}
if (amt < 0) {
if (DEBUG) {
Slog.i(TAG, "**** FINISHED READING: pos=" + pos + " len=" + data.length);
}
outLen[0] = pos;
return data;
}
pos += amt;
if (pos >= data.length) {
byte[] newData = new byte[pos + 16384];
if (DEBUG) {
Slog.i(TAG, "Copying " + pos + " bytes to new array len " + newData.length);
}
System.arraycopy(data, 0, newData, 0, pos);
data = newData;
}
}
}
private void registerNotificationRemoteViews() {
int tagId = FrameworkStatsLog.NOTIFICATION_REMOTE_VIEWS;
mStatsManager.setPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullNotificationRemoteViewsLocked(int atomTag, List<StatsEvent> pulledData) {
INotificationManager notificationManagerService = getINotificationManagerService();
if (notificationManagerService == null) {
return StatsManager.PULL_SKIP;
}
final long callingToken = Binder.clearCallingIdentity();
try {
// determine last pull tine. Copy file trick from pullProcStats?
long wallClockNanos = SystemClock.currentTimeMicro() * 1000L;
long lastNotificationStatsNs = wallClockNanos -
TimeUnit.NANOSECONDS.convert(1, TimeUnit.DAYS);
List<ParcelFileDescriptor> statsFiles = new ArrayList<>();
notificationManagerService.pullStats(lastNotificationStatsNs,
NotificationManagerService.REPORT_REMOTE_VIEWS, true, statsFiles);
if (statsFiles.size() != 1) {
return StatsManager.PULL_SKIP;
}
unpackStreamedData(atomTag, pulledData, statsFiles);
} catch (IOException e) {
Slog.e(TAG, "Getting notistats failed: ", e);
return StatsManager.PULL_SKIP;
} catch (RemoteException e) {
Slog.e(TAG, "Getting notistats failed: ", e);
return StatsManager.PULL_SKIP;
} catch (SecurityException e) {
Slog.e(TAG, "Getting notistats failed: ", e);
return StatsManager.PULL_SKIP;
} finally {
Binder.restoreCallingIdentity(callingToken);
}
return StatsManager.PULL_SUCCESS;
}
private void registerDangerousPermissionStateSampled() {
int tagId = FrameworkStatsLog.DANGEROUS_PERMISSION_STATE_SAMPLED;
mStatsManager.setPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
private void registerBatteryLevel() {
int tagId = FrameworkStatsLog.BATTERY_LEVEL;
mStatsManager.setPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
private void registerRemainingBatteryCapacity() {
int tagId = FrameworkStatsLog.REMAINING_BATTERY_CAPACITY;
mStatsManager.setPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
private void registerFullBatteryCapacity() {
int tagId = FrameworkStatsLog.FULL_BATTERY_CAPACITY;
mStatsManager.setPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
private void registerBatteryVoltage() {
int tagId = FrameworkStatsLog.BATTERY_VOLTAGE;
mStatsManager.setPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
private void registerBatteryCycleCount() {
int tagId = FrameworkStatsLog.BATTERY_CYCLE_COUNT;
mStatsManager.setPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullHealthHalLocked(int atomTag, List<StatsEvent> pulledData) {
IHealth healthService = mHealthService.getLastService();
if (healthService == null) {
return StatsManager.PULL_SKIP;
}
try {
healthService.getHealthInfo((result, value) -> {
int pulledValue;
switch(atomTag) {
case FrameworkStatsLog.BATTERY_LEVEL:
pulledValue = value.legacy.batteryLevel;
break;
case FrameworkStatsLog.REMAINING_BATTERY_CAPACITY:
pulledValue = value.legacy.batteryChargeCounter;
break;
case FrameworkStatsLog.FULL_BATTERY_CAPACITY:
pulledValue = value.legacy.batteryFullCharge;
break;
case FrameworkStatsLog.BATTERY_VOLTAGE:
pulledValue = value.legacy.batteryVoltage;
break;
case FrameworkStatsLog.BATTERY_CYCLE_COUNT:
pulledValue = value.legacy.batteryCycleCount;
break;
default:
throw new IllegalStateException("Invalid atomTag in healthHal puller: "
+ atomTag);
}
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
.writeInt(pulledValue)
.build();
pulledData.add(e);
});
} catch (RemoteException | IllegalStateException e) {
return StatsManager.PULL_SKIP;
}
return StatsManager.PULL_SUCCESS;
}
private void registerSettingsStats() {
int tagId = FrameworkStatsLog.SETTING_SNAPSHOT;
mStatsManager.setPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
DIRECT_EXECUTOR,
mStatsCallbackImpl
);
}
int pullSettingsStatsLocked(int atomTag, List<StatsEvent> pulledData) {
UserManager userManager = mContext.getSystemService(UserManager.class);
if (userManager == null) {
return StatsManager.PULL_SKIP;
}
final long token = Binder.clearCallingIdentity();
try {
for (UserInfo user : userManager.getUsers()) {
final int userId = user.getUserHandle().getIdentifier();
if (userId == UserHandle.USER_SYSTEM) {
pulledData.addAll(SettingsStatsUtil.logGlobalSettings(mContext, atomTag,
UserHandle.USER_SYSTEM));
}
pulledData.addAll(SettingsStatsUtil.logSystemSettings(mContext, atomTag, userId));
pulledData.addAll(SettingsStatsUtil.logSecureSettings(mContext, atomTag, userId));
}
} catch (Exception e) {
Slog.e(TAG, "failed to pullSettingsStats", e);
return StatsManager.PULL_SKIP;
} finally {
Binder.restoreCallingIdentity(token);
}
return StatsManager.PULL_SUCCESS;
}
// Thermal event received from vendor thermal management subsystem
private static final class ThermalEventListener extends IThermalEventListener.Stub {
@Override
public void notifyThrottling(Temperature temp) {
FrameworkStatsLog.write(FrameworkStatsLog.THERMAL_THROTTLING_SEVERITY_STATE_CHANGED,
temp.getType(), temp.getName(), (int) (temp.getValue() * 10), temp.getStatus());
}
}
private static final class ConnectivityStatsCallback extends
ConnectivityManager.NetworkCallback {
@Override
public void onAvailable(Network network) {
FrameworkStatsLog.write(FrameworkStatsLog.CONNECTIVITY_STATE_CHANGED, network.netId,
FrameworkStatsLog.CONNECTIVITY_STATE_CHANGED__STATE__CONNECTED);
}
@Override
public void onLost(Network network) {
FrameworkStatsLog.write(FrameworkStatsLog.CONNECTIVITY_STATE_CHANGED, network.netId,
FrameworkStatsLog.CONNECTIVITY_STATE_CHANGED__STATE__DISCONNECTED);
}
}
private final class StatsSubscriptionsListener
extends SubscriptionManager.OnSubscriptionsChangedListener {
@NonNull
private final SubscriptionManager mSm;
StatsSubscriptionsListener(@NonNull SubscriptionManager sm) {
mSm = sm;
}
@Override
public void onSubscriptionsChanged() {
final List<SubscriptionInfo> currentSubs = mSm.getCompleteActiveSubscriptionInfoList();
for (final SubscriptionInfo sub : currentSubs) {
final SubInfo match = CollectionUtils.find(mHistoricalSubs,
(SubInfo it) -> it.subId == sub.getSubscriptionId());
// SubInfo exists, ignore.
if (match != null) continue;
// Ignore if no valid mcc, mnc, imsi, carrierId.
final int subId = sub.getSubscriptionId();
final String mcc = sub.getMccString();
final String mnc = sub.getMncString();
final String subscriberId = mTelephony.getSubscriberId(subId);
if (TextUtils.isEmpty(subscriberId) || TextUtils.isEmpty(mcc)
|| TextUtils.isEmpty(mnc) || sub.getCarrierId() == UNKNOWN_CARRIER_ID) {
Slog.e(TAG, "subInfo of subId " + subId + " is invalid, ignored.");
continue;
}
final SubInfo subInfo = new SubInfo(subId, sub.getCarrierId(), mcc, mnc,
subscriberId, sub.isOpportunistic());
Slog.i(TAG, "subId " + subId + " added into historical sub list");
synchronized (mDataBytesTransferLock) {
mHistoricalSubs.add(subInfo);
// Since getting snapshot when pulling will also include data before boot,
// query stats as baseline to prevent double count is needed.
mNetworkStatsBaselines.addAll(getDataUsageBytesTransferSnapshotForSub(subInfo));
}
}
}
}
}