blob: b4ac987fc541cc40c922433c81bc74c0abced4b5 [file] [log] [blame]
/*
* 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.content.Intent.ACTION_UID_REMOVED;
import static android.content.Intent.EXTRA_UID;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.NetworkPolicyManager.POLICY_NONE;
import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
import static android.net.NetworkTemplate.MATCH_WIFI;
import static org.easymock.EasyMock.anyInt;
import static org.easymock.EasyMock.aryEq;
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 static org.easymock.EasyMock.isA;
import android.app.IActivityManager;
import android.app.INotificationManager;
import android.app.IProcessObserver;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.net.ConnectivityManager;
import android.net.IConnectivityManager;
import android.net.INetworkPolicyListener;
import android.net.INetworkStatsService;
import android.net.LinkProperties;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkPolicy;
import android.net.NetworkState;
import android.net.NetworkStats;
import android.net.NetworkTemplate;
import android.os.Binder;
import android.os.INetworkManagementService;
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 android.text.format.Time;
import android.util.TrustedTime;
import com.android.server.net.NetworkPolicyManagerService;
import com.google.common.util.concurrent.AbstractFuture;
import org.easymock.Capture;
import org.easymock.EasyMock;
import org.easymock.IAnswer;
import java.io.File;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* Tests for {@link NetworkPolicyManagerService}.
*/
@LargeTest
public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
private static final String TAG = "NetworkPolicyManagerServiceTest";
private static final long TEST_START = 1194220800000L;
private static final String TEST_IFACE = "test0";
private static NetworkTemplate sTemplateWifi = new NetworkTemplate(MATCH_WIFI, null);
private BroadcastInterceptingContext mServiceContext;
private File mPolicyDir;
private IActivityManager mActivityManager;
private IPowerManager mPowerManager;
private INetworkStatsService mStatsService;
private INetworkManagementService mNetworkManagement;
private INetworkPolicyListener mPolicyListener;
private TrustedTime mTime;
private IConnectivityManager mConnManager;
private INotificationManager mNotifManager;
private NetworkPolicyManagerService mService;
private IProcessObserver mProcessObserver;
private Binder mStubBinder = new Binder();
private static final int UID_A = android.os.Process.FIRST_APPLICATION_UID + 800;
private static final int UID_B = android.os.Process.FIRST_APPLICATION_UID + 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" };
}
@Override
public PackageInfo getPackageInfo(String packageName, int flags) {
final PackageInfo info = new PackageInfo();
final Signature signature;
if ("android".equals(packageName)) {
signature = new Signature("F00D");
} else {
signature = new Signature("DEAD");
}
info.signatures = new Signature[] { signature };
return info;
}
};
}
};
mPolicyDir = getContext().getFilesDir();
for (File file : mPolicyDir.listFiles()) {
file.delete();
}
mActivityManager = createMock(IActivityManager.class);
mPowerManager = createMock(IPowerManager.class);
mStatsService = createMock(INetworkStatsService.class);
mNetworkManagement = createMock(INetworkManagementService.class);
mPolicyListener = createMock(INetworkPolicyListener.class);
mTime = createMock(TrustedTime.class);
mConnManager = createMock(IConnectivityManager.class);
mNotifManager = createMock(INotificationManager.class);
mService = new NetworkPolicyManagerService(
mServiceContext, mActivityManager, mPowerManager, mStatsService,
mNetworkManagement, mTime, mPolicyDir);
mService.bindConnectivityManager(mConnManager);
mService.bindNotificationManager(mNotifManager);
// 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();
expectTime(System.currentTimeMillis());
// default behavior is background data enabled
expect(mConnManager.getBackgroundDataSetting()).andReturn(true);
replay();
mService.systemReady();
verifyAndReset();
mProcessObserver = processObserver.getValue();
}
@Override
public void tearDown() throws Exception {
for (File file : mPolicyDir.listFiles()) {
file.delete();
}
mServiceContext = null;
mPolicyDir = null;
mActivityManager = null;
mPowerManager = null;
mStatsService = null;
mPolicyListener = null;
mTime = 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_METERED_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 {
Future<Void> future;
expectSetUidNetworkRules(UID_A, false);
future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true);
future.get();
verifyAndReset();
// push strict policy for foreground uid, verify ALLOW rule
expectSetUidNetworkRules(UID_A, false);
future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
mService.setUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND);
future.get();
verifyAndReset();
// now turn screen off and verify REJECT rule
expect(mPowerManager.isScreenOn()).andReturn(false).atLeastOnce();
expectSetUidNetworkRules(UID_A, true);
future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
replay();
mServiceContext.sendBroadcast(new Intent(Intent.ACTION_SCREEN_OFF));
future.get();
verifyAndReset();
// and turn screen back on, verify ALLOW rule restored
expect(mPowerManager.isScreenOn()).andReturn(true).atLeastOnce();
expectSetUidNetworkRules(UID_A, false);
future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
mServiceContext.sendBroadcast(new Intent(Intent.ACTION_SCREEN_ON));
future.get();
verifyAndReset();
}
public void testPolicyNone() throws Exception {
Future<Void> future;
expectSetUidNetworkRules(UID_A, false);
future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true);
future.get();
verifyAndReset();
// POLICY_NONE should RULE_ALLOW in foreground
expectSetUidNetworkRules(UID_A, false);
future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
mService.setUidPolicy(UID_A, POLICY_NONE);
future.get();
verifyAndReset();
// POLICY_NONE should RULE_ALLOW in background
expectSetUidNetworkRules(UID_A, false);
future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
future.get();
verifyAndReset();
}
public void testPolicyReject() throws Exception {
Future<Void> future;
// POLICY_REJECT should RULE_ALLOW in background
expectSetUidNetworkRules(UID_A, true);
future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
replay();
mService.setUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND);
future.get();
verifyAndReset();
// POLICY_REJECT should RULE_ALLOW in foreground
expectSetUidNetworkRules(UID_A, false);
future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true);
future.get();
verifyAndReset();
// POLICY_REJECT should RULE_REJECT in background
expectSetUidNetworkRules(UID_A, true);
future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
replay();
mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
future.get();
verifyAndReset();
}
public void testPolicyRejectAddRemove() throws Exception {
Future<Void> future;
// POLICY_NONE should have RULE_ALLOW in background
expectSetUidNetworkRules(UID_A, false);
future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
mService.setUidPolicy(UID_A, POLICY_NONE);
future.get();
verifyAndReset();
// adding POLICY_REJECT should cause RULE_REJECT
expectSetUidNetworkRules(UID_A, true);
future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
replay();
mService.setUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND);
future.get();
verifyAndReset();
// removing POLICY_REJECT should return us to RULE_ALLOW
expectSetUidNetworkRules(UID_A, false);
future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
mService.setUidPolicy(UID_A, POLICY_NONE);
future.get();
verifyAndReset();
}
public void testLastCycleBoundaryThisMonth() throws Exception {
// assume cycle day of "5th", which should be in same month
final long currentTime = parseTime("2007-11-14T00:00:00.000Z");
final long expectedCycle = parseTime("2007-11-05T00:00:00.000Z");
final NetworkPolicy policy = new NetworkPolicy(sTemplateWifi, 5, 1024L, 1024L);
final long actualCycle = computeLastCycleBoundary(currentTime, policy);
assertEquals(expectedCycle, actualCycle);
}
public void testLastCycleBoundaryLastMonth() throws Exception {
// assume cycle day of "20th", which should be in last month
final long currentTime = parseTime("2007-11-14T00:00:00.000Z");
final long expectedCycle = parseTime("2007-10-20T00:00:00.000Z");
final NetworkPolicy policy = new NetworkPolicy(sTemplateWifi, 20, 1024L, 1024L);
final long actualCycle = computeLastCycleBoundary(currentTime, policy);
assertEquals(expectedCycle, actualCycle);
}
public void testLastCycleBoundaryThisMonthFebruary() throws Exception {
// assume cycle day of "30th" in february; should go to january
final long currentTime = parseTime("2007-02-14T00:00:00.000Z");
final long expectedCycle = parseTime("2007-01-30T00:00:00.000Z");
final NetworkPolicy policy = new NetworkPolicy(sTemplateWifi, 30, 1024L, 1024L);
final long actualCycle = computeLastCycleBoundary(currentTime, policy);
assertEquals(expectedCycle, actualCycle);
}
public void testLastCycleBoundaryLastMonthFebruary() throws Exception {
// assume cycle day of "30th" in february, which should clamp
final long currentTime = parseTime("2007-03-14T00:00:00.000Z");
final long expectedCycle = parseTime("2007-03-01T00:00:00.000Z");
final NetworkPolicy policy = new NetworkPolicy(sTemplateWifi, 30, 1024L, 1024L);
final long actualCycle = computeLastCycleBoundary(currentTime, policy);
assertEquals(expectedCycle, actualCycle);
}
public void testNetworkPolicyAppliedCycleLastMonth() throws Exception {
long elapsedRealtime = 0;
NetworkState[] state = null;
NetworkStats stats = null;
Future<Void> future;
final long TIME_FEB_15 = 1171497600000L;
final long TIME_MAR_10 = 1173484800000L;
final int CYCLE_DAY = 15;
// first, pretend that wifi network comes online. no policy active,
// which means we shouldn't push limit to interface.
state = new NetworkState[] { buildWifi() };
expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
expectTime(TIME_MAR_10 + elapsedRealtime);
future = expectMeteredIfacesChanged();
replay();
mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
future.get();
verifyAndReset();
// now change cycle to be on 15th, and test in early march, to verify we
// pick cycle day in previous month.
expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
expectTime(TIME_MAR_10 + elapsedRealtime);
// pretend that 512 bytes total have happened
stats = new NetworkStats(elapsedRealtime, 1)
.addEntry(TEST_IFACE, UID_ALL, TAG_NONE, 256L, 256L);
expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, TIME_MAR_10))
.andReturn(stats).atLeastOnce();
// TODO: consider making strongly ordered mock
expectRemoveInterfaceQuota(TEST_IFACE);
expectSetInterfaceQuota(TEST_IFACE, 1536L);
expectClearNotifications();
future = expectMeteredIfacesChanged(TEST_IFACE);
replay();
setNetworkPolicies(new NetworkPolicy(sTemplateWifi, CYCLE_DAY, 1024L, 2048L));
future.get();
verifyAndReset();
}
public void testUidRemovedPolicyCleared() throws Exception {
Future<Void> future;
// POLICY_REJECT should RULE_REJECT in background
expectSetUidNetworkRules(UID_A, true);
future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
replay();
mService.setUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND);
future.get();
verifyAndReset();
// uninstall should clear RULE_REJECT
expectSetUidNetworkRules(UID_A, false);
future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
final Intent intent = new Intent(ACTION_UID_REMOVED);
intent.putExtra(EXTRA_UID, UID_A);
mServiceContext.sendBroadcast(intent);
future.get();
verifyAndReset();
}
private static long parseTime(String time) {
final Time result = new Time();
result.parse3339(time);
return result.toMillis(true);
}
private void setNetworkPolicies(NetworkPolicy... policies) {
mService.setNetworkPolicies(policies);
}
private static NetworkState buildWifi() {
final NetworkInfo info = new NetworkInfo(TYPE_WIFI, 0, null, null);
info.setDetailedState(DetailedState.CONNECTED, null, null);
final LinkProperties prop = new LinkProperties();
prop.setInterfaceName(TEST_IFACE);
return new NetworkState(info, prop, null);
}
private void expectTime(long currentTime) throws Exception {
expect(mTime.forceRefresh()).andReturn(false).anyTimes();
expect(mTime.hasCache()).andReturn(true).anyTimes();
expect(mTime.currentTimeMillis()).andReturn(currentTime).anyTimes();
expect(mTime.getCacheAge()).andReturn(0L).anyTimes();
expect(mTime.getCacheCertainty()).andReturn(0L).anyTimes();
}
private void expectClearNotifications() throws Exception {
mNotifManager.cancelNotificationWithTag(isA(String.class), isA(String.class), anyInt());
expectLastCall().anyTimes();
}
private void expectSetInterfaceQuota(String iface, long quota) throws Exception {
mNetworkManagement.setInterfaceQuota(iface, quota);
expectLastCall().atLeastOnce();
}
private void expectRemoveInterfaceQuota(String iface) throws Exception {
mNetworkManagement.removeInterfaceQuota(iface);
expectLastCall().atLeastOnce();
}
private void expectSetUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces)
throws Exception {
mNetworkManagement.setUidNetworkRules(uid, rejectOnQuotaInterfaces);
expectLastCall().atLeastOnce();
}
private Future<Void> expectRulesChanged(int uid, int policy) throws Exception {
final FutureAnswer future = new FutureAnswer();
mPolicyListener.onUidRulesChanged(eq(uid), eq(policy));
expectLastCall().andAnswer(future);
return future;
}
private Future<Void> expectMeteredIfacesChanged(String... ifaces) throws Exception {
final FutureAnswer future = new FutureAnswer();
mPolicyListener.onMeteredIfacesChanged(aryEq(ifaces));
expectLastCall().andAnswer(future);
return future;
}
private static class FutureAnswer extends AbstractFuture<Void> implements IAnswer<Void> {
@Override
public Void get() throws InterruptedException, ExecutionException {
try {
return get(5, TimeUnit.SECONDS);
} catch (TimeoutException e) {
throw new RuntimeException(e);
}
}
@Override
public Void answer() {
set(null);
return null;
}
}
private void replay() {
EasyMock.replay(mActivityManager, mPowerManager, mStatsService, mPolicyListener,
mNetworkManagement, mTime, mConnManager, mNotifManager);
}
private void verifyAndReset() {
EasyMock.verify(mActivityManager, mPowerManager, mStatsService, mPolicyListener,
mNetworkManagement, mTime, mConnManager, mNotifManager);
EasyMock.reset(mActivityManager, mPowerManager, mStatsService, mPolicyListener,
mNetworkManagement, mTime, mConnManager, mNotifManager);
}
}