1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.car.stats; 18 19 import android.app.StatsManager; 20 import android.app.StatsManager.PullAtomMetadata; 21 import android.content.Context; 22 import android.content.pm.PackageManager; 23 import android.util.ArrayMap; 24 import android.util.Log; 25 import android.util.StatsEvent; 26 27 import com.android.car.CarStatsLog; 28 import com.android.car.stats.VmsClientLogger.ConnectionState; 29 import com.android.internal.annotations.GuardedBy; 30 import com.android.internal.util.ConcurrentUtils; 31 32 import java.io.FileDescriptor; 33 import java.io.PrintWriter; 34 import java.util.Arrays; 35 import java.util.Comparator; 36 import java.util.List; 37 import java.util.Locale; 38 import java.util.Map; 39 import java.util.function.Consumer; 40 import java.util.function.Function; 41 42 /** 43 * Registers pulled atoms with statsd via StatsManager. 44 * 45 * Also implements collection and dumpsys reporting of atoms in CSV format. 46 */ 47 public class CarStatsService { 48 private static final boolean DEBUG = false; 49 private static final String TAG = "CarStatsService"; 50 private static final String VMS_CONNECTION_STATS_DUMPSYS_HEADER = 51 "uid,packageName,attempts,connected,disconnected,terminated,errors"; 52 53 private static final Function<VmsClientLogger, String> VMS_CONNECTION_STATS_DUMPSYS_FORMAT = 54 entry -> String.format(Locale.US, 55 "%d,%s,%d,%d,%d,%d,%d", 56 entry.getUid(), entry.getPackageName(), 57 entry.getConnectionStateCount(ConnectionState.CONNECTING), 58 entry.getConnectionStateCount(ConnectionState.CONNECTED), 59 entry.getConnectionStateCount(ConnectionState.DISCONNECTED), 60 entry.getConnectionStateCount(ConnectionState.TERMINATED), 61 entry.getConnectionStateCount(ConnectionState.CONNECTION_ERROR)); 62 63 private static final String VMS_CLIENT_STATS_DUMPSYS_HEADER = 64 "uid,layerType,layerChannel,layerVersion," 65 + "txBytes,txPackets,rxBytes,rxPackets,droppedBytes,droppedPackets"; 66 67 private static final Function<VmsClientStats, String> VMS_CLIENT_STATS_DUMPSYS_FORMAT = 68 entry -> String.format( 69 "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", 70 entry.getUid(), 71 entry.getLayerType(), entry.getLayerChannel(), entry.getLayerVersion(), 72 entry.getTxBytes(), entry.getTxPackets(), 73 entry.getRxBytes(), entry.getRxPackets(), 74 entry.getDroppedBytes(), entry.getDroppedPackets()); 75 76 private static final Comparator<VmsClientStats> VMS_CLIENT_STATS_ORDER = 77 Comparator.comparingInt(VmsClientStats::getUid) 78 .thenComparingInt(VmsClientStats::getLayerType) 79 .thenComparingInt(VmsClientStats::getLayerChannel) 80 .thenComparingInt(VmsClientStats::getLayerVersion); 81 82 private final Context mContext; 83 private final PackageManager mPackageManager; 84 private final StatsManager mStatsManager; 85 86 @GuardedBy("mVmsClientStats") 87 private final Map<Integer, VmsClientLogger> mVmsClientStats = new ArrayMap<>(); 88 CarStatsService(Context context)89 public CarStatsService(Context context) { 90 mContext = context; 91 mPackageManager = context.getPackageManager(); 92 mStatsManager = (StatsManager) mContext.getSystemService(Context.STATS_MANAGER); 93 } 94 95 /** 96 * Registers VmsClientStats puller with StatsManager. 97 */ init()98 public void init() { 99 PullAtomMetadata metadata = new PullAtomMetadata.Builder() 100 .setAdditiveFields(new int[] {5, 6, 7, 8, 9, 10}) 101 .build(); 102 mStatsManager.setPullAtomCallback( 103 CarStatsLog.VMS_CLIENT_STATS, 104 metadata, 105 ConcurrentUtils.DIRECT_EXECUTOR, 106 (atomTag, data) -> pullVmsClientStats(atomTag, data) 107 ); 108 } 109 110 /** 111 * Gets a logger for the VMS client with a given UID. 112 */ getVmsClientLogger(int clientUid)113 public VmsClientLogger getVmsClientLogger(int clientUid) { 114 synchronized (mVmsClientStats) { 115 return mVmsClientStats.computeIfAbsent( 116 clientUid, 117 uid -> { 118 String packageName = mPackageManager.getNameForUid(uid); 119 if (DEBUG) { 120 Log.d(TAG, "Created VmsClientLog: " + packageName); 121 } 122 return new VmsClientLogger(uid, packageName); 123 }); 124 } 125 } 126 dump(FileDescriptor fd, PrintWriter writer, String[] args)127 public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { 128 List<String> flags = Arrays.asList(args); 129 if (args.length == 0 || flags.contains("--vms-client")) { 130 dumpVmsStats(writer); 131 } 132 } 133 dumpVmsStats(PrintWriter writer)134 private void dumpVmsStats(PrintWriter writer) { 135 synchronized (mVmsClientStats) { 136 writer.println(VMS_CONNECTION_STATS_DUMPSYS_HEADER); 137 mVmsClientStats.values().stream() 138 // Unknown UID will not have connection stats 139 .filter(entry -> entry.getUid() > 0) 140 // Sort stats by UID 141 .sorted(Comparator.comparingInt(VmsClientLogger::getUid)) 142 .forEachOrdered(entry -> writer.println( 143 VMS_CONNECTION_STATS_DUMPSYS_FORMAT.apply(entry))); 144 writer.println(); 145 146 writer.println(VMS_CLIENT_STATS_DUMPSYS_HEADER); 147 dumpVmsClientStats(entry -> writer.println( 148 VMS_CLIENT_STATS_DUMPSYS_FORMAT.apply(entry))); 149 } 150 } 151 pullVmsClientStats(int atomTag, List<StatsEvent> pulledData)152 private int pullVmsClientStats(int atomTag, List<StatsEvent> pulledData) { 153 if (atomTag != CarStatsLog.VMS_CLIENT_STATS) { 154 Log.w(TAG, "Unexpected atom tag: " + atomTag); 155 return StatsManager.PULL_SKIP; 156 } 157 158 dumpVmsClientStats((entry) -> { 159 StatsEvent e = StatsEvent.newBuilder() 160 .setAtomId(atomTag) 161 .writeInt(entry.getUid()) 162 .addBooleanAnnotation(CarStatsLog.ANNOTATION_ID_IS_UID, true) 163 164 .writeInt(entry.getLayerType()) 165 .writeInt(entry.getLayerChannel()) 166 .writeInt(entry.getLayerVersion()) 167 168 .writeLong(entry.getTxBytes()) 169 .writeLong(entry.getTxPackets()) 170 .writeLong(entry.getRxBytes()) 171 .writeLong(entry.getRxPackets()) 172 .writeLong(entry.getDroppedBytes()) 173 .writeLong(entry.getDroppedPackets()) 174 .build(); 175 pulledData.add(e); 176 }); 177 return StatsManager.PULL_SUCCESS; 178 } 179 dumpVmsClientStats(Consumer<VmsClientStats> dumpFn)180 private void dumpVmsClientStats(Consumer<VmsClientStats> dumpFn) { 181 synchronized (mVmsClientStats) { 182 mVmsClientStats.values().stream() 183 .flatMap(log -> log.getLayerEntries().stream()) 184 .sorted(VMS_CLIENT_STATS_ORDER) 185 .forEachOrdered(dumpFn); 186 } 187 } 188 } 189