1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package android.net; 17 18 import static android.content.pm.PackageManager.PERMISSION_GRANTED; 19 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.content.ComponentName; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.ServiceConnection; 26 import android.content.SharedPreferences; 27 import android.content.pm.PackageManager; 28 import android.net.util.SharedLog; 29 import android.os.Build; 30 import android.os.Environment; 31 import android.os.IBinder; 32 import android.os.Process; 33 import android.os.SystemClock; 34 import android.os.UserHandle; 35 import android.provider.DeviceConfig; 36 import android.text.format.DateUtils; 37 import android.util.ArraySet; 38 import android.util.Slog; 39 40 import com.android.internal.annotations.GuardedBy; 41 import com.android.internal.annotations.VisibleForTesting; 42 43 import java.io.File; 44 import java.io.PrintWriter; 45 46 /** 47 * Class used to communicate to the various networking mainline modules running in the network stack 48 * process from {@link com.android.server.SystemServer}. 49 * @hide 50 */ 51 public class ConnectivityModuleConnector { 52 private static final String TAG = ConnectivityModuleConnector.class.getSimpleName(); 53 private static final String IN_PROCESS_SUFFIX = ".InProcess"; 54 55 private static final String PREFS_FILE = "ConnectivityModuleConnector.xml"; 56 private static final String PREF_KEY_LAST_CRASH_TIME = "lastcrash_time"; 57 private static final String CONFIG_MIN_CRASH_INTERVAL_MS = "min_crash_interval"; 58 private static final String CONFIG_MIN_UPTIME_BEFORE_CRASH_MS = "min_uptime_before_crash"; 59 private static final String CONFIG_ALWAYS_RATELIMIT_NETWORKSTACK_CRASH = 60 "always_ratelimit_networkstack_crash"; 61 62 // Even if the network stack is lost, do not crash the system more often than this. 63 // Connectivity would be broken, but if the user needs the device for something urgent 64 // (like calling emergency services) we should not bootloop the device. 65 // This is the default value: the actual value can be adjusted via device config. 66 private static final long DEFAULT_MIN_CRASH_INTERVAL_MS = 6 * DateUtils.HOUR_IN_MILLIS; 67 68 // Even if the network stack is lost, do not crash the system server if it was less than 69 // this much after boot. This avoids bootlooping the device, and crashes should address very 70 // infrequent failures, not failures on boot. 71 private static final long DEFAULT_MIN_UPTIME_BEFORE_CRASH_MS = 30 * DateUtils.MINUTE_IN_MILLIS; 72 73 private static ConnectivityModuleConnector sInstance; 74 75 private Context mContext; 76 @GuardedBy("mLog") 77 private final SharedLog mLog = new SharedLog(TAG); 78 @GuardedBy("mHealthListeners") 79 private final ArraySet<ConnectivityModuleHealthListener> mHealthListeners = new ArraySet<>(); 80 @NonNull 81 private final Dependencies mDeps; 82 ConnectivityModuleConnector()83 private ConnectivityModuleConnector() { 84 this(new DependenciesImpl()); 85 } 86 87 @VisibleForTesting ConnectivityModuleConnector(@onNull Dependencies deps)88 ConnectivityModuleConnector(@NonNull Dependencies deps) { 89 mDeps = deps; 90 } 91 92 /** 93 * Get the {@link ConnectivityModuleConnector} singleton instance. 94 */ getInstance()95 public static synchronized ConnectivityModuleConnector getInstance() { 96 if (sInstance == null) { 97 sInstance = new ConnectivityModuleConnector(); 98 } 99 return sInstance; 100 } 101 102 /** 103 * Initialize the network stack connector. Should be called only once on device startup, before 104 * any client attempts to use the network stack. 105 */ init(Context context)106 public void init(Context context) { 107 log("Network stack init"); 108 mContext = context; 109 } 110 111 /** 112 * Callback interface for severe failures of the NetworkStack. 113 * 114 * <p>Useful for health monitors such as PackageWatchdog. 115 */ 116 public interface ConnectivityModuleHealthListener { 117 /** 118 * Called when there is a severe failure of the network stack. 119 * @param packageName Package name of the network stack. 120 */ onNetworkStackFailure(@onNull String packageName)121 void onNetworkStackFailure(@NonNull String packageName); 122 } 123 124 /** 125 * Callback invoked by the connector once the connection to the corresponding module is 126 * established. 127 */ 128 public interface ModuleServiceCallback { 129 /** 130 * Invoked when the corresponding service has connected. 131 * 132 * @param iBinder Binder object for the service. 133 */ onModuleServiceConnected(@onNull IBinder iBinder)134 void onModuleServiceConnected(@NonNull IBinder iBinder); 135 } 136 137 /** 138 * Interface used to determine the intent to use to bind to the module. Useful for testing. 139 */ 140 @VisibleForTesting 141 protected interface Dependencies { 142 /** 143 * Determine the intent to use to bind to the module. 144 * @return null if the intent could not be resolved, the intent otherwise. 145 */ 146 @Nullable getModuleServiceIntent( @onNull PackageManager pm, @NonNull String serviceIntentBaseAction, @NonNull String servicePermissionName, boolean inSystemProcess)147 Intent getModuleServiceIntent( 148 @NonNull PackageManager pm, @NonNull String serviceIntentBaseAction, 149 @NonNull String servicePermissionName, boolean inSystemProcess); 150 } 151 152 private static class DependenciesImpl implements Dependencies { 153 @Nullable 154 @Override getModuleServiceIntent( @onNull PackageManager pm, @NonNull String serviceIntentBaseAction, @NonNull String servicePermissionName, boolean inSystemProcess)155 public Intent getModuleServiceIntent( 156 @NonNull PackageManager pm, @NonNull String serviceIntentBaseAction, 157 @NonNull String servicePermissionName, boolean inSystemProcess) { 158 final Intent intent = 159 new Intent(inSystemProcess 160 ? serviceIntentBaseAction + IN_PROCESS_SUFFIX 161 : serviceIntentBaseAction); 162 final ComponentName comp = intent.resolveSystemService(pm, 0); 163 if (comp == null) { 164 return null; 165 } 166 intent.setComponent(comp); 167 168 final int uid; 169 try { 170 uid = pm.getPackageUidAsUser(comp.getPackageName(), UserHandle.USER_SYSTEM); 171 } catch (PackageManager.NameNotFoundException e) { 172 throw new SecurityException( 173 "Could not check network stack UID; package not found.", e); 174 } 175 176 final int expectedUid = 177 inSystemProcess ? Process.SYSTEM_UID : Process.NETWORK_STACK_UID; 178 if (uid != expectedUid) { 179 throw new SecurityException("Invalid network stack UID: " + uid); 180 } 181 182 if (!inSystemProcess) { 183 checkModuleServicePermission(pm, comp, servicePermissionName); 184 } 185 186 return intent; 187 } 188 } 189 190 191 /** 192 * Add a {@link ConnectivityModuleHealthListener} to listen to network stack health events. 193 */ registerHealthListener(@onNull ConnectivityModuleHealthListener listener)194 public void registerHealthListener(@NonNull ConnectivityModuleHealthListener listener) { 195 synchronized (mHealthListeners) { 196 mHealthListeners.add(listener); 197 } 198 } 199 200 /** 201 * Start a module running in the network stack or system_server process. Should be called only 202 * once for each module per device startup. 203 * 204 * <p>This method will start a networking module either in the network stack 205 * process, or inside the system server on devices that do not support the corresponding 206 * mainline network . The corresponding networking module service's binder 207 * object will then be delivered asynchronously via the provided {@link ModuleServiceCallback}. 208 * 209 * @param serviceIntentBaseAction Base action to use for constructing the intent needed to 210 * bind to the corresponding module. 211 * @param servicePermissionName Permission to be held by the corresponding module. 212 */ startModuleService( @onNull String serviceIntentBaseAction, @NonNull String servicePermissionName, @NonNull ModuleServiceCallback callback)213 public void startModuleService( 214 @NonNull String serviceIntentBaseAction, 215 @NonNull String servicePermissionName, 216 @NonNull ModuleServiceCallback callback) { 217 log("Starting networking module " + serviceIntentBaseAction); 218 219 final PackageManager pm = mContext.getPackageManager(); 220 221 // Try to bind in-process if the device was shipped with an in-process version 222 Intent intent = mDeps.getModuleServiceIntent(pm, serviceIntentBaseAction, 223 servicePermissionName, true /* inSystemProcess */); 224 225 // Otherwise use the updatable module version 226 if (intent == null) { 227 intent = mDeps.getModuleServiceIntent(pm, serviceIntentBaseAction, 228 servicePermissionName, false /* inSystemProcess */); 229 log("Starting networking module in network_stack process"); 230 } else { 231 log("Starting networking module in system_server process"); 232 } 233 234 if (intent == null) { 235 maybeCrashWithTerribleFailure("Could not resolve the networking module", null); 236 return; 237 } 238 239 final String packageName = intent.getComponent().getPackageName(); 240 241 // Start the network stack. The service will be added to the service manager by the 242 // corresponding client in ModuleServiceCallback.onModuleServiceConnected(). 243 if (!mContext.bindServiceAsUser( 244 intent, new ModuleServiceConnection(packageName, callback), 245 Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM)) { 246 maybeCrashWithTerribleFailure( 247 "Could not bind to networking module in-process, or in app with " 248 + intent, packageName); 249 return; 250 } 251 252 log("Networking module service start requested"); 253 } 254 255 private class ModuleServiceConnection implements ServiceConnection { 256 @NonNull 257 private final String mPackageName; 258 @NonNull 259 private final ModuleServiceCallback mModuleServiceCallback; 260 ModuleServiceConnection( @onNull String packageName, @NonNull ModuleServiceCallback moduleCallback)261 private ModuleServiceConnection( 262 @NonNull String packageName, 263 @NonNull ModuleServiceCallback moduleCallback) { 264 mPackageName = packageName; 265 mModuleServiceCallback = moduleCallback; 266 } 267 268 @Override onServiceConnected(ComponentName name, IBinder service)269 public void onServiceConnected(ComponentName name, IBinder service) { 270 logi("Networking module service connected"); 271 mModuleServiceCallback.onModuleServiceConnected(service); 272 } 273 274 @Override onServiceDisconnected(ComponentName name)275 public void onServiceDisconnected(ComponentName name) { 276 // onServiceDisconnected is not being called on device shutdown, so this method being 277 // called always indicates a bad state for the system server. 278 // This code path is only run by the system server: only the system server binds 279 // to the NetworkStack as a service. Other processes get the NetworkStack from 280 // the ServiceManager. 281 maybeCrashWithTerribleFailure( 282 "Lost network stack. This is not the root cause of any issue, it is a side " 283 + "effect of a crash that happened earlier. Earlier logs should point to the " 284 + "actual issue.", mPackageName); 285 } 286 } 287 checkModuleServicePermission( @onNull PackageManager pm, @NonNull ComponentName comp, @NonNull String servicePermissionName)288 private static void checkModuleServicePermission( 289 @NonNull PackageManager pm, @NonNull ComponentName comp, 290 @NonNull String servicePermissionName) { 291 final int hasPermission = 292 pm.checkPermission(servicePermissionName, comp.getPackageName()); 293 if (hasPermission != PERMISSION_GRANTED) { 294 throw new SecurityException( 295 "Networking module does not have permission " + servicePermissionName); 296 } 297 } 298 maybeCrashWithTerribleFailure(@onNull String message, @Nullable String packageName)299 private synchronized void maybeCrashWithTerribleFailure(@NonNull String message, 300 @Nullable String packageName) { 301 logWtf(message, null); 302 // uptime is monotonic even after a framework restart 303 final long uptime = SystemClock.elapsedRealtime(); 304 final long now = System.currentTimeMillis(); 305 final long minCrashIntervalMs = DeviceConfig.getLong(DeviceConfig.NAMESPACE_CONNECTIVITY, 306 CONFIG_MIN_CRASH_INTERVAL_MS, DEFAULT_MIN_CRASH_INTERVAL_MS); 307 final long minUptimeBeforeCrash = DeviceConfig.getLong(DeviceConfig.NAMESPACE_CONNECTIVITY, 308 CONFIG_MIN_UPTIME_BEFORE_CRASH_MS, DEFAULT_MIN_UPTIME_BEFORE_CRASH_MS); 309 final boolean alwaysRatelimit = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_CONNECTIVITY, 310 CONFIG_ALWAYS_RATELIMIT_NETWORKSTACK_CRASH, false); 311 312 final SharedPreferences prefs = getSharedPreferences(); 313 final long lastCrashTime = tryGetLastCrashTime(prefs); 314 315 // Only crash if there was enough time since boot, and (if known) enough time passed since 316 // the last crash. 317 // time and lastCrashTime may be unreliable if devices have incorrect clock time, but they 318 // are only used to limit the number of crashes compared to only using the time since boot, 319 // which would also be OK behavior by itself. 320 // - If lastCrashTime is incorrectly more than the current time, only look at uptime 321 // - If it is much less than current time, only look at uptime 322 // - If current time is during the next few hours after last crash time, don't crash. 323 // Considering that this only matters if last boot was some time ago, it's likely that 324 // time will be set correctly. Otherwise, not crashing is not a big problem anyway. Being 325 // in this last state would also not last for long since the window is only a few hours. 326 final boolean alwaysCrash = Build.IS_DEBUGGABLE && !alwaysRatelimit; 327 final boolean justBooted = uptime < minUptimeBeforeCrash; 328 final boolean haveLastCrashTime = (lastCrashTime != 0) && (lastCrashTime < now); 329 final boolean haveKnownRecentCrash = 330 haveLastCrashTime && (now < lastCrashTime + minCrashIntervalMs); 331 if (alwaysCrash || (!justBooted && !haveKnownRecentCrash)) { 332 // The system is not bound to its network stack (for example due to a crash in the 333 // network stack process): better crash rather than stay in a bad state where all 334 // networking is broken. 335 // Using device-encrypted SharedPreferences as DeviceConfig does not have a synchronous 336 // API to persist settings before a crash. 337 tryWriteLastCrashTime(prefs, now); 338 throw new IllegalStateException(message); 339 } 340 341 // Here the system crashed recently already. Inform listeners that something is 342 // definitely wrong. 343 if (packageName != null) { 344 final ArraySet<ConnectivityModuleHealthListener> listeners; 345 synchronized (mHealthListeners) { 346 listeners = new ArraySet<>(mHealthListeners); 347 } 348 for (ConnectivityModuleHealthListener listener : listeners) { 349 listener.onNetworkStackFailure(packageName); 350 } 351 } 352 } 353 354 @Nullable 355 private SharedPreferences getSharedPreferences() { 356 try { 357 final File prefsFile = new File( 358 Environment.getDataSystemDeDirectory(UserHandle.USER_SYSTEM), PREFS_FILE); 359 return mContext.createDeviceProtectedStorageContext() 360 .getSharedPreferences(prefsFile, Context.MODE_PRIVATE); 361 } catch (Throwable e) { 362 logWtf("Error loading shared preferences", e); 363 return null; 364 } 365 } 366 367 private long tryGetLastCrashTime(@Nullable SharedPreferences prefs) { 368 if (prefs == null) return 0L; 369 try { 370 return prefs.getLong(PREF_KEY_LAST_CRASH_TIME, 0L); 371 } catch (Throwable e) { 372 logWtf("Error getting last crash time", e); 373 return 0L; 374 } 375 } 376 377 private void tryWriteLastCrashTime(@Nullable SharedPreferences prefs, long value) { 378 if (prefs == null) return; 379 try { 380 prefs.edit().putLong(PREF_KEY_LAST_CRASH_TIME, value).commit(); 381 } catch (Throwable e) { 382 logWtf("Error writing last crash time", e); 383 } 384 } 385 386 private void log(@NonNull String message) { 387 Slog.d(TAG, message); 388 synchronized (mLog) { 389 mLog.log(message); 390 } 391 } 392 393 private void logWtf(@NonNull String message, @Nullable Throwable e) { 394 Slog.wtf(TAG, message, e); 395 synchronized (mLog) { 396 mLog.e(message); 397 } 398 } 399 400 private void loge(@NonNull String message, @Nullable Throwable e) { 401 Slog.e(TAG, message, e); 402 synchronized (mLog) { 403 mLog.e(message); 404 } 405 } 406 407 private void logi(@NonNull String message) { 408 Slog.i(TAG, message); 409 synchronized (mLog) { 410 mLog.i(message); 411 } 412 } 413 414 /** 415 * Dump ConnectivityModuleConnector logs to the specified {@link PrintWriter}. 416 */ 417 public void dump(PrintWriter pw) { 418 // dump is thread-safe on SharedLog 419 mLog.dump(null, pw, null); 420 } 421 } 422