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

Commit 8dca36dc authored by Xiaohui Chen's avatar Xiaohui Chen
Browse files

system_server: optimize app idle parole state change

Currently when app idle parole state changes, all idle apps' states
are updated one by one including firewall modifications which are
very expensive.  This optimization gets rid of individual firewall
rule changes and makes sure we only modify the firewall once at child
chain level.

BUG: 21446713
Change-Id: Iafc415fe0bc127826fe17894d4fedcf1755cb17d
parent 20fab81a
Loading
Loading
Loading
Loading
+9 −2
Original line number Diff line number Diff line
@@ -76,6 +76,12 @@ public abstract class UsageStatsManagerInternal {
     */
    public abstract boolean isAppIdle(String packageName, int userId);

    /**
     * @return True if currently app idle parole mode is on.  This means all idle apps are allow to
     * run for a short period of time.
     */
    public abstract boolean isAppIdleParoleOn();

    /**
     * Sets up a listener for changes to packages being accessed.
     * @param listener A listener within the system process.
@@ -90,8 +96,9 @@ public abstract class UsageStatsManagerInternal {
    public abstract void removeAppIdleStateChangeListener(
            AppIdleStateChangeListener listener);

    public interface AppIdleStateChangeListener {
        void onAppIdleStateChanged(String packageName, int userId, boolean idle);
    public static abstract class AppIdleStateChangeListener {
        public abstract void onAppIdleStateChanged(String packageName, int userId, boolean idle);
        public abstract void onParoleStateChanged(boolean isParoleOn);
    }

}
+80 −63
Original line number Diff line number Diff line
@@ -214,9 +214,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub
     */
    @GuardedBy("mQuotaLock")
    private SparseIntArray mUidFirewallDozableRules = new SparseIntArray();

    private boolean mStandbyChainEnabled = false;
    private boolean mDozableChainEnabled = false;
    /** Set of states for the child firewall chains. True if the chain is active. */
    @GuardedBy("mQuotaLock")
    final SparseBooleanArray mFirewallChainStates = new SparseBooleanArray();

    private Object mIdleTimerLock = new Object();
    /** Set of interfaces with active idle timers. */
@@ -307,9 +307,6 @@ public class NetworkManagementService extends INetworkManagementService.Stub
    }

    public void systemReady() {
        // init firewall states
        mDozableChainEnabled = false;
        mStandbyChainEnabled = true;
        prepareNativeDaemon();
        if (DBG) Slog.d(TAG, "Prepared");
    }
@@ -611,7 +608,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub
                            uidFirewallRules.valueAt(i));
                }
            }
            if (mStandbyChainEnabled) {
            if (mFirewallChainStates.get(FIREWALL_CHAIN_STANDBY)) {
                setFirewallChainEnabled(FIREWALL_CHAIN_STANDBY, true);
            }

@@ -625,7 +622,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub
                            uidFirewallRules.valueAt(i));
                }
            }
            if (mDozableChainEnabled) {
            if (mFirewallChainStates.get(FIREWALL_CHAIN_DOZABLE)) {
                setFirewallChainEnabled(FIREWALL_CHAIN_DOZABLE, true);
            }
        }
@@ -2013,17 +2010,23 @@ public class NetworkManagementService extends INetworkManagementService.Stub
    @Override
    public void setFirewallChainEnabled(int chain, boolean enable) {
        enforceSystemUid();
        synchronized (mQuotaLock) {
            if (mFirewallChainStates.indexOfKey(chain) >= 0 &&
                    mFirewallChainStates.get(chain) == enable) {
                // All is the same, nothing to do.
                return;
            }
            mFirewallChainStates.put(chain, enable);

            final String operation = enable ? "enable_chain" : "disable_chain";
            try {
                String chainName;
                switch(chain) {
                    case FIREWALL_CHAIN_STANDBY:
                        chainName = FIREWALL_CHAIN_NAME_STANDBY;
                    mStandbyChainEnabled = enable;
                        break;
                    case FIREWALL_CHAIN_DOZABLE:
                        chainName = FIREWALL_CHAIN_NAME_DOZABLE;
                    mDozableChainEnabled = enable;
                        break;
                    default:
                        throw new IllegalArgumentException("Bad child chain: " + chain);
@@ -2033,6 +2036,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub
                throw e.rethrowAsParcelableException();
            }
        }
    }

    private int getFirewallType(int chain) {
        switch (chain) {
@@ -2048,6 +2052,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub
    @Override
    public void setFirewallUidRules(int chain, int[] uids, int[] rules) {
        enforceSystemUid();
        synchronized (mQuotaLock) {
            SparseIntArray uidFirewallRules = getUidFirewallRules(chain);
            SparseIntArray newRules = new SparseIntArray();
            // apply new set of rules
@@ -2071,6 +2076,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub
                setFirewallUidRuleInternal(chain, uid, FIREWALL_RULE_DEFAULT);
            }
        }
    }

    @Override
    public void setFirewallUidRule(int chain, int uid, int rule) {
@@ -2094,6 +2100,26 @@ public class NetworkManagementService extends INetworkManagementService.Stub
            }

            try {
                String ruleName = getFirewallRuleName(chain, rule);
                String oldRuleName = getFirewallRuleName(chain, oldUidFirewallRule);

                if (rule == NetworkPolicyManager.FIREWALL_RULE_DEFAULT) {
                    uidFirewallRules.delete(uid);
                } else {
                    uidFirewallRules.put(uid, rule);
                }

                if (!ruleName.equals(oldRuleName)) {
                    mConnector.execute("firewall", "set_uid_rule", getFirewallChainName(chain), uid,
                            ruleName);
                }
            } catch (NativeDaemonConnectorException e) {
                throw e.rethrowAsParcelableException();
            }
        }
    }

    private @NonNull String getFirewallRuleName(int chain, int rule) {
        String ruleName;
        if (getFirewallType(chain) == FIREWALL_TYPE_WHITELIST) {
            if (rule == NetworkPolicyManager.FIREWALL_RULE_ALLOW) {
@@ -2108,18 +2134,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub
                ruleName = "allow";
            }
        }

                if (rule == NetworkPolicyManager.FIREWALL_RULE_DEFAULT) {
                    uidFirewallRules.delete(uid);
                } else {
                    uidFirewallRules.put(uid, rule);
                }
                mConnector.execute("firewall", "set_uid_rule", getFirewallChainName(chain), uid,
                        ruleName);
            } catch (NativeDaemonConnectorException e) {
                throw e.rethrowAsParcelableException();
            }
        }
        return ruleName;
    }

    private @NonNull SparseIntArray getUidFirewallRules(int chain) {
@@ -2272,7 +2287,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub
            pw.println("]");
        }

        pw.println("UID firewall standby chain enabled: " + mStandbyChainEnabled);
        pw.println("UID firewall standby chain enabled: " +
                mFirewallChainStates.get(FIREWALL_CHAIN_STANDBY));
        synchronized (mUidFirewallStandbyRules) {
            pw.print("UID firewall standby rule: [");
            final int size = mUidFirewallStandbyRules.size();
@@ -2285,7 +2301,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub
            pw.println("]");
        }

        pw.println("UID firewall dozable chain enabled: " + mDozableChainEnabled);
        pw.println("UID firewall dozable chain enabled: " +
                mFirewallChainStates.get(FIREWALL_CHAIN_DOZABLE));
        synchronized (mUidFirewallDozableRules) {
            pw.print("UID firewall dozable rule: [");
            final int size = mUidFirewallDozableRules.size();
+15 −36
Original line number Diff line number Diff line
@@ -18,11 +18,6 @@ package com.android.server.content;

import android.app.usage.UsageStatsManagerInternal;
import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
import android.os.UserHandle;

import com.android.server.LocalServices;
@@ -31,53 +26,32 @@ import com.android.server.LocalServices;
 * Helper to listen for app idle and charging status changes and restart backed off
 * sync operations.
 */
class AppIdleMonitor implements AppIdleStateChangeListener {
class AppIdleMonitor extends AppIdleStateChangeListener {

    private final SyncManager mSyncManager;
    private final UsageStatsManagerInternal mUsageStats;
    final BatteryManager mBatteryManager;
    /** Is the device currently plugged into power. */
    private boolean mPluggedIn;
    private boolean mAppIdleParoleOn;

    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            onPluggedIn(mBatteryManager.isCharging());
        }
    };

    AppIdleMonitor(SyncManager syncManager, Context context) {
    AppIdleMonitor(SyncManager syncManager) {
        mSyncManager = syncManager;
        mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
        mUsageStats.addAppIdleStateChangeListener(this);
        mBatteryManager = context.getSystemService(BatteryManager.class);
        mPluggedIn = isPowered();
        registerReceivers(context);
    }
        mAppIdleParoleOn = mUsageStats.isAppIdleParoleOn();

    private void registerReceivers(Context context) {
        // Monitor battery charging state
        IntentFilter filter = new IntentFilter(BatteryManager.ACTION_CHARGING);
        filter.addAction(BatteryManager.ACTION_DISCHARGING);
        context.registerReceiver(mReceiver, filter);
    }

    private boolean isPowered() {
        return mBatteryManager.isCharging();
        mUsageStats.addAppIdleStateChangeListener(this);
    }

    void onPluggedIn(boolean pluggedIn) {
        if (mPluggedIn == pluggedIn) {
    void setAppIdleParoleOn(boolean appIdleParoleOn) {
        if (mAppIdleParoleOn == appIdleParoleOn) {
            return;
        }
        mPluggedIn = pluggedIn;
        if (mPluggedIn) {
        mAppIdleParoleOn = appIdleParoleOn;
        if (mAppIdleParoleOn) {
            mSyncManager.onAppNotIdle(null, UserHandle.USER_ALL);
        }
    }

    boolean isAppIdle(String packageName, int userId) {
        return !mPluggedIn && mUsageStats.isAppIdle(packageName, userId);
        return !mAppIdleParoleOn && mUsageStats.isAppIdle(packageName, userId);
    }

    @Override
@@ -86,4 +60,9 @@ class AppIdleMonitor implements AppIdleStateChangeListener {
        if (idle) return;
        mSyncManager.onAppNotIdle(packageName, userId);
    }

    @Override
    public void onParoleStateChanged(boolean isParoleOn) {
        setAppIdleParoleOn(isParoleOn);
    }
}
+3 −2
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.server.content;
import android.accounts.Account;
import android.accounts.AccountAndUser;
import android.accounts.AccountManager;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.AppGlobals;
@@ -448,7 +449,7 @@ public class SyncManager {
        mSyncAlarmIntent = PendingIntent.getBroadcast(
                mContext, 0 /* ignored */, new Intent(ACTION_SYNC_ALARM), 0);

        mAppIdleMonitor = new AppIdleMonitor(this, mContext);
        mAppIdleMonitor = new AppIdleMonitor(this);

        IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
        context.registerReceiver(mConnectivityIntentReceiver, intentFilter);
@@ -1236,7 +1237,7 @@ public class SyncManager {
     * @param userId The user for which the package has become active. Can be USER_ALL if
     * the device just plugged in.
     */
    void onAppNotIdle(String packageName, int userId) {
    void onAppNotIdle(@Nullable String packageName, int userId) {
        synchronized (mSyncQueue) {
            // For all sync operations in sync queue, if marked as idle, compare with package name
            // and unmark. And clear backoff for the operation.
+48 −61
Original line number Diff line number Diff line
@@ -17,12 +17,7 @@
package com.android.server.job.controllers;

import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
import android.os.BatteryManagerInternal;
import android.util.Slog;

import com.android.server.LocalServices;
@@ -38,8 +33,7 @@ import java.util.ArrayList;
 * for a certain amount of time (maybe hours or days) are considered idle. When the app comes
 * out of idle state, it will be allowed to run scheduled jobs.
 */
public class AppIdleController extends StateController
        implements UsageStatsManagerInternal.AppIdleStateChangeListener {
public class AppIdleController extends StateController {

    private static final String LOG_TAG = "AppIdleController";
    private static final boolean DEBUG = false;
@@ -49,14 +43,7 @@ public class AppIdleController extends StateController
    private static volatile AppIdleController sController;
    final ArrayList<JobStatus> mTrackedTasks = new ArrayList<JobStatus>();
    private final UsageStatsManagerInternal mUsageStatsInternal;
    private final BatteryManager mBatteryManager;
    private boolean mPluggedIn;

    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override public void onReceive(Context context, Intent intent) {
            onPluggedIn(mBatteryManager.isCharging());
        }
    };
    boolean mAppIdleParoleOn;

    public static AppIdleController get(JobSchedulerService service) {
        synchronized (sCreationLock) {
@@ -70,17 +57,8 @@ public class AppIdleController extends StateController
    private AppIdleController(StateChangedListener stateChangedListener, Context context) {
        super(stateChangedListener, context);
        mUsageStatsInternal = LocalServices.getService(UsageStatsManagerInternal.class);
        mBatteryManager = context.getSystemService(BatteryManager.class);
        mPluggedIn = mBatteryManager.isCharging();
        mUsageStatsInternal.addAppIdleStateChangeListener(this);
        registerReceivers();
    }

    private void registerReceivers() {
        // Monitor battery charging state
        IntentFilter filter = new IntentFilter(BatteryManager.ACTION_CHARGING);
        filter.addAction(BatteryManager.ACTION_DISCHARGING);
        mContext.registerReceiver(mReceiver, filter);
        mAppIdleParoleOn = mUsageStatsInternal.isAppIdleParoleOn();
        mUsageStatsInternal.addAppIdleStateChangeListener(new AppIdleStateChangeListener());
    }

    @Override
@@ -88,7 +66,7 @@ public class AppIdleController extends StateController
        synchronized (mTrackedTasks) {
            mTrackedTasks.add(jobStatus);
            String packageName = jobStatus.job.getService().getPackageName();
            final boolean appIdle = !mPluggedIn && mUsageStatsInternal.isAppIdle(packageName,
            final boolean appIdle = !mAppIdleParoleOn && mUsageStatsInternal.isAppIdle(packageName,
                    jobStatus.getUserId());
            if (DEBUG) {
                Slog.d(LOG_TAG, "Start tracking, setting idle state of "
@@ -108,7 +86,7 @@ public class AppIdleController extends StateController
    @Override
    public void dumpControllerState(PrintWriter pw) {
        pw.println("AppIdle");
        pw.println("Plugged In: " + mPluggedIn);
        pw.println("Parole On: " + mAppIdleParoleOn);
        synchronized (mTrackedTasks) {
            for (JobStatus task : mTrackedTasks) {
                pw.print(task.job.getService().getPackageName());
@@ -119,57 +97,66 @@ public class AppIdleController extends StateController
        }
    }

    @Override
    public void onAppIdleStateChanged(String packageName, int userId, boolean idle) {
    void setAppIdleParoleOn(boolean isAppIdleParoleOn) {
        // Flag if any app's idle state has changed
        boolean changed = false;
        synchronized (mTrackedTasks) {
            // If currently plugged in, we don't care about app idle state
            if (mPluggedIn) {
            if (mAppIdleParoleOn == isAppIdleParoleOn) {
                return;
            }
            mAppIdleParoleOn = isAppIdleParoleOn;
            for (JobStatus task : mTrackedTasks) {
                if (task.job.getService().getPackageName().equals(packageName)
                        && task.getUserId() == userId) {
                    if (task.appNotIdleConstraintSatisfied.get() != !idle) {
                String packageName = task.job.getService().getPackageName();
                final boolean appIdle = !mAppIdleParoleOn && mUsageStatsInternal.isAppIdle(packageName,
                        task.getUserId());
                if (DEBUG) {
                            Slog.d(LOG_TAG, "App Idle state changed, setting idle state of "
                                    + packageName + " to " + idle);
                    Slog.d(LOG_TAG, "Setting idle state of " + packageName + " to " + appIdle);
                }
                        task.appNotIdleConstraintSatisfied.set(!idle);
                if (task.appNotIdleConstraintSatisfied.get() == appIdle) {
                    task.appNotIdleConstraintSatisfied.set(!appIdle);
                    changed = true;
                }
            }
        }
        }
        if (changed) {
            mStateChangedListener.onControllerStateChanged();
        }
    }

    void onPluggedIn(boolean pluggedIn) {
        // Flag if any app's idle state has changed
    private class AppIdleStateChangeListener
            extends UsageStatsManagerInternal.AppIdleStateChangeListener {
        @Override
        public void onAppIdleStateChanged(String packageName, int userId, boolean idle) {
            boolean changed = false;
            synchronized (mTrackedTasks) {
            if (mPluggedIn == pluggedIn) {
                if (mAppIdleParoleOn) {
                    return;
                }
            mPluggedIn = pluggedIn;
                for (JobStatus task : mTrackedTasks) {
                String packageName = task.job.getService().getPackageName();
                final boolean appIdle = !mPluggedIn && mUsageStatsInternal.isAppIdle(packageName,
                        task.getUserId());
                    if (task.job.getService().getPackageName().equals(packageName)
                            && task.getUserId() == userId) {
                        if (task.appNotIdleConstraintSatisfied.get() != !idle) {
                            if (DEBUG) {
                    Slog.d(LOG_TAG, "Plugged in " + pluggedIn + ", setting idle state of "
                            + packageName + " to " + appIdle);
                                Slog.d(LOG_TAG, "App Idle state changed, setting idle state of "
                                        + packageName + " to " + idle);
                            }
                if (task.appNotIdleConstraintSatisfied.get() == appIdle) {
                    task.appNotIdleConstraintSatisfied.set(!appIdle);
                            task.appNotIdleConstraintSatisfied.set(!idle);
                            changed = true;
                        }
                    }
                }
            }
            if (changed) {
                mStateChangedListener.onControllerStateChanged();
            }
        }

        @Override
        public void onParoleStateChanged(boolean isParoleOn) {
            if (DEBUG) {
                Slog.d(LOG_TAG, "Parole on: " + isParoleOn);
            }
            setAppIdleParoleOn(isParoleOn);
        }
    }
}
Loading