StatsCompanionService.java revision 0eb58ae39d88a2fbfecabae8b10a4be5e4ebcc8c
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, and enfoceCallingPermission.
161        mContext.sendBroadcastAsUser(new Intent(ACTION_TRIGGER_COLLECTION).setClassName(pkg, cls),
162                UserHandle.SYSTEM);
163    }
164
165    @Override
166    public void sendSubscriberBroadcast(IBinder intentSenderBinder, long configUid, long configKey,
167                                        long subscriptionId, long subscriptionRuleId,
168                                        StatsDimensionsValue dimensionsValue) {
169        if (DEBUG) Slog.d(TAG, "Statsd requested to sendSubscriberBroadcast.");
170        enforceCallingPermission();
171        IntentSender intentSender = new IntentSender(intentSenderBinder);
172        Intent intent = new Intent()
173                .putExtra(StatsManager.EXTRA_STATS_CONFIG_UID, configUid)
174                .putExtra(StatsManager.EXTRA_STATS_CONFIG_KEY, configKey)
175                .putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_ID, subscriptionId)
176                .putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_RULE_ID, subscriptionRuleId)
177                .putExtra(StatsManager.EXTRA_STATS_DIMENSIONS_VALUE, dimensionsValue);
178        try {
179            intentSender.sendIntent(mContext, CODE_SUBSCRIBER_BROADCAST, intent, null, null);
180        } catch (IntentSender.SendIntentException e) {
181            Slog.w(TAG, "Unable to send using IntentSender from uid " + configUid
182                    + "; presumably it had been cancelled.");
183            if (DEBUG) {
184                Slog.d(TAG, String.format("SubscriberBroadcast params {%d %d %d %d %s}",
185                        configUid, configKey, subscriptionId,
186                        subscriptionRuleId, dimensionsValue));
187            }
188        }
189    }
190
191    private final static int[] toIntArray(List<Integer> list) {
192        int[] ret = new int[list.size()];
193        for (int i = 0; i < ret.length; i++) {
194            ret[i] = list.get(i);
195        }
196        return ret;
197    }
198
199    private final static long[] toLongArray(List<Long> list) {
200        long[] ret = new long[list.size()];
201        for (int i = 0; i < ret.length; i++) {
202            ret[i] = list.get(i);
203        }
204        return ret;
205    }
206
207    // Assumes that sStatsdLock is held.
208    private final void informAllUidsLocked(Context context) throws RemoteException {
209        UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
210        PackageManager pm = context.getPackageManager();
211        final List<UserInfo> users = um.getUsers(true);
212        if (DEBUG) {
213            Slog.w(TAG, "Iterating over " + users.size() + " profiles.");
214        }
215
216        List<Integer> uids = new ArrayList();
217        List<Long> versions = new ArrayList();
218        List<String> apps = new ArrayList();
219
220        // Add in all the apps for every user/profile.
221        for (UserInfo profile : users) {
222            List<PackageInfo> pi = pm.getInstalledPackagesAsUser(0, profile.id);
223            for (int j = 0; j < pi.size(); j++) {
224                if (pi.get(j).applicationInfo != null) {
225                    uids.add(pi.get(j).applicationInfo.uid);
226                    versions.add(pi.get(j).getLongVersionCode());
227                    apps.add(pi.get(j).packageName);
228                }
229            }
230        }
231        sStatsd.informAllUidData(toIntArray(uids), toLongArray(versions), apps.toArray(new
232                String[apps.size()]));
233        if (DEBUG) {
234            Slog.w(TAG, "Sent data for " + uids.size() + " apps");
235        }
236    }
237
238    public final static class AppUpdateReceiver extends BroadcastReceiver {
239        @Override
240        public void onReceive(Context context, Intent intent) {
241            /**
242             * App updates actually consist of REMOVE, ADD, and then REPLACE broadcasts. To avoid
243             * waste, we ignore the REMOVE and ADD broadcasts that contain the replacing flag.
244             * If we can't find the value for EXTRA_REPLACING, we default to false.
245             */
246            if (!intent.getAction().equals(Intent.ACTION_PACKAGE_REPLACED)
247                    && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
248                return; // Keep only replacing or normal add and remove.
249            }
250            Slog.i(TAG, "StatsCompanionService noticed an app was updated.");
251            synchronized (sStatsdLock) {
252                if (sStatsd == null) {
253                    Slog.w(TAG, "Could not access statsd to inform it of an app update");
254                    return;
255                }
256                try {
257                    if (intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED)) {
258                        Bundle b = intent.getExtras();
259                        int uid = b.getInt(Intent.EXTRA_UID);
260                        boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
261                        if (!replacing) {
262                            // Don't bother sending an update if we're right about to get another
263                            // intent for the new version that's added.
264                            PackageManager pm = context.getPackageManager();
265                            String app = intent.getData().getSchemeSpecificPart();
266                            sStatsd.informOnePackageRemoved(app, uid);
267                        }
268                    } else {
269                        PackageManager pm = context.getPackageManager();
270                        Bundle b = intent.getExtras();
271                        int uid = b.getInt(Intent.EXTRA_UID);
272                        String app = intent.getData().getSchemeSpecificPart();
273                        PackageInfo pi = pm.getPackageInfo(app, PackageManager.MATCH_ANY_USER);
274                        sStatsd.informOnePackage(app, uid, pi.getLongVersionCode());
275                    }
276                } catch (Exception e) {
277                    Slog.w(TAG, "Failed to inform statsd of an app update", e);
278                }
279            }
280        }
281    }
282
283    public final static class AnomalyAlarmReceiver extends BroadcastReceiver {
284        @Override
285        public void onReceive(Context context, Intent intent) {
286            Slog.i(TAG, "StatsCompanionService believes an anomaly has occurred.");
287            synchronized (sStatsdLock) {
288                if (sStatsd == null) {
289                    Slog.w(TAG, "Could not access statsd to inform it of anomaly alarm firing");
290                    return;
291                }
292                try {
293                    // Two-way call to statsd to retain AlarmManager wakelock
294                    sStatsd.informAnomalyAlarmFired();
295                } catch (RemoteException e) {
296                    Slog.w(TAG, "Failed to inform statsd of anomaly alarm firing", e);
297                }
298            }
299            // AlarmManager releases its own wakelock here.
300        }
301    }
302
303    public final static class PullingAlarmReceiver extends BroadcastReceiver {
304      @Override
305      public void onReceive(Context context, Intent intent) {
306        if (DEBUG)
307          Slog.d(TAG, "Time to poll something.");
308        synchronized (sStatsdLock) {
309          if (sStatsd == null) {
310            Slog.w(TAG, "Could not access statsd to inform it of pulling alarm firing.");
311            return;
312          }
313          try {
314            // Two-way call to statsd to retain AlarmManager wakelock
315            sStatsd.informPollAlarmFired();
316          } catch (RemoteException e) {
317            Slog.w(TAG, "Failed to inform statsd of pulling alarm firing.", e);
318          }
319        }
320        // AlarmManager releases its own wakelock here.
321      }
322    }
323
324    public final static class ShutdownEventReceiver extends BroadcastReceiver {
325        @Override
326        public void onReceive(Context context, Intent intent) {
327            /**
328             * Skip immediately if intent is not relevant to device shutdown.
329             */
330            if (!intent.getAction().equals(Intent.ACTION_REBOOT)
331                    && !intent.getAction().equals(Intent.ACTION_SHUTDOWN)) {
332                return;
333            }
334            Slog.i(TAG, "StatsCompanionService noticed a shutdown.");
335            synchronized (sStatsdLock) {
336                if (sStatsd == null) {
337                    Slog.w(TAG, "Could not access statsd to inform it of a shutdown event.");
338                    return;
339                }
340                try {
341                    sStatsd.writeDataToDisk();
342                } catch (Exception e) {
343                    Slog.w(TAG, "Failed to inform statsd of a shutdown event.", e);
344                }
345            }
346        }
347    }
348
349    @Override // Binder call
350    public void setAnomalyAlarm(long timestampMs) {
351        enforceCallingPermission();
352        if (DEBUG) Slog.d(TAG, "Setting anomaly alarm for " + timestampMs);
353        final long callingToken = Binder.clearCallingIdentity();
354        try {
355            // using RTC, not RTC_WAKEUP, so if device is asleep, will only fire when it awakens.
356            // This alarm is inexact, leaving its exactness completely up to the OS optimizations.
357            // AlarmManager will automatically cancel any previous mAnomalyAlarmIntent alarm.
358            mAlarmManager.set(AlarmManager.RTC, timestampMs, mAnomalyAlarmIntent);
359        } finally {
360            Binder.restoreCallingIdentity(callingToken);
361        }
362    }
363
364    @Override // Binder call
365    public void cancelAnomalyAlarm() {
366        enforceCallingPermission();
367        if (DEBUG) Slog.d(TAG, "Cancelling anomaly alarm");
368        final long callingToken = Binder.clearCallingIdentity();
369        try {
370            mAlarmManager.cancel(mAnomalyAlarmIntent);
371        } finally {
372            Binder.restoreCallingIdentity(callingToken);
373        }
374    }
375
376    @Override // Binder call
377    public void setPullingAlarms(long timestampMs, long intervalMs) {
378      enforceCallingPermission();
379      if (DEBUG)
380        Slog.d(TAG, "Setting pulling alarm for " + timestampMs + " every " + intervalMs + "ms");
381      final long callingToken = Binder.clearCallingIdentity();
382      try {
383        // using RTC, not RTC_WAKEUP, so if device is asleep, will only fire when it awakens.
384        // This alarm is inexact, leaving its exactness completely up to the OS optimizations.
385        // TODO: totally inexact means that stats per bucket could be quite off. Is this okay?
386        mAlarmManager.setRepeating(AlarmManager.RTC, timestampMs, intervalMs, mPullingAlarmIntent);
387      } finally {
388        Binder.restoreCallingIdentity(callingToken);
389      }
390    }
391
392    @Override // Binder call
393    public void cancelPullingAlarms() {
394      enforceCallingPermission();
395      if (DEBUG)
396        Slog.d(TAG, "Cancelling pulling alarm");
397      final long callingToken = Binder.clearCallingIdentity();
398      try {
399        mAlarmManager.cancel(mPullingAlarmIntent);
400      } finally {
401        Binder.restoreCallingIdentity(callingToken);
402      }
403    }
404
405    private StatsLogEventWrapper[] addNetworkStats(int tag, NetworkStats stats, boolean withFGBG) {
406        List<StatsLogEventWrapper> ret = new ArrayList<>();
407        int size = stats.size();
408        NetworkStats.Entry entry = new NetworkStats.Entry(); // For recycling
409        for (int j = 0; j < size; j++) {
410            stats.getValues(j, entry);
411            StatsLogEventWrapper e = new StatsLogEventWrapper(tag, withFGBG ? 6 : 5);
412            e.writeInt(entry.uid);
413            if (withFGBG) {
414                e.writeInt(entry.set);
415            }
416            e.writeLong(entry.rxBytes);
417            e.writeLong(entry.rxPackets);
418            e.writeLong(entry.txBytes);
419            e.writeLong(entry.txPackets);
420            ret.add(e);
421        }
422        return ret.toArray(new StatsLogEventWrapper[ret.size()]);
423    }
424
425    /**
426     * Allows rollups per UID but keeping the set (foreground/background) slicing.
427     * Adapted from groupedByUid in frameworks/base/core/java/android/net/NetworkStats.java
428     */
429    private NetworkStats rollupNetworkStatsByFGBG(NetworkStats stats) {
430        final NetworkStats ret = new NetworkStats(stats.getElapsedRealtime(), 1);
431
432        final NetworkStats.Entry entry = new NetworkStats.Entry();
433        entry.iface = NetworkStats.IFACE_ALL;
434        entry.tag = NetworkStats.TAG_NONE;
435        entry.metered = NetworkStats.METERED_ALL;
436        entry.roaming = NetworkStats.ROAMING_ALL;
437
438        int size = stats.size();
439        NetworkStats.Entry recycle = new NetworkStats.Entry(); // Used for retrieving values
440        for (int i = 0; i < size; i++) {
441            stats.getValues(i, recycle);
442
443            // Skip specific tags, since already counted in TAG_NONE
444            if (recycle.tag != NetworkStats.TAG_NONE) continue;
445
446            entry.set = recycle.set; // Allows slicing by background/foreground
447            entry.uid = recycle.uid;
448            entry.rxBytes = recycle.rxBytes;
449            entry.rxPackets = recycle.rxPackets;
450            entry.txBytes = recycle.txBytes;
451            entry.txPackets = recycle.txPackets;
452            // Operations purposefully omitted since we don't use them for statsd.
453            ret.combineValues(entry);
454        }
455        return ret;
456    }
457
458    /**
459     * Helper method to extract the Parcelable controller info from a
460     * SynchronousResultReceiver.
461     */
462    private static <T extends Parcelable> T awaitControllerInfo(
463            @Nullable SynchronousResultReceiver receiver) {
464        if (receiver == null) {
465            return null;
466        }
467
468        try {
469            final SynchronousResultReceiver.Result result =
470                    receiver.awaitResult(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS);
471            if (result.bundle != null) {
472                // This is the final destination for the Bundle.
473                result.bundle.setDefusable(true);
474
475                final T data = result.bundle.getParcelable(
476                        RESULT_RECEIVER_CONTROLLER_KEY);
477                if (data != null) {
478                    return data;
479                }
480            }
481            Slog.e(TAG, "no controller energy info supplied for " + receiver.getName());
482        } catch (TimeoutException e) {
483            Slog.w(TAG, "timeout reading " + receiver.getName() + " stats");
484        }
485        return null;
486    }
487
488    /**
489     *
490     * Pulls wifi controller activity energy info from WiFiManager
491     */
492    @Override // Binder call
493    public StatsLogEventWrapper[] pullData(int tagId) {
494        enforceCallingPermission();
495        if (DEBUG)
496            Slog.d(TAG, "Pulling " + tagId);
497
498        switch (tagId) {
499            case StatsLog.WIFI_BYTES_TRANSFER: {
500                long token = Binder.clearCallingIdentity();
501                try {
502                    // TODO: Consider caching the following call to get BatteryStatsInternal.
503                    BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
504                    String[] ifaces = bs.getWifiIfaces();
505                    if (ifaces.length == 0) {
506                        return null;
507                    }
508                    NetworkStatsFactory nsf = new NetworkStatsFactory();
509                    // Combine all the metrics per Uid into one record.
510                    NetworkStats stats = nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces,
511                            NetworkStats.TAG_NONE, null).groupedByUid();
512                    return addNetworkStats(tagId, stats, false);
513                } catch (java.io.IOException e) {
514                    Slog.e(TAG, "Pulling netstats for wifi bytes has error", e);
515                } finally {
516                    Binder.restoreCallingIdentity(token);
517                }
518                break;
519            }
520            case StatsLog.MOBILE_BYTES_TRANSFER: {
521                long token = Binder.clearCallingIdentity();
522                try {
523                    BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
524                    String[] ifaces = bs.getMobileIfaces();
525                    if (ifaces.length == 0) {
526                        return null;
527                    }
528                    NetworkStatsFactory nsf = new NetworkStatsFactory();
529                    // Combine all the metrics per Uid into one record.
530                    NetworkStats stats = nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces,
531                        NetworkStats.TAG_NONE, null).groupedByUid();
532                    return addNetworkStats(tagId, stats, false);
533                } catch (java.io.IOException e) {
534                    Slog.e(TAG, "Pulling netstats for mobile bytes has error", e);
535                } finally {
536                    Binder.restoreCallingIdentity(token);
537                }
538                break;
539            }
540            case StatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG: {
541                long token = Binder.clearCallingIdentity();
542                try {
543                    BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
544                    String[] ifaces = bs.getWifiIfaces();
545                    if (ifaces.length == 0) {
546                        return null;
547                    }
548                    NetworkStatsFactory nsf = new NetworkStatsFactory();
549                    NetworkStats stats = rollupNetworkStatsByFGBG(
550                            nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces,
551                            NetworkStats.TAG_NONE, null));
552                    return addNetworkStats(tagId, stats, true);
553                } catch (java.io.IOException e) {
554                    Slog.e(TAG, "Pulling netstats for wifi bytes w/ fg/bg has error", e);
555                } finally {
556                    Binder.restoreCallingIdentity(token);
557                }
558                break;
559            }
560            case StatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG: {
561                long token = Binder.clearCallingIdentity();
562                try {
563                    BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
564                    String[] ifaces = bs.getMobileIfaces();
565                    if (ifaces.length == 0) {
566                        return null;
567                    }
568                    NetworkStatsFactory nsf = new NetworkStatsFactory();
569                    NetworkStats stats = rollupNetworkStatsByFGBG(
570                            nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces,
571                            NetworkStats.TAG_NONE, null));
572                    return addNetworkStats(tagId, stats, true);
573                } catch (java.io.IOException e) {
574                    Slog.e(TAG, "Pulling netstats for mobile bytes w/ fg/bg has error", e);
575                } finally {
576                    Binder.restoreCallingIdentity(token);
577                }
578                break;
579            }
580            case StatsLog.KERNEL_WAKELOCK: {
581                final KernelWakelockStats wakelockStats =
582                        mKernelWakelockReader.readKernelWakelockStats(mTmpWakelockStats);
583                List<StatsLogEventWrapper> ret = new ArrayList();
584                for (Map.Entry<String, KernelWakelockStats.Entry> ent : wakelockStats.entrySet()) {
585                    String name = ent.getKey();
586                    KernelWakelockStats.Entry kws = ent.getValue();
587                    StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 4);
588                    e.writeString(name);
589                    e.writeInt(kws.mCount);
590                    e.writeInt(kws.mVersion);
591                    e.writeLong(kws.mTotalTime);
592                    ret.add(e);
593                }
594                return ret.toArray(new StatsLogEventWrapper[ret.size()]);
595            }
596            case StatsLog.CPU_TIME_PER_FREQ: {
597                List<StatsLogEventWrapper> ret = new ArrayList();
598                for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) {
599                    long[] clusterTimeMs = mKernelCpuSpeedReaders[cluster].readAbsolute();
600                    if (clusterTimeMs != null) {
601                        for (int speed = clusterTimeMs.length - 1; speed >= 0; --speed) {
602                            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 3);
603                            e.writeInt(cluster);
604                            e.writeInt(speed);
605                            e.writeLong(clusterTimeMs[speed]);
606                            ret.add(e);
607                        }
608                    }
609                }
610                return ret.toArray(new StatsLogEventWrapper[ret.size()]);
611            }
612            case StatsLog.WIFI_ACTIVITY_ENERGY_INFO: {
613                List<StatsLogEventWrapper> ret = new ArrayList();
614                long token = Binder.clearCallingIdentity();
615                if (mWifiManager == null) {
616                    mWifiManager = IWifiManager.Stub.asInterface(ServiceManager.getService(
617                            Context.WIFI_SERVICE));
618                }
619                if (mWifiManager != null) {
620                    try {
621                        SynchronousResultReceiver wifiReceiver = new SynchronousResultReceiver("wifi");
622                        mWifiManager.requestActivityInfo(wifiReceiver);
623                        final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver);
624                        StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 6);
625                        e.writeLong(wifiInfo.getTimeStamp());
626                        e.writeInt(wifiInfo.getStackState());
627                        e.writeLong(wifiInfo.getControllerTxTimeMillis());
628                        e.writeLong(wifiInfo.getControllerRxTimeMillis());
629                        e.writeLong(wifiInfo.getControllerIdleTimeMillis());
630                        e.writeLong(wifiInfo.getControllerEnergyUsed());
631                        ret.add(e);
632                        return ret.toArray(new StatsLogEventWrapper[ret.size()]);
633                    } catch (RemoteException e) {
634                        Slog.e(TAG, "Pulling wifiManager for wifi controller activity energy info has error", e);
635                    } finally {
636                        Binder.restoreCallingIdentity(token);
637                    }
638                }
639                break;
640            }
641            case StatsLog.MODEM_ACTIVITY_INFO: {
642                List<StatsLogEventWrapper> ret = new ArrayList();
643                long token = Binder.clearCallingIdentity();
644                if (mTelephony == null) {
645                    mTelephony = TelephonyManager.from(mContext);
646                }
647                if (mTelephony != null) {
648                    SynchronousResultReceiver modemReceiver = new SynchronousResultReceiver("telephony");
649                    mTelephony.requestModemActivityInfo(modemReceiver);
650                    final ModemActivityInfo modemInfo = awaitControllerInfo(modemReceiver);
651                    StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 6);
652                    e.writeLong(modemInfo.getTimestamp());
653                    e.writeLong(modemInfo.getSleepTimeMillis());
654                    e.writeLong(modemInfo.getIdleTimeMillis());
655                    e.writeLong(modemInfo.getTxTimeMillis()[0]);
656                    e.writeLong(modemInfo.getTxTimeMillis()[1]);
657                    e.writeLong(modemInfo.getTxTimeMillis()[2]);
658                    e.writeLong(modemInfo.getTxTimeMillis()[3]);
659                    e.writeLong(modemInfo.getTxTimeMillis()[4]);
660                    e.writeLong(modemInfo.getRxTimeMillis());
661                    e.writeLong(modemInfo.getEnergyUsed());
662                    ret.add(e);
663                    return ret.toArray(new StatsLogEventWrapper[ret.size()]);
664                }
665                break;
666            }
667            case StatsLog.CPU_SUSPEND_TIME: {
668                List<StatsLogEventWrapper> ret = new ArrayList();
669                StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 1);
670                e.writeLong(SystemClock.elapsedRealtime());
671                ret.add(e);
672                return ret.toArray(new StatsLogEventWrapper[ret.size()]);
673            }
674            case StatsLog.CPU_IDLE_TIME: {
675                List<StatsLogEventWrapper> ret = new ArrayList();
676                StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 1);
677                e.writeLong(SystemClock.uptimeMillis());
678                ret.add(e);
679                return ret.toArray(new StatsLogEventWrapper[ret.size()]);
680            }
681            case StatsLog.DISK_SPACE: {
682              List<StatsLogEventWrapper> ret = new ArrayList();
683              StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 3);
684              e.writeLong(mStatFsData.getAvailableBytes());
685              e.writeLong(mStatFsSystem.getAvailableBytes());
686              e.writeLong(mStatFsTemp.getAvailableBytes());
687              ret.add(e);
688              return ret.toArray(new StatsLogEventWrapper[ret.size()]);
689            }
690            case StatsLog.SYSTEM_UPTIME: {
691              List<StatsLogEventWrapper> ret = new ArrayList();
692              StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 1);
693              e.writeLong(SystemClock.uptimeMillis());
694              ret.add(e);
695              return ret.toArray(new StatsLogEventWrapper[ret.size()]);
696            }
697            default:
698                Slog.w(TAG, "No such tagId data as " + tagId);
699                return null;
700        }
701        return null;
702    }
703
704    @Override // Binder call
705    public void statsdReady() {
706        enforceCallingPermission();
707        if (DEBUG) Slog.d(TAG, "learned that statsdReady");
708        sayHiToStatsd(); // tell statsd that we're ready too and link to it
709        mContext.sendBroadcastAsUser(new Intent(StatsManager.ACTION_STATSD_STARTED),
710                UserHandle.SYSTEM,
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