| /* |
| * 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.AppProtoEnums.HOSTING_COMPONENT_TYPE_EMPTY; |
| import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED; |
| import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS; |
| import static android.hardware.display.HdrConversionMode.HDR_CONVERSION_PASSTHROUGH; |
| import static android.hardware.display.HdrConversionMode.HDR_CONVERSION_UNSUPPORTED; |
| import static android.hardware.graphics.common.Hdr.DOLBY_VISION; |
| import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; |
| import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET; |
| import static android.net.NetworkCapabilities.TRANSPORT_WIFI; |
| import static android.net.NetworkStats.METERED_YES; |
| import static android.net.NetworkTemplate.MATCH_ETHERNET; |
| import static android.net.NetworkTemplate.MATCH_MOBILE; |
| import static android.net.NetworkTemplate.MATCH_WIFI; |
| import static android.net.NetworkTemplate.OEM_MANAGED_ALL; |
| import static android.net.NetworkTemplate.OEM_MANAGED_PAID; |
| import static android.net.NetworkTemplate.OEM_MANAGED_PRIVATE; |
| import static android.os.Debug.getIonHeapsSizeKb; |
| import static android.os.Process.LAST_SHARED_APPLICATION_GID; |
| import static android.os.Process.SYSTEM_UID; |
| 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 android.view.Display.HdrCapabilities.HDR_TYPE_INVALID; |
| |
| import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR; |
| import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_BUTTON; |
| import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_FLOATING_MENU; |
| import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_GESTURE; |
| import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__UNKNOWN_TYPE; |
| 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.internal.util.FrameworkStatsLog.TIME_ZONE_DETECTOR_STATE__DETECTION_MODE__UNKNOWN; |
| 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 libcore.io.IoUtils.closeQuietly; |
| |
| 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.annotation.UserIdInt; |
| 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.PendingIntentStats; |
| import android.app.ProcessMemoryState; |
| import android.app.RuntimeAppOpAccessMessage; |
| import android.app.StatsManager; |
| import android.app.StatsManager.PullAtomMetadata; |
| import android.app.usage.NetworkStatsManager; |
| import android.bluetooth.BluetoothActivityEnergyInfo; |
| import android.bluetooth.BluetoothAdapter; |
| import android.bluetooth.UidTraffic; |
| import android.content.ContentResolver; |
| import android.content.Context; |
| import android.content.pm.ApplicationInfo; |
| import android.content.pm.IncrementalStatesInfo; |
| import android.content.pm.PackageInfo; |
| import android.content.pm.PackageManager; |
| import android.content.pm.PackageManagerInternal; |
| import android.content.pm.PermissionInfo; |
| import android.content.pm.UserInfo; |
| import android.hardware.biometrics.BiometricsProtoEnums; |
| import android.hardware.display.DisplayManager; |
| import android.hardware.face.FaceManager; |
| import android.hardware.fingerprint.FingerprintManager; |
| import android.media.AudioManager; |
| import android.media.MediaDrm; |
| import android.media.UnsupportedSchemeException; |
| import android.net.ConnectivityManager; |
| 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.BatteryStatsManager; |
| import android.os.BatteryUsageStats; |
| 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.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.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.Pair; |
| import android.util.Slog; |
| import android.util.SparseArray; |
| import android.util.SparseIntArray; |
| import android.util.StatsEvent; |
| import android.util.proto.ProtoOutputStream; |
| import android.uwb.UwbActivityEnergyInfo; |
| import android.uwb.UwbManager; |
| import android.view.Display; |
| |
| import com.android.internal.annotations.GuardedBy; |
| import com.android.internal.app.procstats.IProcessStats; |
| import com.android.internal.app.procstats.ProcessStats; |
| import com.android.internal.app.procstats.StatsEventOutput; |
| import com.android.internal.os.BackgroundThread; |
| import com.android.internal.os.BinderCallsStats.ExportedCallStat; |
| import com.android.internal.os.KernelAllocationStats; |
| 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.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.util.CollectionUtils; |
| import com.android.internal.util.FrameworkStatsLog; |
| import com.android.net.module.util.NetworkStatsUtils; |
| import com.android.role.RoleManagerLocal; |
| import com.android.server.BinderCallsStatsService; |
| import com.android.server.LocalManagerRegistry; |
| import com.android.server.LocalServices; |
| import com.android.server.PinnerService; |
| import com.android.server.PinnerService.PinnedFileStats; |
| import com.android.server.SystemService; |
| import com.android.server.SystemServiceManager; |
| import com.android.server.am.MemoryStatUtil.MemoryStat; |
| import com.android.server.health.HealthServiceWrapper; |
| import com.android.server.notification.NotificationManagerService; |
| import com.android.server.pm.UserManagerInternal; |
| import com.android.server.power.stats.KernelWakelockReader; |
| import com.android.server.power.stats.KernelWakelockStats; |
| import com.android.server.power.stats.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes; |
| 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.ByteArrayOutputStream; |
| import java.io.File; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.time.Instant; |
| import java.time.temporal.ChronoUnit; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.MissingResourceException; |
| import java.util.NoSuchElementException; |
| 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.concurrent.atomic.AtomicInteger; |
| import java.util.function.Function; |
| |
| /** |
| * 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 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 UwbManager mUwbManager; |
| private SubscriptionManager mSubscriptionManager; |
| private NetworkStatsManager mNetworkStatsManager; |
| |
| @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 HealthServiceWrapper mHealthService; |
| |
| @Nullable |
| @GuardedBy("mCpuTimePerThreadFreqLock") |
| private KernelCpuThreadReaderDiff mKernelCpuThreadReader; |
| |
| 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 mUwbActivityInfoLock = 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 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 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.UWB_ACTIVITY_INFO: |
| synchronized (mUwbActivityInfoLock) { |
| return pullUwbActivityInfoLocked(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: |
| return pullProcessMemorySnapshot(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(atomTag, data); |
| } |
| case FrameworkStatsLog.PROC_STATS_PKG_PROC: |
| synchronized (mProcStatsLock) { |
| return pullProcStatsLocked(atomTag, data); |
| } |
| case FrameworkStatsLog.PROCESS_STATE: |
| synchronized (mProcStatsLock) { |
| return pullProcessStateLocked(atomTag, data); |
| } |
| case FrameworkStatsLog.PROCESS_ASSOCIATION: |
| synchronized (mProcStatsLock) { |
| return pullProcessAssociationLocked(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.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.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); |
| case FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS: |
| return pullAccessibilityShortcutStatsLocked(atomTag, data); |
| case FrameworkStatsLog.ACCESSIBILITY_FLOATING_MENU_STATS: |
| return pullAccessibilityFloatingMenuStatsLocked(atomTag, data); |
| case FrameworkStatsLog.MEDIA_CAPABILITIES: |
| return pullMediaCapabilitiesStats(atomTag, data); |
| case FrameworkStatsLog.PINNED_FILE_SIZES_PER_PACKAGE: |
| return pullSystemServerPinnerStats(atomTag, data); |
| case FrameworkStatsLog.PENDING_INTENTS_PER_PACKAGE: |
| return pullPendingIntentsPerPackage(atomTag, data); |
| case FrameworkStatsLog.HDR_CAPABILITIES: |
| return pullHdrCapabilities(atomTag, data); |
| case FrameworkStatsLog.CACHED_APPS_HIGH_WATERMARK: |
| return pullCachedAppsHighWatermark(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) { |
| BackgroundThread.getHandler().post(() -> { |
| // Network stats related pullers can only be initialized after service is ready. |
| initAndRegisterNetworkStatsPullers(); |
| // For services that are not ready at boot phase PHASE_SYSTEM_SERVICES_READY |
| initAndRegisterDeferredPullers(); |
| }); |
| } |
| } |
| |
| // 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); |
| mNetworkStatsManager = mContext.getSystemService(NetworkStatsManager.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 |
| try { |
| mHealthService = HealthServiceWrapper.create(null); |
| } catch (RemoteException | NoSuchElementException 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(); |
| registerProcessState(); |
| registerProcessAssociation(); |
| registerDiskIO(); |
| registerPowerProfile(); |
| registerProcessCpuTime(); |
| registerCpuTimePerThreadFreq(); |
| registerDeviceCalculatedPowerUse(); |
| registerDebugElapsedClock(); |
| registerDebugFailingElapsedClock(); |
| registerBuildInformation(); |
| registerRoleHolder(); |
| registerTimeZoneDataInfo(); |
| registerTimeZoneDetectorState(); |
| registerExternalStorageInfo(); |
| registerAppsOnExternalStorageInfo(); |
| registerFaceSettings(); |
| registerAppOps(); |
| registerAttributedAppOps(); |
| registerRuntimeAppOpAccessMessage(); |
| registerNotificationRemoteViews(); |
| registerDangerousPermissionState(); |
| registerDangerousPermissionStateSampled(); |
| registerBatteryLevel(); |
| registerRemainingBatteryCapacity(); |
| registerFullBatteryCapacity(); |
| registerBatteryVoltage(); |
| registerBatteryCycleCount(); |
| registerSettingsStats(); |
| registerInstalledIncrementalPackages(); |
| registerKeystoreStorageStats(); |
| registerKeystoreKeyCreationWithGeneralInfo(); |
| registerKeystoreKeyCreationWithAuthInfo(); |
| registerKeystoreKeyCreationWithPurposeModesInfo(); |
| registerKeystoreAtomWithOverflow(); |
| registerKeystoreKeyOperationWithPurposeAndModesInfo(); |
| registerKeystoreKeyOperationWithGeneralInfo(); |
| registerRkpErrorStats(); |
| registerKeystoreCrashStats(); |
| registerAccessibilityShortcutStats(); |
| registerAccessibilityFloatingMenuStats(); |
| registerMediaCapabilitiesStats(); |
| registerPendingIntentsPerPackagePuller(); |
| registerPinnerServiceStats(); |
| registerHdrCapabilitiesPuller(); |
| registerCachedAppsHighWatermarkPuller(); |
| } |
| |
| 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(); |
| } |
| |
| private void initAndRegisterDeferredPullers() { |
| mUwbManager = mContext.getSystemService(UwbManager.class); |
| |
| registerUwbActivityInfo(); |
| } |
| |
| 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(sliceNetworkStatsByUid(stats), |
| 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(sliceNetworkStatsByUid(stats), |
| 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( |
| new NetworkTemplate.Builder(MATCH_WIFI).build(), /*includeTags=*/true); |
| final NetworkStats cellularStats = getUidNetworkStatsSnapshotForTemplate( |
| new NetworkTemplate.Builder(MATCH_MOBILE) |
| .setMeteredness(METERED_YES).build(), /*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( |
| removeEmptyEntries(item.stats.subtract(baseline.stats)), item.transports, |
| item.slicedByFgbg, item.slicedByTag, item.slicedByMetered, item.ratType, |
| item.subInfo, item.oemManaged); |
| |
| // If no diff, skip. |
| if (!diff.stats.iterator().hasNext()) 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; |
| } |
| |
| @NonNull |
| private static NetworkStats removeEmptyEntries(NetworkStats stats) { |
| NetworkStats ret = new NetworkStats(0, 1); |
| for (NetworkStats.Entry e : stats) { |
| if (e.getRxBytes() != 0 || e.getRxPackets() != 0 || e.getTxBytes() != 0 |
| || e.getTxPackets() != 0 || e.getOperations() != 0) { |
| ret = ret.addEntry(e); |
| } |
| } |
| return ret; |
| } |
| |
| private void addNetworkStats(int atomTag, @NonNull List<StatsEvent> ret, |
| @NonNull NetworkStatsExt statsExt) { |
| for (NetworkStats.Entry entry : statsExt.stats) { |
| StatsEvent statsEvent; |
| if (statsExt.slicedByFgbg) { |
| // MobileBytesTransferByFgBg atom or WifiBytesTransferByFgBg atom. |
| statsEvent = FrameworkStatsLog.buildStatsEvent( |
| atomTag, entry.getUid(), |
| (entry.getSet() > 0), entry.getRxBytes(), entry.getRxPackets(), |
| entry.getTxBytes(), entry.getTxPackets()); |
| } else { |
| // MobileBytesTransfer atom or WifiBytesTransfer atom. |
| statsEvent = FrameworkStatsLog.buildStatsEvent( |
| atomTag, entry.getUid(), entry.getRxBytes(), |
| entry.getRxPackets(), entry.getTxBytes(), entry.getTxPackets()); |
| } |
| ret.add(statsEvent); |
| } |
| } |
| |
| private void addBytesTransferByTagAndMeteredAtoms(@NonNull NetworkStatsExt statsExt, |
| @NonNull List<StatsEvent> pulledData) { |
| for (NetworkStats.Entry entry : statsExt.stats) { |
| pulledData.add(FrameworkStatsLog.buildStatsEvent( |
| FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED, entry.getUid(), |
| entry.getMetered() == NetworkStats.METERED_YES, entry.getTag(), |
| entry.getRxBytes(), entry.getRxPackets(), entry.getTxBytes(), |
| entry.getTxPackets())); |
| } |
| } |
| |
| private void addDataUsageBytesTransferAtoms(@NonNull NetworkStatsExt statsExt, |
| @NonNull List<StatsEvent> pulledData) { |
| |
| // Workaround for 5G NSA mode, see {@link NetworkStatsManager#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 == NetworkStatsManager.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; |
| |
| for (NetworkStats.Entry entry : statsExt.stats) { |
| pulledData.add(FrameworkStatsLog.buildStatsEvent( |
| FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER, |
| entry.getSet(), entry.getRxBytes(), entry.getRxPackets(), |
| entry.getTxBytes(), entry.getTxPackets(), |
| 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 int oemManaged = statsExt.oemManaged; |
| for (final int transport : statsExt.transports) { |
| for (NetworkStats.Entry entry : statsExt.stats) { |
| pulledData.add(FrameworkStatsLog.buildStatsEvent( |
| FrameworkStatsLog.OEM_MANAGED_BYTES_TRANSFER, entry.getUid(), |
| (entry.getSet() > 0), oemManaged, transport, entry.getRxBytes(), |
| entry.getRxPackets(), entry.getTxBytes(), entry.getTxPackets())); |
| } |
| } |
| } |
| |
| @NonNull |
| private List<NetworkStatsExt> getDataUsageBytesTransferSnapshotForOemManaged() { |
| final List<Pair<Integer, Integer>> matchRulesAndTransports = List.of( |
| new Pair(MATCH_ETHERNET, TRANSPORT_ETHERNET), |
| new Pair(MATCH_MOBILE, TRANSPORT_CELLULAR), |
| new Pair(MATCH_WIFI, TRANSPORT_WIFI) |
| ); |
| final int[] oemManagedTypes = new int[]{OEM_MANAGED_PAID | OEM_MANAGED_PRIVATE, |
| OEM_MANAGED_PAID, OEM_MANAGED_PRIVATE}; |
| |
| final List<NetworkStatsExt> ret = new ArrayList<>(); |
| |
| for (Pair<Integer, Integer> ruleAndTransport : matchRulesAndTransports) { |
| final Integer matchRule = ruleAndTransport.first; |
| for (final int oemManaged : oemManagedTypes) { |
| // Subscriber Ids and Wifi Network Keys will not be set since the purpose is to |
| // slice statistics of different OEM managed networks among all network types. |
| // Thus, specifying networks through their identifiers are not needed. |
| final NetworkTemplate template = new NetworkTemplate.Builder(matchRule) |
| .setOemManaged(oemManaged).build(); |
| final NetworkStats stats = getUidNetworkStatsSnapshotForTemplate(template, false); |
| final Integer transport = ruleAndTransport.second; |
| if (stats != null) { |
| ret.add(new NetworkStatsExt(sliceNetworkStatsByUidAndFgbg(stats), |
| new int[]{transport}, /*slicedByFgbg=*/true, /*slicedByTag=*/false, |
| /*slicedByMetered=*/false, TelephonyManager.NETWORK_TYPE_UNKNOWN, |
| /*subInfo=*/null, oemManaged)); |
| } |
| } |
| } |
| |
| return ret; |
| } |
| |
| /** |
| * Create a snapshot of NetworkStats for a given transport. |
| */ |
| @Nullable |
| private NetworkStats getUidNetworkStatsSnapshotForTransport(int transport) { |
| NetworkTemplate template = null; |
| switch (transport) { |
| case TRANSPORT_CELLULAR: |
| template = new NetworkTemplate.Builder(MATCH_MOBILE) |
| .setMeteredness(METERED_YES).build(); |
| break; |
| case TRANSPORT_WIFI: |
| template = new NetworkTemplate.Builder(MATCH_WIFI).build(); |
| break; |
| default: |
| Log.wtf(TAG, "Unexpected transport."); |
| } |
| 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); |
| |
| // 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. |
| if (template.getMatchRule() == MATCH_WIFI && template.getSubscriberIds().isEmpty()) { |
| mNetworkStatsManager.forceUpdate(); |
| } |
| |
| final android.app.usage.NetworkStats queryNonTaggedStats = |
| mNetworkStatsManager.querySummary( |
| template, currentTimeInMillis - elapsedMillisSinceBoot - bucketDuration, |
| currentTimeInMillis); |
| |
| final NetworkStats nonTaggedStats = |
| NetworkStatsUtils.fromPublicNetworkStats(queryNonTaggedStats); |
| if (!includeTags) return nonTaggedStats; |
| |
| final android.app.usage.NetworkStats queryTaggedStats = |
| mNetworkStatsManager.queryTaggedSummary(template, |
| currentTimeInMillis - elapsedMillisSinceBoot - bucketDuration, |
| currentTimeInMillis); |
| final NetworkStats taggedStats = |
| NetworkStatsUtils.fromPublicNetworkStats(queryTaggedStats); |
| return nonTaggedStats.add(taggedStats); |
| } |
| |
| @NonNull |
| private List<NetworkStatsExt> getDataUsageBytesTransferSnapshotForSub( |
| @NonNull SubInfo subInfo) { |
| final List<NetworkStatsExt> ret = new ArrayList<>(); |
| for (final int ratType : getAllCollapsedRatTypes()) { |
| final NetworkTemplate template = |
| new NetworkTemplate.Builder(MATCH_MOBILE) |
| .setSubscriberIds(Set.of(subInfo.subscriberId)) |
| .setRatType(ratType) |
| .setMeteredness(METERED_YES).build(); |
| 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; |
| } |
| |
| /** |
| * Return all supported collapsed RAT types that could be returned by |
| * {@link android.app.usage.NetworkStatsManager#getCollapsedRatType(int)}. |
| */ |
| @NonNull |
| private static int[] getAllCollapsedRatTypes() { |
| final int[] ratTypes = TelephonyManager.getAllNetworkTypes(); |
| final HashSet<Integer> collapsedRatTypes = new HashSet<>(); |
| for (final int ratType : ratTypes) { |
| collapsedRatTypes.add(NetworkStatsManager.getCollapsedRatType(ratType)); |
| } |
| // Add NETWORK_TYPE_5G_NSA to the returned list since 5G NSA is a virtual RAT type and |
| // it is not in TelephonyManager#NETWORK_TYPE_* constants. |
| // See {@link NetworkStatsManager#NETWORK_TYPE_5G_NSA}. |
| collapsedRatTypes.add( |
| NetworkStatsManager.getCollapsedRatType(NetworkStatsManager.NETWORK_TYPE_5G_NSA)); |
| // Ensure that unknown type is returned. |
| collapsedRatTypes.add(TelephonyManager.NETWORK_TYPE_UNKNOWN); |
| return com.android.net.module.util.CollectionUtils.toIntArray(collapsedRatTypes); |
| } |
| |
| @NonNull |
| private NetworkStats sliceNetworkStatsByUid(@NonNull NetworkStats stats) { |
| return sliceNetworkStats(stats, |
| (entry) -> { |
| return new NetworkStats.Entry(null /* IFACE_ALL */, entry.getUid(), |
| NetworkStats.SET_ALL, NetworkStats.TAG_NONE, |
| NetworkStats.METERED_ALL, NetworkStats.ROAMING_ALL, |
| NetworkStats.DEFAULT_NETWORK_ALL, |
| entry.getRxBytes(), entry.getRxPackets(), |
| entry.getTxBytes(), entry.getTxPackets(), 0); |
| }); |
| } |
| |
| @NonNull |
| private NetworkStats sliceNetworkStatsByFgbg(@NonNull NetworkStats stats) { |
| return sliceNetworkStats(stats, |
| (entry) -> { |
| return new NetworkStats.Entry(null /* IFACE_ALL */, NetworkStats.UID_ALL, |
| entry.getSet(), NetworkStats.TAG_NONE, |
| NetworkStats.METERED_ALL, NetworkStats.ROAMING_ALL, |
| NetworkStats.DEFAULT_NETWORK_ALL, |
| entry.getRxBytes(), entry.getRxPackets(), |
| entry.getTxBytes(), entry.getTxPackets(), 0); |
| }); |
| } |
| |
| @NonNull |
| private NetworkStats sliceNetworkStatsByUidAndFgbg(@NonNull NetworkStats stats) { |
| return sliceNetworkStats(stats, |
| (entry) -> { |
| return new NetworkStats.Entry(null /* IFACE_ALL */, entry.getUid(), |
| entry.getSet(), NetworkStats.TAG_NONE, |
| NetworkStats.METERED_ALL, NetworkStats.ROAMING_ALL, |
| NetworkStats.DEFAULT_NETWORK_ALL, |
| entry.getRxBytes(), entry.getRxPackets(), |
| entry.getTxBytes(), entry.getTxPackets(), 0); |
| }); |
| } |
| |
| @NonNull |
| private NetworkStats sliceNetworkStatsByUidTagAndMetered(@NonNull NetworkStats stats) { |
| return sliceNetworkStats(stats, |
| (entry) -> { |
| return new NetworkStats.Entry(null /* IFACE_ALL */, entry.getUid(), |
| NetworkStats.SET_ALL, entry.getTag(), |
| entry.getMetered(), NetworkStats.ROAMING_ALL, |
| NetworkStats.DEFAULT_NETWORK_ALL, |
| entry.getRxBytes(), entry.getRxPackets(), |
| entry.getTxBytes(), entry.getTxPackets(), 0); |
| }); |
| } |
| |
| /** |
| * 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. |
| * |
| * @param slicer An operation taking one parameter, NetworkStats.Entry, that should be used to |
| * get the state from entry to replace the default value. |
| * 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: |
| * (entry) -> { |
| * return new NetworkStats.Entry(null, entry.getUid(), |
| * NetworkStats.SET_ALL, entry.getTag(), |
| * NetworkStats.METERED_ALL, NetworkStats.ROAMING_ALL, |
| * NetworkStats.DEFAULT_NETWORK_ALL, |
| * entry.getRxBytes(), entry.getRxPackets(), |
| * entry.getTxBytes(), entry.getTxPackets(), 0); |
| * } |
| * @return new NeworkStats object appropriately sliced |
| */ |
| @NonNull |
| private NetworkStats sliceNetworkStats(@NonNull NetworkStats stats, |
| @NonNull Function<NetworkStats.Entry, NetworkStats.Entry> slicer) { |
| NetworkStats ret = new NetworkStats(0, 1); |
| for (NetworkStats.Entry e : stats) { |
| ret = ret.addEntry(slicer.apply(e)); |
| } |
| 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( |
| Runnable::run, |
| new BluetoothAdapter.OnBluetoothActivityEnergyInfoCallback() { |
| @Override |
| public void onBluetoothActivityEnergyInfoAvailable( |
| BluetoothActivityEnergyInfo info) { |
| Bundle bundle = new Bundle(); |
| bundle.putParcelable( |
| BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY, info); |
| bluetoothReceiver.send(0, bundle); |
| } |
| |
| @Override |
| public void onBluetoothActivityEnergyInfoError(int errorCode) { |
| Slog.w(TAG, "error reading Bluetooth stats: " + errorCode); |
| Bundle bundle = new Bundle(); |
| bundle.putParcelable( |
| BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY, null); |
| bluetoothReceiver.send(0, bundle); |
| } |
| } |
| ); |
| 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) { |
| 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.getTimestampMillis(), |
| info.getBluetoothStackState(), info.getControllerTxTimeMillis(), |
| info.getControllerRxTimeMillis(), info.getControllerIdleTimeMillis(), |
| info.getControllerEnergyUsed())); |
| return StatsManager.PULL_SUCCESS; |
| } |
| |
| private void registerUwbActivityInfo() { |
| int tagId = FrameworkStatsLog.UWB_ACTIVITY_INFO; |
| mStatsManager.setPullAtomCallback( |
| tagId, |
| null, // use default PullAtomMetadata values |
| DIRECT_EXECUTOR, |
| mStatsCallbackImpl |
| ); |
| } |
| |
| int pullUwbActivityInfoLocked(int atomTag, List<StatsEvent> pulledData) { |
| final long token = Binder.clearCallingIdentity(); |
| try { |
| SynchronousResultReceiver uwbReceiver = new SynchronousResultReceiver("uwb"); |
| mUwbManager.getUwbActivityEnergyInfoAsync(Runnable::run, |
| info -> { |
| Bundle bundle = new Bundle(); |
| bundle.putParcelable(BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY, info); |
| uwbReceiver.send(0, bundle); |
| } |
| ); |
| final UwbActivityEnergyInfo uwbInfo = awaitControllerInfo(uwbReceiver); |
| if (uwbInfo == null) { |
| return StatsManager.PULL_SKIP; |
| } |
| pulledData.add( |
| FrameworkStatsLog.buildStatsEvent(atomTag, |
| uwbInfo.getControllerTxDurationMillis(), |
| uwbInfo.getControllerRxDurationMillis(), |
| uwbInfo.getControllerIdleDurationMillis(), |
| uwbInfo.getControllerWakeCount())); |
| } catch (RuntimeException e) { |
| Slog.e(TAG, "failed to getUwbActivityEnergyInfoAsync", e); |
| return StatsManager.PULL_SKIP; |
| } finally { |
| Binder.restoreCallingIdentity(token); |
| } |
| 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 pullProcessMemorySnapshot(int atomTag, List<StatsEvent> pulledData) { |
| List<ProcessMemoryState> managedProcessList = |
| LocalServices.getService(ActivityManagerInternal.class) |
| .getMemoryStateForProcesses(); |
| KernelAllocationStats.ProcessGpuMem[] gpuAllocations = |
| KernelAllocationStats.getGpuAllocations(); |
| SparseIntArray gpuMemPerPid = new SparseIntArray(gpuAllocations.length); |
| for (KernelAllocationStats.ProcessGpuMem processGpuMem : gpuAllocations) { |
| gpuMemPerPid.put(processGpuMem.pid, processGpuMem.gpuMemoryKb); |
| } |
| 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, |
| gpuMemPerPid.get(managedProcess.pid), managedProcess.hasForegroundServices, |
| snapshot.rssShmemKilobytes, managedProcess.mHostingComponentTypes, |
| managedProcess.mHistoricalHostingComponentTypes)); |
| } |
| // 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, |
| gpuMemPerPid.get(pid), false /* has_foreground_services */, |
| snapshot.rssShmemKilobytes, |
| // Native processes don't really have a hosting component type. |
| HOSTING_COMPONENT_TYPE_EMPTY, |
| HOSTING_COMPONENT_TYPE_EMPTY)); |
| } |
| 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) { |
| KernelAllocationStats.ProcessDmabuf[] procBufs = |
| KernelAllocationStats.getDmabufAllocations(); |
| |
| if (procBufs == null) { |
| return StatsManager.PULL_SKIP; |
| } |
| for (KernelAllocationStats.ProcessDmabuf procBuf : procBufs) { |
| pulledData.add(FrameworkStatsLog.buildStatsEvent( |
| atomTag, |
| procBuf.uid, |
| procBuf.processName, |
| procBuf.oomScore, |
| procBuf.retainedSizeKb, |
| procBuf.retainedBuffersCount, |
| 0, /* mapped_dmabuf_kb - deprecated */ |
| 0, /* mapped_dmabuf_count - deprecated */ |
| procBuf.surfaceFlingerSizeKb, |
| procBuf.surfaceFlingerCount |
| )); |
| } |
| 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, |
| metrics.shmemKb, |
| metrics.totalKb, |
| metrics.freeKb, |
| metrics.availableKb, |
| metrics.activeKb, |
| metrics.inactiveKb, |
| metrics.activeAnonKb, |
| metrics.inactiveAnonKb, |
| metrics.activeFileKb, |
| metrics.inactiveFileKb, |
| metrics.swapTotalKb, |
| metrics.swapFreeKb, |
| metrics.cmaTotalKb, |
| metrics.cmaFreeKb)); |
| 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.isFileEncrypted(); |
| |
| //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()); |
| StatFs metadataFsSystem = new StatFs(Environment.getMetadataDirectory().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())); |
| |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, |
| FrameworkStatsLog.DIRECTORY_USAGE__DIRECTORY__METADATA, |
| metadataFsSystem.getAvailableBytes(), metadataFsSystem.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 void registerProcessState() { |
| int tagId = FrameworkStatsLog.PROCESS_STATE; |
| mStatsManager.setPullAtomCallback( |
| tagId, |
| null, // use default PullAtomMetadata values |
| DIRECT_EXECUTOR, |
| mStatsCallbackImpl); |
| } |
| |
| private void registerProcessAssociation() { |
| int tagId = FrameworkStatsLog.PROCESS_ASSOCIATION; |
| mStatsManager.setPullAtomCallback( |
| tagId, |
| null, // use default PullAtomMetadata values |
| DIRECT_EXECUTOR, |
| mStatsCallbackImpl); |
| } |
| |
| @GuardedBy("mProcStatsLock") |
| private ProcessStats getStatsFromProcessStatsService(int atomTag) { |
| IProcessStats processStatsService = getIProcessStatsService(); |
| if (processStatsService == null) { |
| return null; |
| } |
| final long token = Binder.clearCallingIdentity(); |
| try { |
| // force procstats to flush & combine old files into one store |
| long lastHighWaterMark = readProcStatsHighWaterMark(atomTag); |
| ProcessStats procStats = new ProcessStats(false); |
| // Force processStatsService to aggregate all in-storage and in-memory data. |
| long highWaterMark = |
| processStatsService.getCommittedStatsMerged( |
| lastHighWaterMark, |
| ProcessStats.REPORT_ALL, // ignored since committedStats below is null. |
| true, |
| null, // committedStats |
| procStats); |
| new File( |
| mBaseDir.getAbsolutePath() |
| + "/" |
| + highWaterMarkFilePrefix(atomTag) |
| + "_" |
| + lastHighWaterMark) |
| .delete(); |
| new File( |
| mBaseDir.getAbsolutePath() |
| + "/" |
| + highWaterMarkFilePrefix(atomTag) |
| + "_" |
| + highWaterMark) |
| .createNewFile(); |
| return procStats; |
| } catch (RemoteException | IOException e) { |
| Slog.e(TAG, "Getting procstats failed: ", e); |
| return null; |
| } finally { |
| Binder.restoreCallingIdentity(token); |
| } |
| } |
| |
| @GuardedBy("mProcStatsLock") |
| private int pullProcStatsLocked(int atomTag, List<StatsEvent> pulledData) { |
| ProcessStats procStats = getStatsFromProcessStatsService(atomTag); |
| if (procStats == null) { |
| return StatsManager.PULL_SKIP; |
| } |
| ProtoOutputStream[] protoStreams = new ProtoOutputStream[MAX_PROCSTATS_SHARDS]; |
| for (int i = 0; i < protoStreams.length; i++) { |
| protoStreams[i] = new ProtoOutputStream(); |
| } |
| 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)); |
| } |
| } |
| return StatsManager.PULL_SUCCESS; |
| } |
| |
| @GuardedBy("mProcStatsLock") |
| private int pullProcessStateLocked(int atomTag, List<StatsEvent> pulledData) { |
| ProcessStats procStats = getStatsFromProcessStatsService(atomTag); |
| if (procStats == null) { |
| return StatsManager.PULL_SKIP; |
| } |
| procStats.dumpProcessState(atomTag, new StatsEventOutput(pulledData)); |
| return StatsManager.PULL_SUCCESS; |
| } |
| |
| @GuardedBy("mProcStatsLock") |
| private int pullProcessAssociationLocked(int atomTag, List<StatsEvent> pulledData) { |
| ProcessStats procStats = getStatsFromProcessStatsService(atomTag); |
| if (procStats == null) { |
| return StatsManager.PULL_SKIP; |
| } |
| procStats.dumpProcessAssociation(atomTag, new StatsEventOutput(pulledData)); |
| return StatsManager.PULL_SUCCESS; |
| } |
| |
| private String highWaterMarkFilePrefix(int atomTag) { |
| // For backward compatibility, use the legacy ProcessStats enum value as the prefix for |
| // PROC_STATS and PROC_STATS_PKG_PROC. |
| if (atomTag == FrameworkStatsLog.PROC_STATS) { |
| return String.valueOf(ProcessStats.REPORT_ALL); |
| } |
| if (atomTag == FrameworkStatsLog.PROC_STATS_PKG_PROC) { |
| return String.valueOf(ProcessStats.REPORT_PKG_PROC_STATS); |
| } |
| return "atom-" + atomTag; |
| } |
| |
| // read high watermark for section |
| private long readProcStatsHighWaterMark(int atomTag) { |
| try { |
| File[] files = |
| mBaseDir.listFiles( |
| (d, name) -> { |
| return name.toLowerCase() |
| .startsWith(highWaterMarkFilePrefix(atomTag) + '_'); |
| }); |
| 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 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) { |
| final BatteryStatsManager bsm = mContext.getSystemService(BatteryStatsManager.class); |
| try { |
| final BatteryUsageStats stats = bsm.getBatteryUsageStats(); |
| pulledData.add(FrameworkStatsLog.buildStatsEvent( |
| atomTag, milliAmpHrsToNanoAmpSecs(stats.getConsumedPower()))); |
| return StatsManager.PULL_SUCCESS; |
| } catch (Exception e) { |
| Log.e(TAG, "Could not obtain battery usage stats", e); |
| return StatsManager.PULL_SKIP; |
| } |
| } |
| |
| 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.getUserLocationEnabledSetting(), |
| metricsState.getAutoDetectionEnabledSetting(), |
| metricsState.getGeoDetectionEnabledSetting(), |
| convertToMetricsDetectionMode(metricsState.getDetectionMode()), |
| metricsState.getDeviceTimeZoneIdOrdinal(), |
| convertTimeZoneSuggestionToProtoBytes( |
| metricsState.getLatestManualSuggestion()), |
| convertTimeZoneSuggestionToProtoBytes( |
| metricsState.getLatestTelephonySuggestion()), |
| convertTimeZoneSuggestionToProtoBytes( |
| metricsState.getLatestGeolocationSuggestion()), |
| metricsState.isTelephonyTimeZoneFallbackSupported(), |
| metricsState.getDeviceTimeZoneId(), |
| metricsState.isEnhancedMetricsCollectionEnabled(), |
| metricsState.getGeoDetectionRunInBackgroundEnabled() |
| )); |
| } 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 static int convertToMetricsDetectionMode( |
| @MetricsTimeZoneDetectorState.DetectionMode 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: |
| return TIME_ZONE_DETECTOR_STATE__DETECTION_MODE__UNKNOWN; |
| } |
| } |
| |
| @Nullable |
| private static byte[] convertTimeZoneSuggestionToProtoBytes( |
| @Nullable MetricsTimeZoneDetectorState.MetricsTimeZoneSuggestion suggestion) { |
| if (suggestion == null) { |
| return null; |
| } |
| |
| // We don't get access to the atoms.proto definition for nested proto fields, so we use |
| // an identically specified proto. |
| ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); |
| ProtoOutputStream protoOutputStream = new ProtoOutputStream(byteArrayOutputStream); |
| int typeProtoValue = suggestion.isCertain() |
| ? android.app.time.MetricsTimeZoneSuggestion.CERTAIN |
| : android.app.time.MetricsTimeZoneSuggestion.UNCERTAIN; |
| protoOutputStream.write(android.app.time.MetricsTimeZoneSuggestion.TYPE, |
| typeProtoValue); |
| if (suggestion.isCertain()) { |
| for (int zoneIdOrdinal : suggestion.getZoneIdOrdinals()) { |
| protoOutputStream.write( |
| android.app.time.MetricsTimeZoneSuggestion.TIME_ZONE_ORDINALS, |
| zoneIdOrdinal); |
| } |
| String[] zoneIds = suggestion.getZoneIds(); |
| if (zoneIds != null) { |
| for (String zoneId : zoneIds) { |
| protoOutputStream.write( |
| android.app.time.MetricsTimeZoneSuggestion.TIME_ZONE_IDS, |
| zoneId); |
| } |
| } |
| } |
| protoOutputStream.flush(); |
| closeQuietly(byteArrayOutputStream); |
| return byteArrayOutputStream.toByteArray(); |
| } |
| |
| 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) { |
| if (mHealthService == null) { |
| return StatsManager.PULL_SKIP; |
| } |
| android.hardware.health.HealthInfo healthInfo; |
| try { |
| healthInfo = mHealthService.getHealthInfo(); |
| } catch (RemoteException | IllegalStateException e) { |
| return StatsManager.PULL_SKIP; |
| } |
| if (healthInfo == null) { |
| return StatsManager.PULL_SKIP; |
| } |
| |
| int pulledValue; |
| switch (atomTag) { |
| case FrameworkStatsLog.BATTERY_LEVEL: |
| pulledValue = healthInfo.batteryLevel; |
| break; |
| case FrameworkStatsLog.REMAINING_BATTERY_CAPACITY: |
| pulledValue = healthInfo.batteryChargeCounterUah; |
| break; |
| case FrameworkStatsLog.FULL_BATTERY_CAPACITY: |
| pulledValue = healthInfo.batteryFullChargeUah; |
| break; |
| case FrameworkStatsLog.BATTERY_VOLTAGE: |
| pulledValue = healthInfo.batteryVoltageMillivolts; |
| break; |
| case FrameworkStatsLog.BATTERY_CYCLE_COUNT: |
| pulledValue = healthInfo.batteryCycleCount; |
| break; |
| default: |
| return StatsManager.PULL_SKIP; |
| } |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, pulledValue)); |
| 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(); |
| final PackageManagerInternal pmIntenral = |
| LocalServices.getService(PackageManagerInternal.class); |
| if (!pm.hasSystemFeature(PackageManager.FEATURE_INCREMENTAL_DELIVERY)) { |
| // Incremental is not enabled on this device. The result list will be empty. |
| return StatsManager.PULL_SUCCESS; |
| } |
| final long token = Binder.clearCallingIdentity(); |
| try { |
| final int[] userIds = LocalServices.getService(UserManagerInternal.class).getUserIds(); |
| for (int userId : userIds) { |
| final List<PackageInfo> installedPackages = pm.getInstalledPackagesAsUser( |
| 0, userId); |
| for (PackageInfo pi : installedPackages) { |
| if (IncrementalManager.isIncrementalPath( |
| pi.applicationInfo.getBaseCodePath())) { |
| final IncrementalStatesInfo info = pmIntenral.getIncrementalStatesInfo( |
| pi.packageName, SYSTEM_UID, userId); |
| pulledData.add( |
| FrameworkStatsLog.buildStatsEvent(atomTag, pi.applicationInfo.uid, |
| info.isLoading(), info.getLoadingCompletedTime())); |
| } |
| } |
| } |
| } catch (Exception e) { |
| Slog.e(TAG, "failed to pullInstalledIncrementalPackagesLocked", e); |
| return StatsManager.PULL_SKIP; |
| } finally { |
| Binder.restoreCallingIdentity(token); |
| } |
| return StatsManager.PULL_SUCCESS; |
| } |
| |
| private void registerKeystoreStorageStats() { |
| mStatsManager.setPullAtomCallback( |
| FrameworkStatsLog.KEYSTORE2_STORAGE_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); |
| } |
| |
| private void registerAccessibilityShortcutStats() { |
| int tagId = FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS; |
| mStatsManager.setPullAtomCallback( |
| tagId, |
| null, // use default PullAtomMetadata values |
| DIRECT_EXECUTOR, |
| mStatsCallbackImpl |
| ); |
| } |
| |
| private void registerAccessibilityFloatingMenuStats() { |
| int tagId = FrameworkStatsLog.ACCESSIBILITY_FLOATING_MENU_STATS; |
| mStatsManager.setPullAtomCallback( |
| tagId, |
| null, // use default PullAtomMetadata values |
| DIRECT_EXECUTOR, |
| mStatsCallbackImpl |
| ); |
| } |
| |
| private void registerMediaCapabilitiesStats() { |
| int tagId = FrameworkStatsLog.MEDIA_CAPABILITIES; |
| mStatsManager.setPullAtomCallback( |
| tagId, |
| 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 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, |
| atom.security_level)); |
| } |
| 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.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); |
| } |
| } |
| |
| int pullAccessibilityShortcutStatsLocked(int atomTag, List<StatsEvent> pulledData) { |
| UserManager userManager = mContext.getSystemService(UserManager.class); |
| if (userManager == null) { |
| return StatsManager.PULL_SKIP; |
| } |
| final long token = Binder.clearCallingIdentity(); |
| try { |
| final ContentResolver resolver = mContext.getContentResolver(); |
| final int hardware_shortcut_type = |
| FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__VOLUME_KEY; |
| final int triple_tap_shortcut = |
| FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__TRIPLE_TAP; |
| for (UserInfo userInfo : userManager.getUsers()) { |
| final int userId = userInfo.getUserHandle().getIdentifier(); |
| |
| if (isAccessibilityShortcutUser(mContext, userId)) { |
| final int software_shortcut_type = convertToAccessibilityShortcutType( |
| Settings.Secure.getIntForUser(resolver, |
| Settings.Secure.ACCESSIBILITY_BUTTON_MODE, 0, userId)); |
| final String software_shortcut_list = Settings.Secure.getStringForUser(resolver, |
| Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, userId); |
| final int software_shortcut_service_num = countAccessibilityServices( |
| software_shortcut_list); |
| |
| final String hardware_shortcut_list = Settings.Secure.getStringForUser(resolver, |
| Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, userId); |
| final int hardware_shortcut_service_num = countAccessibilityServices( |
| hardware_shortcut_list); |
| |
| // only allow magnification to use it for now |
| final int triple_tap_service_num = Settings.Secure.getIntForUser(resolver, |
| Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, 0, userId); |
| |
| pulledData.add( |
| FrameworkStatsLog.buildStatsEvent(atomTag, |
| software_shortcut_type, software_shortcut_service_num, |
| hardware_shortcut_type, hardware_shortcut_service_num, |
| triple_tap_shortcut, triple_tap_service_num)); |
| } |
| } |
| } catch (RuntimeException e) { |
| Slog.e(TAG, "pulling accessibility shortcuts stats failed at getUsers", e); |
| return StatsManager.PULL_SKIP; |
| } finally { |
| Binder.restoreCallingIdentity(token); |
| } |
| return StatsManager.PULL_SUCCESS; |
| } |
| |
| int pullAccessibilityFloatingMenuStatsLocked(int atomTag, List<StatsEvent> pulledData) { |
| UserManager userManager = mContext.getSystemService(UserManager.class); |
| if (userManager == null) { |
| return StatsManager.PULL_SKIP; |
| } |
| final long token = Binder.clearCallingIdentity(); |
| try { |
| final ContentResolver resolver = mContext.getContentResolver(); |
| final int defaultSize = 0; |
| final int defaultIconType = 0; |
| final int defaultFadeEnabled = 1; |
| final float defaultOpacity = 0.55f; |
| |
| for (UserInfo userInfo : userManager.getUsers()) { |
| final int userId = userInfo.getUserHandle().getIdentifier(); |
| |
| if (isAccessibilityFloatingMenuUser(mContext, userId)) { |
| final int size = Settings.Secure.getIntForUser(resolver, |
| Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE, defaultSize, userId); |
| final int type = Settings.Secure.getIntForUser(resolver, |
| Settings.Secure.ACCESSIBILITY_FLOATING_MENU_ICON_TYPE, |
| defaultIconType, userId); |
| final boolean fadeEnabled = (Settings.Secure.getIntForUser(resolver, |
| Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED, |
| defaultFadeEnabled, userId)) == 1; |
| final float opacity = Settings.Secure.getFloatForUser(resolver, |
| Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY, |
| defaultOpacity, userId); |
| |
| pulledData.add( |
| FrameworkStatsLog.buildStatsEvent(atomTag, size, type, fadeEnabled, |
| opacity)); |
| } |
| } |
| } catch (RuntimeException e) { |
| Slog.e(TAG, "pulling accessibility floating menu stats failed at getUsers", e); |
| return StatsManager.PULL_SKIP; |
| } finally { |
| Binder.restoreCallingIdentity(token); |
| } |
| return StatsManager.PULL_SUCCESS; |
| } |
| |
| int pullMediaCapabilitiesStats(int atomTag, List<StatsEvent> pulledData) { |
| if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) { |
| return StatsManager.PULL_SKIP; |
| } |
| AudioManager audioManager = mContext.getSystemService(AudioManager.class); |
| if (audioManager == null) { |
| return StatsManager.PULL_SKIP; |
| } |
| |
| // get the surround sound metrics information |
| Map<Integer, Boolean> surroundEncodingsMap = audioManager.getSurroundFormats(); |
| byte[] surroundEncodings = toBytes(new ArrayList(surroundEncodingsMap.keySet())); |
| byte[] sinkSurroundEncodings = toBytes(audioManager.getReportedSurroundFormats()); |
| List<Integer> disabledSurroundEncodingsList = new ArrayList<>(); |
| List<Integer> enabledSurroundEncodingsList = new ArrayList<>(); |
| for (int surroundEncoding : surroundEncodingsMap.keySet()) { |
| if (!audioManager.isSurroundFormatEnabled(surroundEncoding)) { |
| disabledSurroundEncodingsList.add(surroundEncoding); |
| } else { |
| enabledSurroundEncodingsList.add(surroundEncoding); |
| } |
| } |
| byte[] disabledSurroundEncodings = toBytes(disabledSurroundEncodingsList); |
| byte[] enabledSurroundEncodings = toBytes(enabledSurroundEncodingsList); |
| int surroundOutputMode = audioManager.getEncodedSurroundMode(); |
| |
| DisplayManager displayManager = mContext.getSystemService(DisplayManager.class); |
| Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY); |
| // get the display capabilities metrics information |
| Display.HdrCapabilities hdrCapabilities = display.getHdrCapabilities(); |
| byte[] sinkHdrFormats = new byte[]{}; |
| if (hdrCapabilities != null) { |
| sinkHdrFormats = toBytes(hdrCapabilities.getSupportedHdrTypes()); |
| } |
| byte[] sinkDisplayModes = toBytes(display.getSupportedModes()); |
| int hdcpLevel = -1; |
| List<UUID> uuids = MediaDrm.getSupportedCryptoSchemes(); |
| try { |
| if (!uuids.isEmpty()) { |
| MediaDrm mediaDrm = new MediaDrm(uuids.get(0)); |
| hdcpLevel = mediaDrm.getConnectedHdcpLevel(); |
| } |
| } catch (UnsupportedSchemeException exception) { |
| Slog.e(TAG, "pulling hdcp level failed.", exception); |
| hdcpLevel = -1; |
| } |
| |
| // get the display settings metrics information |
| int matchContentFrameRateUserPreference = |
| displayManager.getMatchContentFrameRateUserPreference(); |
| byte[] userDisabledHdrTypes = toBytes(displayManager.getUserDisabledHdrTypes()); |
| Display.Mode userPreferredDisplayMode = |
| displayManager.getGlobalUserPreferredDisplayMode(); |
| int userPreferredWidth = userPreferredDisplayMode != null |
| ? userPreferredDisplayMode.getPhysicalWidth() : -1; |
| int userPreferredHeight = userPreferredDisplayMode != null |
| ? userPreferredDisplayMode.getPhysicalHeight() : -1; |
| float userPreferredRefreshRate = userPreferredDisplayMode != null |
| ? userPreferredDisplayMode.getRefreshRate() : 0.0f; |
| boolean hasUserDisabledAllm = false; |
| try { |
| hasUserDisabledAllm = Settings.Secure.getIntForUser( |
| mContext.getContentResolver(), |
| Settings.Secure.MINIMAL_POST_PROCESSING_ALLOWED, |
| 1) == 0; |
| } catch (Settings.SettingNotFoundException exception) { |
| Slog.e( |
| TAG, "unable to find setting for MINIMAL_POST_PROCESSING_ALLOWED.", |
| exception); |
| hasUserDisabledAllm = false; |
| } |
| |
| pulledData.add( |
| FrameworkStatsLog.buildStatsEvent( |
| atomTag, surroundEncodings, sinkSurroundEncodings, |
| disabledSurroundEncodings, enabledSurroundEncodings, surroundOutputMode, |
| sinkHdrFormats, sinkDisplayModes, hdcpLevel, |
| matchContentFrameRateUserPreference, userDisabledHdrTypes, |
| userPreferredWidth, userPreferredHeight, userPreferredRefreshRate, |
| hasUserDisabledAllm)); |
| |
| return StatsManager.PULL_SUCCESS; |
| } |
| |
| private void registerPendingIntentsPerPackagePuller() { |
| int tagId = FrameworkStatsLog.PENDING_INTENTS_PER_PACKAGE; |
| mStatsManager.setPullAtomCallback( |
| tagId, |
| null, // use default PullAtomMetadata values |
| DIRECT_EXECUTOR, |
| mStatsCallbackImpl |
| ); |
| } |
| |
| private int pullHdrCapabilities(int atomTag, List<StatsEvent> pulledData) { |
| DisplayManager displayManager = mContext.getSystemService(DisplayManager.class); |
| Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY); |
| |
| int hdrConversionMode = displayManager.getHdrConversionMode().getConversionMode(); |
| int preferredHdrType = displayManager.getHdrConversionMode().getPreferredHdrOutputType(); |
| boolean userDisabledHdrConversion = hdrConversionMode == HDR_CONVERSION_PASSTHROUGH; |
| int forceHdrFormat = preferredHdrType == HDR_TYPE_INVALID ? 0 : preferredHdrType; |
| boolean hasDolbyVisionIssue = hasDolbyVisionIssue(display); |
| byte[] hdrOutputTypes = toBytes(displayManager.getSupportedHdrOutputTypes()); |
| boolean hdrOutputControlSupported = hdrConversionMode != HDR_CONVERSION_UNSUPPORTED; |
| |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, hdrOutputTypes, |
| userDisabledHdrConversion, forceHdrFormat, hasDolbyVisionIssue, |
| hdrOutputControlSupported)); |
| |
| return StatsManager.PULL_SUCCESS; |
| } |
| |
| private int pullCachedAppsHighWatermark(int atomTag, List<StatsEvent> pulledData) { |
| pulledData.add(LocalServices.getService(ActivityManagerInternal.class) |
| .getCachedAppsHighWatermarkStats(atomTag, true)); |
| return StatsManager.PULL_SUCCESS; |
| } |
| |
| private boolean hasDolbyVisionIssue(Display display) { |
| AtomicInteger modesSupportingDolbyVision = new AtomicInteger(); |
| Arrays.stream(display.getSupportedModes()) |
| .map(Display.Mode::getSupportedHdrTypes) |
| .filter(types -> Arrays.stream(types).anyMatch(hdrType -> hdrType == DOLBY_VISION)) |
| .forEach(ignored -> modesSupportingDolbyVision.incrementAndGet()); |
| |
| if (modesSupportingDolbyVision.get() != 0 |
| && modesSupportingDolbyVision.get() < display.getSupportedModes().length) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| private int pullPendingIntentsPerPackage(int atomTag, List<StatsEvent> pulledData) { |
| List<PendingIntentStats> pendingIntentStats = |
| LocalServices.getService(ActivityManagerInternal.class).getPendingIntentStats(); |
| for (PendingIntentStats stats : pendingIntentStats) { |
| pulledData.add(FrameworkStatsLog.buildStatsEvent( |
| atomTag, stats.uid, stats.count, stats.sizeKb)); |
| } |
| return StatsManager.PULL_SUCCESS; |
| } |
| |
| private void registerPinnerServiceStats() { |
| int tagId = FrameworkStatsLog.PINNED_FILE_SIZES_PER_PACKAGE; |
| mStatsManager.setPullAtomCallback( |
| tagId, |
| null, // use default PullAtomMetadata values |
| DIRECT_EXECUTOR, |
| mStatsCallbackImpl |
| ); |
| } |
| |
| private void registerHdrCapabilitiesPuller() { |
| int tagId = FrameworkStatsLog.HDR_CAPABILITIES; |
| mStatsManager.setPullAtomCallback( |
| tagId, |
| null, // use default PullAtomMetadata values |
| DIRECT_EXECUTOR, |
| mStatsCallbackImpl |
| ); |
| } |
| |
| private void registerCachedAppsHighWatermarkPuller() { |
| final int tagId = FrameworkStatsLog.CACHED_APPS_HIGH_WATERMARK; |
| mStatsManager.setPullAtomCallback( |
| tagId, |
| null, // use default PullAtomMetadata values |
| DIRECT_EXECUTOR, |
| mStatsCallbackImpl |
| ); |
| } |
| |
| int pullSystemServerPinnerStats(int atomTag, List<StatsEvent> pulledData) { |
| PinnerService pinnerService = LocalServices.getService(PinnerService.class); |
| List<PinnedFileStats> pinnedFileStats = pinnerService.dumpDataForStatsd(); |
| for (PinnedFileStats pfstats : pinnedFileStats) { |
| pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, |
| pfstats.uid, pfstats.filename, pfstats.sizeKb)); |
| } |
| return StatsManager.PULL_SUCCESS; |
| } |
| |
| private byte[] toBytes(List<Integer> audioEncodings) { |
| ProtoOutputStream protoOutputStream = new ProtoOutputStream(); |
| for (int audioEncoding : audioEncodings) { |
| protoOutputStream.write( |
| ProtoOutputStream.FIELD_COUNT_REPEATED | ProtoOutputStream.FIELD_TYPE_ENUM | 1, |
| audioEncoding); |
| } |
| return protoOutputStream.getBytes(); |
| } |
| |
| private byte[] toBytes(int[] array) { |
| ProtoOutputStream protoOutputStream = new ProtoOutputStream(); |
| for (int element : array) { |
| protoOutputStream.write( |
| ProtoOutputStream.FIELD_COUNT_REPEATED | ProtoOutputStream.FIELD_TYPE_ENUM | 1, |
| element); |
| } |
| return protoOutputStream.getBytes(); |
| } |
| |
| private byte[] toBytes(Display.Mode[] displayModes) { |
| Map<Integer, Integer> modeGroupIds = createModeGroups(displayModes); |
| ProtoOutputStream protoOutputStream = new ProtoOutputStream(); |
| for (Display.Mode element : displayModes) { |
| ProtoOutputStream protoOutputStreamMode = new ProtoOutputStream(); |
| protoOutputStreamMode.write( |
| ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_INT32 | 1, |
| element.getPhysicalHeight()); |
| protoOutputStreamMode.write( |
| ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_INT32 | 2, |
| element.getPhysicalWidth()); |
| protoOutputStreamMode.write( |
| ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_FLOAT | 3, |
| element.getRefreshRate()); |
| protoOutputStreamMode.write( |
| ProtoOutputStream.FIELD_COUNT_SINGLE | ProtoOutputStream.FIELD_TYPE_INT32 | 4, |
| modeGroupIds.get(element.getModeId())); |
| protoOutputStream.write( |
| ProtoOutputStream.FIELD_COUNT_REPEATED |
| | ProtoOutputStream.FIELD_TYPE_MESSAGE | 1, |
| protoOutputStreamMode.getBytes()); |
| } |
| return protoOutputStream.getBytes(); |
| } |
| |
| // Returns map modeId -> groupId such that all modes with the same group have alternative |
| // refresh rates |
| private Map<Integer, Integer> createModeGroups(Display.Mode[] supportedModes) { |
| Map<Integer, Integer> modeGroupIds = new ArrayMap<>(); |
| int groupId = 1; |
| for (Display.Mode mode : supportedModes) { |
| if (modeGroupIds.containsKey(mode.getModeId())) { |
| continue; |
| } |
| modeGroupIds.put(mode.getModeId(), groupId); |
| for (float refreshRate : mode.getAlternativeRefreshRates()) { |
| int alternativeModeId = findModeId(supportedModes, mode.getPhysicalWidth(), |
| mode.getPhysicalHeight(), refreshRate); |
| if (alternativeModeId != -1 && !modeGroupIds.containsKey(alternativeModeId)) { |
| modeGroupIds.put(alternativeModeId, groupId); |
| } |
| } |
| groupId++; |
| } |
| return modeGroupIds; |
| } |
| |
| private int findModeId(Display.Mode[] modes, int width, int height, float refreshRate) { |
| for (Display.Mode mode : modes) { |
| if (mode.matches(width, height, refreshRate)) { |
| return mode.getModeId(); |
| } |
| } |
| return -1; |
| } |
| |
| /** |
| * Counts how many accessibility services (including features) there are in the colon-separated |
| * string list. |
| * |
| * @param semicolonList colon-separated string, it should be |
| * {@link Settings.Secure#ACCESSIBILITY_BUTTON_TARGETS} or |
| * {@link Settings.Secure#ACCESSIBILITY_SHORTCUT_TARGET_SERVICE}. |
| * @return The number of accessibility services |
| */ |
| private int countAccessibilityServices(String semicolonList) { |
| if (TextUtils.isEmpty(semicolonList)) { |
| return 0; |
| } |
| final int semiColonNums = (int) semicolonList.chars().filter(ch -> ch == ':').count(); |
| return TextUtils.isEmpty(semicolonList) ? 0 : semiColonNums + 1; |
| } |
| |
| private boolean isAccessibilityShortcutUser(Context context, @UserIdInt int userId) { |
| final ContentResolver resolver = context.getContentResolver(); |
| |
| final String software_shortcut_list = Settings.Secure.getStringForUser(resolver, |
| Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, userId); |
| final String hardware_shortcut_list = Settings.Secure.getStringForUser(resolver, |
| Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, userId); |
| final boolean hardware_shortcut_dialog_shown = Settings.Secure.getIntForUser(resolver, |
| Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0, userId) == 1; |
| final boolean software_shortcut_enabled = !TextUtils.isEmpty(software_shortcut_list); |
| final boolean hardware_shortcut_enabled = |
| hardware_shortcut_dialog_shown && !TextUtils.isEmpty(hardware_shortcut_list); |
| final boolean triple_tap_shortcut_enabled = Settings.Secure.getIntForUser(resolver, |
| Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, 0, userId) == 1; |
| |
| return software_shortcut_enabled || hardware_shortcut_enabled |
| || triple_tap_shortcut_enabled; |
| } |
| |
| private boolean isAccessibilityFloatingMenuUser(Context context, @UserIdInt int userId) { |
| final ContentResolver resolver = context.getContentResolver(); |
| final int mode = Settings.Secure.getIntForUser(resolver, |
| Settings.Secure.ACCESSIBILITY_BUTTON_MODE, 0, userId); |
| final String software_string = Settings.Secure.getStringForUser(resolver, |
| Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, userId); |
| |
| return (mode == Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) |
| && !TextUtils.isEmpty(software_string); |
| } |
| |
| private int convertToAccessibilityShortcutType(int shortcutType) { |
| switch (shortcutType) { |
| case Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR: |
| return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_BUTTON; |
| case Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU: |
| return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_FLOATING_MENU; |
| case Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE: |
| return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_GESTURE; |
| default: |
| return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__UNKNOWN_TYPE; |
| } |
| } |
| |
| // 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)); |
| } |
| } |
| } |
| } |
| } |