[SM16] Collect metrics per subscription

Test: adb shell cmd stats pull-source 10082
Test: atest UidAtomTests#testMobileBytesTransfer \
      UidAtomTests#testMobileBytesTransferByFgBg \
      UidAtomTests#testDataUsageBytesTransfer
Bug: 148905838

Change-Id: Icc27f6314f8c6eed2ede0500dd0257ea97a13332
This commit is contained in:
junyulai
2020-04-10 15:11:54 +08:00
parent ee3ea79c38
commit aa31d3cb11
2 changed files with 169 additions and 19 deletions

View File

@@ -33,12 +33,15 @@ import static android.os.Process.getUidForPid;
import static android.os.storage.VolumeInfo.TYPE_PRIVATE; import static android.os.storage.VolumeInfo.TYPE_PRIVATE;
import static android.os.storage.VolumeInfo.TYPE_PUBLIC; import static android.os.storage.VolumeInfo.TYPE_PUBLIC;
import static android.provider.Settings.Global.NETSTATS_UID_BUCKET_DURATION; import static android.provider.Settings.Global.NETSTATS_UID_BUCKET_DURATION;
import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID;
import static android.util.MathUtils.abs; import static android.util.MathUtils.abs;
import static android.util.MathUtils.constrain; import static android.util.MathUtils.constrain;
import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR; import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_IS_UID; import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_IS_UID;
import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_TRUNCATE_TIMESTAMP; import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_TRUNCATE_TIMESTAMP;
import static com.android.internal.util.FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER__OPPORTUNISTIC_DATA_SUB__NOT_OPPORTUNISTIC;
import static com.android.internal.util.FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER__OPPORTUNISTIC_DATA_SUB__OPPORTUNISTIC;
import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem; import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
import static com.android.server.stats.pull.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs; 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.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs;
@@ -112,7 +115,10 @@ import android.provider.DeviceConfig;
import android.provider.Settings; import android.provider.Settings;
import android.stats.storage.StorageEnums; import android.stats.storage.StorageEnums;
import android.telephony.ModemActivityInfo; import android.telephony.ModemActivityInfo;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager; import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.ArrayMap; import android.util.ArrayMap;
import android.util.ArraySet; import android.util.ArraySet;
import android.util.Log; import android.util.Log;
@@ -154,6 +160,7 @@ import com.android.server.notification.NotificationManagerService;
import com.android.server.role.RoleManagerInternal; import com.android.server.role.RoleManagerInternal;
import com.android.server.stats.pull.IonMemoryUtil.IonAllocations; import com.android.server.stats.pull.IonMemoryUtil.IonAllocations;
import com.android.server.stats.pull.ProcfsMemoryUtil.MemorySnapshot; import com.android.server.stats.pull.ProcfsMemoryUtil.MemorySnapshot;
import com.android.server.stats.pull.netstats.SubInfo;
import com.android.server.storage.DiskStatsFileLogger; import com.android.server.storage.DiskStatsFileLogger;
import com.android.server.storage.DiskStatsLoggingService; import com.android.server.storage.DiskStatsLoggingService;
@@ -175,10 +182,12 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.MissingResourceException; import java.util.MissingResourceException;
import java.util.Objects;
import java.util.Random; import java.util.Random;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@@ -269,6 +278,7 @@ public class StatsPullAtomService extends SystemService {
private StorageManager mStorageManager; private StorageManager mStorageManager;
private WifiManager mWifiManager; private WifiManager mWifiManager;
private TelephonyManager mTelephony; private TelephonyManager mTelephony;
private SubscriptionManager mSubscriptionManager;
private KernelWakelockReader mKernelWakelockReader; private KernelWakelockReader mKernelWakelockReader;
private KernelWakelockStats mTmpWakelockStats; private KernelWakelockStats mTmpWakelockStats;
@@ -304,6 +314,11 @@ public class StatsPullAtomService extends SystemService {
@NonNull @NonNull
private final List<NetworkStatsExt> mNetworkStatsBaselines = new ArrayList<>(); private final List<NetworkStatsExt> mNetworkStatsBaselines = new ArrayList<>();
// Listener for monitoring subscriptions changed event.
private StatsSubscriptionsListener mStatsSubscriptionsListener;
// List that store SubInfo of subscriptions that ever appeared since boot.
private final CopyOnWriteArrayList<SubInfo> mHistoricalSubs = new CopyOnWriteArrayList<>();
public StatsPullAtomService(Context context) { public StatsPullAtomService(Context context) {
super(context); super(context);
mContext = context; mContext = context;
@@ -484,6 +499,9 @@ public class StatsPullAtomService extends SystemService {
mStatsManager = (StatsManager) mContext.getSystemService(Context.STATS_MANAGER); mStatsManager = (StatsManager) mContext.getSystemService(Context.STATS_MANAGER);
mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
mTelephony = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_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); mStorageManager = (StorageManager) mContext.getSystemService(StorageManager.class);
// Initialize DiskIO // Initialize DiskIO
@@ -653,6 +671,11 @@ public class StatsPullAtomService extends SystemService {
mNetworkStatsBaselines.addAll( mNetworkStatsBaselines.addAll(
collectNetworkStatsSnapshotForAtom(FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER)); collectNetworkStatsSnapshotForAtom(FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER));
// Listen to subscription changes to record historical subscriptions that activated before
// pulling, this is used by {@link #pullMobileBytesTransfer}.
mSubscriptionManager.addOnSubscriptionsChangedListener(
BackgroundThread.getExecutor(), mStatsSubscriptionsListener);
registerWifiBytesTransfer(); registerWifiBytesTransfer();
registerWifiBytesTransferBackground(); registerWifiBytesTransferBackground();
registerMobileBytesTransfer(); registerMobileBytesTransfer();
@@ -805,14 +828,17 @@ public class StatsPullAtomService extends SystemService {
public final boolean slicedByTag; public final boolean slicedByTag;
public final boolean slicedByMetered; public final boolean slicedByMetered;
public final int ratType; public final int ratType;
@Nullable
public final SubInfo subInfo;
NetworkStatsExt(@NonNull NetworkStats stats, int[] transports, boolean slicedByFgbg) { NetworkStatsExt(@NonNull NetworkStats stats, int[] transports, boolean slicedByFgbg) {
this(stats, transports, slicedByFgbg, /*slicedByTag=*/false, /*slicedByMetered=*/false, this(stats, transports, slicedByFgbg, /*slicedByTag=*/false, /*slicedByMetered=*/false,
TelephonyManager.NETWORK_TYPE_UNKNOWN); TelephonyManager.NETWORK_TYPE_UNKNOWN, /*subInfo=*/null);
} }
NetworkStatsExt(@NonNull NetworkStats stats, int[] transports, boolean slicedByFgbg, NetworkStatsExt(@NonNull NetworkStats stats, int[] transports, boolean slicedByFgbg,
boolean slicedByTag, boolean slicedByMetered, int ratType) { boolean slicedByTag, boolean slicedByMetered, int ratType,
@Nullable SubInfo subInfo) {
this.stats = stats; this.stats = stats;
// Sort transports array so that we can test for equality without considering order. // Sort transports array so that we can test for equality without considering order.
@@ -823,12 +849,13 @@ public class StatsPullAtomService extends SystemService {
this.slicedByTag = slicedByTag; this.slicedByTag = slicedByTag;
this.slicedByMetered = slicedByMetered; this.slicedByMetered = slicedByMetered;
this.ratType = ratType; this.ratType = ratType;
this.subInfo = subInfo;
} }
public boolean hasSameSlicing(@NonNull NetworkStatsExt other) { public boolean hasSameSlicing(@NonNull NetworkStatsExt other) {
return Arrays.equals(transports, other.transports) && slicedByFgbg == other.slicedByFgbg return Arrays.equals(transports, other.transports) && slicedByFgbg == other.slicedByFgbg
&& slicedByTag == other.slicedByTag && slicedByMetered == other.slicedByMetered && slicedByTag == other.slicedByTag && slicedByMetered == other.slicedByMetered
&& ratType == other.ratType; && ratType == other.ratType && Objects.equals(subInfo, other.subInfo);
} }
} }
@@ -880,20 +907,14 @@ public class StatsPullAtomService extends SystemService {
ret.add(new NetworkStatsExt(sliceNetworkStatsByUidTagAndMetered(stats), ret.add(new NetworkStatsExt(sliceNetworkStatsByUidTagAndMetered(stats),
new int[] {TRANSPORT_WIFI, TRANSPORT_CELLULAR}, new int[] {TRANSPORT_WIFI, TRANSPORT_CELLULAR},
/*slicedByFgbg=*/false, /*slicedByTag=*/true, /*slicedByFgbg=*/false, /*slicedByTag=*/true,
/*slicedByMetered=*/true, TelephonyManager.NETWORK_TYPE_UNKNOWN)); /*slicedByMetered=*/true, TelephonyManager.NETWORK_TYPE_UNKNOWN,
/*subInfo=*/null));
} }
break; break;
} }
case FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER: { case FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER: {
for (final int ratType : getAllCollapsedRatTypes()) { for (final SubInfo subInfo : mHistoricalSubs) {
final NetworkTemplate template = buildTemplateMobileWithRatType(null, ratType); ret.addAll(getDataUsageBytesTransferSnapshotForSub(subInfo));
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));
}
} }
break; break;
} }
@@ -924,7 +945,8 @@ public class StatsPullAtomService extends SystemService {
} }
final NetworkStatsExt diff = new NetworkStatsExt( final NetworkStatsExt diff = new NetworkStatsExt(
item.stats.subtract(baseline.stats).removeEmptyEntries(), item.transports, item.stats.subtract(baseline.stats).removeEmptyEntries(), item.transports,
item.slicedByFgbg, item.slicedByTag, item.slicedByMetered, item.ratType); item.slicedByFgbg, item.slicedByTag, item.slicedByMetered, item.ratType,
item.subInfo);
// If no diff, skip. // If no diff, skip.
if (diff.stats.size() == 0) continue; if (diff.stats.size() == 0) continue;
@@ -1006,11 +1028,14 @@ public class StatsPullAtomService extends SystemService {
.writeLong(entry.txBytes) .writeLong(entry.txBytes)
.writeLong(entry.txPackets) .writeLong(entry.txPackets)
.writeInt(statsExt.ratType) .writeInt(statsExt.ratType)
// TODO: Fill information about subscription. // Fill information about subscription, these cannot be null since invalid data
.writeString(/*sim_mcc=*/null) // would be filtered when adding into subInfo list.
.writeString(/*sim_mnc=*/null) .writeString(statsExt.subInfo.mcc)
.writeInt(/*carrier_id=*/0) .writeString(statsExt.subInfo.mnc)
.writeInt(/*opportunistic_data_sub=*/0) .writeInt(statsExt.subInfo.carrierId)
.writeInt(statsExt.subInfo.isOpportunistic
? DATA_USAGE_BYTES_TRANSFER__OPPORTUNISTIC_DATA_SUB__OPPORTUNISTIC
: DATA_USAGE_BYTES_TRANSFER__OPPORTUNISTIC_DATA_SUB__NOT_OPPORTUNISTIC)
.build(); .build();
pulledData.add(e); pulledData.add(e);
} }
@@ -1051,6 +1076,23 @@ public class StatsPullAtomService extends SystemService {
return null; return null;
} }
@NonNull private List<NetworkStatsExt> getDataUsageBytesTransferSnapshotForSub(
@NonNull SubInfo subInfo) {
final List<NetworkStatsExt> ret = new ArrayList<>();
for (final int ratType : getAllCollapsedRatTypes()) {
final NetworkTemplate template =
buildTemplateMobileWithRatType(subInfo.subscriberId, ratType);
final NetworkStats stats =
getUidNetworkStatsSnapshotForTemplate(template, /*includeTags=*/false);
if (stats != null) {
ret.add(new NetworkStatsExt(sliceNetworkStatsByFgbg(stats),
new int[] {TRANSPORT_CELLULAR}, /*slicedByFgbg=*/true,
/*slicedByTag=*/false, /*slicedByMetered=*/false, ratType, subInfo));
}
}
return ret;
}
@NonNull private NetworkStats sliceNetworkStatsByFgbg(@NonNull NetworkStats stats) { @NonNull private NetworkStats sliceNetworkStatsByFgbg(@NonNull NetworkStats stats) {
return sliceNetworkStats(stats, return sliceNetworkStats(stats,
(newEntry, oldEntry) -> { (newEntry, oldEntry) -> {
@@ -3645,4 +3687,46 @@ public class StatsPullAtomService extends SystemService {
FrameworkStatsLog.CONNECTIVITY_STATE_CHANGED__STATE__DISCONNECTED); 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");
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));
}
}
}
} }

View File

@@ -0,0 +1,66 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.stats.pull.netstats;
import android.annotation.NonNull;
import java.util.Objects;
/**
* Information for a subscription that needed for sending NetworkStats related atoms.
*
* @hide
*/
public final class SubInfo {
public final int subId;
public final int carrierId;
@NonNull
public final String mcc;
@NonNull
public final String mnc;
@NonNull
public final String subscriberId;
public final boolean isOpportunistic;
public SubInfo(int subId, int carrierId, @NonNull String mcc, @NonNull String mnc,
@NonNull String subscriberId, boolean isOpportunistic) {
this.subId = subId;
this.carrierId = carrierId;
this.mcc = mcc;
this.mnc = mnc;
this.subscriberId = subscriberId;
this.isOpportunistic = isOpportunistic;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final SubInfo other = (SubInfo) o;
return subId == other.subId
&& carrierId == other.carrierId
&& isOpportunistic == other.isOpportunistic
&& mcc.equals(other.mcc)
&& mnc.equals(other.mnc)
&& subscriberId.equals(other.subscriberId);
}
@Override
public int hashCode() {
return Objects.hash(subId, mcc, mnc, carrierId, subscriberId, isOpportunistic);
}
}