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.Manifest; 20 import android.content.Context; 21 import android.content.pm.PackageManager; 22 import android.os.StatsLogEventWrapper; 23 import android.os.SystemClock; 24 import android.util.ArrayMap; 25 import android.util.Log; 26 import android.util.StatsLog; 27 28 import com.android.car.stats.VmsClientLogger.ConnectionState; 29 import com.android.internal.annotations.GuardedBy; 30 import com.android.internal.car.ICarStatsService; 31 32 import java.io.FileDescriptor; 33 import java.io.PrintWriter; 34 import java.util.ArrayList; 35 import java.util.Arrays; 36 import java.util.Comparator; 37 import java.util.List; 38 import java.util.Locale; 39 import java.util.Map; 40 import java.util.function.Consumer; 41 import java.util.function.Function; 42 43 /** 44 * Implementation of {@link ICarStatsService}, for reporting pulled atoms via statsd. 45 * 46 * Also implements collection and dumpsys reporting of atoms in CSV format. 47 */ 48 public class CarStatsService extends ICarStatsService.Stub { 49 private static final boolean DEBUG = false; 50 private static final String TAG = "CarStatsService"; 51 private static final String VMS_CONNECTION_STATS_DUMPSYS_HEADER = 52 "uid,packageName,attempts,connected,disconnected,terminated,errors"; 53 54 private static final Function<VmsClientLogger, String> VMS_CONNECTION_STATS_DUMPSYS_FORMAT = 55 entry -> String.format(Locale.US, 56 "%d,%s,%d,%d,%d,%d,%d", 57 entry.getUid(), entry.getPackageName(), 58 entry.getConnectionStateCount(ConnectionState.CONNECTING), 59 entry.getConnectionStateCount(ConnectionState.CONNECTED), 60 entry.getConnectionStateCount(ConnectionState.DISCONNECTED), 61 entry.getConnectionStateCount(ConnectionState.TERMINATED), 62 entry.getConnectionStateCount(ConnectionState.CONNECTION_ERROR)); 63 64 private static final String VMS_CLIENT_STATS_DUMPSYS_HEADER = 65 "uid,layerType,layerChannel,layerVersion," 66 + "txBytes,txPackets,rxBytes,rxPackets,droppedBytes,droppedPackets"; 67 68 private static final Function<VmsClientStats, String> VMS_CLIENT_STATS_DUMPSYS_FORMAT = 69 entry -> String.format( 70 "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", 71 entry.getUid(), 72 entry.getLayerType(), entry.getLayerChannel(), entry.getLayerVersion(), 73 entry.getTxBytes(), entry.getTxPackets(), 74 entry.getRxBytes(), entry.getRxPackets(), 75 entry.getDroppedBytes(), entry.getDroppedPackets()); 76 77 private static final Comparator<VmsClientStats> VMS_CLIENT_STATS_ORDER = 78 Comparator.comparingInt(VmsClientStats::getUid) 79 .thenComparingInt(VmsClientStats::getLayerType) 80 .thenComparingInt(VmsClientStats::getLayerChannel) 81 .thenComparingInt(VmsClientStats::getLayerVersion); 82 83 private final Context mContext; 84 private final PackageManager mPackageManager; 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 } 93 94 /** 95 * Gets a logger for the VMS client with a given UID. 96 */ getVmsClientLogger(int clientUid)97 public VmsClientLogger getVmsClientLogger(int clientUid) { 98 synchronized (mVmsClientStats) { 99 return mVmsClientStats.computeIfAbsent( 100 clientUid, 101 uid -> { 102 String packageName = mPackageManager.getNameForUid(uid); 103 if (DEBUG) { 104 Log.d(TAG, "Created VmsClientLog: " + packageName); 105 } 106 return new VmsClientLogger(uid, packageName); 107 }); 108 } 109 } 110 111 @Override dump(FileDescriptor fd, PrintWriter writer, String[] args)112 public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { 113 List<String> flags = Arrays.asList(args); 114 if (args.length == 0 || flags.contains("--vms-client")) { 115 dumpVmsStats(writer); 116 } 117 } 118 119 @Override pullData(int tagId)120 public StatsLogEventWrapper[] pullData(int tagId) { 121 mContext.enforceCallingPermission(Manifest.permission.DUMP, null); 122 if (tagId != StatsLog.VMS_CLIENT_STATS) { 123 Log.w(TAG, "Unexpected tagId: " + tagId); 124 return null; 125 } 126 127 List<StatsLogEventWrapper> ret = new ArrayList<>(); 128 long elapsedNanos = SystemClock.elapsedRealtimeNanos(); 129 long wallClockNanos = SystemClock.currentTimeMicro() * 1000L; 130 pullVmsClientStats(tagId, elapsedNanos, wallClockNanos, ret); 131 return ret.toArray(new StatsLogEventWrapper[0]); 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 tagId, long elapsedNanos, long wallClockNanos, List<StatsLogEventWrapper> pulledData)152 private void pullVmsClientStats(int tagId, long elapsedNanos, long wallClockNanos, 153 List<StatsLogEventWrapper> pulledData) { 154 dumpVmsClientStats((entry) -> { 155 StatsLogEventWrapper e = 156 new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); 157 e.writeInt(entry.getUid()); 158 159 e.writeInt(entry.getLayerType()); 160 e.writeInt(entry.getLayerChannel()); 161 e.writeInt(entry.getLayerVersion()); 162 163 e.writeLong(entry.getTxBytes()); 164 e.writeLong(entry.getTxPackets()); 165 e.writeLong(entry.getRxBytes()); 166 e.writeLong(entry.getRxPackets()); 167 e.writeLong(entry.getDroppedBytes()); 168 e.writeLong(entry.getDroppedPackets()); 169 pulledData.add(e); 170 }); 171 } 172 dumpVmsClientStats(Consumer<VmsClientStats> dumpFn)173 private void dumpVmsClientStats(Consumer<VmsClientStats> dumpFn) { 174 synchronized (mVmsClientStats) { 175 mVmsClientStats.values().stream() 176 .flatMap(log -> log.getLayerEntries().stream()) 177 .sorted(VMS_CLIENT_STATS_ORDER) 178 .forEachOrdered(dumpFn); 179 } 180 } 181 } 182