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

Commit 9599cc5f authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

Tests for NetworkPolicyManager rule generation.

Verifies that policy changes trigger rule updates that respect current
foregroundActivities status.  Also verifies logic that promotes a UID
based on its most-foreground PID.  Verifies that policy changes result
in immediate rule changes.

Also verifies that BACKGROUND_DATA_SETTING_CHANGED broadcasts are sent
by policy changes.

Change-Id: I4fd0dad9e1dbccee2c5968244bb1814e6cb2c6e1
parent 1b861278
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -28,6 +28,8 @@ interface INetworkPolicyManager {
    void setUidPolicy(int uid, int policy);
    int getUidPolicy(int uid);

    boolean isUidForeground(int uid);

    void registerListener(INetworkPolicyListener listener);
    void unregisterListener(INetworkPolicyListener listener);

+9 −5
Original line number Diff line number Diff line
@@ -188,6 +188,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
        synchronized (mRulesLock) {
            oldPolicy = getUidPolicy(uid);
            mUidPolicy.put(uid, policy);
            updateRulesForUidL(uid);
        }

        // TODO: consider dispatching BACKGROUND_DATA_SETTING broadcast
@@ -272,10 +273,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
        }
    }

    private boolean isUidForegroundL(int uid) {
    @Override
    public boolean isUidForeground(int uid) {
        synchronized (mRulesLock) {
            // only really in foreground when screen is also on
            return mUidForeground.get(uid, false) && mScreenOn;
        }
    }

    /**
     * Foreground for PID changed; recompute foreground at UID level. If
@@ -328,7 +332,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {

    private void updateRulesForUidL(int uid) {
        final int uidPolicy = getUidPolicy(uid);
        final boolean uidForeground = isUidForegroundL(uid);
        final boolean uidForeground = isUidForeground(uid);

        // derive active rules based on policy and active state
        int uidRules = RULE_ALLOW_ALL;
+3 −1
Original line number Diff line number Diff line
@@ -24,6 +24,8 @@
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.BROADCAST_STICKY" />
    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
    <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
    <uses-permission android:name="android.permission.MANAGE_APP_TOKENS" />

    <application>
        <uses-library android:name="android.test.runner" />
+132 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2011 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;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;

import com.google.common.collect.Lists;
import com.google.common.util.concurrent.AbstractFuture;

import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Future;

/**
 * {@link ContextWrapper} that can attach listeners for upcoming
 * {@link Context#sendBroadcast(Intent)}.
 */
public class BroadcastInterceptingContext extends ContextWrapper {
    private static final String TAG = "WatchingContext";

    private final List<BroadcastInterceptor> mInterceptors = Lists.newArrayList();

    public class BroadcastInterceptor extends AbstractFuture<Intent> {
        private final BroadcastReceiver mReceiver;
        private final IntentFilter mFilter;

        public BroadcastInterceptor(BroadcastReceiver receiver, IntentFilter filter) {
            mReceiver = receiver;
            mFilter = filter;
        }

        public boolean dispatchBroadcast(Intent intent) {
            if (mFilter.match(getContentResolver(), intent, false, TAG) > 0) {
                if (mReceiver != null) {
                    final Context context = BroadcastInterceptingContext.this;
                    mReceiver.onReceive(context, intent);
                    return false;
                } else {
                    set(intent);
                    return true;
                }
            } else {
                return false;
            }
        }
    }

    public BroadcastInterceptingContext(Context base) {
        super(base);
    }

    public Future<Intent> nextBroadcastIntent(String action) {
        return nextBroadcastIntent(new IntentFilter(action));
    }

    public Future<Intent> nextBroadcastIntent(IntentFilter filter) {
        final BroadcastInterceptor interceptor = new BroadcastInterceptor(null, filter);
        synchronized (mInterceptors) {
            mInterceptors.add(interceptor);
        }
        return interceptor;
    }

    @Override
    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
        synchronized (mInterceptors) {
            mInterceptors.add(new BroadcastInterceptor(receiver, filter));
        }
        return null;
    }

    @Override
    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
            String broadcastPermission, Handler scheduler) {
        return registerReceiver(receiver, filter);
    }

    @Override
    public void unregisterReceiver(BroadcastReceiver receiver) {
        synchronized (mInterceptors) {
            final Iterator<BroadcastInterceptor> i = mInterceptors.iterator();
            while (i.hasNext()) {
                final BroadcastInterceptor interceptor = i.next();
                if (receiver.equals(interceptor.mReceiver)) {
                    i.remove();
                }
            }
        }
    }

    @Override
    public void sendBroadcast(Intent intent) {
        synchronized (mInterceptors) {
            final Iterator<BroadcastInterceptor> i = mInterceptors.iterator();
            while (i.hasNext()) {
                final BroadcastInterceptor interceptor = i.next();
                if (interceptor.dispatchBroadcast(intent)) {
                    i.remove();
                }
            }
        }
    }

    @Override
    public void sendStickyBroadcast(Intent intent) {
        sendBroadcast(intent);
    }

    @Override
    public void removeStickyBroadcast(Intent intent) {
        // ignored
    }
}
+272 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2011 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;

import static android.net.NetworkPolicyManager.POLICY_NONE;
import static android.net.NetworkPolicyManager.POLICY_REJECT_PAID_BACKGROUND;
import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
import static android.net.NetworkPolicyManager.RULE_REJECT_PAID;
import static org.easymock.EasyMock.capture;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.eq;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.expectLastCall;

import android.app.IActivityManager;
import android.app.IProcessObserver;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.INetworkPolicyListener;
import android.os.Binder;
import android.os.IPowerManager;
import android.test.AndroidTestCase;
import android.test.mock.MockPackageManager;
import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.Suppress;

import com.android.server.net.NetworkPolicyManagerService;

import org.easymock.Capture;
import org.easymock.EasyMock;

import java.util.concurrent.Future;

/**
 * Tests for {@link NetworkPolicyManagerService}.
 */
@LargeTest
public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
    private static final String TAG = "NetworkPolicyManagerServiceTest";

    private BroadcastInterceptingContext mServiceContext;

    private IActivityManager mActivityManager;
    private IPowerManager mPowerManager;
    private INetworkPolicyListener mPolicyListener;

    private NetworkPolicyManagerService mService;
    private IProcessObserver mProcessObserver;

    private Binder mStubBinder = new Binder();

    private static final int UID_A = 800;
    private static final int UID_B = 801;

    private static final int PID_1 = 400;
    private static final int PID_2 = 401;
    private static final int PID_3 = 402;

    @Override
    public void setUp() throws Exception {
        super.setUp();

        // intercept various broadcasts, and pretend that uids have packages
        mServiceContext = new BroadcastInterceptingContext(getContext()) {
            @Override
            public PackageManager getPackageManager() {
                return new MockPackageManager() {
                    @Override
                    public String[] getPackagesForUid(int uid) {
                        return new String[] { "com.example" };
                    }
                };
            }
        };

        mActivityManager = createMock(IActivityManager.class);
        mPowerManager = createMock(IPowerManager.class);
        mPolicyListener = createMock(INetworkPolicyListener.class);

        mService = new NetworkPolicyManagerService(
                mServiceContext, mActivityManager, mPowerManager);

        // RemoteCallbackList needs a binder to use as key
        expect(mPolicyListener.asBinder()).andReturn(mStubBinder).atLeastOnce();
        replay();
        mService.registerListener(mPolicyListener);
        verifyAndReset();

        // catch the registered IProcessObserver during systemReady()
        final Capture<IProcessObserver> processObserver = new Capture<IProcessObserver>();
        mActivityManager.registerProcessObserver(capture(processObserver));
        expectLastCall().atLeastOnce();

        // expect to answer screen status during systemReady()
        expect(mPowerManager.isScreenOn()).andReturn(true).atLeastOnce();

        replay();
        mService.systemReady();
        verifyAndReset();

        mProcessObserver = processObserver.getValue();

    }

    @Override
    public void tearDown() throws Exception {
        mServiceContext = null;

        mActivityManager = null;
        mPowerManager = null;
        mPolicyListener = null;

        mService = null;
        mProcessObserver = null;

        super.tearDown();
    }

    @Suppress
    public void testPolicyChangeTriggersBroadcast() throws Exception {
        mService.setUidPolicy(UID_A, POLICY_NONE);

        // change background policy and expect broadcast
        final Future<Intent> backgroundChanged = mServiceContext.nextBroadcastIntent(
                ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED);

        mService.setUidPolicy(UID_A, POLICY_REJECT_PAID_BACKGROUND);

        backgroundChanged.get();
    }

    public void testPidForegroundCombined() throws Exception {
        // push all uid into background
        mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
        mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, false);
        mProcessObserver.onForegroundActivitiesChanged(PID_3, UID_B, false);
        assertFalse(mService.isUidForeground(UID_A));
        assertFalse(mService.isUidForeground(UID_B));

        // push one of the shared pids into foreground
        mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, true);
        assertTrue(mService.isUidForeground(UID_A));
        assertFalse(mService.isUidForeground(UID_B));

        // and swap another uid into foreground
        mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, false);
        mProcessObserver.onForegroundActivitiesChanged(PID_3, UID_B, true);
        assertFalse(mService.isUidForeground(UID_A));
        assertTrue(mService.isUidForeground(UID_B));

        // push both pid into foreground
        mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true);
        mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, true);
        assertTrue(mService.isUidForeground(UID_A));

        // pull one out, should still be foreground
        mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
        assertTrue(mService.isUidForeground(UID_A));

        // pull final pid out, should now be background
        mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, false);
        assertFalse(mService.isUidForeground(UID_A));
    }

    public void testScreenChangesRules() throws Exception {
        // push strict policy for foreground uid, verify ALLOW rule
        expectRulesChanged(UID_A, RULE_ALLOW_ALL);
        replay();
        mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true);
        mService.setUidPolicy(UID_A, POLICY_REJECT_PAID_BACKGROUND);
        verifyAndReset();

        // now turn screen off and verify REJECT rule
        expect(mPowerManager.isScreenOn()).andReturn(false).atLeastOnce();
        expectRulesChanged(UID_A, RULE_REJECT_PAID);
        replay();
        mServiceContext.sendBroadcast(new Intent(Intent.ACTION_SCREEN_OFF));
        verifyAndReset();

        // and turn screen back on, verify ALLOW rule restored
        expect(mPowerManager.isScreenOn()).andReturn(true).atLeastOnce();
        expectRulesChanged(UID_A, RULE_ALLOW_ALL);
        replay();
        mServiceContext.sendBroadcast(new Intent(Intent.ACTION_SCREEN_ON));
        verifyAndReset();
    }

    public void testPolicyNone() throws Exception {
        // POLICY_NONE should RULE_ALLOW in foreground
        expectRulesChanged(UID_A, RULE_ALLOW_ALL);
        replay();
        mService.setUidPolicy(UID_A, POLICY_NONE);
        mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true);
        verifyAndReset();

        // POLICY_NONE should RULE_ALLOW in background
        expectRulesChanged(UID_A, RULE_ALLOW_ALL);
        replay();
        mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
        verifyAndReset();
    }

    public void testPolicyReject() throws Exception {
        // POLICY_REJECT should RULE_ALLOW in background
        expectRulesChanged(UID_A, RULE_REJECT_PAID);
        replay();
        mService.setUidPolicy(UID_A, POLICY_REJECT_PAID_BACKGROUND);
        verifyAndReset();

        // POLICY_REJECT should RULE_ALLOW in foreground
        expectRulesChanged(UID_A, RULE_ALLOW_ALL);
        replay();
        mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true);
        verifyAndReset();

        // POLICY_REJECT should RULE_REJECT in background
        expectRulesChanged(UID_A, RULE_REJECT_PAID);
        replay();
        mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
        verifyAndReset();
    }

    public void testPolicyRejectAddRemove() throws Exception {
        // POLICY_NONE should have RULE_ALLOW in background
        expectRulesChanged(UID_A, RULE_ALLOW_ALL);
        replay();
        mService.setUidPolicy(UID_A, POLICY_NONE);
        mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
        verifyAndReset();

        // adding POLICY_REJECT should cause RULE_REJECT
        expectRulesChanged(UID_A, RULE_REJECT_PAID);
        replay();
        mService.setUidPolicy(UID_A, POLICY_REJECT_PAID_BACKGROUND);
        verifyAndReset();

        // removing POLICY_REJECT should return us to RULE_ALLOW
        expectRulesChanged(UID_A, RULE_ALLOW_ALL);
        replay();
        mService.setUidPolicy(UID_A, POLICY_NONE);
        verifyAndReset();
    }

    private void expectRulesChanged(int uid, int policy) throws Exception {
        mPolicyListener.onRulesChanged(eq(uid), eq(policy));
        expectLastCall().atLeastOnce();
    }

    private void replay() {
        EasyMock.replay(mActivityManager, mPowerManager, mPolicyListener);
    }

    private void verifyAndReset() {
        EasyMock.verify(mActivityManager, mPowerManager, mPolicyListener);
        EasyMock.reset(mActivityManager, mPowerManager, mPolicyListener);
    }
}
Loading