StatsCompanionService.java revision 369208256755a5ded185112dd2ceecadb5986fa5
1/*
2 * Copyright (C) 2017 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 */
16package com.android.server.stats;
17
18import android.annotation.Nullable;
19import android.app.AlarmManager;
20import android.app.PendingIntent;
21import android.app.StatsManager;
22import android.content.BroadcastReceiver;
23import android.content.Context;
24import android.content.Intent;
25import android.content.IntentFilter;
26import android.content.IntentSender;
27import android.content.pm.PackageInfo;
28import android.content.pm.PackageManager;
29import android.content.pm.UserInfo;
30import android.net.NetworkStats;
31import android.net.wifi.IWifiManager;
32import android.net.wifi.WifiActivityEnergyInfo;
33import android.os.StatsDimensionsValue;
34import android.os.BatteryStatsInternal;
35import android.os.Binder;
36import android.os.Bundle;
37import android.os.Environment;
38import android.os.IBinder;
39import android.os.IStatsCompanionService;
40import android.os.IStatsManager;
41import android.os.Parcelable;
42import android.os.Process;
43import android.os.RemoteException;
44import android.os.ServiceManager;
45import android.os.StatFs;
46import android.os.StatsLogEventWrapper;
47import android.os.SynchronousResultReceiver;
48import android.os.SystemClock;
49import android.os.UserHandle;
50import android.os.UserManager;
51import android.telephony.ModemActivityInfo;
52import android.telephony.TelephonyManager;
53import android.util.Slog;
54import android.util.StatsLog;
55
56import com.android.internal.annotations.GuardedBy;
57import com.android.internal.net.NetworkStatsFactory;
58import com.android.internal.os.KernelCpuSpeedReader;
59import com.android.internal.os.KernelWakelockReader;
60import com.android.internal.os.KernelWakelockStats;
61import com.android.internal.os.PowerProfile;
62import com.android.server.LocalServices;
63import com.android.server.SystemService;
64
65import java.util.ArrayList;
66import java.util.List;
67import java.util.Map;
68import java.util.concurrent.TimeoutException;
69
70/**
71 * Helper service for statsd (the native stats management service in cmds/statsd/).
72 * Used for registering and receiving alarms on behalf of statsd.
73 *
74 * @hide
75 */
76public class StatsCompanionService extends IStatsCompanionService.Stub {
77    /**
78     * How long to wait on an individual subsystem to return its stats.
79     */
80    private static final long EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS = 2000;
81
82    public static final String RESULT_RECEIVER_CONTROLLER_KEY = "controller_activity";
83
84    static final String TAG = "StatsCompanionService";
85    static final boolean DEBUG = true;
86
87    public static final String ACTION_TRIGGER_COLLECTION =
88        "com.android.server.stats.action.TRIGGER_COLLECTION";
89
90    public static final int CODE_SUBSCRIBER_BROADCAST = 1;
91
92    private final Context mContext;
93    private final AlarmManager mAlarmManager;
94    @GuardedBy("sStatsdLock")
95    private static IStatsManager sStatsd;
96    private static final Object sStatsdLock = new Object();
97
98    private final PendingIntent mAnomalyAlarmIntent;
99    private final PendingIntent mPullingAlarmIntent;
100    private final BroadcastReceiver mAppUpdateReceiver;
101    private final BroadcastReceiver mUserUpdateReceiver;
102    private final ShutdownEventReceiver mShutdownEventReceiver;
103    private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader();
104    private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
105    private final KernelCpuSpeedReader[] mKernelCpuSpeedReaders;
106    private IWifiManager mWifiManager = null;
107    private TelephonyManager mTelephony = null;
108    private final StatFs mStatFsData = new StatFs(Environment.getDataDirectory().getAbsolutePath());
109    private final StatFs mStatFsSystem =
110        new StatFs(Environment.getRootDirectory().getAbsolutePath());
111    private final StatFs mStatFsTemp =
112        new StatFs(Environment.getDownloadCacheDirectory().getAbsolutePath());
113
114    public StatsCompanionService(Context context) {
115        super();
116        mContext = context;
117        mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
118
119        mAnomalyAlarmIntent = PendingIntent.getBroadcast(mContext, 0,
120                new Intent(mContext, AnomalyAlarmReceiver.class), 0);
121        mPullingAlarmIntent = PendingIntent.getBroadcast(
122            mContext, 0, new Intent(mContext, PullingAlarmReceiver.class), 0);
123        mAppUpdateReceiver = new AppUpdateReceiver();
124        mUserUpdateReceiver = new BroadcastReceiver() {
125            @Override
126            public void onReceive(Context context, Intent intent) {
127                synchronized (sStatsdLock) {
128                    sStatsd = fetchStatsdService();
129                    if (sStatsd == null) {
130                        Slog.w(TAG, "Could not access statsd");
131                        return;
132                    }
133                    try {
134                        // Pull the latest state of UID->app name, version mapping.
135                        // Needed since the new user basically has a version of every app.
136                        informAllUidsLocked(context);
137                    } catch (RemoteException e) {
138                        Slog.e(TAG, "Failed to inform statsd latest update of all apps", e);
139                        forgetEverything();
140                    }
141                }
142            }
143        };
144        mShutdownEventReceiver = new ShutdownEventReceiver();
145        Slog.w(TAG, "Registered receiver for ACTION_PACKAGE_REPLACE AND ADDED.");
146        PowerProfile powerProfile = new PowerProfile(context);
147        final int numClusters = powerProfile.getNumCpuClusters();
148        mKernelCpuSpeedReaders = new KernelCpuSpeedReader[numClusters];
149        int firstCpuOfCluster = 0;
150        for (int i = 0; i < numClusters; i++) {
151            final int numSpeedSteps = powerProfile.getNumSpeedStepsInCpuCluster(i);
152            mKernelCpuSpeedReaders[i] = new KernelCpuSpeedReader(firstCpuOfCluster,
153                            numSpeedSteps);
154            firstCpuOfCluster += powerProfile.getNumCoresInCpuCluster(i);
155        }
156    }
157
158    @Override
159    public void sendBroadcast(String pkg, String cls) {
160        // TODO: Use a pending intent.
161        enforceCallingPermission();
162        mContext.sendBroadcastAsUser(new Intent(ACTION_TRIGGER_COLLECTION).setClassName(pkg, cls),
163                UserHandle.SYSTEM);
164    }
165
166    @Override
167    public void sendSubscriberBroadcast(IBinder intentSenderBinder, long configUid, long configKey,
168                                        long subscriptionId, long subscriptionRuleId,
169                                        StatsDimensionsValue dimensionsValue) {
170        if (DEBUG) Slog.d(TAG, "Statsd requested to sendSubscriberBroadcast.");
171        enforceCallingPermission();
172        IntentSender intentSender = new IntentSender(intentSenderBinder);
173        Intent intent = new Intent()
174                .putExtra(StatsManager.EXTRA_STATS_CONFIG_UID, configUid)
175                .putExtra(StatsManager.EXTRA_STATS_CONFIG_KEY, configKey)
176                .putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_ID, subscriptionId)
177                .putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_RULE_ID, subscriptionRuleId)
178                .putExtra(StatsManager.EXTRA_STATS_DIMENSIONS_VALUE, dimensionsValue);
179        try {
180            intentSender.sendIntent(mContext, CODE_SUBSCRIBER_BROADCAST, intent, null, null);
181        } catch (IntentSender.SendIntentException e) {
182            Slog.w(TAG, "Unable to send using IntentSender from uid " + configUid
183                    + "; presumably it had been cancelled.");
184            if (DEBUG) {
185                Slog.d(TAG, String.format("SubscriberBroadcast params {%d %d %d %d %s}",
186                        configUid, configKey, subscriptionId,
187                        subscriptionRuleId, dimensionsValue));
188            }
189        }
190    }
191
192    private final static int[] toIntArray(List<Integer> list) {
193        int[] ret = new int[list.size()];
194        for (int i = 0; i < ret.length; i++) {
195            ret[i] = list.get(i);
196        }
197        return ret;
198    }
199
200    private final static long[] toLongArray(List<Long> list) {
201        long[] ret = new long[list.size()];
202        for (int i = 0; i < ret.length; i++) {
203            ret[i] = list.get(i);
204        }
205        return ret;
206    }
207
208    // Assumes that sStatsdLock is held.
209    private final void informAllUidsLocked(Context context) throws RemoteException {
210        UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
211        PackageManager pm = context.getPackageManager();
212        final List<UserInfo> users = um.getUsers(true);
213        if (DEBUG) {
214            Slog.w(TAG, "Iterating over " + users.size() + " profiles.");
215        }
216
217        List<Integer> uids = new ArrayList();
218        List<Long> versions = new ArrayList();
219        List<String> apps = new ArrayList();
220
221        // Add in all the apps for every user/profile.
222        for (UserInfo profile : users) {
223            List<PackageInfo> pi = pm.getInstalledPackagesAsUser(0, profile.id);
224            for (int j = 0; j < pi.size(); j++) {
225                if (pi.get(j).applicationInfo != null) {
226                    uids.add(pi.get(j).applicationInfo.uid);
227                    versions.add(pi.get(j).getLongVersionCode());
228                    apps.add(pi.get(j).packageName);
229                }
230            }
231        }
232        sStatsd.informAllUidData(toIntArray(uids), toLongArray(versions), apps.toArray(new
233                String[apps.size()]));
234        if (DEBUG) {
235            Slog.w(TAG, "Sent data for " + uids.size() + " apps");
236        }
237    }
238
239    private final static class AppUpdateReceiver extends BroadcastReceiver {
240        @Override
241        public void onReceive(Context context, Intent intent) {
242            /**
243             * App updates actually consist of REMOVE, ADD, and then REPLACE broadcasts. To avoid
244             * waste, we ignore the REMOVE and ADD broadcasts that contain the replacing flag.
245             * If we can't find the value for EXTRA_REPLACING, we default to false.
246             */
247            if (!intent.getAction().equals(Intent.ACTION_PACKAGE_REPLACED)
248                    && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
249                return; // Keep only replacing or normal add and remove.
250            }
251            Slog.i(TAG, "StatsCompanionService noticed an app was updated.");
252            synchronized (sStatsdLock) {
253                if (sStatsd == null) {
254                    Slog.w(TAG, "Could not access statsd to inform it of an app update");
255                    return;
256                }
257                try {
258                    if (intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED)) {
259                        Bundle b = intent.getExtras();
260                        int uid = b.getInt(Intent.EXTRA_UID);
261                        boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
262                        if (!replacing) {
263                            // Don't bother sending an update if we're right about to get another
264                            // intent for the new version that's added.
265                            PackageManager pm = context.getPackageManager();
266                            String app = intent.getData().getSchemeSpecificPart();
267                            sStatsd.informOnePackageRemoved(app, uid);
268                        }
269                    } else {
270                        PackageManager pm = context.getPackageManager();
271                        Bundle b = intent.getExtras();
272                        int uid = b.getInt(Intent.EXTRA_UID);
273                        String app = intent.getData().getSchemeSpecificPart();
274                        PackageInfo pi = pm.getPackageInfo(app, PackageManager.MATCH_ANY_USER);
275                        sStatsd.informOnePackage(app, uid, pi.getLongVersionCode());
276                    }
277                } catch (Exception e) {
278                    Slog.w(TAG, "Failed to inform statsd of an app update", e);
279                }
280            }
281        }
282    }
283
284    private final static class AnomalyAlarmReceiver extends BroadcastReceiver {
285        @Override
286        public void onReceive(Context context, Intent intent) {
287            Slog.i(TAG, "StatsCompanionService believes an anomaly has occurred.");
288            synchronized (sStatsdLock) {
289                if (sStatsd == null) {
290                    Slog.w(TAG, "Could not access statsd to inform it of anomaly alarm firing");
291                    return;
292                }
293                try {
294                    // Two-way call to statsd to retain AlarmManager wakelock
295                    sStatsd.informAnomalyAlarmFired();
296                } catch (RemoteException e) {
297                    Slog.w(TAG, "Failed to inform statsd of anomaly alarm firing", e);
298                }
299            }
300            // AlarmManager releases its own wakelock here.
301        }
302    }
303
304    private final static class PullingAlarmReceiver extends BroadcastReceiver {
305      @Override
306      public void onReceive(Context context, Intent intent) {
307        if (DEBUG)
308          Slog.d(TAG, "Time to poll something.");
309        synchronized (sStatsdLock) {
310          if (sStatsd == null) {
311            Slog.w(TAG, "Could not access statsd to inform it of pulling alarm firing.");
312            return;
313          }
314          try {
315            // Two-way call to statsd to retain AlarmManager wakelock
316            sStatsd.informPollAlarmFired();
317          } catch (RemoteException e) {
318            Slog.w(TAG, "Failed to inform statsd of pulling alarm firing.", e);
319          }
320        }
321        // AlarmManager releases its own wakelock here.
322      }
323    }
324
325    private final static class ShutdownEventReceiver extends BroadcastReceiver {
326        @Override
327        public void onReceive(Context context, Intent intent) {
328            /**
329             * Skip immediately if intent is not relevant to device shutdown.
330             */
331            if (!intent.getAction().equals(Intent.ACTION_REBOOT)
332                    && !intent.getAction().equals(Intent.ACTION_SHUTDOWN)) {
333                return;
334            }
335            Slog.i(TAG, "StatsCompanionService noticed a shutdown.");
336            synchronized (sStatsdLock) {
337                if (sStatsd == null) {
338                    Slog.w(TAG, "Could not access statsd to inform it of a shutdown event.");
339                    return;
340                }
341                try {
342                    sStatsd.writeDataToDisk();
343                } catch (Exception e) {
344                    Slog.w(TAG, "Failed to inform statsd of a shutdown event.", e);
345                }
346            }
347        }
348    }
349
350    @Override // Binder call
351    public void setAnomalyAlarm(long timestampMs) {
352        enforceCallingPermission();
353        if (DEBUG) Slog.d(TAG, "Setting anomaly alarm for " + timestampMs);
354        final long callingToken = Binder.clearCallingIdentity();
355        try {
356            // using RTC, not RTC_WAKEUP, so if device is asleep, will only fire when it awakens.
357            // This alarm is inexact, leaving its exactness completely up to the OS optimizations.
358            // AlarmManager will automatically cancel any previous mAnomalyAlarmIntent alarm.
359            mAlarmManager.set(AlarmManager.RTC, timestampMs, mAnomalyAlarmIntent);
360        } finally {
361            Binder.restoreCallingIdentity(callingToken);
362        }
363    }
364
365    @Override // Binder call
366    public void cancelAnomalyAlarm() {
367        enforceCallingPermission();
368        if (DEBUG) Slog.d(TAG, "Cancelling anomaly alarm");
369        final long callingToken = Binder.clearCallingIdentity();
370        try {
371            mAlarmManager.cancel(mAnomalyAlarmIntent);
372        } finally {
373            Binder.restoreCallingIdentity(callingToken);
374        }
375    }
376
377    @Override // Binder call
378    public void setPullingAlarms(long timestampMs, long intervalMs) {
379      enforceCallingPermission();
380      if (DEBUG)
381        Slog.d(TAG, "Setting pulling alarm for " + timestampMs + " every " + intervalMs + "ms");
382      final long callingToken = Binder.clearCallingIdentity();
383      try {
384        // using RTC, not RTC_WAKEUP, so if device is asleep, will only fire when it awakens.
385        // This alarm is inexact, leaving its exactness completely up to the OS optimizations.
386        // TODO: totally inexact means that stats per bucket could be quite off. Is this okay?
387        mAlarmManager.setRepeating(AlarmManager.RTC, timestampMs, intervalMs, mPullingAlarmIntent);
388      } finally {
389        Binder.restoreCallingIdentity(callingToken);
390      }
391    }
392
393    @Override // Binder call
394    public void cancelPullingAlarms() {
395      enforceCallingPermission();
396      if (DEBUG)
397        Slog.d(TAG, "Cancelling pulling alarm");
398      final long callingToken = Binder.clearCallingIdentity();
399      try {
400        mAlarmManager.cancel(mPullingAlarmIntent);
401      } finally {
402        Binder.restoreCallingIdentity(callingToken);
403      }
404    }
405
406    private StatsLogEventWrapper[] addNetworkStats(int tag, NetworkStats stats, boolean withFGBG) {
407        List<StatsLogEventWrapper> ret = new ArrayList<>();
408        int size = stats.size();
409        NetworkStats.Entry entry = new NetworkStats.Entry(); // For recycling
410        for (int j = 0; j < size; j++) {
411            stats.getValues(j, entry);
412            StatsLogEventWrapper e = new StatsLogEventWrapper(tag, withFGBG ? 6 : 5);
413            e.writeInt(entry.uid);
414            if (withFGBG) {
415                e.writeInt(entry.set);
416            }
417            e.writeLong(entry.rxBytes);
418            e.writeLong(entry.rxPackets);
419            e.writeLong(entry.txBytes);
420            e.writeLong(entry.txPackets);
421            ret.add(e);
422        }
423        return ret.toArray(new StatsLogEventWrapper[ret.size()]);
424    }
425
426    /**
427     * Allows rollups per UID but keeping the set (foreground/background) slicing.
428     * Adapted from groupedByUid in frameworks/base/core/java/android/net/NetworkStats.java
429     */
430    private NetworkStats rollupNetworkStatsByFGBG(NetworkStats stats) {
431        final NetworkStats ret = new NetworkStats(stats.getElapsedRealtime(), 1);
432
433        final NetworkStats.Entry entry = new NetworkStats.Entry();
434        entry.iface = NetworkStats.IFACE_ALL;
435        entry.tag = NetworkStats.TAG_NONE;
436        entry.metered = NetworkStats.METERED_ALL;
437        entry.roaming = NetworkStats.ROAMING_ALL;
438
439        int size = stats.size();
440        NetworkStats.Entry recycle = new NetworkStats.Entry(); // Used for retrieving values
441        for (int i = 0; i < size; i++) {
442            stats.getValues(i, recycle);
443
444            // Skip specific tags, since already counted in TAG_NONE
445            if (recycle.tag != NetworkStats.TAG_NONE) continue;
446
447            entry.set = recycle.set; // Allows slicing by background/foreground
448            entry.uid = recycle.uid;
449            entry.rxBytes = recycle.rxBytes;
450            entry.rxPackets = recycle.rxPackets;
451            entry.txBytes = recycle.txBytes;
452            entry.txPackets = recycle.txPackets;
453            // Operations purposefully omitted since we don't use them for statsd.
454            ret.combineValues(entry);
455        }
456        return ret;
457    }
458
459    /**
460     * Helper method to extract the Parcelable controller info from a
461     * SynchronousResultReceiver.
462     */
463    private static <T extends Parcelable> T awaitControllerInfo(
464            @Nullable SynchronousResultReceiver receiver) {
465        if (receiver == null) {
466            return null;
467        }
468
469        try {
470            final SynchronousResultReceiver.Result result =
471                    receiver.awaitResult(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS);
472            if (result.bundle != null) {
473                // This is the final destination for the Bundle.
474                result.bundle.setDefusable(true);
475
476                final T data = result.bundle.getParcelable(
477                        RESULT_RECEIVER_CONTROLLER_KEY);
478                if (data != null) {
479                    return data;
480                }
481            }
482            Slog.e(TAG, "no controller energy info supplied for " + receiver.getName());
483        } catch (TimeoutException e) {
484            Slog.w(TAG, "timeout reading " + receiver.getName() + " stats");
485        }
486        return null;
487    }
488
489    /**
490     *
491     * Pulls wifi controller activity energy info from WiFiManager
492     */
493    @Override // Binder call
494    public StatsLogEventWrapper[] pullData(int tagId) {
495        enforceCallingPermission();
496        if (DEBUG)
497            Slog.d(TAG, "Pulling " + tagId);
498
499        switch (tagId) {
500            case StatsLog.WIFI_BYTES_TRANSFER: {
501                long token = Binder.clearCallingIdentity();
502                try {
503                    // TODO: Consider caching the following call to get BatteryStatsInternal.
504                    BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
505                    String[] ifaces = bs.getWifiIfaces();
506                    if (ifaces.length == 0) {
507                        return null;
508                    }
509                    NetworkStatsFactory nsf = new NetworkStatsFactory();
510                    // Combine all the metrics per Uid into one record.
511                    NetworkStats stats = nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces,
512                            NetworkStats.TAG_NONE, null).groupedByUid();
513                    return addNetworkStats(tagId, stats, false);
514                } catch (java.io.IOException e) {
515                    Slog.e(TAG, "Pulling netstats for wifi bytes has error", e);
516                } finally {
517                    Binder.restoreCallingIdentity(token);
518                }
519                break;
520            }
521            case StatsLog.MOBILE_BYTES_TRANSFER: {
522                long token = Binder.clearCallingIdentity();
523                try {
524                    BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
525                    String[] ifaces = bs.getMobileIfaces();
526                    if (ifaces.length == 0) {
527                        return null;
528                    }
529                    NetworkStatsFactory nsf = new NetworkStatsFactory();
530                    // Combine all the metrics per Uid into one record.
531                    NetworkStats stats = nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces,
532                        NetworkStats.TAG_NONE, null).groupedByUid();
533                    return addNetworkStats(tagId, stats, false);
534                } catch (java.io.IOException e) {
535                    Slog.e(TAG, "Pulling netstats for mobile bytes has error", e);
536                } finally {
537                    Binder.restoreCallingIdentity(token);
538                }
539                break;
540            }
541            case StatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG: {
542                long token = Binder.clearCallingIdentity();
543                try {
544                    BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
545                    String[] ifaces = bs.getWifiIfaces();
546                    if (ifaces.length == 0) {
547                        return null;
548                    }
549                    NetworkStatsFactory nsf = new NetworkStatsFactory();
550                    NetworkStats stats = rollupNetworkStatsByFGBG(
551                            nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces,
552                            NetworkStats.TAG_NONE, null));
553                    return addNetworkStats(tagId, stats, true);
554                } catch (java.io.IOException e) {
555                    Slog.e(TAG, "Pulling netstats for wifi bytes w/ fg/bg has error", e);
556                } finally {
557                    Binder.restoreCallingIdentity(token);
558                }
559                break;
560            }
561            case StatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG: {
562                long token = Binder.clearCallingIdentity();
563                try {
564                    BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
565                    String[] ifaces = bs.getMobileIfaces();
566                    if (ifaces.length == 0) {
567                        return null;
568                    }
569                    NetworkStatsFactory nsf = new NetworkStatsFactory();
570                    NetworkStats stats = rollupNetworkStatsByFGBG(
571                            nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces,
572                            NetworkStats.TAG_NONE, null));
573                    return addNetworkStats(tagId, stats, true);
574                } catch (java.io.IOException e) {
575                    Slog.e(TAG, "Pulling netstats for mobile bytes w/ fg/bg has error", e);
576                } finally {
577                    Binder.restoreCallingIdentity(token);
578                }
579                break;
580            }
581            case StatsLog.KERNEL_WAKELOCK: {
582                final KernelWakelockStats wakelockStats =
583                        mKernelWakelockReader.readKernelWakelockStats(mTmpWakelockStats);
584                List<StatsLogEventWrapper> ret = new ArrayList();
585                for (Map.Entry<String, KernelWakelockStats.Entry> ent : wakelockStats.entrySet()) {
586                    String name = ent.getKey();
587                    KernelWakelockStats.Entry kws = ent.getValue();
588                    StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 4);
589                    e.writeString(name);
590                    e.writeInt(kws.mCount);
591                    e.writeInt(kws.mVersion);
592                    e.writeLong(kws.mTotalTime);
593                    ret.add(e);
594                }
595                return ret.toArray(new StatsLogEventWrapper[ret.size()]);
596            }
597            case StatsLog.CPU_TIME_PER_FREQ: {
598                List<StatsLogEventWrapper> ret = new ArrayList();
599                for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) {
600                    long[] clusterTimeMs = mKernelCpuSpeedReaders[cluster].readAbsolute();
601                    if (clusterTimeMs != null) {
602                        for (int speed = clusterTimeMs.length - 1; speed >= 0; --speed) {
603                            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 3);
604                            e.writeInt(cluster);
605                            e.writeInt(speed);
606                            e.writeLong(clusterTimeMs[speed]);
607                            ret.add(e);
608                        }
609                    }
610                }
611                return ret.toArray(new StatsLogEventWrapper[ret.size()]);
612            }
613            case StatsLog.WIFI_ACTIVITY_ENERGY_INFO: {
614                List<StatsLogEventWrapper> ret = new ArrayList();
615                long token = Binder.clearCallingIdentity();
616                if (mWifiManager == null) {
617                    mWifiManager = IWifiManager.Stub.asInterface(ServiceManager.getService(
618                            Context.WIFI_SERVICE));
619                }
620                if (mWifiManager != null) {
621                    try {
622                        SynchronousResultReceiver wifiReceiver = new SynchronousResultReceiver("wifi");
623                        mWifiManager.requestActivityInfo(wifiReceiver);
624                        final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver);
625                        StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 6);
626                        e.writeLong(wifiInfo.getTimeStamp());
627                        e.writeInt(wifiInfo.getStackState());
628                        e.writeLong(wifiInfo.getControllerTxTimeMillis());
629                        e.writeLong(wifiInfo.getControllerRxTimeMillis());
630                        e.writeLong(wifiInfo.getControllerIdleTimeMillis());
631                        e.writeLong(wifiInfo.getControllerEnergyUsed());
632                        ret.add(e);
633                        return ret.toArray(new StatsLogEventWrapper[ret.size()]);
634                    } catch (RemoteException e) {
635                        Slog.e(TAG, "Pulling wifiManager for wifi controller activity energy info has error", e);
636                    } finally {
637                        Binder.restoreCallingIdentity(token);
638                    }
639                }
640                break;
641            }
642            case StatsLog.MODEM_ACTIVITY_INFO: {
643                List<StatsLogEventWrapper> ret = new ArrayList();
644                long token = Binder.clearCallingIdentity();
645                if (mTelephony == null) {
646                    mTelephony = TelephonyManager.from(mContext);
647                }
648                if (mTelephony != null) {
649                    SynchronousResultReceiver modemReceiver = new SynchronousResultReceiver("telephony");
650                    mTelephony.requestModemActivityInfo(modemReceiver);
651                    final ModemActivityInfo modemInfo = awaitControllerInfo(modemReceiver);
652                    StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 6);
653                    e.writeLong(modemInfo.getTimestamp());
654                    e.writeLong(modemInfo.getSleepTimeMillis());
655                    e.writeLong(modemInfo.getIdleTimeMillis());
656                    e.writeLong(modemInfo.getTxTimeMillis()[0]);
657                    e.writeLong(modemInfo.getTxTimeMillis()[1]);
658                    e.writeLong(modemInfo.getTxTimeMillis()[2]);
659                    e.writeLong(modemInfo.getTxTimeMillis()[3]);
660                    e.writeLong(modemInfo.getTxTimeMillis()[4]);
661                    e.writeLong(modemInfo.getRxTimeMillis());
662                    e.writeLong(modemInfo.getEnergyUsed());
663                    ret.add(e);
664                    return ret.toArray(new StatsLogEventWrapper[ret.size()]);
665                }
666                break;
667            }
668            case StatsLog.CPU_SUSPEND_TIME: {
669                List<StatsLogEventWrapper> ret = new ArrayList();
670                StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 1);
671                e.writeLong(SystemClock.elapsedRealtime());
672                ret.add(e);
673                return ret.toArray(new StatsLogEventWrapper[ret.size()]);
674            }
675            case StatsLog.CPU_IDLE_TIME: {
676                List<StatsLogEventWrapper> ret = new ArrayList();
677                StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 1);
678                e.writeLong(SystemClock.uptimeMillis());
679                ret.add(e);
680                return ret.toArray(new StatsLogEventWrapper[ret.size()]);
681            }
682            case StatsLog.DISK_SPACE: {
683              List<StatsLogEventWrapper> ret = new ArrayList();
684              StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 3);
685              e.writeLong(mStatFsData.getAvailableBytes());
686              e.writeLong(mStatFsSystem.getAvailableBytes());
687              e.writeLong(mStatFsTemp.getAvailableBytes());
688              ret.add(e);
689              return ret.toArray(new StatsLogEventWrapper[ret.size()]);
690            }
691            case StatsLog.SYSTEM_UPTIME: {
692              List<StatsLogEventWrapper> ret = new ArrayList();
693              StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 1);
694              e.writeLong(SystemClock.uptimeMillis());
695              ret.add(e);
696              return ret.toArray(new StatsLogEventWrapper[ret.size()]);
697            }
698            default:
699                Slog.w(TAG, "No such tagId data as " + tagId);
700                return null;
701        }
702        return null;
703    }
704
705    @Override // Binder call
706    public void statsdReady() {
707        enforceCallingPermission();
708        if (DEBUG) Slog.d(TAG, "learned that statsdReady");
709        sayHiToStatsd(); // tell statsd that we're ready too and link to it
710        mContext.sendBroadcast(new Intent(StatsManager.ACTION_STATSD_STARTED),
711                android.Manifest.permission.DUMP);
712    }
713
714    @Override
715    public void triggerUidSnapshot() {
716      enforceCallingPermission();
717      synchronized (sStatsdLock) {
718        try {
719          informAllUidsLocked(mContext);
720        } catch (RemoteException e) {
721          Slog.e(TAG, "Failed to trigger uid snapshot.", e);
722        }
723      }
724    }
725
726    private void enforceCallingPermission() {
727        if (Binder.getCallingPid() == Process.myPid()) {
728            return;
729        }
730        mContext.enforceCallingPermission(android.Manifest.permission.STATSCOMPANION, null);
731    }
732
733    // Lifecycle and related code
734
735    /**
736     * Fetches the statsd IBinder service
737     */
738    private static IStatsManager fetchStatsdService() {
739        return IStatsManager.Stub.asInterface(ServiceManager.getService("stats"));
740    }
741
742    public static final class Lifecycle extends SystemService {
743        private StatsCompanionService mStatsCompanionService;
744
745        public Lifecycle(Context context) {
746            super(context);
747        }
748
749        @Override
750        public void onStart() {
751            mStatsCompanionService = new StatsCompanionService(getContext());
752            try {
753                publishBinderService(Context.STATS_COMPANION_SERVICE, mStatsCompanionService);
754                if (DEBUG) Slog.d(TAG, "Published " + Context.STATS_COMPANION_SERVICE);
755            } catch (Exception e) {
756                Slog.e(TAG, "Failed to publishBinderService", e);
757            }
758        }
759
760        @Override
761        public void onBootPhase(int phase) {
762            super.onBootPhase(phase);
763            if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
764                mStatsCompanionService.systemReady();
765            }
766        }
767    }
768
769    /**
770     * Now that the android system is ready, StatsCompanion is ready too, so inform statsd.
771     */
772    private void systemReady() {
773        if (DEBUG) Slog.d(TAG, "Learned that systemReady");
774        sayHiToStatsd();
775    }
776
777    /**
778     * Tells statsd that statscompanion is ready. If the binder call returns, link to statsd.
779     */
780    private void sayHiToStatsd() {
781        synchronized (sStatsdLock) {
782            if (sStatsd != null) {
783                Slog.e(TAG, "Trying to fetch statsd, but it was already fetched",
784                        new IllegalStateException("sStatsd is not null when being fetched"));
785                return;
786            }
787            sStatsd = fetchStatsdService();
788            if (sStatsd == null) {
789                Slog.w(TAG, "Could not access statsd");
790                return;
791            }
792            if (DEBUG) Slog.d(TAG, "Saying hi to statsd");
793            try {
794                sStatsd.statsCompanionReady();
795                // If the statsCompanionReady two-way binder call returns, link to statsd.
796                try {
797                    sStatsd.asBinder().linkToDeath(new StatsdDeathRecipient(), 0);
798                } catch (RemoteException e) {
799                    Slog.e(TAG, "linkToDeath(StatsdDeathRecipient) failed", e);
800                    forgetEverything();
801                }
802                // Setup broadcast receiver for updates.
803                IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REPLACED);
804                filter.addAction(Intent.ACTION_PACKAGE_ADDED);
805                filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
806                filter.addDataScheme("package");
807                mContext.registerReceiverAsUser(mAppUpdateReceiver, UserHandle.ALL, filter, null,
808                        null);
809
810                // Setup receiver for user initialize (which happens once for a new user) and
811                // if a user is removed.
812                filter = new IntentFilter(Intent.ACTION_USER_INITIALIZE);
813                filter.addAction(Intent.ACTION_USER_REMOVED);
814                mContext.registerReceiverAsUser(mUserUpdateReceiver, UserHandle.ALL,
815                        filter, null, null);
816
817                // Setup receiver for device reboots or shutdowns.
818                filter = new IntentFilter(Intent.ACTION_REBOOT);
819                filter.addAction(Intent.ACTION_SHUTDOWN);
820                mContext.registerReceiverAsUser(
821                        mShutdownEventReceiver, UserHandle.ALL, filter, null, null);
822                final long token = Binder.clearCallingIdentity();
823                try {
824                    // Pull the latest state of UID->app name, version mapping when statsd starts.
825                    informAllUidsLocked(mContext);
826                } finally {
827                    restoreCallingIdentity(token);
828                }
829            } catch (RemoteException e) {
830                Slog.e(TAG, "Failed to inform statsd that statscompanion is ready", e);
831                forgetEverything();
832            }
833        }
834    }
835
836    private class StatsdDeathRecipient implements IBinder.DeathRecipient {
837        @Override
838        public void binderDied() {
839            Slog.i(TAG, "Statsd is dead - erase all my knowledge.");
840            forgetEverything();
841        }
842    }
843
844    private void forgetEverything() {
845        synchronized (sStatsdLock) {
846            sStatsd = null;
847            mContext.unregisterReceiver(mAppUpdateReceiver);
848            mContext.unregisterReceiver(mUserUpdateReceiver);
849            mContext.unregisterReceiver(mShutdownEventReceiver);
850            cancelAnomalyAlarm();
851            cancelPullingAlarms();
852        }
853    }
854
855}
856