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

Commit c6e64922 authored by Kweku Adams's avatar Kweku Adams
Browse files

Add initial EconomicPolicies.

The Economic Policy includes pricing information and daily ARC
requirements and suggestions.

Bug: 158300259
Test: Android builds
Change-Id: I40b674f7f9db5ba3c00d430eecd700e1d17f7c94
parent e233de4a
Loading
Loading
Loading
Loading
+121 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.tare;

import static com.android.server.tare.TareUtils.arcToNarc;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.ArrayMap;

/**
 * Policy defining pricing information and daily ARC requirements and suggestions for
 * AlarmManager.
 */
public class AlarmManagerEconomicPolicy extends EconomicPolicy {
    public static final String ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE =
            "ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE";
    public static final String ACTION_ALARM_WAKEUP_EXACT = "ALARM_WAKEUP_EXACT";
    public static final String ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE =
            "ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE";
    public static final String ACTION_ALARM_WAKEUP_INEXACT = "ALARM_WAKEUP_INEXACT";
    public static final String ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE =
            "ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE";
    public static final String ACTION_ALARM_NONWAKEUP_EXACT = "ALARM_NONWAKEUP_EXACT";
    public static final String ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE =
            "ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE";
    public static final String ACTION_ALARM_NONWAKEUP_INEXACT = "ALARM_NONWAKEUP_INEXACT";
    public static final String ACTION_ALARM_CLOCK = "ALARM_CLOCK";

    private final ArrayMap<String, Action> mActions = new ArrayMap<>();
    private final ArrayMap<String, Reward> mRewards = new ArrayMap<>();

    AlarmManagerEconomicPolicy(InternalResourceService irs) {
        super(irs);
        loadActions();
        loadRewards();
    }

    @Override
    long getMinSatiatedBalance(final int userId, @NonNull final String pkgName) {
        // TODO: take exemption into account
        return arcToNarc(160);
    }

    @Override
    long getMaxSatiatedBalance() {
        return arcToNarc(1440);
    }


    @Override
    long getMaxSatiatedCirculation() {
        return arcToNarc(52000);
    }

    @Nullable
    @Override
    Action getAction(@NonNull String actionName) {
        return mActions.get(actionName);
    }

    @Nullable
    @Override
    Reward getReward(@NonNull String rewardName) {
        return mRewards.get(rewardName);
    }

    private void loadActions() {
        mActions.put(ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE,
                new Action(ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE, arcToNarc(3), arcToNarc(5)));
        mActions.put(ACTION_ALARM_WAKEUP_EXACT,
                new Action(ACTION_ALARM_WAKEUP_EXACT, arcToNarc(3), arcToNarc(4)));
        mActions.put(ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE,
                new Action(ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE,
                        arcToNarc(3), arcToNarc(4)));
        mActions.put(ACTION_ALARM_WAKEUP_INEXACT,
                new Action(ACTION_ALARM_WAKEUP_INEXACT, arcToNarc(3), arcToNarc(3)));
        mActions.put(ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE,
                new Action(ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE,
                        arcToNarc(1), arcToNarc(3)));
        mActions.put(ACTION_ALARM_NONWAKEUP_EXACT,
                new Action(ACTION_ALARM_NONWAKEUP_EXACT, arcToNarc(1), arcToNarc(2)));
        mActions.put(ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE,
                new Action(ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE,
                        arcToNarc(1), arcToNarc(2)));
        mActions.put(ACTION_ALARM_NONWAKEUP_INEXACT,
                new Action(ACTION_ALARM_NONWAKEUP_INEXACT, arcToNarc(1), arcToNarc(1)));
        mActions.put(ACTION_ALARM_CLOCK,
                new Action(ACTION_ALARM_CLOCK, arcToNarc(5), arcToNarc(10)));
    }

    private void loadRewards() {
        mRewards.put(REWARD_TOP_ACTIVITY,
                new Reward(REWARD_TOP_ACTIVITY,
                        arcToNarc(0), /* .01 arcs */ arcToNarc(1) / 100, arcToNarc(500)));
        mRewards.put(REWARD_NOTIFICATION_SEEN,
                new Reward(REWARD_NOTIFICATION_SEEN, arcToNarc(3), arcToNarc(0), arcToNarc(60)));
        mRewards.put(REWARD_NOTIFICATION_INTERACTION,
                new Reward(REWARD_NOTIFICATION_INTERACTION,
                        arcToNarc(5), arcToNarc(0), arcToNarc(500)));
        mRewards.put(REWARD_WIDGET_INTERACTION,
                new Reward(REWARD_WIDGET_INTERACTION, arcToNarc(10), arcToNarc(0), arcToNarc(500)));
        mRewards.put(REWARD_OTHER_USER_INTERACTION,
                new Reward(REWARD_OTHER_USER_INTERACTION,
                        arcToNarc(10), arcToNarc(0), arcToNarc(500)));
    }
}
+117 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.tare;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.ArrayMap;
import android.util.ArraySet;


/** Combines all enabled policies into one. */
public class CompleteEconomicPolicy extends EconomicPolicy {
    private final ArraySet<EconomicPolicy> mEnabledEconomicPolicies = new ArraySet<>();
    /** Lazily populated set of actions covered by this policy. */
    private final ArrayMap<String, Action> mActions = new ArrayMap<>();
    /** Lazily populated set of rewards covered by this policy. */
    private final ArrayMap<String, Reward> mRewards = new ArrayMap<>();
    private final long mMaxSatiatedBalance;
    private final long mMaxSatiatedCirculation;

    CompleteEconomicPolicy(@NonNull InternalResourceService irs) {
        super(irs);
        mEnabledEconomicPolicies.add(new AlarmManagerEconomicPolicy(irs));
        mEnabledEconomicPolicies.add(new JobSchedulerEconomicPolicy(irs));

        long max = 0;
        for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
            max += mEnabledEconomicPolicies.valueAt(i).getMaxSatiatedBalance();
        }
        mMaxSatiatedBalance = max;

        max = 0;
        for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
            max += mEnabledEconomicPolicies.valueAt(i).getMaxSatiatedCirculation();
        }
        mMaxSatiatedCirculation = max;
    }

    @Override
    public long getMinSatiatedBalance(final int userId, @NonNull final String pkgName) {
        long min = 0;
        for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
            min += mEnabledEconomicPolicies.valueAt(i).getMinSatiatedBalance(userId, pkgName);
        }
        return min;
    }

    @Override
    public long getMaxSatiatedBalance() {
        return mMaxSatiatedBalance;
    }

    @Override
    public long getMaxSatiatedCirculation() {
        return mMaxSatiatedCirculation;
    }

    @Nullable
    @Override
    public Action getAction(@NonNull String actionName) {
        if (mActions.containsKey(actionName)) {
            return mActions.get(actionName);
        }

        long ctp = 0, price = 0;
        boolean exists = false;
        for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
            Action a = mEnabledEconomicPolicies.valueAt(i).getAction(actionName);
            if (a != null) {
                exists = true;
                ctp += a.costToProduce;
                price += a.basePrice;
            }
        }
        final Action action = exists ? new Action(actionName, ctp, price) : null;
        mActions.put(actionName, action);
        return action;
    }

    @Nullable
    @Override
    public Reward getReward(@NonNull String rewardName) {
        if (mRewards.containsKey(rewardName)) {
            return mRewards.get(rewardName);
        }

        long instantReward = 0, ongoingReward = 0, maxReward = 0;
        boolean exists = false;
        for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
            Reward r = mEnabledEconomicPolicies.valueAt(i).getReward(rewardName);
            if (r != null) {
                exists = true;
                instantReward += r.instantReward;
                ongoingReward += r.ongoingRewardPerSecond;
                maxReward += r.maxDailyReward;
            }
        }
        final Reward reward = exists
                ? new Reward(rewardName, instantReward, ongoingReward, maxReward) : null;
        mRewards.put(rewardName, reward);
        return reward;
    }
}
+128 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.tare;

import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringDef;
import android.util.IndentingPrintWriter;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * An EconomicPolicy includes pricing information and daily ARC requirements and suggestions.
 * Policies are defined per participating system service. This allows each service’s EconomicPolicy
 * to be isolated while allowing the core economic system to scale across policies to achieve a
 * logical system-wide value system.
 */
public abstract class EconomicPolicy {
    private static final String TAG = "TARE-" + EconomicPolicy.class.getSimpleName();

    // fixme ensure actions and rewards never use the same strings
    static final String REWARD_TOP_ACTIVITY = "REWARD_TOP_ACTIVITY";
    static final String REWARD_NOTIFICATION_SEEN = "REWARD_NOTIFICATION_SEEN";
    static final String REWARD_NOTIFICATION_INTERACTION = "REWARD_NOTIFICATION_INTERACTION";
    static final String REWARD_WIDGET_INTERACTION = "REWARD_WIDGET_INTERACTION";
    static final String REWARD_OTHER_USER_INTERACTION = "REWARD_OTHER_USER_INTERACTION";

    @StringDef({
            REWARD_TOP_ACTIVITY,
            REWARD_NOTIFICATION_SEEN,
            REWARD_NOTIFICATION_INTERACTION,
            REWARD_WIDGET_INTERACTION,
            REWARD_OTHER_USER_INTERACTION,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface UtilityReward {
    }

    static class Action {
        @NonNull
        public final String name;
        /**
         * How many ARCs the system says it takes to perform this action.
         */
        public final long costToProduce;
        /**
         * The base price to perform this action. If this is
         * less than the {@link #costToProduce}, then the system should not perform
         * the action unless a multiplier lowers the cost to produce.
         */
        public final long basePrice;

        Action(@NonNull String name, long costToProduce, long basePrice) {
            this.name = name;
            this.costToProduce = costToProduce;
            this.basePrice = basePrice;
        }
    }

    static class Reward {
        @NonNull
        @UtilityReward
        public final String name;
        public final long instantReward;
        /** Reward credited per second of ongoing activity. */
        public final long ongoingRewardPerSecond;
        /** The maximum amount an app can earn from this reward within a 24 hour period. */
        public final long maxDailyReward;

        Reward(@NonNull String name, long instantReward, long ongoingReward, long maxDailyReward) {
            this.name = name;
            this.instantReward = instantReward;
            this.ongoingRewardPerSecond = ongoingReward;
            this.maxDailyReward = maxDailyReward;
        }
    }

    EconomicPolicy(@NonNull InternalResourceService irs) {
    }

    @CallSuper
    void onSystemServicesReady() {
    }

    /**
     * Returns the minimum suggested balance an app should have when the device is at 100% battery.
     * This takes into account any exemptions the app may have.
     */
    abstract long getMinSatiatedBalance(int userId, @NonNull String pkgName);

    /**
     * Returns the maximum balance an app should have when the device is at 100% battery. This
     * exists to ensure that no single app accumulate all available resources and increases fairness
     * for all apps.
     */
    abstract long getMaxSatiatedBalance();

    /**
     * Returns the maximum number of narcs that should be in circulation at once when the device is
     * at 100% battery.
     */
    abstract long getMaxSatiatedCirculation();

    @Nullable
    abstract Action getAction(@NonNull String actionName);

    @Nullable
    abstract Reward getReward(@NonNull String rewardName);

    void dump(IndentingPrintWriter pw) {
    }
}
+120 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.tare;

import static com.android.server.tare.TareUtils.arcToNarc;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.ArrayMap;

/**
 * Policy defining pricing information and daily ARC requirements and suggestions for
 * JobScheduler.
 */
public class JobSchedulerEconomicPolicy extends EconomicPolicy {
    public static final String ACTION_JOB_MAX_START = "JOB_MAX_START";
    public static final String ACTION_JOB_MAX_RUNNING = "JOB_MAX_RUNNING";
    public static final String ACTION_JOB_HIGH_START = "JOB_HIGH_START";
    public static final String ACTION_JOB_HIGH_RUNNING = "JOB_HIGH_RUNNING";
    public static final String ACTION_JOB_DEFAULT_START = "JOB_DEFAULT_START";
    public static final String ACTION_JOB_DEFAULT_RUNNING = "JOB_DEFAULT_RUNNING";
    public static final String ACTION_JOB_LOW_START = "JOB_LOW_START";
    public static final String ACTION_JOB_LOW_RUNNING = "JOB_LOW_RUNNING";
    public static final String ACTION_JOB_MIN_START = "JOB_MIN_START";
    public static final String ACTION_JOB_MIN_RUNNING = "JOB_MIN_RUNNING";
    public static final String ACTION_JOB_TIMEOUT = "JOB_TIMEOUT";

    private final ArrayMap<String, Action> mActions = new ArrayMap<>();
    private final ArrayMap<String, Reward> mRewards = new ArrayMap<>();

    JobSchedulerEconomicPolicy(InternalResourceService irs) {
        super(irs);
        loadActions();
        loadRewards();
    }

    @Override
    long getMinSatiatedBalance(final int userId, @NonNull final String pkgName) {
        // TODO: incorporate time since usage
        return arcToNarc(2000);
    }

    @Override
    long getMaxSatiatedBalance() {
        return arcToNarc(60000);
    }

    @Override
    long getMaxSatiatedCirculation() {
        return arcToNarc(691200);
    }

    @Nullable
    @Override
    Action getAction(@NonNull String actionName) {
        return mActions.get(actionName);
    }

    @Nullable
    @Override
    Reward getReward(@NonNull String rewardName) {
        return mRewards.get(rewardName);
    }

    private void loadActions() {
        mActions.put(ACTION_JOB_MAX_START,
                new Action(ACTION_JOB_MAX_START, arcToNarc(3), arcToNarc(10)));
        mActions.put(ACTION_JOB_MAX_RUNNING,
                new Action(ACTION_JOB_MAX_RUNNING, arcToNarc(2), arcToNarc(5)));
        mActions.put(ACTION_JOB_HIGH_START,
                new Action(ACTION_JOB_HIGH_START, arcToNarc(3), arcToNarc(8)));
        mActions.put(ACTION_JOB_HIGH_RUNNING,
                new Action(ACTION_JOB_HIGH_RUNNING, arcToNarc(2), arcToNarc(4)));
        mActions.put(ACTION_JOB_DEFAULT_START,
                new Action(ACTION_JOB_DEFAULT_START, arcToNarc(3), arcToNarc(6)));
        mActions.put(ACTION_JOB_DEFAULT_RUNNING,
                new Action(ACTION_JOB_DEFAULT_RUNNING, arcToNarc(2), arcToNarc(3)));
        mActions.put(ACTION_JOB_LOW_START,
                new Action(ACTION_JOB_LOW_START, arcToNarc(3), arcToNarc(4)));
        mActions.put(ACTION_JOB_LOW_RUNNING,
                new Action(ACTION_JOB_LOW_RUNNING, arcToNarc(2), arcToNarc(2)));
        mActions.put(ACTION_JOB_MIN_START,
                new Action(ACTION_JOB_MIN_START, arcToNarc(3), arcToNarc(2)));
        mActions.put(ACTION_JOB_MIN_RUNNING,
                new Action(ACTION_JOB_MIN_RUNNING, arcToNarc(2), arcToNarc(1)));
        mActions.put(ACTION_JOB_TIMEOUT,
                new Action(ACTION_JOB_TIMEOUT, arcToNarc(30), arcToNarc(60)));
    }

    private void loadRewards() {
        mRewards.put(REWARD_TOP_ACTIVITY,
                new Reward(REWARD_TOP_ACTIVITY,
                        arcToNarc(0), /* .5 arcs */ arcToNarc(5) / 10, arcToNarc(15000)));
        mRewards.put(REWARD_NOTIFICATION_SEEN,
                new Reward(REWARD_NOTIFICATION_SEEN, arcToNarc(1), arcToNarc(0), arcToNarc(10)));
        mRewards.put(REWARD_NOTIFICATION_INTERACTION,
                new Reward(REWARD_NOTIFICATION_INTERACTION,
                        arcToNarc(5), arcToNarc(0), arcToNarc(5000)));
        mRewards.put(REWARD_WIDGET_INTERACTION,
                new Reward(REWARD_WIDGET_INTERACTION,
                        arcToNarc(10), arcToNarc(0), arcToNarc(5000)));
        mRewards.put(REWARD_OTHER_USER_INTERACTION,
                new Reward(REWARD_OTHER_USER_INTERACTION,
                        arcToNarc(10), arcToNarc(0), arcToNarc(5000)));
    }
}