summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/PackageWatchdog.java16
-rw-r--r--services/java/com/android/server/SystemServer.java12
-rw-r--r--services/net/java/android/net/ConnectivityModuleConnector.java389
-rw-r--r--services/net/java/android/net/NetworkStackClient.java249
-rw-r--r--tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java18
5 files changed, 426 insertions, 258 deletions
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index dee89e5d5c4d..ea3dd3d4ba8d 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -25,7 +25,7 @@ import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.VersionedPackage;
-import android.net.NetworkStackClient;
+import android.net.ConnectivityModuleConnector;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
@@ -116,7 +116,7 @@ public class PackageWatchdog {
// File containing the XML data of monitored packages /data/system/package-watchdog.xml
private final AtomicFile mPolicyFile;
private final ExplicitHealthCheckController mHealthCheckController;
- private final NetworkStackClient mNetworkStackClient;
+ private final ConnectivityModuleConnector mConnectivityModuleConnector;
@GuardedBy("mLock")
private boolean mIsPackagesReady;
// Flag to control whether explicit health checks are supported or not
@@ -138,7 +138,7 @@ public class PackageWatchdog {
"package-watchdog.xml")),
new Handler(Looper.myLooper()), BackgroundThread.getHandler(),
new ExplicitHealthCheckController(context),
- NetworkStackClient.getInstance());
+ ConnectivityModuleConnector.getInstance());
}
/**
@@ -147,13 +147,13 @@ public class PackageWatchdog {
@VisibleForTesting
PackageWatchdog(Context context, AtomicFile policyFile, Handler shortTaskHandler,
Handler longTaskHandler, ExplicitHealthCheckController controller,
- NetworkStackClient networkStackClient) {
+ ConnectivityModuleConnector connectivityModuleConnector) {
mContext = context;
mPolicyFile = policyFile;
mShortTaskHandler = shortTaskHandler;
mLongTaskHandler = longTaskHandler;
mHealthCheckController = controller;
- mNetworkStackClient = networkStackClient;
+ mConnectivityModuleConnector = connectivityModuleConnector;
loadFromFile();
}
@@ -179,7 +179,7 @@ public class PackageWatchdog {
() -> syncRequestsAsync());
setPropertyChangedListenerLocked();
updateConfigs();
- registerNetworkStackHealthListener();
+ registerConnectivityModuleHealthListener();
}
}
@@ -743,11 +743,11 @@ public class PackageWatchdog {
}
}
- private void registerNetworkStackHealthListener() {
+ private void registerConnectivityModuleHealthListener() {
// TODO: have an internal method to trigger a rollback by reporting high severity errors,
// and rely on ActivityManager to inform the watchdog of severe network stack crashes
// instead of having this listener in parallel.
- mNetworkStackClient.registerHealthListener(
+ mConnectivityModuleConnector.registerHealthListener(
packageName -> {
final VersionedPackage pkg = getVersionedPackage(packageName);
if (pkg == null) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 656af982859e..e6ed2edba807 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -37,6 +37,8 @@ import android.content.res.Resources.Theme;
import android.database.sqlite.SQLiteCompatibilityWalFlags;
import android.database.sqlite.SQLiteGlobal;
import android.hardware.display.DisplayManagerInternal;
+import android.net.ConnectivityModuleConnector;
+import android.net.Network;
import android.net.NetworkStackClient;
import android.os.BaseBundle;
import android.os.Binder;
@@ -1269,6 +1271,14 @@ public final class SystemServer {
mSystemServiceManager.startService(CONTENT_SUGGESTIONS_SERVICE_CLASS);
traceEnd();
+ traceBeginAndSlog("InitConnectivityModuleConnector");
+ try {
+ ConnectivityModuleConnector.getInstance().init(context);
+ } catch (Throwable e) {
+ reportWtf("initializing ConnectivityModuleConnector", e);
+ }
+ traceEnd();
+
traceBeginAndSlog("InitNetworkStackClient");
try {
NetworkStackClient.getInstance().init();
@@ -2163,7 +2173,7 @@ public final class SystemServer {
// ActivityManagerService.mSystemReady and ActivityManagerService.mProcessesReady
// are set to true. Be careful if moving this to a different place in the
// startup sequence.
- NetworkStackClient.getInstance().start(context);
+ NetworkStackClient.getInstance().start();
} catch (Throwable e) {
reportWtf("starting Network Stack", e);
}
diff --git a/services/net/java/android/net/ConnectivityModuleConnector.java b/services/net/java/android/net/ConnectivityModuleConnector.java
new file mode 100644
index 000000000000..7333f583e8e8
--- /dev/null
+++ b/services/net/java/android/net/ConnectivityModuleConnector.java
@@ -0,0 +1,389 @@
+/*
+ * Copyright (C) 2019 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 static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.net.util.SharedLog;
+import android.os.Build;
+import android.os.Environment;
+import android.os.IBinder;
+import android.os.Process;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.DeviceConfig;
+import android.text.format.DateUtils;
+import android.util.ArraySet;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.File;
+import java.io.PrintWriter;
+
+/**
+ * Class used to communicate to the various networking mainline modules running in the network stack
+ * process from {@link com.android.server.SystemServer}.
+ * @hide
+ */
+public class ConnectivityModuleConnector {
+ private static final String TAG = ConnectivityModuleConnector.class.getSimpleName();
+ private static final String IN_PROCESS_SUFFIX = ".InProcess";
+
+ private static final String PREFS_FILE = "ConnectivityModuleConnector.xml";
+ private static final String PREF_KEY_LAST_CRASH_TIME = "lastcrash_time";
+ private static final String CONFIG_MIN_CRASH_INTERVAL_MS = "min_crash_interval";
+ private static final String CONFIG_MIN_UPTIME_BEFORE_CRASH_MS = "min_uptime_before_crash";
+ private static final String CONFIG_ALWAYS_RATELIMIT_NETWORKSTACK_CRASH =
+ "always_ratelimit_networkstack_crash";
+
+ // Even if the network stack is lost, do not crash the system more often than this.
+ // Connectivity would be broken, but if the user needs the device for something urgent
+ // (like calling emergency services) we should not bootloop the device.
+ // This is the default value: the actual value can be adjusted via device config.
+ private static final long DEFAULT_MIN_CRASH_INTERVAL_MS = 6 * DateUtils.HOUR_IN_MILLIS;
+
+ // Even if the network stack is lost, do not crash the system server if it was less than
+ // this much after boot. This avoids bootlooping the device, and crashes should address very
+ // infrequent failures, not failures on boot.
+ private static final long DEFAULT_MIN_UPTIME_BEFORE_CRASH_MS = 30 * DateUtils.MINUTE_IN_MILLIS;
+
+ private static ConnectivityModuleConnector sInstance;
+
+ private Context mContext;
+ @GuardedBy("mLog")
+ private final SharedLog mLog = new SharedLog(TAG);
+ @GuardedBy("mHealthListeners")
+ private final ArraySet<ConnectivityModuleHealthListener> mHealthListeners = new ArraySet<>();
+
+ private ConnectivityModuleConnector() { }
+
+ /**
+ * Get the {@link ConnectivityModuleConnector} singleton instance.
+ */
+ public static synchronized ConnectivityModuleConnector getInstance() {
+ if (sInstance == null) {
+ sInstance = new ConnectivityModuleConnector();
+ }
+ return sInstance;
+ }
+
+ /**
+ * Initialize the network stack connector. Should be called only once on device startup, before
+ * any client attempts to use the network stack.
+ */
+ public void init(Context context) {
+ log("Network stack init");
+ mContext = context;
+ }
+
+ /**
+ * Callback interface for severe failures of the NetworkStack.
+ *
+ * <p>Useful for health monitors such as PackageWatchdog.
+ */
+ public interface ConnectivityModuleHealthListener {
+ /**
+ * Called when there is a severe failure of the network stack.
+ * @param packageName Package name of the network stack.
+ */
+ void onNetworkStackFailure(@NonNull String packageName);
+ }
+
+ /**
+ * Callback invoked by the connector once the connection to the corresponding module is
+ * established.
+ */
+ public interface ModuleServiceCallback {
+ /**
+ * Invoked when the corresponding service has connected.
+ *
+ * @param iBinder Binder object for the service.
+ */
+ void onModuleServiceConnected(@NonNull IBinder iBinder);
+ }
+
+
+ /**
+ * Add a {@link ConnectivityModuleHealthListener} to listen to network stack health events.
+ */
+ public void registerHealthListener(@NonNull ConnectivityModuleHealthListener listener) {
+ synchronized (mHealthListeners) {
+ mHealthListeners.add(listener);
+ }
+ }
+
+ /**
+ * Start a module running in the network stack or system_server process. Should be called only
+ * once for each module per device startup.
+ *
+ * <p>This method will start a networking module either in the network stack
+ * process, or inside the system server on devices that do not support the corresponding
+ * mainline network . The corresponding networking module service's binder
+ * object will then be delivered asynchronously via the provided {@link ModuleServiceCallback}.
+ *
+ * @param serviceIntentBaseAction Base action to use for constructing the intent needed to
+ * bind to the corresponding module.
+ * @param servicePermissionName Permission to be held by the corresponding module.
+ */
+ public void startModuleService(
+ @NonNull String serviceIntentBaseAction,
+ @NonNull String servicePermissionName,
+ @NonNull ModuleServiceCallback callback) {
+ log("Starting networking module " + serviceIntentBaseAction);
+
+ final PackageManager pm = mContext.getPackageManager();
+
+ // Try to bind in-process if the device was shipped with an in-process version
+ Intent intent = getModuleServiceIntent(pm, serviceIntentBaseAction, servicePermissionName,
+ true /* inSystemProcess */);
+
+ // Otherwise use the updatable module version
+ if (intent == null) {
+ intent = getModuleServiceIntent(pm, serviceIntentBaseAction, servicePermissionName,
+ false /* inSystemProcess */);
+ log("Starting networking module in network_stack process");
+ } else {
+ log("Starting networking module in system_server process");
+ }
+
+ if (intent == null) {
+ maybeCrashWithTerribleFailure("Could not resolve the networking module", null);
+ return;
+ }
+
+ final String packageName = intent.getComponent().getPackageName();
+
+ // Start the network stack. The service will be added to the service manager by the
+ // corresponding client in ModuleServiceCallback.onModuleServiceConnected().
+ if (!mContext.bindServiceAsUser(
+ intent, new ModuleServiceConnection(packageName, callback),
+ Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM)) {
+ maybeCrashWithTerribleFailure(
+ "Could not bind to networking module in-process, or in app with "
+ + intent, packageName);
+ return;
+ }
+
+ log("Networking module service start requested");
+ }
+
+ private class ModuleServiceConnection implements ServiceConnection {
+ @NonNull
+ private final String mPackageName;
+ @NonNull
+ private final ModuleServiceCallback mModuleServiceCallback;
+
+ private ModuleServiceConnection(
+ @NonNull String packageName,
+ @NonNull ModuleServiceCallback moduleCallback) {
+ mPackageName = packageName;
+ mModuleServiceCallback = moduleCallback;
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ logi("Networking module service connected");
+ mModuleServiceCallback.onModuleServiceConnected(service);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ // onServiceDisconnected is not being called on device shutdown, so this method being
+ // called always indicates a bad state for the system server.
+ // This code path is only run by the system server: only the system server binds
+ // to the NetworkStack as a service. Other processes get the NetworkStack from
+ // the ServiceManager.
+ maybeCrashWithTerribleFailure("Lost network stack", mPackageName);
+ }
+ }
+
+ @Nullable
+ private Intent getModuleServiceIntent(
+ @NonNull PackageManager pm, @NonNull String serviceIntentBaseAction,
+ @NonNull String servicePermissionName, boolean inSystemProcess) {
+ final Intent intent =
+ new Intent(inSystemProcess
+ ? serviceIntentBaseAction + IN_PROCESS_SUFFIX
+ : serviceIntentBaseAction);
+ final ComponentName comp = intent.resolveSystemService(pm, 0);
+ if (comp == null) {
+ return null;
+ }
+ intent.setComponent(comp);
+
+ int uid = -1;
+ try {
+ uid = pm.getPackageUidAsUser(comp.getPackageName(), UserHandle.USER_SYSTEM);
+ } catch (PackageManager.NameNotFoundException e) {
+ logWtf("Networking module package not found", e);
+ // Fall through
+ }
+
+ final int expectedUid = inSystemProcess ? Process.SYSTEM_UID : Process.NETWORK_STACK_UID;
+ if (uid != expectedUid) {
+ throw new SecurityException("Invalid network stack UID: " + uid);
+ }
+
+ if (!inSystemProcess) {
+ checkModuleServicePermission(pm, comp, servicePermissionName);
+ }
+
+ return intent;
+ }
+
+ private void checkModuleServicePermission(
+ @NonNull PackageManager pm, @NonNull ComponentName comp,
+ @NonNull String servicePermissionName) {
+ final int hasPermission =
+ pm.checkPermission(servicePermissionName, comp.getPackageName());
+ if (hasPermission != PERMISSION_GRANTED) {
+ throw new SecurityException(
+ "Networking module does not have permission " + servicePermissionName);
+ }
+ }
+
+ private synchronized void maybeCrashWithTerribleFailure(@NonNull String message,
+ @Nullable String packageName) {
+ logWtf(message, null);
+ // uptime is monotonic even after a framework restart
+ final long uptime = SystemClock.elapsedRealtime();
+ final long now = System.currentTimeMillis();
+ final long minCrashIntervalMs = DeviceConfig.getLong(DeviceConfig.NAMESPACE_CONNECTIVITY,
+ CONFIG_MIN_CRASH_INTERVAL_MS, DEFAULT_MIN_CRASH_INTERVAL_MS);
+ final long minUptimeBeforeCrash = DeviceConfig.getLong(DeviceConfig.NAMESPACE_CONNECTIVITY,
+ CONFIG_MIN_UPTIME_BEFORE_CRASH_MS, DEFAULT_MIN_UPTIME_BEFORE_CRASH_MS);
+ final boolean alwaysRatelimit = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_CONNECTIVITY,
+ CONFIG_ALWAYS_RATELIMIT_NETWORKSTACK_CRASH, false);
+
+ final SharedPreferences prefs = getSharedPreferences();
+ final long lastCrashTime = tryGetLastCrashTime(prefs);
+
+ // Only crash if there was enough time since boot, and (if known) enough time passed since
+ // the last crash.
+ // time and lastCrashTime may be unreliable if devices have incorrect clock time, but they
+ // are only used to limit the number of crashes compared to only using the time since boot,
+ // which would also be OK behavior by itself.
+ // - If lastCrashTime is incorrectly more than the current time, only look at uptime
+ // - If it is much less than current time, only look at uptime
+ // - If current time is during the next few hours after last crash time, don't crash.
+ // Considering that this only matters if last boot was some time ago, it's likely that
+ // time will be set correctly. Otherwise, not crashing is not a big problem anyway. Being
+ // in this last state would also not last for long since the window is only a few hours.
+ final boolean alwaysCrash = Build.IS_DEBUGGABLE && !alwaysRatelimit;
+ final boolean justBooted = uptime < minUptimeBeforeCrash;
+ final boolean haveLastCrashTime = (lastCrashTime != 0) && (lastCrashTime < now);
+ final boolean haveKnownRecentCrash =
+ haveLastCrashTime && (now < lastCrashTime + minCrashIntervalMs);
+ if (alwaysCrash || (!justBooted && !haveKnownRecentCrash)) {
+ // The system is not bound to its network stack (for example due to a crash in the
+ // network stack process): better crash rather than stay in a bad state where all
+ // networking is broken.
+ // Using device-encrypted SharedPreferences as DeviceConfig does not have a synchronous
+ // API to persist settings before a crash.
+ tryWriteLastCrashTime(prefs, now);
+ throw new IllegalStateException(message);
+ }
+
+ // Here the system crashed recently already. Inform listeners that something is
+ // definitely wrong.
+ if (packageName != null) {
+ final ArraySet<ConnectivityModuleHealthListener> listeners;
+ synchronized (mHealthListeners) {
+ listeners = new ArraySet<>(mHealthListeners);
+ }
+ for (ConnectivityModuleHealthListener listener : listeners) {
+ listener.onNetworkStackFailure(packageName);
+ }
+ }
+ }
+
+ @Nullable
+ private SharedPreferences getSharedPreferences() {
+ try {
+ final File prefsFile = new File(
+ Environment.getDataSystemDeDirectory(UserHandle.USER_SYSTEM), PREFS_FILE);
+ return mContext.createDeviceProtectedStorageContext()
+ .getSharedPreferences(prefsFile, Context.MODE_PRIVATE);
+ } catch (Throwable e) {
+ logWtf("Error loading shared preferences", e);
+ return null;
+ }
+ }
+
+ private long tryGetLastCrashTime(@Nullable SharedPreferences prefs) {
+ if (prefs == null) return 0L;
+ try {
+ return prefs.getLong(PREF_KEY_LAST_CRASH_TIME, 0L);
+ } catch (Throwable e) {
+ logWtf("Error getting last crash time", e);
+ return 0L;
+ }
+ }
+
+ private void tryWriteLastCrashTime(@Nullable SharedPreferences prefs, long value) {
+ if (prefs == null) return;
+ try {
+ prefs.edit().putLong(PREF_KEY_LAST_CRASH_TIME, value).commit();
+ } catch (Throwable e) {
+ logWtf("Error writing last crash time", e);
+ }
+ }
+
+ private void log(@NonNull String message) {
+ Slog.d(TAG, message);
+ synchronized (mLog) {
+ mLog.log(message);
+ }
+ }
+
+ private void logWtf(@NonNull String message, @Nullable Throwable e) {
+ Slog.wtf(TAG, message, e);
+ synchronized (mLog) {
+ mLog.e(message);
+ }
+ }
+
+ private void loge(@NonNull String message, @Nullable Throwable e) {
+ Slog.e(TAG, message, e);
+ synchronized (mLog) {
+ mLog.e(message);
+ }
+ }
+
+ private void logi(@NonNull String message) {
+ Slog.i(TAG, message);
+ synchronized (mLog) {
+ mLog.i(message);
+ }
+ }
+
+ /**
+ * Dump ConnectivityModuleConnector logs to the specified {@link PrintWriter}.
+ */
+ public void dump(PrintWriter pw) {
+ // dump is thread-safe on SharedLog
+ mLog.dump(null, pw, null);
+ }
+}
diff --git a/services/net/java/android/net/NetworkStackClient.java b/services/net/java/android/net/NetworkStackClient.java
index 56b728c87180..abb46664e40d 100644
--- a/services/net/java/android/net/NetworkStackClient.java
+++ b/services/net/java/android/net/NetworkStackClient.java
@@ -15,40 +15,27 @@
*/
package android.net;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
import android.net.dhcp.DhcpServingParamsParcel;
import android.net.dhcp.IDhcpServerCallbacks;
import android.net.ip.IIpClientCallbacks;
import android.net.util.SharedLog;
import android.os.Binder;
-import android.os.Build;
-import android.os.Environment;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.SystemClock;
import android.os.UserHandle;
-import android.provider.DeviceConfig;
-import android.text.format.DateUtils;
-import android.util.ArraySet;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
-import java.io.File;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -60,24 +47,6 @@ public class NetworkStackClient {
private static final String TAG = NetworkStackClient.class.getSimpleName();
private static final int NETWORKSTACK_TIMEOUT_MS = 10_000;
- private static final String IN_PROCESS_SUFFIX = ".InProcess";
- private static final String PREFS_FILE = "NetworkStackClientPrefs.xml";
- private static final String PREF_KEY_LAST_CRASH_TIME = "lastcrash_time";
- private static final String CONFIG_MIN_CRASH_INTERVAL_MS = "min_crash_interval";
- private static final String CONFIG_MIN_UPTIME_BEFORE_CRASH_MS = "min_uptime_before_crash";
- private static final String CONFIG_ALWAYS_RATELIMIT_NETWORKSTACK_CRASH =
- "always_ratelimit_networkstack_crash";
-
- // Even if the network stack is lost, do not crash the system more often than this.
- // Connectivity would be broken, but if the user needs the device for something urgent
- // (like calling emergency services) we should not bootloop the device.
- // This is the default value: the actual value can be adjusted via device config.
- private static final long DEFAULT_MIN_CRASH_INTERVAL_MS = 6 * DateUtils.HOUR_IN_MILLIS;
-
- // Even if the network stack is lost, do not crash the system server if it was less than
- // this much after boot. This avoids bootlooping the device, and crashes should address very
- // infrequent failures, not failures on boot.
- private static final long DEFAULT_MIN_UPTIME_BEFORE_CRASH_MS = 30 * DateUtils.MINUTE_IN_MILLIS;
private static NetworkStackClient sInstance;
@@ -93,26 +62,10 @@ public class NetworkStackClient {
private volatile boolean mWasSystemServerInitialized = false;
- @GuardedBy("mHealthListeners")
- private final ArraySet<NetworkStackHealthListener> mHealthListeners = new ArraySet<>();
-
private interface NetworkStackCallback {
void onNetworkStackConnected(INetworkStackConnector connector);
}
- /**
- * Callback interface for severe failures of the NetworkStack.
- *
- * <p>Useful for health monitors such as PackageWatchdog.
- */
- public interface NetworkStackHealthListener {
- /**
- * Called when there is a severe failure of the network stack.
- * @param packageName Package name of the network stack.
- */
- void onNetworkStackFailure(@NonNull String packageName);
- }
-
private NetworkStackClient() { }
/**
@@ -126,15 +79,6 @@ public class NetworkStackClient {
}
/**
- * Add a {@link NetworkStackHealthListener} to listen to network stack health events.
- */
- public void registerHealthListener(@NonNull NetworkStackHealthListener listener) {
- synchronized (mHealthListeners) {
- mHealthListeners.add(listener);
- }
- }
-
- /**
* Create a DHCP server according to the specified parameters.
*
* <p>The server will be returned asynchronously through the provided callbacks.
@@ -195,32 +139,13 @@ public class NetworkStackClient {
});
}
- private class NetworkStackConnection implements ServiceConnection {
- @NonNull
- private final Context mContext;
- @NonNull
- private final String mPackageName;
-
- private NetworkStackConnection(@NonNull Context context, @NonNull String packageName) {
- mContext = context;
- mPackageName = packageName;
- }
-
+ private class NetworkStackConnection implements
+ ConnectivityModuleConnector.ModuleServiceCallback {
@Override
- public void onServiceConnected(ComponentName name, IBinder service) {
+ public void onModuleServiceConnected(IBinder service) {
logi("Network stack service connected");
registerNetworkStackService(service);
}
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- // onServiceDisconnected is not being called on device shutdown, so this method being
- // called always indicates a bad state for the system server.
- // This code path is only run by the system server: only the system server binds
- // to the NetworkStack as a service. Other processes get the NetworkStack from
- // the ServiceManager.
- maybeCrashWithTerribleFailure("Lost network stack", mContext, mPackageName);
- }
}
private void registerNetworkStackService(@NonNull IBinder service) {
@@ -259,171 +184,13 @@ public class NetworkStackClient {
* connector will then be delivered asynchronously to clients that requested it before it was
* started.
*/
- public void start(Context context) {
- log("Starting network stack");
-
- final PackageManager pm = context.getPackageManager();
-
- // Try to bind in-process if the device was shipped with an in-process version
- Intent intent = getNetworkStackIntent(pm, true /* inSystemProcess */);
-
- // Otherwise use the updatable module version
- if (intent == null) {
- intent = getNetworkStackIntent(pm, false /* inSystemProcess */);
- log("Starting network stack process");
- } else {
- log("Starting network stack in-process");
- }
-
- if (intent == null) {
- maybeCrashWithTerribleFailure("Could not resolve the network stack", context, null);
- return;
- }
-
- final String packageName = intent.getComponent().getPackageName();
-
- // Start the network stack. The service will be added to the service manager in
- // NetworkStackConnection.onServiceConnected().
- if (!context.bindServiceAsUser(intent, new NetworkStackConnection(context, packageName),
- Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM)) {
- maybeCrashWithTerribleFailure(
- "Could not bind to network stack in-process, or in app with " + intent,
- context, packageName);
- return;
- }
-
+ public void start() {
+ ConnectivityModuleConnector.getInstance().startModuleService(
+ INetworkStackConnector.class.getName(), PERMISSION_MAINLINE_NETWORK_STACK,
+ new NetworkStackConnection());
log("Network stack service start requested");
}
- @Nullable
- private Intent getNetworkStackIntent(@NonNull PackageManager pm, boolean inSystemProcess) {
- final String baseAction = INetworkStackConnector.class.getName();
- final Intent intent =
- new Intent(inSystemProcess ? baseAction + IN_PROCESS_SUFFIX : baseAction);
- final ComponentName comp = intent.resolveSystemService(pm, 0);
-
- if (comp == null) {
- return null;
- }
- intent.setComponent(comp);
-
- int uid = -1;
- try {
- uid = pm.getPackageUidAsUser(comp.getPackageName(), UserHandle.USER_SYSTEM);
- } catch (PackageManager.NameNotFoundException e) {
- logWtf("Network stack package not found", e);
- // Fall through
- }
-
- final int expectedUid = inSystemProcess ? Process.SYSTEM_UID : Process.NETWORK_STACK_UID;
- if (uid != expectedUid) {
- throw new SecurityException("Invalid network stack UID: " + uid);
- }
-
- if (!inSystemProcess) {
- checkNetworkStackPermission(pm, comp);
- }
-
- return intent;
- }
-
- private void checkNetworkStackPermission(
- @NonNull PackageManager pm, @NonNull ComponentName comp) {
- final int hasPermission =
- pm.checkPermission(PERMISSION_MAINLINE_NETWORK_STACK, comp.getPackageName());
- if (hasPermission != PERMISSION_GRANTED) {
- throw new SecurityException(
- "Network stack does not have permission " + PERMISSION_MAINLINE_NETWORK_STACK);
- }
- }
-
- private void maybeCrashWithTerribleFailure(@NonNull String message,
- @NonNull Context context, @Nullable String packageName) {
- logWtf(message, null);
- // uptime is monotonic even after a framework restart
- final long uptime = SystemClock.elapsedRealtime();
- final long now = System.currentTimeMillis();
- final long minCrashIntervalMs = DeviceConfig.getLong(DeviceConfig.NAMESPACE_CONNECTIVITY,
- CONFIG_MIN_CRASH_INTERVAL_MS, DEFAULT_MIN_CRASH_INTERVAL_MS);
- final long minUptimeBeforeCrash = DeviceConfig.getLong(DeviceConfig.NAMESPACE_CONNECTIVITY,
- CONFIG_MIN_UPTIME_BEFORE_CRASH_MS, DEFAULT_MIN_UPTIME_BEFORE_CRASH_MS);
- final boolean alwaysRatelimit = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_CONNECTIVITY,
- CONFIG_ALWAYS_RATELIMIT_NETWORKSTACK_CRASH, false);
-
- final SharedPreferences prefs = getSharedPreferences(context);
- final long lastCrashTime = tryGetLastCrashTime(prefs);
-
- // Only crash if there was enough time since boot, and (if known) enough time passed since
- // the last crash.
- // time and lastCrashTime may be unreliable if devices have incorrect clock time, but they
- // are only used to limit the number of crashes compared to only using the time since boot,
- // which would also be OK behavior by itself.
- // - If lastCrashTime is incorrectly more than the current time, only look at uptime
- // - If it is much less than current time, only look at uptime
- // - If current time is during the next few hours after last crash time, don't crash.
- // Considering that this only matters if last boot was some time ago, it's likely that
- // time will be set correctly. Otherwise, not crashing is not a big problem anyway. Being
- // in this last state would also not last for long since the window is only a few hours.
- final boolean alwaysCrash = Build.IS_DEBUGGABLE && !alwaysRatelimit;
- final boolean justBooted = uptime < minUptimeBeforeCrash;
- final boolean haveLastCrashTime = (lastCrashTime != 0) && (lastCrashTime < now);
- final boolean haveKnownRecentCrash =
- haveLastCrashTime && (now < lastCrashTime + minCrashIntervalMs);
- if (alwaysCrash || (!justBooted && !haveKnownRecentCrash)) {
- // The system is not bound to its network stack (for example due to a crash in the
- // network stack process): better crash rather than stay in a bad state where all
- // networking is broken.
- // Using device-encrypted SharedPreferences as DeviceConfig does not have a synchronous
- // API to persist settings before a crash.
- tryWriteLastCrashTime(prefs, now);
- throw new IllegalStateException(message);
- }
-
- // Here the system crashed recently already. Inform listeners that something is
- // definitely wrong.
- if (packageName != null) {
- final ArraySet<NetworkStackHealthListener> listeners;
- synchronized (mHealthListeners) {
- listeners = new ArraySet<>(mHealthListeners);
- }
- for (NetworkStackHealthListener listener : listeners) {
- listener.onNetworkStackFailure(packageName);
- }
- }
- }
-
- @Nullable
- private SharedPreferences getSharedPreferences(@NonNull Context context) {
- try {
- final File prefsFile = new File(
- Environment.getDataSystemDeDirectory(UserHandle.USER_SYSTEM), PREFS_FILE);
- return context.createDeviceProtectedStorageContext()
- .getSharedPreferences(prefsFile, Context.MODE_PRIVATE);
- } catch (Throwable e) {
- logWtf("Error loading shared preferences", e);
- return null;
- }
- }
-
- private long tryGetLastCrashTime(@Nullable SharedPreferences prefs) {
- if (prefs == null) return 0L;
- try {
- return prefs.getLong(PREF_KEY_LAST_CRASH_TIME, 0L);
- } catch (Throwable e) {
- logWtf("Error getting last crash time", e);
- return 0L;
- }
- }
-
- private void tryWriteLastCrashTime(@Nullable SharedPreferences prefs, long value) {
- if (prefs == null) return;
- try {
- prefs.edit().putLong(PREF_KEY_LAST_CRASH_TIME, value).commit();
- } catch (Throwable e) {
- logWtf("Error writing last crash time", e);
- }
- }
-
/**
* Log a message in the local log.
*/
@@ -524,6 +291,8 @@ public class NetworkStackClient {
public void dump(PrintWriter pw) {
// dump is thread-safe on SharedLog
mLog.dump(null, pw, null);
+ // dump connectivity module connector logs.
+ ConnectivityModuleConnector.getInstance().dump(pw);
final int requestsQueueLength;
synchronized (mPendingNetStackRequests) {
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index 232b5cb17023..c42201fa0d3e 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -35,8 +35,8 @@ import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.VersionedPackage;
-import android.net.NetworkStackClient;
-import android.net.NetworkStackClient.NetworkStackHealthListener;
+import android.net.ConnectivityModuleConnector;
+import android.net.ConnectivityModuleConnector.ConnectivityModuleHealthListener;
import android.os.Handler;
import android.os.test.TestLooper;
import android.provider.DeviceConfig;
@@ -86,11 +86,11 @@ public class PackageWatchdogTest {
private TestLooper mTestLooper;
private Context mSpyContext;
@Mock
- private NetworkStackClient mMockNetworkStackClient;
+ private ConnectivityModuleConnector mConnectivityModuleConnector;
@Mock
private PackageManager mMockPackageManager;
@Captor
- private ArgumentCaptor<NetworkStackHealthListener> mNetworkStackCallbackCaptor;
+ private ArgumentCaptor<ConnectivityModuleHealthListener> mConnectivityModuleCallbackCaptor;
@Before
public void setUp() throws Exception {
@@ -736,7 +736,7 @@ public class PackageWatchdogTest {
wd.startObservingHealth(observer, Collections.singletonList(APP_A), SHORT_DURATION);
// Notify of NetworkStack failure
- mNetworkStackCallbackCaptor.getValue().onNetworkStackFailure(APP_A);
+ mConnectivityModuleCallbackCaptor.getValue().onNetworkStackFailure(APP_A);
// Run handler so package failures are dispatched to observers
mTestLooper.dispatchAll();
@@ -782,18 +782,18 @@ public class PackageWatchdogTest {
Handler handler = new Handler(mTestLooper.getLooper());
PackageWatchdog watchdog =
new PackageWatchdog(mSpyContext, policyFile, handler, handler, controller,
- mMockNetworkStackClient);
+ mConnectivityModuleConnector);
// Verify controller is not automatically started
assertFalse(controller.mIsEnabled);
if (withPackagesReady) {
// Only capture the NetworkStack callback for the latest registered watchdog
- reset(mMockNetworkStackClient);
+ reset(mConnectivityModuleConnector);
watchdog.onPackagesReady();
// Verify controller by default is started when packages are ready
assertTrue(controller.mIsEnabled);
- verify(mMockNetworkStackClient).registerHealthListener(
- mNetworkStackCallbackCaptor.capture());
+ verify(mConnectivityModuleConnector).registerHealthListener(
+ mConnectivityModuleCallbackCaptor.capture());
}
return watchdog;
}