diff options
-rw-r--r-- | Android.mk | 1 | ||||
-rw-r--r-- | core/java/android/net/INetworkPolicyManager.aidl | 34 | ||||
-rw-r--r-- | core/java/android/net/NetworkPolicyManager.java | 68 | ||||
-rw-r--r-- | services/java/com/android/server/am/ActivityManagerService.java | 127 | ||||
-rw-r--r-- | services/java/com/android/server/am/ProcessRecord.java | 1 | ||||
-rw-r--r-- | services/java/com/android/server/net/NetworkPolicyManagerService.java | 165 |
6 files changed, 360 insertions, 36 deletions
@@ -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 + } + } + +} |