Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 94726412 authored by Bookatz's avatar Bookatz
Browse files

Set up StatsCompanionService.java

Introduces StatsCompanionService.java and sets it up as a system service.

This service is a helper for statsd. It will be responsible for setting
and cancelling alarms related to polling stats and anomaly detection.

It currently does not have selinux permission, so must be started
manually (or with selinux temporarily disabled).

Test: disable enforcing selinux, then set and cancel alarms and make
sure they work.

Change-Id: I3bd73acdd998ee424696cce40965134c14220d8f
parent 5feb6244
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -270,6 +270,7 @@ LOCAL_SRC_FILES += \
	core/java/android/os/IRecoverySystemProgressListener.aidl \
	core/java/android/os/IRemoteCallback.aidl \
	core/java/android/os/ISchedulingPolicyService.aidl \
	core/java/android/os/IStatsCompanionService.aidl \
	core/java/android/os/IStatsManager.aidl \
	core/java/android/os/IThermalEventListener.aidl \
	core/java/android/os/IThermalService.aidl \
+7 −0
Original line number Diff line number Diff line
@@ -2991,6 +2991,7 @@ public abstract class Context {
            //@hide: CONTEXTHUB_SERVICE,
            SYSTEM_HEALTH_SERVICE,
            //@hide: INCIDENT_SERVICE,
            //@hide: STATS_COMPANION_SERVICE,
            COMPANION_DEVICE_SERVICE
    })
    @Retention(RetentionPolicy.SOURCE)
@@ -4019,6 +4020,12 @@ public abstract class Context {
     */
    public static final String INCIDENT_SERVICE = "incident";

    /**
     * Service to assist statsd in obtaining general stats.
     * @hide
     */
    public static final String STATS_COMPANION_SERVICE = "statscompanion";

    /**
     * Use with {@link #getSystemService} to retrieve a {@link
     * android.content.om.OverlayManager} for managing overlay packages.
+44 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.os;

/**
  * Binder interface to communicate with the Java-based statistics service helper.
  * {@hide}
  */
interface IStatsCompanionService {
    /**
    * Register an alarm for anomaly detection to fire at the given timestamp (ms since epoch).
    * If anomaly alarm had already been registered, it will be replaced with the new timestamp.
    * Uses AlarmManager.set API, so  if the timestamp is in the past, alarm fires immediately, and
    * alarm is inexact.
    */
    void setAnomalyAlarm(long timestampMs);
    /** Cancel any anomaly detection alarm. */
    void cancelAnomalyAlarm();

    /**
      * Register a repeating alarm for polling to fire at the given timestamp and every
      * intervalMs thereafter (in ms since epoch).
      * If polling alarm had already been registered, it will be replaced by new one.
      * Uses AlarmManager.setRepeating API, so if the timestamp is in past, alarm fires immediately,
      * and alarm is inexact.
      */
    void setPollingAlarms(long timestampMs, long intervalMs);
    /** Cancel any repeating polling alarm. */
    void cancelPollingAlarms();
}
+16 −0
Original line number Diff line number Diff line
@@ -3074,6 +3074,12 @@
    <permission android:name="android.permission.BATTERY_STATS"
        android:protectionLevel="signature|privileged|development" />

    <!--Allows an application to manage statscompanion.
    <p>Not for use by third-party applications.
         @hide -->
    <permission android:name="android.permission.STATSCOMPANION"
        android:protectionLevel="signature" />

    <!-- @SystemApi Allows an application to control the backup and restore process.
    <p>Not for use by third-party applications.
         @hide pending API council -->
@@ -3854,6 +3860,16 @@
            </intent-filter>
        </receiver>

        <receiver android:name="com.android.server.stats.StatsCompanionService$AnomalyAlarmReceiver"
                  android:permission="android.permission.STATSCOMPANION"
                  android:exported="false">
        </receiver>

        <receiver android:name="com.android.server.stats.StatsCompanionService$PollingAlarmReceiver"
                  android:permission="android.permission.STATSCOMPANION"
                  android:exported="false">
        </receiver>

        <service android:name="android.hardware.location.GeofenceHardwareService"
            android:permission="android.permission.LOCATION_HARDWARE"
            android:exported="false" />
+165 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.server.stats;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.IStatsCompanionService;
import android.os.IStatsManager;
import android.os.Process;
import android.os.ServiceManager;
import android.util.Slog;

import com.android.server.SystemService;

/**
 * Helper service for statsd (the native stats management service in cmds/statsd/).
 * Used for registering and receiving alarms on behalf of statsd.
 */
public class StatsCompanionService extends IStatsCompanionService.Stub {
    static final String TAG = "StatsCompanionService";
    static final boolean DEBUG = true;

    private final Context mContext;
    private final AlarmManager mAlarmManager;
    private final IStatsManager mStatsd;

    private final PendingIntent mAnomalyAlarmIntent;
    private final PendingIntent mPollingAlarmIntent;

    public final static class AnomalyAlarmReceiver extends BroadcastReceiver  {
        @Override
        public void onReceive(Context context, Intent intent) {
            Slog.i(TAG, "StatsCompanionService believes an anomaly has occurred.");
            // TODO: mStatsd.informAlarm(); // should be twoway so device won't sleep before acting?
            // AlarmManager releases its own wakelock here.
        }
    };

    public final static class PollingAlarmReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (DEBUG) Slog.d(TAG, "Time to poll something.");
            // TODO: mStatsd.poll(); // should be twoway so device won't sleep before acting?
            // AlarmManager releases its own wakelock here.
        }
    };

    public StatsCompanionService(Context context) {
        super();
        mContext = context;
        mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);

        mAnomalyAlarmIntent = PendingIntent.getBroadcast(mContext, 0,
                new Intent(mContext, AnomalyAlarmReceiver.class), 0);
        mPollingAlarmIntent = PendingIntent.getBroadcast(mContext, 0,
                new Intent(mContext, PollingAlarmReceiver.class), 0);

        mStatsd = getStatsdService();
    }

    /** Returns the statsd IBinder service */
    public static IStatsManager getStatsdService() {
        return IStatsManager.Stub.asInterface(ServiceManager.getService("statsd"));
    }

    public static final class Lifecycle extends SystemService {
        private StatsCompanionService mStatsCompanionService;

        public Lifecycle(Context context) {
            super(context);
        }

        @Override
        public void onStart() {
            mStatsCompanionService = new StatsCompanionService(getContext());
            try {
                publishBinderService(Context.STATS_COMPANION_SERVICE, mStatsCompanionService);
                if (DEBUG) Slog.d(TAG, "Published " + Context.STATS_COMPANION_SERVICE);
            } catch (Exception e) {
                Slog.e(TAG, "Failed to publishBinderService", e);
            }
        }
    }

    @Override // Binder call
    public void setAnomalyAlarm(long timestampMs) {
        enforceCallingPermission();
        if (DEBUG) Slog.d(TAG, "Setting anomaly alarm for " + timestampMs);
        final long callingToken = Binder.clearCallingIdentity();
        try {
            // using RTC, not RTC_WAKEUP, so if device is asleep, will only fire when it awakens.
            // This alarm is inexact, leaving its exactness completely up to the OS optimizations.
            // AlarmManager will automatically cancel any previous mAnomalyAlarmIntent alarm.
            mAlarmManager.set(AlarmManager.RTC, timestampMs, mAnomalyAlarmIntent);
        } finally {
            Binder.restoreCallingIdentity(callingToken);
        }
    }

    @Override // Binder call
    public void cancelAnomalyAlarm() {
        enforceCallingPermission();
        if (DEBUG) Slog.d(TAG, "Cancelling anomaly alarm");
        final long callingToken = Binder.clearCallingIdentity();
        try {
            mAlarmManager.cancel(mAnomalyAlarmIntent);
        } finally {
            Binder.restoreCallingIdentity(callingToken);
        }
    }

    @Override // Binder call
    public void setPollingAlarms(long timestampMs, long intervalMs) {
        enforceCallingPermission();
        if (DEBUG) Slog.d(TAG, "Setting polling alarm for " + timestampMs
                + " every " + intervalMs + "ms");
        final long callingToken = Binder.clearCallingIdentity();
        try {
            // using RTC, not RTC_WAKEUP, so if device is asleep, will only fire when it awakens.
            // This alarm is inexact, leaving its exactness completely up to the OS optimizations.
            // TODO: totally inexact means that stats per bucket could be quite off. Is this okay?
            mAlarmManager.setRepeating(AlarmManager.RTC, timestampMs, intervalMs,
                    mPollingAlarmIntent);
        } finally {
            Binder.restoreCallingIdentity(callingToken);
        }
    }

    @Override // Binder call
    public void cancelPollingAlarms() {
        enforceCallingPermission();
        if (DEBUG) Slog.d(TAG, "Cancelling polling alarm");
        final long callingToken = Binder.clearCallingIdentity();
        try {
            mAlarmManager.cancel(mPollingAlarmIntent);
        } finally {
            Binder.restoreCallingIdentity(callingToken);
        }
    }

    private void enforceCallingPermission() {
        if (Binder.getCallingPid() == Process.myPid()) {
            return;
        }
        mContext.enforceCallingPermission(android.Manifest.permission.STATSCOMPANION, null);
    }

}
Loading