| /* |
| * Copyright 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.NetworkIdentity.OEM_PAID; |
| import static android.net.NetworkIdentity.OEM_PRIVATE; |
| import static android.net.NetworkStats.DEFAULT_NETWORK_ALL; |
| import static android.net.NetworkStats.METERED_ALL; |
| import static android.net.NetworkStats.ROAMING_ALL; |
| import static android.net.NetworkTemplate.MATCH_ETHERNET; |
| import static android.net.NetworkTemplate.MATCH_MOBILE_WILDCARD; |
| import static android.net.NetworkTemplate.MATCH_WIFI_WILDCARD; |
| import static android.net.NetworkTemplate.NETWORK_TYPE_ALL; |
| import static android.net.NetworkTemplate.OEM_MANAGED_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.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.internal.util.FrameworkStatsLog.TIME_ZONE_DETECTOR_STATE__DETECTION_MODE__GEO; |
| import static com.android.internal.util.FrameworkStatsLog.TIME_ZONE_DETECTOR_STATE__DETECTION_MODE__MANUAL; |
| import static com.android.internal.util.FrameworkStatsLog.TIME_ZONE_DETECTOR_STATE__DETECTION_MODE__TELEPHONY; |
| 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.BiometricsProtoEnums; |
| import android.hardware.face.FaceManager; |
| 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.BatteryStatsInternal; |
| 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.OutcomeReceiver; |
| import android.os.ParcelFileDescriptor; |
| import android.os.Parcelable; |
| import android.os.Process; |
| import android.os.RemoteException; |
| import android.os.ServiceManager; |
| import android.os.ServiceSpecificException; |
| 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.incremental.IncrementalManager; |
| 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.security.metrics.CrashStats; |
| import android.security.metrics.IKeystoreMetrics; |
| import android.security.metrics.KeyCreationWithAuthInfo; |
| import android.security.metrics.KeyCreationWithGeneralInfo; |
| import android.security.metrics.KeyCreationWithPurposeAndModesInfo; |
| import android.security.metrics.KeyOperationWithGeneralInfo; |
| import android.security.metrics.KeyOperationWithPurposeAndModesInfo; |
| import android.security.metrics.Keystore2AtomWithOverflow; |
| import android.security.metrics.KeystoreAtom; |
| import android.security.metrics.KeystoreAtomPayload; |
| import android.security.metrics.RkpErrorStats; |
| import android.security.metrics.RkpPoolStats; |
| import android.security.metrics.StorageStats; |
| 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.DmabufInfoReader; |
| import com.android.internal.os.KernelCpuBpfTracking; |
| import com.android.internal.os.KernelCpuThreadReader; |
| import com.android.internal.os.KernelCpuThreadReaderDiff; |
| import com.android.internal.os.KernelCpuThreadReaderSettingsObserver; |
| import com.android.internal.os.KernelCpuTotalBpfMapReader; |
| 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.KernelSingleProcessCpuThreadReader.ProcessCpuUsage; |
| 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.SelectedProcessCpuThreadReader; |
| import com.android.internal.os.StoragedUidIoStatsReader; |
| import com.android.internal.os.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes; |
| import com.android.internal.util.CollectionUtils; |
| import com.android.internal.util.FrameworkStatsLog; |
| import com.android.role.RoleManagerLocal; |
| import com.android.server.BatteryService; |
| import com.android.server.BinderCallsStatsService; |
| import com.android.server.LocalManagerRegistry; |
| 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.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 com.android.server.timezonedetector.MetricsTimeZoneDetectorState; |
| import com.android.server.timezonedetector.TimeZoneDetectorInternal; |
| |
| 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.Comparator; |
| 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.ExecutionException; |
| 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; |
| |
| /** Number of entries in CpuCyclesPerUidCluster atom stored in an array for each cluster. */ |
| private static final int CPU_CYCLES_PER_UID_CLUSTER_VALUES = 3; |
| |
| 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; |
| |
| // 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<>(); |
| |
| private SelectedProcessCpuThreadReader mSurfaceFlingerProcessCpuThreadReader; |
| |
| // Only access via getIKeystoreMetricsService |
| @GuardedBy("mKeystoreLock") |
| private IKeystoreMetrics mIKeystoreMetrics; |
| |
| // Puller locks |
| private final Object mDataBytesTransferLock = new Object(); |
| private final Object mBluetoothBytesTransferLock = new Object(); |
| private final Object mKernelWakelockLock = new Object(); |
| private final Object mCpuTimePerClusterFreqLock = 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 mTimeZoneDetectionInfoLock = 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(); |
| private final Object mInstalledIncrementalPackagesLock = new Object(); |
| private final Object mKeystoreLock = new Object(); |
| |
| public StatsPullAtomService(Context context) { |
| super(context); |
| mContext = context; |
| } |
| |
| private native void initializeNativePullers(); |
| |
| /** |
| * 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: |
| case FrameworkStatsLog.OEM_MANAGED_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_CLUSTER_FREQ: |
| synchronized (mCpuTimePerClusterFreqLock) { |
| return pullCpuTimePerClusterFreqLocked(atomTag, data); |
| } |
| case FrameworkStatsLog.CPU_TIME_PER_UID: |
| synchronized (mCpuTimePerUidLock) { |
| return pullCpuTimePerUidLocked(atomTag, data); |
| } |
| case FrameworkStatsLog.CPU_CYCLES_PER_UID_CLUSTER: |
| // Use the same lock as CPU_TIME_PER_UID_FREQ because data is pulled from |
| // the same source. |
| synchronized (mCpuTimePerUidFreqLock) { |
| return pullCpuCyclesPerUidClusterLocked(atomTag, data); |
| } |
| case FrameworkStatsLog.CPU_TIME_PER_UID_FREQ: |
| synchronized (mCpuTimePerUidFreqLock) { |
| return pullCpuTimePerUidFreqLocked(atomTag, data); |
| } |
| case FrameworkStatsLog.CPU_CYCLES_PER_THREAD_GROUP_CLUSTER: |
| return pullCpuCyclesPerThreadGroupCluster(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.PROCESS_DMABUF_MEMORY: |
| return pullProcessDmabufMemory(atomTag, data); |
| case FrameworkStatsLog.SYSTEM_MEMORY: |
| return pullSystemMemory(atomTag, data); |
| case FrameworkStatsLog.VMSTAT: |
| return pullVmStat(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: |
| // fall-through - same call covers two cases |
| 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.TIME_ZONE_DETECTOR_STATE: |
| synchronized (mTimeZoneDetectionInfoLock) { |
| return pullTimeZoneDetectorStateLocked(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); |
| } |
| case FrameworkStatsLog.INSTALLED_INCREMENTAL_PACKAGE: |
| synchronized (mInstalledIncrementalPackagesLock) { |
| return pullInstalledIncrementalPackagesLocked(atomTag, data); |
| } |
| case FrameworkStatsLog.KEYSTORE2_STORAGE_STATS: |
| case FrameworkStatsLog.RKP_POOL_STATS: |
| case FrameworkStatsLog.KEYSTORE2_KEY_CREATION_WITH_GENERAL_INFO: |
| case FrameworkStatsLog.KEYSTORE2_KEY_CREATION_WITH_AUTH_INFO: |
| case FrameworkStatsLog.KEYSTORE2_KEY_CREATION_WITH_PURPOSE_AND_MODES_INFO: |
| case FrameworkStatsLog.KEYSTORE2_ATOM_WITH_OVERFLOW: |
| case FrameworkStatsLog.KEYSTORE2_KEY_OPERATION_WITH_PURPOSE_AND_MODES_INFO: |
| case FrameworkStatsLog.KEYSTORE2_KEY_OPERATION_WITH_GENERAL_INFO: |
| case FrameworkStatsLog.RKP_ERROR_STATS: |
| case FrameworkStatsLog.KEYSTORE2_CRASH_STATS: |
| return pullKeystoreAtoms(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(() -> { |
| initializeNativePullers(); // Initialize pullers that need JNI. |
| 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(); |
| |
| // 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; |
| } |
| } |
| } |
| |
| mSurfaceFlingerProcessCpuThreadReader = |
| new SelectedProcessCpuThreadReader("/system/bin/surfaceflinger"); |
| |
| getIKeystoreMetricsService(); |
| } |
| |
| 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(); |
| registerCpuTimePerClusterFreq(); |
| registerCpuTimePerUid(); |
| registerCpuCyclesPerUidCluster(); |
| registerCpuTimePerUidFreq(); |
| registerCpuCyclesPerThreadGroupCluster(); |
| registerCpuActiveTime(); |
| registerCpuClusterTime(); |
| registerWifiActivityInfo(); |
| registerModemActivityInfo(); |
| registerBluetoothActivityInfo(); |
| registerSystemElapsedRealtime(); |
| registerSystemUptime(); |
| registerProcessMemoryState(); |
| registerProcessMemoryHighWaterMark(); |
| registerProcessMemorySnapshot(); |
| registerSystemIonHeapSize(); |
| registerIonHeapSize(); |
| registerProcessSystemIonHeapSize(); |
| registerSystemMemory(); |
| registerProcessDmabufMemory(); |
| registerVmStat(); |
| 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(); |
| registerTimeZoneDetectorState(); |
| registerExternalStorageInfo(); |
| registerAppsOnExternalStorageInfo(); |
| registerFaceSettings(); |
| registerAppOps(); |
| registerAttributedAppOps(); |
| registerRuntimeAppOpAccessMessage(); |
| registerNotificationRemoteViews(); |
| registerDangerousPermissionState(); |
| registerDangerousPermissionStateSampled(); |
| registerBatteryLevel(); |
| registerRemainingBatteryCapacity(); |
| registerFullBatteryCapacity(); |
| registerBatteryVoltage(); |
| registerBatteryCycleCount(); |
| registerSettingsStats(); |
| registerInstalledIncrementalPackages(); |
| registerKeystoreStorageStats(); |
| registerRkpPoolStats(); |
| registerKeystoreKeyCreationWithGeneralInfo(); |
| registerKeystoreKeyCreationWithAuthInfo(); |
| registerKeystoreKeyCreationWithPurposeModesInfo(); |
| registerKeystoreAtomWithOverflow(); |
| registerKeystoreKeyOperationWithPurposeAndModesInfo(); |
| registerKeystoreKeyOperationWithGeneralInfo(); |
| registerRkpErrorStats(); |
| registerKeystoreCrashStats(); |
| } |
| |
| 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)); |
| mNetworkStatsBaselines.addAll( |
| collectNetworkStatsSnapshotForAtom(FrameworkStatsLog.OEM_MANAGED_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(); |
| registerOemManagedBytesTransfer(); |
| } |
| |
| /** |
| * 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 IKeystoreMetrics getIKeystoreMetricsService() { |
| synchronized (mKeystoreLock) { |
| if (mIKeystoreMetrics == null) { |
| mIKeystoreMetrics = IKeystoreMetrics.Stub.asInterface( |
| ServiceManager.getService("android.security.metrics")); |
| if (mIKeystoreMetrics != null) { |
| try { |
| mIKeystoreMetrics.asBinder().linkToDeath(() -> { |
| synchronized (mKeystoreLock) { |
| mIKeystoreMetrics = null; |
| } |
| }, /* flags */ 0); |
| } catch (RemoteException e) { |
| Slog.e(TAG, "linkToDeath with IKeystoreMetrics failed", e); |
| mIKeystoreMetrics = null; |
| } |
| } |
| } |
| return mIKeystoreMetrics; |
| } |
| } |
| |
| 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, OEM_MANAGED_ALL)); |
| } |
| break; |
| } |
| case FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER: { |
| for (final SubInfo subInfo : mHistoricalSubs) { |
| ret.addAll(getDataUsageBytesTransferSnapshotForSub(subInfo)); |
| } |
| break; |
| } |
| case FrameworkStatsLog.OEM_MANAGED_BYTES_TRANSFER: { |
| ret.addAll(getDataUsageBytesTransferSnapshotForOemManaged()); |
| 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, item.oemManaged); |
| |
| // 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; |
| case FrameworkStatsLog.OEM_MANAGED_BYTES_TRANSFER: |
| addOemDataUsageBytesTransferAtoms(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 statsEvent; |
| |
| if (statsExt.slicedByFgbg) { |
| // MobileBytesTransferByFgBg atom or WifiBytesTransferByFgBg atom. |
| statsEvent = FrameworkStatsLog.buildStatsEvent( |
| atomTag, entry.uid, |
| (entry.set > 0), entry.rxBytes, entry.rxPackets, entry.txBytes, |
| entry.txPackets); |
| } else { |
| // MobileBytesTransfer atom or WifiBytesTransfer atom. |
| statsEvent = FrameworkStatsLog.buildStatsEvent( |
| atomTag, entry.uid, entry.rxBytes, |
| entry.rxPackets, entry.txBytes, entry.txPackets); |
| } |
| ret.add(statsEvent); |
| } |
| } |
| |
| 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); |
| pulledData.add(FrameworkStatsLog.buildStatsEvent( |
| FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED, entry.uid, |
| entry.metered == NetworkStats.METERED_YES, entry.tag, entry.rxBytes, |
| entry.rxPackets, entry.txBytes, entry.txPackets)); |
| } |
| } |
| |
| 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); |
| pulledData.add(FrameworkStatsLog.buildStatsEvent( |
| FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER, entry.set, entry.rxBytes, |
| entry.rxPackets, entry.txBytes, entry.txPackets, |
| 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. |
| statsExt.subInfo.mcc, statsExt.subInfo.mnc, statsExt.subInfo.carrierId, |
| statsExt.subInfo.isOpportunistic |
| ? DATA_USAGE_BYTES_TRANSFER__OPPORTUNISTIC_DATA_SUB__OPPORTUNISTIC |
| : DATA_USAGE_BYTES_TRANSFER__OPPORTUNISTIC_DATA_SUB__NOT_OPPORTUNISTIC, |
| isNR)); |
| } |
| } |
| |
| private void addOemDataUsageBytesTransferAtoms(@NonNull NetworkStatsExt statsExt, |
| @NonNull List<StatsEvent> pulledData) { |
| final NetworkStats.Entry entry = new NetworkStats.Entry(); // for recycling |
| final int oemManaged = statsExt.oemManaged; |
| for (final int transport : statsExt.transports) { |
| for (int i = 0; i < statsExt.stats.size(); i++) { |
| statsExt.stats.getValues(i, entry); |
| pulledData.add(FrameworkStatsLog.buildStatsEvent( |
| FrameworkStatsLog.OEM_MANAGED_BYTES_TRANSFER, entry.uid, (entry.set > 0), |
| oemManaged, transport, entry.rxBytes, entry.rxPackets, entry.txBytes, |
| entry.txPackets)); |
| } |
| } |
| } |
| |
| @NonNull private List<NetworkStatsExt> getDataUsageBytesTransferSnapshotForOemManaged() { |
| final int[] transports = new int[] {MATCH_ETHERNET, MATCH_MOBILE_WILDCARD, |
| MATCH_WIFI_WILDCARD}; |
| final int[] oemManagedTypes = new int[] {OEM_PAID | OEM_PRIVATE, OEM_PAID, OEM_PRIVATE}; |
| |
| final List<NetworkStatsExt> ret = new ArrayList<>(); |
| |
| for (final int transport : transports) { |
| for (final int oemManaged : oemManagedTypes) { |
| /* A null subscriberId will set wildcard=true, since we aren't trying to select a |
| specific ssid or subscriber. */ |
| final NetworkTemplate template = new NetworkTemplate(transport, |
| /*subscriberId=*/null, /*matchSubscriberIds=*/null, /*networkId=*/null, |
| METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, |
| oemManaged); |
| final NetworkStats stats = getUidNetworkStatsSnapshotForTemplate(template, true); |
| if (stats != null) { |
| ret.add(new NetworkStatsExt(sliceNetworkStatsByUidTagAndMetered(stats), |
| new int[] {transport}, /*slicedByFgbg=*/true, /*slicedByTag=*/true, |
| /*slicedByMetered=*/true, TelephonyManager.NETWORK_TYPE_UNKNOWN, |
| /*subInfo=*/null, oemManaged)); |
| } |
| } |
| } |
| |
| return ret; |
| } |
| |
| /** |
| * 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, |
| OEM_MANAGED_ALL)); |
| } |
| } |
| 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 registerOemManagedBytesTransfer() { |
| int tagId = FrameworkStatsLog.OEM_MANAGED_BYTES_TRANSFER; |
| PullAtomMetadata metadata = new PullAtomMetadata.Builder() |
| .setAdditiveFields(new int[] {5, 6, 7, 8}) |
| .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()) { |
| pulledData.add(FrameworkStatsLog.buildStatsEvent( |
| atomTag, traffic.getUid(), traffic.getRxBytes(), traffic.getTxBytes())); |
| } |
| 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(); |
| pulledData.add(FrameworkStatsLog.buildStatsEvent( |
| atomTag, name, kws.mCount, kws.mVersion, kws.mTotalTime)); |
| } |
| return StatsManager.PULL_SUCCESS; |
| } |
| |
| private void registerCpuTimePerClusterFreq() { |
| if (KernelCpuBpfTracking.isSupported()) { |
| int tagId = FrameworkStatsLog.CPU_TIME_PER_CLUSTER_FREQ; |
| PullAtomMetadata metadata = new PullAtomMetadata.Builder() |
| .setAdditiveFields(new int[] {3}) |
| .build(); |
| mStatsManager.setPullAtomCallback( |
| tagId, |
| metadata, |
| DIRECT_EXECUTOR, |
| mStatsCallbackImpl |
| ); |
| } |
| } |
| |
| int pullCpuTimePerClusterFreqLocked(int atomTag, List<StatsEvent> pulledData) { |
| int[] freqsClusters = KernelCpuBpfTracking.getFreqsClusters(); |
| long[] freqs = KernelCpuBpfTracking.getFreqs(); |
| long[] timesMs = KernelCpuTotalBpfMapReader.read(); |
| if (timesMs == null) { |
| return StatsManager.PULL_SKIP; |
| } |
| for (int freqIndex = 0; freqIndex < timesMs.length; ++freqIndex) { |
| int cluster = freqsClusters[freqIndex]; |
| int freq = (int) freqs[freqIndex]; |
| long timeMs = timesMs[freqIndex]; |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, cluster, freq, timeMs)); |
| } |
| 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]; |
| pulledData.add( |
| FrameworkStatsLog.buildStatsEvent(atomTag, uid, userTimeUs, systemTimeUs)); |
| }); |
| return StatsManager.PULL_SUCCESS; |
| } |
| |
| private void registerCpuCyclesPerUidCluster() { |
| // If eBPF tracking is not support, the procfs fallback is used if the kernel knows about |
| // CPU frequencies. |
| if (KernelCpuBpfTracking.isSupported() || KernelCpuBpfTracking.getClusters() > 0) { |
| int tagId = FrameworkStatsLog.CPU_CYCLES_PER_UID_CLUSTER; |
| PullAtomMetadata metadata = new PullAtomMetadata.Builder() |
| .setAdditiveFields(new int[] {3, 4, 5}) |
| .build(); |
| mStatsManager.setPullAtomCallback( |
| tagId, |
| metadata, |
| DIRECT_EXECUTOR, |
| mStatsCallbackImpl |
| ); |
| } |
| } |
| |
| int pullCpuCyclesPerUidClusterLocked(int atomTag, List<StatsEvent> pulledData) { |
| PowerProfile powerProfile = new PowerProfile(mContext); |
| int[] freqsClusters = KernelCpuBpfTracking.getFreqsClusters(); |
| int clusters = KernelCpuBpfTracking.getClusters(); |
| long[] freqs = KernelCpuBpfTracking.getFreqs(); |
| double[] freqsPowers = new double[freqs.length]; |
| // Initialize frequency power mapping. |
| { |
| int freqClusterIndex = 0; |
| int lastCluster = -1; |
| for (int freqIndex = 0; freqIndex < freqs.length; ++freqIndex, ++freqClusterIndex) { |
| int cluster = freqsClusters[freqIndex]; |
| if (cluster != lastCluster) { |
| freqClusterIndex = 0; |
| } |
| lastCluster = cluster; |
| |
| freqsPowers[freqIndex] = |
| powerProfile.getAveragePowerForCpuCore(cluster, freqClusterIndex); |
| } |
| } |
| |
| // Aggregate 0: mcycles, 1: runtime ms, 2: power profile estimate for the same uids for |
| // each cluster. |
| SparseArray<double[]> 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); |
| } |
| |
| double[] values = aggregated.get(uid); |
| if (values == null) { |
| values = new double[clusters * CPU_CYCLES_PER_UID_CLUSTER_VALUES]; |
| aggregated.put(uid, values); |
| } |
| |
| for (int freqIndex = 0; freqIndex < cpuFreqTimeMs.length; ++freqIndex) { |
| int cluster = freqsClusters[freqIndex]; |
| long timeMs = cpuFreqTimeMs[freqIndex]; |
| values[cluster * CPU_CYCLES_PER_UID_CLUSTER_VALUES] += freqs[freqIndex] * timeMs; |
| values[cluster * CPU_CYCLES_PER_UID_CLUSTER_VALUES + 1] += timeMs; |
| values[cluster * CPU_CYCLES_PER_UID_CLUSTER_VALUES + 2] += |
| freqsPowers[freqIndex] * timeMs; |
| } |
| }); |
| |
| int size = aggregated.size(); |
| for (int i = 0; i < size; ++i) { |
| int uid = aggregated.keyAt(i); |
| double[] values = aggregated.valueAt(i); |
| for (int cluster = 0; cluster < clusters; ++cluster) { |
| pulledData.add(FrameworkStatsLog.buildStatsEvent( |
| atomTag, uid, cluster, |
| (long) (values[cluster * CPU_CYCLES_PER_UID_CLUSTER_VALUES] / 1e6), |
| (long) values[cluster * CPU_CYCLES_PER_UID_CLUSTER_VALUES + 1], |
| (long) (values[cluster * CPU_CYCLES_PER_UID_CLUSTER_VALUES + 2] / 1e3))); |
| } |
| } |
| 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) { |
| pulledData.add(FrameworkStatsLog.buildStatsEvent( |
| atomTag, uid, freqIndex, aggCpuFreqTimeMs[freqIndex])); |
| } |
| } |
| } |
| return StatsManager.PULL_SUCCESS; |
| } |
| |
| private void registerCpuCyclesPerThreadGroupCluster() { |
| if (KernelCpuBpfTracking.isSupported()) { |
| int tagId = FrameworkStatsLog.CPU_CYCLES_PER_THREAD_GROUP_CLUSTER; |
| PullAtomMetadata metadata = new PullAtomMetadata.Builder() |
| .setAdditiveFields(new int[] {3, 4}) |
| .build(); |
| mStatsManager.setPullAtomCallback( |
| tagId, |
| metadata, |
| DIRECT_EXECUTOR, |
| mStatsCallbackImpl |
| ); |
| } |
| } |
| |
| int pullCpuCyclesPerThreadGroupCluster(int atomTag, List<StatsEvent> pulledData) { |
| SystemServiceCpuThreadTimes times = LocalServices.getService(BatteryStatsInternal.class) |
| .getSystemServiceCpuThreadTimes(); |
| if (times == null) { |
| return StatsManager.PULL_SKIP; |
| } |
| |
| addCpuCyclesPerThreadGroupClusterAtoms(atomTag, pulledData, |
| FrameworkStatsLog.CPU_CYCLES_PER_THREAD_GROUP_CLUSTER__THREAD_GROUP__SYSTEM_SERVER, |
| times.threadCpuTimesUs); |
| addCpuCyclesPerThreadGroupClusterAtoms(atomTag, pulledData, |
| FrameworkStatsLog.CPU_CYCLES_PER_THREAD_GROUP_CLUSTER__THREAD_GROUP__SYSTEM_SERVER_BINDER, |
| times.binderThreadCpuTimesUs); |
| |
| ProcessCpuUsage surfaceFlingerTimes = mSurfaceFlingerProcessCpuThreadReader.readAbsolute(); |
| if (surfaceFlingerTimes != null && surfaceFlingerTimes.threadCpuTimesMillis != null) { |
| long[] surfaceFlingerTimesUs = |
| new long[surfaceFlingerTimes.threadCpuTimesMillis.length]; |
| for (int i = 0; i < surfaceFlingerTimesUs.length; ++i) { |
| surfaceFlingerTimesUs[i] = surfaceFlingerTimes.threadCpuTimesMillis[i] * 1_000; |
| } |
| addCpuCyclesPerThreadGroupClusterAtoms(atomTag, pulledData, |
| FrameworkStatsLog.CPU_CYCLES_PER_THREAD_GROUP_CLUSTER__THREAD_GROUP__SURFACE_FLINGER, |
| surfaceFlingerTimesUs); |
| } |
| |
| return StatsManager.PULL_SUCCESS; |
| } |
| |
| private static void addCpuCyclesPerThreadGroupClusterAtoms( |
| int atomTag, List<StatsEvent> pulledData, int threadGroup, long[] cpuTimesUs) { |
| int[] freqsClusters = KernelCpuBpfTracking.getFreqsClusters(); |
| int clusters = KernelCpuBpfTracking.getClusters(); |
| long[] freqs = KernelCpuBpfTracking.getFreqs(); |
| long[] aggregatedCycles = new long[clusters]; |
| long[] aggregatedTimesUs = new long[clusters]; |
| for (int i = 0; i < cpuTimesUs.length; ++i) { |
| aggregatedCycles[freqsClusters[i]] += freqs[i] * cpuTimesUs[i] / 1_000; |
| aggregatedTimesUs[freqsClusters[i]] += cpuTimesUs[i]; |
| } |
| for (int cluster = 0; cluster < clusters; ++cluster) { |
| pulledData.add(FrameworkStatsLog.buildStatsEvent( |
| atomTag, threadGroup, cluster, aggregatedCycles[cluster] / 1_000_000L, |
| aggregatedTimesUs[cluster] / 1_000)); |
| } |
| } |
| |
| 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) -> { |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, uid, cpuActiveTimesMs)); |
| }); |
| 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++) { |
| pulledData.add( |
| FrameworkStatsLog.buildStatsEvent(atomTag, uid, i, cpuClusterTimesMs[i])); |
| } |
| }); |
| 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) { |
| final 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; |
| } |
| pulledData.add( |
| FrameworkStatsLog.buildStatsEvent(atomTag, wifiInfo.getTimeSinceBootMillis(), |
| wifiInfo.getStackState(), wifiInfo.getControllerTxDurationMillis(), |
| wifiInfo.getControllerRxDurationMillis(), |
| wifiInfo.getControllerIdleDurationMillis(), |
| wifiInfo.getControllerEnergyUsedMicroJoules())); |
| } 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) { |
| final long token = Binder.clearCallingIdentity(); |
| try { |
| CompletableFuture<ModemActivityInfo> modemFuture = new CompletableFuture<>(); |
| mTelephony.requestModemActivityInfo(Runnable::run, |
| new OutcomeReceiver<ModemActivityInfo, |
| TelephonyManager.ModemActivityInfoException>() { |
| @Override |
| public void onResult(ModemActivityInfo result) { |
| modemFuture.complete(result); |
| } |
| |
| @Override |
| public void onError(TelephonyManager.ModemActivityInfoException e) { |
| Slog.w(TAG, "error reading modem stats:" + e); |
| modemFuture.complete(null); |
| } |
| }); |
| |
| ModemActivityInfo modemInfo; |
| try { |
| modemInfo = modemFuture.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS, |
| TimeUnit.MILLISECONDS); |
| } catch (TimeoutException | InterruptedException e) { |
| Slog.w(TAG, "timeout or interrupt reading modem stats: " + e); |
| return StatsManager.PULL_SKIP; |
| } catch (ExecutionException e) { |
| Slog.w(TAG, "exception reading modem stats: " + e.getCause()); |
| return StatsManager.PULL_SKIP; |
| } |
| |
| if (modemInfo == null) { |
| return StatsManager.PULL_SKIP; |
| } |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, |
| modemInfo.getTimestampMillis(), |
| modemInfo.getSleepTimeMillis(), modemInfo.getIdleTimeMillis(), |
| modemInfo.getTransmitDurationMillisAtPowerLevel(0), |
| modemInfo.getTransmitDurationMillisAtPowerLevel(1), |
| modemInfo.getTransmitDurationMillisAtPowerLevel(2), |
| modemInfo.getTransmitDurationMillisAtPowerLevel(3), |
| modemInfo.getTransmitDurationMillisAtPowerLevel(4), |
| modemInfo.getReceiveTimeMillis(), |
| -1 /*`energy_used` field name deprecated, use -1 to indicate as unused.*/)); |
| } 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; |
| } |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, info.getTimeStamp(), |
| info.getBluetoothStackState(), info.getControllerTxTimeMillis(), |
| info.getControllerRxTimeMillis(), info.getControllerIdleTimeMillis(), |
| info.getControllerEnergyUsed())); |
| 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) { |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, SystemClock.elapsedRealtime())); |
| 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) { |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, SystemClock.uptimeMillis())); |
| 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; |
| } |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, processMemoryState.uid, |
| processMemoryState.processName, processMemoryState.oomScore, memoryStat.pgfault, |
| memoryStat.pgmajfault, memoryStat.rssInBytes, memoryStat.cacheInBytes, |
| memoryStat.swapInBytes, -1 /*unused*/, -1 /*unused*/, -1 /*unused*/)); |
| } |
| 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; |
| } |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, managedProcess.uid, |
| managedProcess.processName, |
| // RSS high-water mark in bytes. |
| snapshot.rssHighWaterMarkInKilobytes * 1024L, |
| snapshot.rssHighWaterMarkInKilobytes)); |
| } |
| // 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; |
| } |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, snapshot.uid, |
| processCmdlines.valueAt(i), |
| // RSS high-water mark in bytes. |
| snapshot.rssHighWaterMarkInKilobytes * 1024L, |
| snapshot.rssHighWaterMarkInKilobytes)); |
| } |
| // 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; |
| } |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, managedProcess.uid, |
| managedProcess.processName, managedProcess.pid, managedProcess.oomScore, |
| snapshot.rssInKilobytes, snapshot.anonRssInKilobytes, snapshot.swapInKilobytes, |
| snapshot.anonRssInKilobytes + snapshot.swapInKilobytes)); |
| } |
| // 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; |
| } |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, snapshot.uid, |
| processCmdlines.valueAt(i), pid, |
| -1001 /*Placeholder for native processes, OOM_SCORE_ADJ_MIN - 1.*/, |
| snapshot.rssInKilobytes, snapshot.anonRssInKilobytes, snapshot.swapInKilobytes, |
| snapshot.anonRssInKilobytes + snapshot.swapInKilobytes)); |
| } |
| 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(); |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, systemIonHeapSizeInBytes)); |
| 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(); |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, ionHeapSizeInKilobytes)); |
| 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) { |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, getUidForPid(allocations.pid), |
| readCmdlineFromProcfs(allocations.pid), |
| (int) (allocations.totalSizeInBytes / 1024), allocations.count, |
| (int) (allocations.maxSizeInBytes / 1024))); |
| } |
| return StatsManager.PULL_SUCCESS; |
| } |
| |
| private void registerProcessDmabufMemory() { |
| int tagId = FrameworkStatsLog.PROCESS_DMABUF_MEMORY; |
| mStatsManager.setPullAtomCallback( |
| tagId, |
| null, // use default PullAtomMetadata values |
| DIRECT_EXECUTOR, |
| mStatsCallbackImpl |
| ); |
| } |
| |
| int pullProcessDmabufMemory(int atomTag, List<StatsEvent> pulledData) { |
| List<ProcessMemoryState> managedProcessList = |
| LocalServices.getService(ActivityManagerInternal.class) |
| .getMemoryStateForProcesses(); |
| managedProcessList.sort(Comparator.comparingInt(x -> x.oomScore)); |
| for (ProcessMemoryState process : managedProcessList) { |
| if (process.uid == Process.SYSTEM_UID) { |
| continue; |
| } |
| DmabufInfoReader.ProcessDmabuf proc = DmabufInfoReader.getProcessStats(process.pid); |
| if (proc == null || (proc.retainedBuffersCount <= 0 && proc.mappedBuffersCount <= 0)) { |
| continue; |
| } |
| pulledData.add( |
| FrameworkStatsLog.buildStatsEvent( |
| atomTag, |
| process.uid, |
| process.processName, |
| process.oomScore, |
| proc.retainedSizeKb, |
| proc.retainedBuffersCount, |
| proc.mappedSizeKb, |
| proc.mappedBuffersCount)); |
| } |
| return StatsManager.PULL_SUCCESS; |
| } |
| |
| private void registerSystemMemory() { |
| int tagId = FrameworkStatsLog.SYSTEM_MEMORY; |
| mStatsManager.setPullAtomCallback( |
| tagId, |
| null, // use default PullAtomMetadata values |
| DIRECT_EXECUTOR, |
| mStatsCallbackImpl |
| ); |
| } |
| |
| int pullSystemMemory(int atomTag, List<StatsEvent> pulledData) { |
| SystemMemoryUtil.Metrics metrics = SystemMemoryUtil.getMetrics(); |
| pulledData.add( |
| FrameworkStatsLog.buildStatsEvent( |
| atomTag, |
| metrics.unreclaimableSlabKb, |
| metrics.vmallocUsedKb, |
| metrics.pageTablesKb, |
| metrics.kernelStackKb, |
| metrics.totalIonKb, |
| metrics.unaccountedKb, |
| metrics.gpuTotalUsageKb, |
| metrics.gpuPrivateAllocationsKb, |
| metrics.dmaBufTotalExportedKb)); |
| return StatsManager.PULL_SUCCESS; |
| } |
| |
| private void registerVmStat() { |
| int tagId = FrameworkStatsLog.VMSTAT; |
| mStatsManager.setPullAtomCallback( |
| tagId, |
| null, // use default PullAtomMetadata values |
| DIRECT_EXECUTOR, |
| mStatsCallbackImpl |
| ); |
| } |
| |
| int pullVmStat(int atomTag, List<StatsEvent> pulledData) { |
| ProcfsMemoryUtil.VmStat vmStat = ProcfsMemoryUtil.readVmStat(); |
| if (vmStat != null) { |
| pulledData.add( |
| FrameworkStatsLog.buildStatsEvent( |
| atomTag, |
| vmStat.oomKillCount)); |
| } |
| 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) { |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, temp.getType(), |
| temp.getName(), (int) (temp.getValue() * 10), temp.getStatus())); |
| } |
| } 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) { |
| pulledData.add(FrameworkStatsLog.buildStatsEvent( |
| atomTag, device.getType(), device.getName(), (int) (device.getValue()))); |
| } |
| } 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) { |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, callStat.workSourceUid, |
| callStat.className, callStat.methodName, callStat.callCount, |
| callStat.exceptionCount, callStat.latencyMicros, callStat.maxLatencyMicros, |
| callStat.cpuTimeMicros, callStat.maxCpuTimeMicros, callStat.maxReplySizeBytes, |
| callStat.maxRequestSizeBytes, callStat.recordedCallCount, |
| callStat.screenInteractive, callStat.callingUid)); |
| } |
| 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()) { |
| pulledData.add( |
| FrameworkStatsLog.buildStatsEvent(atomTag, entry.getKey(), entry.getValue())); |
| } |
| 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) { |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, entry.workSourceUid, |
| entry.handlerClassName, entry.threadName, entry.messageName, entry.messageCount, |
| entry.exceptionCount, entry.recordedMessageCount, entry.totalLatencyMicros, |
| entry.cpuUsageMicros, entry.isInteractive, entry.maxCpuUsageMicros, |
| entry.maxLatencyMicros, entry.recordedDelayMessageCount, entry.delayMillis, |
| entry.maxDelayMillis)); |
| } |
| 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. |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, latency, fileBased, writeSpeed)); |
| 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()); |
| |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, |
| FrameworkStatsLog.DIRECTORY_USAGE__DIRECTORY__DATA, statFsData.getAvailableBytes(), |
| statFsData.getTotalBytes())); |
| |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, |
| FrameworkStatsLog.DIRECTORY_USAGE__DIRECTORY__CACHE, |
| statFsCache.getAvailableBytes(), statFsCache.getTotalBytes())); |
| |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, |
| FrameworkStatsLog.DIRECTORY_USAGE__DIRECTORY__SYSTEM, |
| statFsSystem.getAvailableBytes(), statFsSystem.getTotalBytes())); |
| 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); |
| // Validity 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++) { |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, pkg_names.getString(i), |
| app_sizes.optLong(i, /* fallback */ -1L), |
| app_data_sizes.optLong(i, /* fallback */ -1L), |
| app_cache_sizes.optLong(i, /* fallback */ -1L), cache_time)); |
| } |
| } 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); |
| |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, |
| FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__APP_SIZE, |
| json.optLong(DiskStatsFileLogger.APP_SIZE_AGG_KEY, /* fallback */ -1L), |
| cacheTime)); |
| |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, |
| FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__APP_DATA_SIZE, |
| json.optLong(DiskStatsFileLogger.APP_DATA_SIZE_AGG_KEY, /* fallback */ -1L), |
| cacheTime)); |
| |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, |
| FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__APP_CACHE_SIZE, |
| json.optLong(DiskStatsFileLogger.APP_CACHE_AGG_KEY, /* fallback */ -1L), |
| cacheTime)); |
| |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, |
| FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__PHOTOS, |
| json.optLong(DiskStatsFileLogger.PHOTOS_KEY, /* fallback */ -1L), cacheTime)); |
| |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, |
| FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__VIDEOS, |
| json.optLong(DiskStatsFileLogger.VIDEOS_KEY, /* fallback */ -1L), cacheTime)); |
| |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, |
| FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__AUDIO, |
| json.optLong(DiskStatsFileLogger.AUDIO_KEY, /* fallback */ -1L), cacheTime)); |
| |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, |
| FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__DOWNLOADS, |
| json.optLong(DiskStatsFileLogger.DOWNLOADS_KEY, /* fallback */ -1L), |
| cacheTime)); |
| |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, |
| FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__SYSTEM, |
| json.optLong(DiskStatsFileLogger.SYSTEM_KEY, /* fallback */ -1L), cacheTime)); |
| |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, |
| FrameworkStatsLog.CATEGORY_SIZE__CATEGORY__OTHER, |
| json.optLong(DiskStatsFileLogger.MISC_KEY, /* fallback */ -1L), cacheTime)); |
| } 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; |
| } |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, userId, numEnrolled)); |
| } |
| } 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) { |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, 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. |
| i)); |
| } |
| } |
| |
| 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) -> { |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, uid, fgCharsRead, |
| fgCharsWrite, fgBytesRead, fgBytesWrite, bgCharsRead, bgCharsWrite, |
| bgBytesRead, bgBytesWrite, fgFsync, bgFsync)); |
| }); |
| 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(); |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, proto.getBytes())); |
| 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); |
| pulledData.add(FrameworkStatsLog.buildStatsEvent( |
| atomTag, st.uid, st.name, st.base_utime, st.base_stime)); |
| } |
| 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; |
| } |
| |
| int[] frequencies = new int[CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES]; |
| int[] usageTimesMillis = new int[CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES]; |
| for (int k = 0; k < CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES; k++) { |
| if (k < cpuFrequencies.length) { |
| frequencies[k] = cpuFrequencies[k]; |
| usageTimesMillis[k] = 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 |
| frequencies[k] = 0; |
| usageTimesMillis[k] = 0; |
| } |
| } |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, processCpuUsage.uid, |
| processCpuUsage.processId, threadCpuUsage.threadId, |
| processCpuUsage.processName, threadCpuUsage.threadName, frequencies[0], |
| usageTimesMillis[0], frequencies[1], usageTimesMillis[1], frequencies[2], |
| usageTimesMillis[2], frequencies[3], usageTimesMillis[3], frequencies[4], |
| usageTimesMillis[4], frequencies[5], usageTimesMillis[5], frequencies[6], |
| usageTimesMillis[6], frequencies[7], usageTimesMillis[7])); |
| } |
| } |
| 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(); |
| pulledData.add(FrameworkStatsLog.buildStatsEvent( |
| atomTag, milliAmpHrsToNanoAmpSecs(bsHelper.getComputedPower()))); |
| 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; |
| } |
| pulledData.add(FrameworkStatsLog.buildStatsEvent( |
| atomTag, bs.uidObj.getUid(), milliAmpHrsToNanoAmpSecs(bs.totalPowerMah))); |
| } |
| 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. |
| } |
| pulledData.add(FrameworkStatsLog.buildStatsEvent( |
| atomTag, bs.drainType.ordinal(), milliAmpHrsToNanoAmpSecs(bs.totalPowerMah))); |
| } |
| 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; |
| |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, mDebugElapsedClockPullCount, |
| elapsedMillis, |
| // Log it twice to be able to test multi-value aggregation from ValueMetric. |
| elapsedMillis, clockDiffMillis, 1 /* always set */)); |
| |
| if (mDebugElapsedClockPullCount % 2 == 1) { |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, mDebugElapsedClockPullCount, |
| elapsedMillis, |
| // Log it twice to be able to test multi-value aggregation from ValueMetric. |
| elapsedMillis, clockDiffMillis, 2 /* set on odd pulls */)); |
| } |
| |
| 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; |
| } |
| |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, |
| mDebugFailingElapsedClockPullCount, elapsedMillis, |
| // Log it twice to be able to test multi-value aggregation from ValueMetric. |
| elapsedMillis, |
| mDebugFailingElapsedClockPreviousValue == 0 |
| ? 0 |
| : elapsedMillis - mDebugFailingElapsedClockPreviousValue)); |
| |
| 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) { |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, Build.FINGERPRINT, Build.BRAND, |
| Build.PRODUCT, Build.DEVICE, Build.VERSION.RELEASE_OR_CODENAME, Build.ID, |
| Build.VERSION.INCREMENTAL, Build.TYPE, Build.TAGS)); |
| 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) { |
| final long callingToken = Binder.clearCallingIdentity(); |
| try { |
| PackageManager pm = mContext.getPackageManager(); |
| RoleManagerLocal roleManagerLocal = LocalManagerRegistry.getManager( |
| RoleManagerLocal.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(); |
| |
| Map<String, Set<String>> roles = roleManagerLocal.getRolesAndHolders(userId); |
| |
| for (Map.Entry<String, Set<String>> roleEntry : roles.entrySet()) { |
| String roleName = roleEntry.getKey(); |
| Set<String> packageNames = roleEntry.getValue(); |
| |
| for (String packageName : packageNames) { |
| PackageInfo pkg; |
| try { |
| pkg = pm.getPackageInfoAsUser(packageName, 0, userId); |
| } catch (PackageManager.NameNotFoundException e) { |
| Slog.w(TAG, "Role holder " + packageName + " not found"); |
| return StatsManager.PULL_SKIP; |
| } |
| |
| pulledData.add(FrameworkStatsLog.buildStatsEvent( |
| atomTag, pkg.applicationInfo.uid, packageName, roleName)); |
| } |
| } |
| } |
| } 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 (permName.startsWith(COMMON_PERMISSION_PREFIX)) { |
| permName = permName.substring(COMMON_PERMISSION_PREFIX.length()); |
| } |
| |
| StatsEvent e; |
| if (atomTag == FrameworkStatsLog.DANGEROUS_PERMISSION_STATE) { |
| e = FrameworkStatsLog.buildStatsEvent(atomTag, permName, |
| pkg.applicationInfo.uid, "", |
| (pkg.requestedPermissionsFlags[permNum] |
| & REQUESTED_PERMISSION_GRANTED) |
| != 0, |
| permissionFlags, permissionInfo.getProtection() |
| | permissionInfo.getProtectionFlags()); |
| } else { |
| // DangeorusPermissionStateSampled atom. |
| e = FrameworkStatsLog.buildStatsEvent(atomTag, permName, |
| pkg.applicationInfo.uid, |
| (pkg.requestedPermissionsFlags[permNum] |
| & REQUESTED_PERMISSION_GRANTED) |
| != 0, |
| permissionFlags, permissionInfo.getProtection() |
| | permissionInfo.getProtectionFlags()); |
| } |
| pulledData.add(e); |
| } |
| } |
| } |
| } 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; |
| } |
| |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, tzDbVersion)); |
| return StatsManager.PULL_SUCCESS; |
| } |
| |
| private void registerTimeZoneDetectorState() { |
| int tagId = FrameworkStatsLog.TIME_ZONE_DETECTOR_STATE; |
| mStatsManager.setPullAtomCallback( |
| tagId, |
| null, // use default PullAtomMetadata values |
| DIRECT_EXECUTOR, |
| mStatsCallbackImpl |
| ); |
| } |
| |
| int pullTimeZoneDetectorStateLocked(int atomTag, List<StatsEvent> pulledData) { |
| final long token = Binder.clearCallingIdentity(); |
| try { |
| TimeZoneDetectorInternal timeZoneDetectorInternal = |
| LocalServices.getService(TimeZoneDetectorInternal.class); |
| MetricsTimeZoneDetectorState metricsState = |
| timeZoneDetectorInternal.generateMetricsState(); |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, |
| metricsState.isTelephonyDetectionSupported(), |
| metricsState.isGeoDetectionSupported(), |
| metricsState.isUserLocationEnabled(), |
| metricsState.getAutoDetectionEnabledSetting(), |
| metricsState.getGeoDetectionEnabledSetting(), |
| convertToMetricsDetectionMode(metricsState.getDetectionMode()), |
| metricsState.getDeviceTimeZoneIdOrdinal(), |
| metricsState.getLatestManualSuggestionProtoBytes(), |
| metricsState.getLatestTelephonySuggestionProtoBytes(), |
| metricsState.getLatestGeolocationSuggestionProtoBytes() |
| )); |
| } catch (RuntimeException e) { |
| Slog.e(TAG, "Getting time zone detection state failed: ", e); |
| return StatsManager.PULL_SKIP; |
| } finally { |
| Binder.restoreCallingIdentity(token); |
| } |
| return StatsManager.PULL_SUCCESS; |
| } |
| |
| private int convertToMetricsDetectionMode(int detectionMode) { |
| switch (detectionMode) { |
| case MetricsTimeZoneDetectorState.DETECTION_MODE_MANUAL: |
| return TIME_ZONE_DETECTOR_STATE__DETECTION_MODE__MANUAL; |
| case MetricsTimeZoneDetectorState.DETECTION_MODE_GEO: |
| return TIME_ZONE_DETECTOR_STATE__DETECTION_MODE__GEO; |
| case MetricsTimeZoneDetectorState.DETECTION_MODE_TELEPHONY: |
| return TIME_ZONE_DETECTOR_STATE__DETECTION_MODE__TELEPHONY; |
| default: |
| throw new IllegalArgumentException("" + detectionMode); |
| } |
| } |
| |
| 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; |
| } |
| |
| pulledData.add(FrameworkStatsLog.buildStatsEvent( |
| atomTag, externalStorageType, volumeType, diskInfo.size)); |
| } |
| } |
| 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) { |
| pulledData.add(FrameworkStatsLog.buildStatsEvent( |
| atomTag, externalStorageType, appInfo.packageName)); |
| } |
| } |
| 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(); |
| for (int userNum = 0; userNum < numUsers; userNum++) { |
| int userId = users.get(userNum).getUserHandle().getIdentifier(); |
| |
| 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); |
| |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, |
| unlockKeyguardEnabled != 0, unlockDismissesKeyguard != 0, |
| unlockAttentionRequired != 0, unlockAppEnabled != 0, |
| unlockAlwaysRequireConfirmation != 0, unlockDiversityRequired != 0)); |
| } |
| } 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 = ((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 e; |
| if (atomTag == FrameworkStatsLog.ATTRIBUTED_APP_OPS) { |
| e = FrameworkStatsLog.buildStatsEvent(atomTag, entry.mUid, entry.mPackageName, |
| entry.mAttributionTag, entry.mOp.getOpCode(), |
| entry.mOp.getForegroundAccessCount(OP_FLAGS_PULLED), |
| entry.mOp.getBackgroundAccessCount(OP_FLAGS_PULLED), |
| entry.mOp.getForegroundRejectCount(OP_FLAGS_PULLED), |
| entry.mOp.getBackgroundRejectCount(OP_FLAGS_PULLED), |
| entry.mOp.getForegroundAccessDuration(OP_FLAGS_PULLED), |
| entry.mOp.getBackgroundAccessDuration(OP_FLAGS_PULLED), |
| mDangerousAppOpsList.contains(entry.mOp.getOpCode()), samplingRate); |
| } else { |
| // AppOps atom. |
| e = FrameworkStatsLog.buildStatsEvent(atomTag, entry.mUid, entry.mPackageName, |
| entry.mOp.getOpCode(), entry.mOp.getForegroundAccessCount(OP_FLAGS_PULLED), |
| entry.mOp.getBackgroundAccessCount(OP_FLAGS_PULLED), |
| entry.mOp.getForegroundRejectCount(OP_FLAGS_PULLED), |
| entry.mOp.getBackgroundRejectCount(OP_FLAGS_PULLED), |
| entry.mOp.getForegroundAccessDuration(OP_FLAGS_PULLED), |
| entry.mOp.getBackgroundAccessDuration(OP_FLAGS_PULLED), |
| mDangerousAppOpsList.contains(entry.mOp.getOpCode())); |
| } |
| pulledData.add(e); |
| } |
| 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; |
| } |
| |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, message.getUid(), |
| message.getPackageName(), "", |
| message.getAttributionTag() == null ? "" : message.getAttributionTag(), |
| message.getMessage(), message.getSamplingStrategy(), |
| AppOpsManager.strOpToOp(message.getOp()))); |
| } 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); |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, Arrays.copyOf(stats, len[0]))); |
| } |
| |
| 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); |
| } |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, pulledValue)); |
| }); |
| } 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; |
| } |
| |
| private void registerInstalledIncrementalPackages() { |
| int tagId = FrameworkStatsLog.INSTALLED_INCREMENTAL_PACKAGE; |
| mStatsManager.setPullAtomCallback( |
| tagId, |
| null, // use default PullAtomMetadata values |
| DIRECT_EXECUTOR, |
| mStatsCallbackImpl |
| ); |
| } |
| |
| int pullInstalledIncrementalPackagesLocked(int atomTag, List<StatsEvent> pulledData) { |
| final PackageManager pm = mContext.getPackageManager(); |
| if (!pm.hasSystemFeature(PackageManager.FEATURE_INCREMENTAL_DELIVERY)) { |
| // Incremental is not enabled on this device. The result list will be empty. |
| return StatsManager.PULL_SUCCESS; |
| } |
| List<PackageInfo> installedPackages = pm.getInstalledPackages(0); |
| for (PackageInfo pi : installedPackages) { |
| if (IncrementalManager.isIncrementalPath(pi.applicationInfo.getBaseCodePath())) { |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, pi.applicationInfo.uid)); |
| } |
| } |
| return StatsManager.PULL_SUCCESS; |
| } |
| |
| private void registerKeystoreStorageStats() { |
| mStatsManager.setPullAtomCallback( |
| FrameworkStatsLog.KEYSTORE2_STORAGE_STATS, |
| null, // use default PullAtomMetadata values, |
| DIRECT_EXECUTOR, |
| mStatsCallbackImpl); |
| } |
| |
| private void registerRkpPoolStats() { |
| mStatsManager.setPullAtomCallback( |
| FrameworkStatsLog.RKP_POOL_STATS, |
| null, // use default PullAtomMetadata values, |
| DIRECT_EXECUTOR, |
| mStatsCallbackImpl); |
| } |
| |
| private void registerKeystoreKeyCreationWithGeneralInfo() { |
| mStatsManager.setPullAtomCallback( |
| FrameworkStatsLog.KEYSTORE2_KEY_CREATION_WITH_GENERAL_INFO, |
| null, // use default PullAtomMetadata values, |
| DIRECT_EXECUTOR, |
| mStatsCallbackImpl); |
| } |
| |
| private void registerKeystoreKeyCreationWithAuthInfo() { |
| mStatsManager.setPullAtomCallback( |
| FrameworkStatsLog.KEYSTORE2_KEY_CREATION_WITH_AUTH_INFO, |
| null, // use default PullAtomMetadata values, |
| DIRECT_EXECUTOR, |
| mStatsCallbackImpl); |
| } |
| |
| private void registerKeystoreKeyCreationWithPurposeModesInfo() { |
| mStatsManager.setPullAtomCallback( |
| FrameworkStatsLog.KEYSTORE2_KEY_CREATION_WITH_PURPOSE_AND_MODES_INFO, |
| null, // use default PullAtomMetadata values, |
| DIRECT_EXECUTOR, |
| mStatsCallbackImpl); |
| } |
| |
| private void registerKeystoreAtomWithOverflow() { |
| mStatsManager.setPullAtomCallback( |
| FrameworkStatsLog.KEYSTORE2_ATOM_WITH_OVERFLOW, |
| null, // use default PullAtomMetadata values, |
| DIRECT_EXECUTOR, |
| mStatsCallbackImpl); |
| } |
| |
| private void registerKeystoreKeyOperationWithPurposeAndModesInfo() { |
| mStatsManager.setPullAtomCallback( |
| FrameworkStatsLog.KEYSTORE2_KEY_OPERATION_WITH_PURPOSE_AND_MODES_INFO, |
| null, // use default PullAtomMetadata values, |
| DIRECT_EXECUTOR, |
| mStatsCallbackImpl); |
| } |
| |
| private void registerKeystoreKeyOperationWithGeneralInfo() { |
| mStatsManager.setPullAtomCallback( |
| FrameworkStatsLog.KEYSTORE2_KEY_OPERATION_WITH_GENERAL_INFO, |
| null, // use default PullAtomMetadata values, |
| DIRECT_EXECUTOR, |
| mStatsCallbackImpl); |
| } |
| |
| private void registerRkpErrorStats() { |
| mStatsManager.setPullAtomCallback( |
| FrameworkStatsLog.RKP_ERROR_STATS, |
| null, // use default PullAtomMetadata values, |
| DIRECT_EXECUTOR, |
| mStatsCallbackImpl); |
| } |
| |
| private void registerKeystoreCrashStats() { |
| mStatsManager.setPullAtomCallback( |
| FrameworkStatsLog.KEYSTORE2_CRASH_STATS, |
| null, // use default PullAtomMetadata values, |
| DIRECT_EXECUTOR, |
| mStatsCallbackImpl); |
| } |
| |
| int parseKeystoreStorageStats(KeystoreAtom[] atoms, List<StatsEvent> pulledData) { |
| for (KeystoreAtom atomWrapper : atoms) { |
| if (atomWrapper.payload.getTag() != KeystoreAtomPayload.storageStats) { |
| return StatsManager.PULL_SKIP; |
| } |
| StorageStats atom = atomWrapper.payload.getStorageStats(); |
| pulledData.add(FrameworkStatsLog.buildStatsEvent( |
| FrameworkStatsLog.KEYSTORE2_STORAGE_STATS, atom.storage_type, |
| atom.size, atom.unused_size)); |
| } |
| return StatsManager.PULL_SUCCESS; |
| } |
| |
| int parseRkpPoolStats(KeystoreAtom[] atoms, List<StatsEvent> pulledData) { |
| for (KeystoreAtom atomWrapper : atoms) { |
| if (atomWrapper.payload.getTag() != KeystoreAtomPayload.rkpPoolStats) { |
| return StatsManager.PULL_SKIP; |
| } |
| RkpPoolStats atom = atomWrapper.payload.getRkpPoolStats(); |
| pulledData.add(FrameworkStatsLog.buildStatsEvent( |
| FrameworkStatsLog.RKP_POOL_STATS, atom.security_level, atom.expiring, |
| atom.unassigned, atom.attested, atom.total)); |
| } |
| return StatsManager.PULL_SUCCESS; |
| } |
| |
| int parseKeystoreKeyCreationWithGeneralInfo(KeystoreAtom[] atoms, List<StatsEvent> pulledData) { |
| for (KeystoreAtom atomWrapper : atoms) { |
| if (atomWrapper.payload.getTag() |
| != KeystoreAtomPayload.keyCreationWithGeneralInfo) { |
| return StatsManager.PULL_SKIP; |
| } |
| KeyCreationWithGeneralInfo atom = atomWrapper.payload.getKeyCreationWithGeneralInfo(); |
| pulledData.add(FrameworkStatsLog.buildStatsEvent( |
| FrameworkStatsLog.KEYSTORE2_KEY_CREATION_WITH_GENERAL_INFO, atom.algorithm, |
| atom.key_size, atom.ec_curve, atom.key_origin, atom.error_code, |
| atom.attestation_requested, atomWrapper.count)); |
| } |
| return StatsManager.PULL_SUCCESS; |
| } |
| |
| int parseKeystoreKeyCreationWithAuthInfo(KeystoreAtom[] atoms, List<StatsEvent> pulledData) { |
| for (KeystoreAtom atomWrapper : atoms) { |
| if (atomWrapper.payload.getTag() != KeystoreAtomPayload.keyCreationWithAuthInfo) { |
| return StatsManager.PULL_SKIP; |
| } |
| KeyCreationWithAuthInfo atom = atomWrapper.payload.getKeyCreationWithAuthInfo(); |
| pulledData.add(FrameworkStatsLog.buildStatsEvent( |
| FrameworkStatsLog.KEYSTORE2_KEY_CREATION_WITH_AUTH_INFO, atom.user_auth_type, |
| atom.log10_auth_key_timeout_seconds, atom.security_level, atomWrapper.count)); |
| } |
| return StatsManager.PULL_SUCCESS; |
| } |
| |
| |
| int parseKeystoreKeyCreationWithPurposeModesInfo(KeystoreAtom[] atoms, |
| List<StatsEvent> pulledData) { |
| for (KeystoreAtom atomWrapper : atoms) { |
| if (atomWrapper.payload.getTag() |
| != KeystoreAtomPayload.keyCreationWithPurposeAndModesInfo) { |
| return StatsManager.PULL_SKIP; |
| } |
| KeyCreationWithPurposeAndModesInfo atom = |
| atomWrapper.payload.getKeyCreationWithPurposeAndModesInfo(); |
| pulledData.add(FrameworkStatsLog.buildStatsEvent( |
| FrameworkStatsLog.KEYSTORE2_KEY_CREATION_WITH_PURPOSE_AND_MODES_INFO, |
| atom.algorithm, atom.purpose_bitmap, |
| atom.padding_mode_bitmap, atom.digest_bitmap, atom.block_mode_bitmap, |
| atomWrapper.count)); |
| } |
| return StatsManager.PULL_SUCCESS; |
| } |
| |
| int parseKeystoreAtomWithOverflow(KeystoreAtom[] atoms, List<StatsEvent> pulledData) { |
| for (KeystoreAtom atomWrapper : atoms) { |
| if (atomWrapper.payload.getTag() |
| != KeystoreAtomPayload.keystore2AtomWithOverflow) { |
| return StatsManager.PULL_SKIP; |
| } |
| Keystore2AtomWithOverflow atom = atomWrapper.payload.getKeystore2AtomWithOverflow(); |
| pulledData.add(FrameworkStatsLog.buildStatsEvent( |
| FrameworkStatsLog.KEYSTORE2_ATOM_WITH_OVERFLOW, atom.atom_id, |
| atomWrapper.count)); |
| } |
| return StatsManager.PULL_SUCCESS; |
| } |
| |
| int parseKeystoreKeyOperationWithPurposeModesInfo(KeystoreAtom[] atoms, |
| List<StatsEvent> pulledData) { |
| for (KeystoreAtom atomWrapper : atoms) { |
| if (atomWrapper.payload.getTag() |
| != KeystoreAtomPayload.keyOperationWithPurposeAndModesInfo) { |
| return StatsManager.PULL_SKIP; |
| } |
| KeyOperationWithPurposeAndModesInfo atom = |
| atomWrapper.payload.getKeyOperationWithPurposeAndModesInfo(); |
| pulledData.add(FrameworkStatsLog.buildStatsEvent( |
| FrameworkStatsLog.KEYSTORE2_KEY_OPERATION_WITH_PURPOSE_AND_MODES_INFO, |
| atom.purpose, atom.padding_mode_bitmap, atom.digest_bitmap, |
| atom.block_mode_bitmap, atomWrapper.count)); |
| } |
| return StatsManager.PULL_SUCCESS; |
| } |
| |
| int parseKeystoreKeyOperationWithGeneralInfo(KeystoreAtom[] atoms, |
| List<StatsEvent> pulledData) { |
| for (KeystoreAtom atomWrapper : atoms) { |
| if (atomWrapper.payload.getTag() |
| != KeystoreAtomPayload.keyOperationWithGeneralInfo) { |
| return StatsManager.PULL_SKIP; |
| } |
| KeyOperationWithGeneralInfo atom = atomWrapper.payload.getKeyOperationWithGeneralInfo(); |
| pulledData.add(FrameworkStatsLog.buildStatsEvent( |
| FrameworkStatsLog.KEYSTORE2_KEY_OPERATION_WITH_GENERAL_INFO, atom.outcome, |
| atom.error_code, atom.key_upgraded, atom.security_level, atomWrapper.count)); |
| } |
| return StatsManager.PULL_SUCCESS; |
| } |
| |
| int parseRkpErrorStats(KeystoreAtom[] atoms, |
| List<StatsEvent> pulledData) { |
| for (KeystoreAtom atomWrapper : atoms) { |
| if (atomWrapper.payload.getTag() != KeystoreAtomPayload.rkpErrorStats) { |
| return StatsManager.PULL_SKIP; |
| } |
| RkpErrorStats atom = atomWrapper.payload.getRkpErrorStats(); |
| pulledData.add(FrameworkStatsLog.buildStatsEvent( |
| FrameworkStatsLog.RKP_ERROR_STATS, atom.rkpError, atomWrapper.count)); |
| } |
| return StatsManager.PULL_SUCCESS; |
| } |
| |
| int parseKeystoreCrashStats(KeystoreAtom[] atoms, |
| List<StatsEvent> pulledData) { |
| for (KeystoreAtom atomWrapper : atoms) { |
| if (atomWrapper.payload.getTag() != KeystoreAtomPayload.crashStats) { |
| return StatsManager.PULL_SKIP; |
| } |
| CrashStats atom = atomWrapper.payload.getCrashStats(); |
| pulledData.add(FrameworkStatsLog.buildStatsEvent( |
| FrameworkStatsLog.KEYSTORE2_CRASH_STATS, atom.count_of_crash_events)); |
| } |
| return StatsManager.PULL_SUCCESS; |
| } |
| |
| int pullKeystoreAtoms(int atomTag, List<StatsEvent> pulledData) { |
| IKeystoreMetrics keystoreMetricsService = getIKeystoreMetricsService(); |
| if (keystoreMetricsService == null) { |
| Slog.w(TAG, "Keystore service is null"); |
| return StatsManager.PULL_SKIP; |
| } |
| final long callingToken = Binder.clearCallingIdentity(); |
| try { |
| KeystoreAtom[] atoms = keystoreMetricsService.pullMetrics(atomTag); |
| switch (atomTag) { |
| case FrameworkStatsLog.KEYSTORE2_STORAGE_STATS: |
| return parseKeystoreStorageStats(atoms, pulledData); |
| case FrameworkStatsLog.RKP_POOL_STATS: |
| return parseRkpPoolStats(atoms, pulledData); |
| case FrameworkStatsLog.KEYSTORE2_KEY_CREATION_WITH_GENERAL_INFO: |
| return parseKeystoreKeyCreationWithGeneralInfo(atoms, pulledData); |
| case FrameworkStatsLog.KEYSTORE2_KEY_CREATION_WITH_AUTH_INFO: |
| return parseKeystoreKeyCreationWithAuthInfo(atoms, pulledData); |
| case FrameworkStatsLog.KEYSTORE2_KEY_CREATION_WITH_PURPOSE_AND_MODES_INFO: |
| return parseKeystoreKeyCreationWithPurposeModesInfo(atoms, pulledData); |
| case FrameworkStatsLog.KEYSTORE2_ATOM_WITH_OVERFLOW: |
| return parseKeystoreAtomWithOverflow(atoms, pulledData); |
| case FrameworkStatsLog.KEYSTORE2_KEY_OPERATION_WITH_PURPOSE_AND_MODES_INFO: |
| return parseKeystoreKeyOperationWithPurposeModesInfo(atoms, pulledData); |
| case FrameworkStatsLog.KEYSTORE2_KEY_OPERATION_WITH_GENERAL_INFO: |
| return parseKeystoreKeyOperationWithGeneralInfo(atoms, pulledData); |
| case FrameworkStatsLog.RKP_ERROR_STATS: |
| return parseRkpErrorStats(atoms, pulledData); |
| case FrameworkStatsLog.KEYSTORE2_CRASH_STATS: |
| return parseKeystoreCrashStats(atoms, pulledData); |
| default: |
| Slog.w(TAG, "Unsupported keystore atom: " + atomTag); |
| return StatsManager.PULL_SKIP; |
| } |
| } catch (RemoteException e) { |
| // Should not happen. |
| Slog.e(TAG, "Disconnected from keystore service. Cannot pull.", e); |
| return StatsManager.PULL_SKIP; |
| } catch (ServiceSpecificException e) { |
| Slog.e(TAG, "pulling keystore metrics failed", e); |
| return StatsManager.PULL_SKIP; |
| } finally { |
| Binder.restoreCallingIdentity(callingToken); |
| } |
| } |
| |
| // 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.getNetId(), |
| FrameworkStatsLog.CONNECTIVITY_STATE_CHANGED__STATE__CONNECTED); |
| } |
| |
| @Override |
| public void onLost(Network network) { |
| FrameworkStatsLog.write(FrameworkStatsLog.CONNECTIVITY_STATE_CHANGED, |
| network.getNetId(), |
| 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)); |
| } |
| } |
| } |
| } |
| |
| } |