summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk1
-rw-r--r--core/java/android/net/INetworkPolicyManager.aidl34
-rw-r--r--core/java/android/net/NetworkPolicyManager.java68
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java127
-rw-r--r--services/java/com/android/server/am/ProcessRecord.java1
-rw-r--r--services/java/com/android/server/net/NetworkPolicyManagerService.java165
6 files changed, 360 insertions, 36 deletions
diff --git a/Android.mk b/Android.mk
index 9bd30fe..b8a48a4 100644
--- a/Android.mk
+++ b/Android.mk
@@ -109,6 +109,7 @@ LOCAL_SRC_FILES += \
core/java/android/net/IConnectivityManager.aidl \
core/java/android/net/INetworkManagementEventObserver.aidl \
core/java/android/net/IThrottleManager.aidl \
+ core/java/android/net/INetworkPolicyManager.aidl \
core/java/android/nfc/ILlcpConnectionlessSocket.aidl \
core/java/android/nfc/ILlcpServiceSocket.aidl \
core/java/android/nfc/ILlcpSocket.aidl \
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
new file mode 100644
index 0000000..fa6eae5
--- /dev/null
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -0,0 +1,34 @@
+/*
+ * 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 android.net;
+
+/**
+ * Interface that creates and modifies network policy rules.
+ *
+ * {@hide}
+ */
+interface INetworkPolicyManager {
+
+ void onForegroundActivitiesChanged(int uid, int pid, boolean foregroundActivities);
+ void onProcessDied(int uid, int pid);
+
+ void setUidPolicy(int uid, int policy);
+ int getUidPolicy(int uid);
+
+ // TODO: build API to surface stats details for settings UI
+
+}
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
new file mode 100644
index 0000000..2312bd9
--- /dev/null
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -0,0 +1,68 @@
+/*
+ * 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 android.net;
+
+import android.os.RemoteException;
+
+/**
+ * Manager for creating and modifying network policy rules.
+ *
+ * {@hide}
+ */
+public class NetworkPolicyManager {
+
+ /** No specific network policy, use system default. */
+ public static final int POLICY_NONE = 0x0;
+ /** Reject network usage when application in background. */
+ public static final int POLICY_REJECT_BACKGROUND = 0x1;
+ /** Reject network usage on paid network connections. */
+ public static final int POLICY_REJECT_PAID = 0x2;
+ /** Application should conserve data. */
+ public static final int POLICY_CONSERVE_DATA = 0x4;
+
+ private INetworkPolicyManager mService;
+
+ public NetworkPolicyManager(INetworkPolicyManager service) {
+ if (service == null) {
+ throw new IllegalArgumentException("missing INetworkPolicyManager");
+ }
+ mService = service;
+ }
+
+ /**
+ * Set policy flags for specific UID.
+ *
+ * @param policy {@link #POLICY_NONE} or combination of
+ * {@link #POLICY_REJECT_BACKGROUND}, {@link #POLICY_REJECT_PAID},
+ * or {@link #POLICY_CONSERVE_DATA}.
+ */
+ public void setUidPolicy(int uid, int policy) {
+ try {
+ mService.setUidPolicy(uid, policy);
+ } catch (RemoteException e) {
+ }
+ }
+
+ public int getUidPolicy(int uid) {
+ try {
+ return mService.getUidPolicy(uid);
+ } catch (RemoteException e) {
+ return POLICY_NONE;
+ }
+ }
+
+}
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 586bb6b..4ccc572 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -25,6 +25,7 @@ import com.android.server.ProcessStats;
import com.android.server.SystemServer;
import com.android.server.Watchdog;
import com.android.server.am.ActivityStack.ActivityState;
+import com.android.server.net.NetworkPolicyManagerService;
import com.android.server.wm.WindowManagerService;
import dalvik.system.Zygote;
@@ -751,6 +752,8 @@ public final class ActivityManagerService extends ActivityManagerNative
*/
final UsageStatsService mUsageStatsService;
+ final NetworkPolicyManagerService mNetworkPolicyService;
+
/**
* Current configuration information. HistoryRecord objects are given
* a reference to this object to indicate which configuration they are
@@ -971,6 +974,8 @@ public final class ActivityManagerService extends ActivityManagerNative
static final int CHECK_EXCESSIVE_WAKE_LOCKS_MSG = 27;
static final int CLEAR_DNS_CACHE = 28;
static final int UPDATE_HTTP_PROXY = 29;
+ static final int DISPATCH_FOREGROUND_ACTIVITIES_CHANGED = 30;
+ static final int DISPATCH_PROCESS_DIED = 31;
AlertDialog mUidAlert;
@@ -1271,6 +1276,19 @@ public final class ActivityManagerService extends ActivityManagerNative
sendMessageDelayed(nmsg, POWER_CHECK_DELAY);
}
} break;
+ case DISPATCH_FOREGROUND_ACTIVITIES_CHANGED: {
+ // Flag might have changed during dispatch, but it's always
+ // consistent since we dispatch for every change.
+ final ProcessRecord app = (ProcessRecord) msg.obj;
+ mNetworkPolicyService.onForegroundActivitiesChanged(
+ app.info.uid, app.pid, app.foregroundActivities);
+ break;
+ }
+ case DISPATCH_PROCESS_DIED: {
+ final ProcessRecord app = (ProcessRecord) msg.obj;
+ mNetworkPolicyService.onProcessDied(app.info.uid, app.pid);
+ break;
+ }
}
}
};
@@ -1340,6 +1358,7 @@ public final class ActivityManagerService extends ActivityManagerNative
m.mBatteryStatsService.publish(context);
m.mUsageStatsService.publish(context);
+ m.mNetworkPolicyService.publish(context);
synchronized (thr) {
thr.mReady = true;
@@ -1461,6 +1480,8 @@ public final class ActivityManagerService extends ActivityManagerNative
mUsageStatsService = new UsageStatsService(new File(
systemDir, "usagestats").toString());
+ mNetworkPolicyService = new NetworkPolicyManagerService();
+
GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
@@ -1741,7 +1762,7 @@ public final class ActivityManagerService extends ActivityManagerNative
mLruSeq++;
updateLruProcessInternalLocked(app, oomAdj, updateActivityTime, 0);
}
-
+
final ProcessRecord getProcessRecordLocked(
String processName, int uid) {
if (uid == Process.SYSTEM_UID) {
@@ -3377,7 +3398,8 @@ public final class ActivityManagerService extends ActivityManagerNative
private final boolean forceStopPackageLocked(String name, int uid,
boolean callerWillRestart, boolean purgeCache, boolean doit) {
- int i, N;
+ int i;
+ int N;
if (uid < 0) {
try {
@@ -6100,6 +6122,7 @@ public final class ActivityManagerService extends ActivityManagerNative
mUsageStatsService.shutdown();
mBatteryStatsService.shutdown();
+ mNetworkPolicyService.shutdown();
return timedout;
}
@@ -8729,9 +8752,17 @@ public final class ActivityManagerService extends ActivityManagerNative
schedGroup = Integer.toString(r.setSchedGroup);
break;
}
- pw.println(String.format("%s%s #%2d: adj=%s/%s %s (%s)",
+ String foreground;
+ if (r.foregroundActivities) {
+ foreground = "A";
+ } else if (r.foregroundServices) {
+ foreground = "S";
+ } else {
+ foreground = " ";
+ }
+ pw.println(String.format("%s%s #%2d: adj=%s/%s%s %s (%s)",
prefix, (r.persistent ? persistentLabel : normalLabel),
- N-i, oomAdj, schedGroup, r.toShortString(), r.adjType));
+ N-i, oomAdj, schedGroup, foreground, r.toShortString(), r.adjType));
if (r.adjSource != null || r.adjTarget != null) {
pw.print(prefix);
pw.print(" ");
@@ -9107,6 +9138,7 @@ public final class ActivityManagerService extends ActivityManagerNative
app.thread = null;
app.forcingToForeground = null;
app.foregroundServices = false;
+ app.foregroundActivities = false;
killServicesLocked(app, true);
@@ -9200,6 +9232,8 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ mHandler.obtainMessage(DISPATCH_PROCESS_DIED, app).sendToTarget();
+
// If the caller is restarting this app, then leave it in its
// current lists and let the caller take care of it.
if (restarting) {
@@ -12426,25 +12460,30 @@ public final class ActivityManagerService extends ActivityManagerNative
app.keeping = true;
app.curSchedGroup = Process.THREAD_GROUP_DEFAULT;
return (app.curAdj=app.maxAdj);
- }
-
+ }
+
+ final boolean hadForegroundActivities = app.foregroundActivities;
+
app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN;
app.adjSource = null;
app.adjTarget = null;
app.keeping = false;
app.empty = false;
app.hidden = false;
+ app.foregroundActivities = false;
+
+ final int activitiesSize = app.activities.size();
// Determine the importance of the process, starting with most
// important to least, and assign an appropriate OOM adjustment.
int adj;
int schedGroup;
- int N;
if (app == TOP_APP) {
// The last app on the list is the foreground app.
adj = FOREGROUND_APP_ADJ;
schedGroup = Process.THREAD_GROUP_DEFAULT;
app.adjType = "top-activity";
+ app.foregroundActivities = true;
} else if (app.instrumentationClass != null) {
// Don't want to kill running instrumentation.
adj = FOREGROUND_APP_ADJ;
@@ -12463,54 +12502,64 @@ public final class ActivityManagerService extends ActivityManagerNative
adj = FOREGROUND_APP_ADJ;
schedGroup = Process.THREAD_GROUP_DEFAULT;
app.adjType = "exec-service";
- } else if ((N=app.activities.size()) != 0) {
+ } else if (activitiesSize > 0) {
// This app is in the background with paused activities.
- app.hidden = true;
+ // We inspect activities to potentially upgrade adjustment further below.
adj = hiddenAdj;
schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
+ app.hidden = true;
app.adjType = "bg-activities";
- N = app.activities.size();
- for (int j=0; j<N; j++) {
- ActivityRecord r = app.activities.get(j);
- if (r.visible) {
- // This app has a visible activity!
- app.hidden = false;
- adj = VISIBLE_APP_ADJ;
- schedGroup = Process.THREAD_GROUP_DEFAULT;
- app.adjType = "visible";
- break;
- } else if (r.state == ActivityState.PAUSING
- || r.state == ActivityState.PAUSED
- || r.state == ActivityState.STOPPING) {
- adj = PERCEPTIBLE_APP_ADJ;
- app.adjType = "stopping";
- }
- }
} else {
// A very not-needed process. If this is lower in the lru list,
// we will push it in to the empty bucket.
+ adj = hiddenAdj;
+ schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
app.hidden = true;
app.empty = true;
- schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
- adj = hiddenAdj;
app.adjType = "bg-empty";
}
-
+
+ // Examine all activities if not already foreground.
+ if (!app.foregroundActivities && activitiesSize > 0) {
+ for (int j = 0; j < activitiesSize; j++) {
+ final ActivityRecord r = app.activities.get(j);
+ if (r.visible) {
+ // App has a visible activity; only upgrade adjustment.
+ if (adj > VISIBLE_APP_ADJ) {
+ adj = VISIBLE_APP_ADJ;
+ app.adjType = "visible";
+ }
+ schedGroup = Process.THREAD_GROUP_DEFAULT;
+ app.hidden = false;
+ app.foregroundActivities = true;
+ break;
+ } else if (r.state == ActivityState.PAUSING || r.state == ActivityState.PAUSED
+ || r.state == ActivityState.STOPPING) {
+ // Only upgrade adjustment.
+ if (adj > PERCEPTIBLE_APP_ADJ) {
+ adj = PERCEPTIBLE_APP_ADJ;
+ app.adjType = "stopping";
+ }
+ app.foregroundActivities = true;
+ }
+ }
+ }
+
if (adj > PERCEPTIBLE_APP_ADJ) {
if (app.foregroundServices) {
// The user is aware of this app, so make it visible.
adj = PERCEPTIBLE_APP_ADJ;
- schedGroup = Process.THREAD_GROUP_DEFAULT;
app.adjType = "foreground-service";
+ schedGroup = Process.THREAD_GROUP_DEFAULT;
} else if (app.forcingToForeground != null) {
// The user is aware of this app, so make it visible.
adj = PERCEPTIBLE_APP_ADJ;
- schedGroup = Process.THREAD_GROUP_DEFAULT;
app.adjType = "force-foreground";
app.adjSource = app.forcingToForeground;
+ schedGroup = Process.THREAD_GROUP_DEFAULT;
}
}
-
+
if (adj > HEAVY_WEIGHT_APP_ADJ && app == mHeavyWeightProcess) {
// We don't want to kill the current heavy-weight process.
adj = HEAVY_WEIGHT_APP_ADJ;
@@ -12730,7 +12779,11 @@ public final class ActivityManagerService extends ActivityManagerNative
app.curAdj = adj;
app.curSchedGroup = schedGroup;
-
+
+ if (hadForegroundActivities != app.foregroundActivities) {
+ mHandler.obtainMessage(DISPATCH_FOREGROUND_ACTIVITIES_CHANGED, app).sendToTarget();
+ }
+
return adj;
}
@@ -12963,13 +13016,15 @@ public final class ActivityManagerService extends ActivityManagerNative
}
private final boolean updateOomAdjLocked(
- ProcessRecord app, int hiddenAdj, ProcessRecord TOP_APP) {
+ ProcessRecord app, int hiddenAdj, ProcessRecord TOP_APP) {
app.hiddenAdj = hiddenAdj;
if (app.thread == null) {
return true;
}
+ boolean success = true;
+
final boolean wasKeeping = app.keeping;
int adj = computeOomAdjLocked(app, hiddenAdj, TOP_APP, false);
@@ -13009,7 +13064,7 @@ public final class ActivityManagerService extends ActivityManagerNative
" oom adj to " + adj);
app.setAdj = adj;
} else {
- return false;
+ success = false;
}
}
if (app.setSchedGroup != app.curSchedGroup) {
@@ -13048,7 +13103,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- return true;
+ return success;
}
private final ActivityRecord resumedAppLocked() {
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index 5be35ee..beef136 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -64,6 +64,7 @@ class ProcessRecord {
boolean keeping; // Actively running code so don't kill due to that?
boolean setIsForeground; // Running foreground UI when last set?
boolean foregroundServices; // Running any services that are foreground?
+ boolean foregroundActivities; // Running any activities that are foreground?
boolean bad; // True if disabled in the bad process list
boolean killedBackground; // True when proc has been killed due to too many bg
String waitingToKill; // Process is waiting to be killed when in the bg; reason
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
new file mode 100644
index 0000000..a7a4f07
--- /dev/null
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -0,0 +1,165 @@
+/*
+ * 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.net;
+
+import static android.Manifest.permission.MANAGE_APP_TOKENS;
+import static android.Manifest.permission.UPDATE_DEVICE_STATS;
+import static android.net.NetworkPolicyManager.POLICY_NONE;
+import static android.net.NetworkPolicyManager.POLICY_REJECT_PAID;
+import static android.net.NetworkPolicyManager.POLICY_REJECT_BACKGROUND;
+
+import android.content.Context;
+import android.net.INetworkPolicyManager;
+import android.os.ServiceManager;
+import android.util.Log;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
+
+/**
+ * Service that maintains low-level network policy rules and collects usage
+ * statistics to drive those rules.
+ */
+public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
+ private static final String TAG = "NetworkPolicy";
+ private static final boolean LOGD = true;
+
+ private static final String SERVICE_NAME = "netpolicy";
+
+ private Context mContext;
+
+ /** Current network policy for each UID. */
+ private SparseIntArray mUidPolicy;
+
+ /** Foreground at both UID and PID granularity. */
+ private SparseBooleanArray mUidForeground;
+ private SparseArray<SparseBooleanArray> mUidPidForeground;
+
+ // TODO: periodically poll network stats and write to disk
+ // TODO: save/restore policy information from disk
+
+ // TODO: watch screen on/off broadcasts to track foreground
+
+ public void publish(Context context) {
+ mContext = context;
+ ServiceManager.addService(SERVICE_NAME, asBinder());
+
+ mUidPolicy = new SparseIntArray();
+ mUidForeground = new SparseBooleanArray();
+ mUidPidForeground = new SparseArray<SparseBooleanArray>();
+
+ // TODO: register for NetworkManagementService callbacks
+ // TODO: read current policy+stats from disk and generate NMS rules
+ }
+
+ public void shutdown() {
+ // TODO: persist any pending stats during clean shutdown
+
+ mUidPolicy = null;
+ mUidForeground = null;
+ mUidPidForeground = null;
+ }
+
+ @Override
+ public void onForegroundActivitiesChanged(int uid, int pid, boolean foreground) {
+ // only someone like AMS should only be calling us
+ mContext.enforceCallingOrSelfPermission(
+ MANAGE_APP_TOKENS, "requires MANAGE_APP_TOKENS permission");
+
+ // because a uid can have multiple pids running inside, we need to
+ // remember all pid states and summarize foreground at uid level.
+
+ // record foreground for this specific pid
+ SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
+ if (pidForeground == null) {
+ pidForeground = new SparseBooleanArray(2);
+ mUidPidForeground.put(uid, pidForeground);
+ }
+ pidForeground.put(pid, foreground);
+ computeUidForeground(uid);
+ }
+
+ @Override
+ public void onProcessDied(int uid, int pid) {
+ // only someone like AMS should only be calling us
+ mContext.enforceCallingOrSelfPermission(
+ MANAGE_APP_TOKENS, "requires MANAGE_APP_TOKENS permission");
+
+ // clear records and recompute, when they exist
+ final SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
+ if (pidForeground != null) {
+ pidForeground.delete(pid);
+ computeUidForeground(uid);
+ }
+ }
+
+ @Override
+ public void setUidPolicy(int uid, int policy) {
+ mContext.enforceCallingOrSelfPermission(
+ UPDATE_DEVICE_STATS, "requires UPDATE_DEVICE_STATS permission");
+ mUidPolicy.put(uid, policy);
+ }
+
+ @Override
+ public int getUidPolicy(int uid) {
+ return mUidPolicy.get(uid, POLICY_NONE);
+ }
+
+ /**
+ * Foreground for PID changed; recompute foreground at UID level. If
+ * changed, will trigger {@link #updateRulesForUid(int)}.
+ */
+ private void computeUidForeground(int uid) {
+ final SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
+
+ // current pid is dropping foreground; examine other pids
+ boolean uidForeground = false;
+ final int size = pidForeground.size();
+ for (int i = 0; i < size; i++) {
+ if (pidForeground.valueAt(i)) {
+ uidForeground = true;
+ break;
+ }
+ }
+
+ final boolean oldUidForeground = mUidForeground.get(uid, false);
+ if (oldUidForeground != uidForeground) {
+ // foreground changed, push updated rules
+ mUidForeground.put(uid, uidForeground);
+ updateRulesForUid(uid);
+ }
+ }
+
+ private void updateRulesForUid(int uid) {
+ final boolean uidForeground = mUidForeground.get(uid, false);
+ final int uidPolicy = getUidPolicy(uid);
+
+ if (LOGD) {
+ Log.d(TAG, "updateRulesForUid(uid=" + uid + ") found foreground=" + uidForeground
+ + " and policy=" + uidPolicy);
+ }
+
+ if (!uidForeground && (uidPolicy & POLICY_REJECT_BACKGROUND) != 0) {
+ // TODO: build updated rules and push to NMS
+ } else if ((uidPolicy & POLICY_REJECT_PAID) != 0) {
+ // TODO: build updated rules and push to NMS
+ } else {
+ // TODO: build updated rules and push to NMS
+ }
+ }
+
+}