Commit a4620793 authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

Observe screen on/off events in NetworkPolicy.

The POLICY_REJECT_BACKGROUND policy requires that network traffic be
blocked when a UID goes into the background.  Even if the UID has an
activity in the foreground, it's considered "background" if the screen
is turned off.

This changes watches for SCREEN_ON/OFF broadcasts, and rule generation
now observes screen state.  It also introduces an observer pattern so
that ActivityManager doesn't directly know about NetworkPolicy, and
moves the service management into SystemServer.

Change-Id: Ie7a84929d3ca60ae4578d47e19d5a8da10fd8d58
parent 442fa21e
......@@ -71,6 +71,7 @@ LOCAL_SRC_FILES += \
core/java/android/app/IBackupAgent.aidl \
core/java/android/app/IInstrumentationWatcher.aidl \
core/java/android/app/INotificationManager.aidl \
core/java/android/app/IProcessObserver.aidl \
core/java/android/app/ISearchManager.aidl \
core/java/android/app/ISearchManagerCallback.aidl \
core/java/android/app/IServiceConnection.aidl \
......
......@@ -1467,6 +1467,22 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
return true;
}
case REGISTER_PROCESS_OBSERVER_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IProcessObserver observer = IProcessObserver.Stub.asInterface(
data.readStrongBinder());
registerProcessObserver(observer);
return true;
}
case UNREGISTER_PROCESS_OBSERVER_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IProcessObserver observer = IProcessObserver.Stub.asInterface(
data.readStrongBinder());
unregisterProcessObserver(observer);
return true;
}
}
return super.onTransact(code, data, reply, flags);
......@@ -3300,5 +3316,27 @@ class ActivityManagerProxy implements IActivityManager
return result;
}
public void registerProcessObserver(IProcessObserver observer) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(observer != null ? observer.asBinder() : null);
mRemote.transact(REGISTER_PROCESS_OBSERVER_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
reply.recycle();
}
public void unregisterProcessObserver(IProcessObserver observer) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(observer != null ? observer.asBinder() : null);
mRemote.transact(UNREGISTER_PROCESS_OBSERVER_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
reply.recycle();
}
private IBinder mRemote;
}
......@@ -355,6 +355,9 @@ public interface IActivityManager extends IInterface {
public boolean removeTask(int taskId, int flags) throws RemoteException;
public void registerProcessObserver(IProcessObserver observer) throws RemoteException;
public void unregisterProcessObserver(IProcessObserver observer) throws RemoteException;
/*
* Private non-Binder interfaces
*/
......@@ -577,4 +580,6 @@ public interface IActivityManager extends IInterface {
int SWITCH_USER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+127;
int REMOVE_SUB_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+128;
int REMOVE_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+129;
int REGISTER_PROCESS_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+130;
int UNREGISTER_PROCESS_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+131;
}
/*
* 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.app;
/** {@hide} */
oneway interface IProcessObserver {
void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities);
void onProcessDied(int pid, int uid);
}
......@@ -23,9 +23,6 @@ package android.net;
*/
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);
......
......@@ -18,6 +18,7 @@ package com.android.server;
import com.android.server.accessibility.AccessibilityManagerService;
import com.android.server.am.ActivityManagerService;
import com.android.server.net.NetworkPolicyManagerService;
import com.android.server.pm.PackageManagerService;
import com.android.server.usb.UsbService;
import com.android.server.wm.WindowManagerService;
......@@ -119,6 +120,7 @@ class ServerThread extends Thread {
LightsService lights = null;
PowerManagerService power = null;
BatteryService battery = null;
NetworkPolicyManagerService networkPolicy = null;
ConnectivityService connectivity = null;
IPackageManager pm = null;
Context context = null;
......@@ -281,6 +283,15 @@ class ServerThread extends Thread {
Slog.e(TAG, "Failure starting NetStat Service", e);
}
try {
Slog.i(TAG, "NetworkPolicy Service");
networkPolicy = new NetworkPolicyManagerService(
context, ActivityManagerService.self(), power);
ServiceManager.addService(Context.NETWORK_POLICY_SERVICE, networkPolicy);
} catch (Throwable e) {
Slog.e(TAG, "Failure starting Connectivity Service", e);
}
try {
Slog.i(TAG, "NetworkManagement Service");
ServiceManager.addService(
......@@ -528,6 +539,7 @@ class ServerThread extends Thread {
// These are needed to propagate to the runnable below.
final Context contextF = context;
final BatteryService batteryF = battery;
final NetworkPolicyManagerService networkPolicyF = networkPolicy;
final ConnectivityService connectivityF = connectivity;
final DockObserver dockF = dock;
final UsbService usbF = usb;
......@@ -553,6 +565,7 @@ class ServerThread extends Thread {
startSystemUi(contextF);
if (batteryF != null) batteryF.systemReady();
if (networkPolicyF != null) networkPolicyF.systemReady();
if (connectivityF != null) connectivityF.systemReady();
if (dockF != null) dockF.systemReady();
if (usbF != null) usbF.systemReady();
......
......@@ -43,6 +43,7 @@ import android.app.IActivityWatcher;
import android.app.IApplicationThread;
import android.app.IInstrumentationWatcher;
import android.app.INotificationManager;
import android.app.IProcessObserver;
import android.app.IServiceConnection;
import android.app.IThumbnailReceiver;
import android.app.IThumbnailRetriever;
......@@ -752,8 +753,6 @@ 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
......@@ -885,7 +884,10 @@ public final class ActivityManagerService extends ActivityManagerNative
 
final RemoteCallbackList<IActivityWatcher> mWatchers
= new RemoteCallbackList<IActivityWatcher>();
final RemoteCallbackList<IProcessObserver> mProcessObservers
= new RemoteCallbackList<IProcessObserver>();
/**
* Callback of last caller to {@link #requestPss}.
*/
......@@ -1277,16 +1279,15 @@ public final class ActivityManagerService extends ActivityManagerNative
}
} 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);
final boolean foregroundActivities = msg.arg1 != 0;
dispatchForegroundActivitiesChanged(
app.pid, app.info.uid, foregroundActivities);
break;
}
case DISPATCH_PROCESS_DIED: {
final ProcessRecord app = (ProcessRecord) msg.obj;
mNetworkPolicyService.onProcessDied(app.info.uid, app.pid);
dispatchProcessDied(app.pid, app.info.uid);
break;
}
}
......@@ -1358,7 +1359,6 @@ public final class ActivityManagerService extends ActivityManagerNative
m.mBatteryStatsService.publish(context);
m.mUsageStatsService.publish(context);
m.mNetworkPolicyService.publish(context);
synchronized (thr) {
thr.mReady = true;
......@@ -1480,8 +1480,6 @@ 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);
 
......@@ -2152,6 +2150,36 @@ public final class ActivityManagerService extends ActivityManagerNative
mWatchers.finishBroadcast();
}
 
private void dispatchForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
int i = mProcessObservers.beginBroadcast();
while (i > 0) {
i--;
final IProcessObserver observer = mProcessObservers.getBroadcastItem(i);
if (observer != null) {
try {
observer.onForegroundActivitiesChanged(pid, uid, foregroundActivities);
} catch (RemoteException e) {
}
}
}
mProcessObservers.finishBroadcast();
}
private void dispatchProcessDied(int pid, int uid) {
int i = mProcessObservers.beginBroadcast();
while (i > 0) {
i--;
final IProcessObserver observer = mProcessObservers.getBroadcastItem(i);
if (observer != null) {
try {
observer.onProcessDied(pid, uid);
} catch (RemoteException e) {
}
}
}
mProcessObservers.finishBroadcast();
}
final void doPendingActivityLaunchesLocked(boolean doResume) {
final int N = mPendingActivityLaunches.size();
if (N <= 0) {
......@@ -6084,7 +6112,6 @@ public final class ActivityManagerService extends ActivityManagerNative
mUsageStatsService.shutdown();
mBatteryStatsService.shutdown();
mNetworkPolicyService.shutdown();
return timedout;
}
......@@ -6241,6 +6268,14 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
 
public void registerProcessObserver(IProcessObserver observer) {
mProcessObservers.register(observer);
}
public void unregisterProcessObserver(IProcessObserver observer) {
mProcessObservers.unregister(observer);
}
public void setImmersive(IBinder token, boolean immersive) {
synchronized(this) {
int index = (token != null) ? mMainStack.indexOfTokenLocked(token) : -1;
......@@ -12755,7 +12790,8 @@ public final class ActivityManagerService extends ActivityManagerNative
app.curSchedGroup = schedGroup;
 
if (hadForegroundActivities != app.foregroundActivities) {
mHandler.obtainMessage(DISPATCH_FOREGROUND_ACTIVITIES_CHANGED, app).sendToTarget();
mHandler.obtainMessage(DISPATCH_FOREGROUND_ACTIVITIES_CHANGED,
app.foregroundActivities ? 1 : 0, 0, app).sendToTarget();
}
 
return adj;
......
......@@ -19,13 +19,20 @@ 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 static android.net.NetworkPolicyManager.POLICY_REJECT_PAID;
import android.app.IActivityManager;
import android.app.IProcessObserver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.INetworkPolicyManager;
import android.os.ServiceManager;
import android.os.IPowerManager;
import android.os.RemoteException;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
......@@ -39,89 +46,138 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private static final boolean LOGD = true;
private Context mContext;
private IActivityManager mActivityManager;
private IPowerManager mPowerManager;
private Object mRulesLock = new Object();
private boolean mScreenOn = false;
/** Current network policy for each UID. */
private SparseIntArray mUidPolicy;
private SparseIntArray mUidPolicy = new SparseIntArray();
/** Foreground at both UID and PID granularity. */
private SparseBooleanArray mUidForeground;
private SparseArray<SparseBooleanArray> mUidPidForeground;
private SparseBooleanArray mUidForeground = new SparseBooleanArray();
private SparseArray<SparseBooleanArray> mUidPidForeground = new SparseArray<
SparseBooleanArray>();
// 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 NetworkPolicyManagerService(
Context context, IActivityManager activityManager, IPowerManager powerManager) {
mContext = checkNotNull(context, "missing context");
mActivityManager = checkNotNull(activityManager, "missing activityManager");
mPowerManager = checkNotNull(powerManager, "missing powerManager");
}
public void publish(Context context) {
mContext = context;
ServiceManager.addService(Context.NETWORK_POLICY_SERVICE, asBinder());
public void systemReady() {
// TODO: read current policy+stats from disk and generate NMS rules
mUidPolicy = new SparseIntArray();
mUidForeground = new SparseBooleanArray();
mUidPidForeground = new SparseArray<SparseBooleanArray>();
updateScreenOn();
// TODO: register for NetworkManagementService callbacks
// TODO: read current policy+stats from disk and generate NMS rules
}
try {
mActivityManager.registerProcessObserver(mProcessObserver);
} catch (RemoteException e) {
// ouch, no foregroundActivities updates means some processes may
// never get network access.
Slog.e(TAG, "unable to register IProcessObserver", e);
}
public void shutdown() {
// TODO: persist any pending stats during clean shutdown
// TODO: traverse existing processes to know foreground state, or have
// activitymanager dispatch current state when new observer attached.
mUidPolicy = null;
mUidForeground = null;
mUidPidForeground = null;
}
final IntentFilter screenFilter = new IntentFilter();
screenFilter.addAction(Intent.ACTION_SCREEN_ON);
screenFilter.addAction(Intent.ACTION_SCREEN_OFF);
mContext.registerReceiver(mScreenReceiver, screenFilter);
@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");
final IntentFilter shutdownFilter = new IntentFilter();
shutdownFilter.addAction(Intent.ACTION_SHUTDOWN);
mContext.registerReceiver(mShutdownReceiver, shutdownFilter);
// 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);
private IProcessObserver mProcessObserver = new IProcessObserver.Stub() {
@Override
public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
// only someone like AMS should only be calling us
mContext.enforceCallingOrSelfPermission(
MANAGE_APP_TOKENS, "requires MANAGE_APP_TOKENS permission");
synchronized (mRulesLock) {
// 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, foregroundActivities);
computeUidForegroundL(uid);
}
}
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");
@Override
public void onProcessDied(int pid, int uid) {
// only someone like AMS should only be calling us
mContext.enforceCallingOrSelfPermission(
MANAGE_APP_TOKENS, "requires MANAGE_APP_TOKENS permission");
synchronized (mRulesLock) {
// clear records and recompute, when they exist
final SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
if (pidForeground != null) {
pidForeground.delete(pid);
computeUidForegroundL(uid);
}
}
}
};
private BroadcastReceiver mScreenReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
synchronized (mRulesLock) {
// screen-related broadcasts are protected by system, no need
// for permissions check.
updateScreenOn();
}
}
};
// clear records and recompute, when they exist
final SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
if (pidForeground != null) {
pidForeground.delete(pid);
computeUidForeground(uid);
private BroadcastReceiver mShutdownReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// TODO: persist any pending stats during clean shutdown
Log.d(TAG, "persisting stats");
}
}
};
@Override
public void setUidPolicy(int uid, int policy) {
mContext.enforceCallingOrSelfPermission(
UPDATE_DEVICE_STATS, "requires UPDATE_DEVICE_STATS permission");
mUidPolicy.put(uid, policy);
synchronized (mRulesLock) {
mUidPolicy.put(uid, policy);
}
}
@Override
public int getUidPolicy(int uid) {
return mUidPolicy.get(uid, POLICY_NONE);
synchronized (mRulesLock) {
return mUidPolicy.get(uid, POLICY_NONE);
}
}
/**
* Foreground for PID changed; recompute foreground at UID level. If
* changed, will trigger {@link #updateRulesForUid(int)}.
* changed, will trigger {@link #updateRulesForUidL(int)}.
*/
private void computeUidForeground(int uid) {
private void computeUidForegroundL(int uid) {
final SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
// current pid is dropping foreground; examine other pids
......@@ -138,12 +194,37 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
if (oldUidForeground != uidForeground) {
// foreground changed, push updated rules
mUidForeground.put(uid, uidForeground);
updateRulesForUid(uid);
updateRulesForUidL(uid);
}
}
private void updateScreenOn() {
synchronized (mRulesLock) {
try {
mScreenOn = mPowerManager.isScreenOn();
} catch (RemoteException e) {
}
updateRulesForScreenL();
}
}
private void updateRulesForUid(int uid) {
final boolean uidForeground = mUidForeground.get(uid, false);
/**
* Update rules that might be changed by {@link #mScreenOn} value.
*/
private void updateRulesForScreenL() {
// only update rules for anyone with foreground activities
final int size = mUidForeground.size();
for (int i = 0; i < size; i++) {
if (mUidForeground.valueAt(i)) {
final int uid = mUidForeground.keyAt(i);
updateRulesForUidL(uid);
}
}
}
private void updateRulesForUidL(int uid) {
// only really in foreground when screen on
final boolean uidForeground = mUidForeground.get(uid, false) && mScreenOn;
final int uidPolicy = getUidPolicy(uid);
if (LOGD) {
......@@ -160,4 +241,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
}
private static <T> T checkNotNull(T value, String message) {
if (value == null) {
throw new NullPointerException(message);
}
return value;
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment