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("Lost network stack", mPackageName); 282 } 283 } 284 checkModuleServicePermission( @onNull PackageManager pm, @NonNull ComponentName comp, @NonNull String servicePermissionName)285 private static void checkModuleServicePermission( 286 @NonNull PackageManager pm, @NonNull ComponentName comp, 287 @NonNull String servicePermissionName) { 288 final int hasPermission = 289 pm.checkPermission(servicePermissionName, comp.getPackageName()); 290 if (hasPermission != PERMISSION_GRANTED) { 291 throw new SecurityException( 292 "Networking module does not have permission " + servicePermissionName); 293 } 294 } 295 maybeCrashWithTerribleFailure(@onNull String message, @Nullable String packageName)296 private synchronized void maybeCrashWithTerribleFailure(@NonNull String message, 297 @Nullable String packageName) { 298 logWtf(message, null); 299 // uptime is monotonic even after a framework restart 300 final long uptime = SystemClock.elapsedRealtime(); 301 final long now = System.currentTimeMillis(); 302 final long minCrashIntervalMs = DeviceConfig.getLong(DeviceConfig.NAMESPACE_CONNECTIVITY, 303 CONFIG_MIN_CRASH_INTERVAL_MS, DEFAULT_MIN_CRASH_INTERVAL_MS); 304 final long minUptimeBeforeCrash = DeviceConfig.getLong(DeviceConfig.NAMESPACE_CONNECTIVITY, 305 CONFIG_MIN_UPTIME_BEFORE_CRASH_MS, DEFAULT_MIN_UPTIME_BEFORE_CRASH_MS); 306 final boolean alwaysRatelimit = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_CONNECTIVITY, 307 CONFIG_ALWAYS_RATELIMIT_NETWORKSTACK_CRASH, false); 308 309 final SharedPreferences prefs = getSharedPreferences(); 310 final long lastCrashTime = tryGetLastCrashTime(prefs); 311 312 // Only crash if there was enough time since boot, and (if known) enough time passed since 313 // the last crash. 314 // time and lastCrashTime may be unreliable if devices have incorrect clock time, but they 315 // are only used to limit the number of crashes compared to only using the time since boot, 316 // which would also be OK behavior by itself. 317 // - If lastCrashTime is incorrectly more than the current time, only look at uptime 318 // - If it is much less than current time, only look at uptime 319 // - If current time is during the next few hours after last crash time, don't crash. 320 // Considering that this only matters if last boot was some time ago, it's likely that 321 // time will be set correctly. Otherwise, not crashing is not a big problem anyway. Being 322 // in this last state would also not last for long since the window is only a few hours. 323 final boolean alwaysCrash = Build.IS_DEBUGGABLE && !alwaysRatelimit; 324 final boolean justBooted = uptime < minUptimeBeforeCrash; 325 final boolean haveLastCrashTime = (lastCrashTime != 0) && (lastCrashTime < now); 326 final boolean haveKnownRecentCrash = 327 haveLastCrashTime && (now < lastCrashTime + minCrashIntervalMs); 328 if (alwaysCrash || (!justBooted && !haveKnownRecentCrash)) { 329 // The system is not bound to its network stack (for example due to a crash in the 330 // network stack process): better crash rather than stay in a bad state where all 331 // networking is broken. 332 // Using device-encrypted SharedPreferences as DeviceConfig does not have a synchronous 333 // API to persist settings before a crash. 334 tryWriteLastCrashTime(prefs, now); 335 throw new IllegalStateException(message); 336 } 337 338 // Here the system crashed recently already. Inform listeners that something is 339 // definitely wrong. 340 if (packageName != null) { 341 final ArraySet<ConnectivityModuleHealthListener> listeners; 342 synchronized (mHealthListeners) { 343 listeners = new ArraySet<>(mHealthListeners); 344 } 345 for (ConnectivityModuleHealthListener listener : listeners) { 346 listener.onNetworkStackFailure(packageName); 347 } 348 } 349 } 350 351 @Nullable 352 private SharedPreferences getSharedPreferences() { 353 try { 354 final File prefsFile = new File( 355 Environment.getDataSystemDeDirectory(UserHandle.USER_SYSTEM), PREFS_FILE); 356 return mContext.createDeviceProtectedStorageContext() 357 .getSharedPreferences(prefsFile, Context.MODE_PRIVATE); 358 } catch (Throwable e) { 359 logWtf("Error loading shared preferences", e); 360 return null; 361 } 362 } 363 364 private long tryGetLastCrashTime(@Nullable SharedPreferences prefs) { 365 if (prefs == null) return 0L; 366 try { 367 return prefs.getLong(PREF_KEY_LAST_CRASH_TIME, 0L); 368 } catch (Throwable e) { 369 logWtf("Error getting last crash time", e); 370 return 0L; 371 } 372 } 373 374 private void tryWriteLastCrashTime(@Nullable SharedPreferences prefs, long value) { 375 if (prefs == null) return; 376 try { 377 prefs.edit().putLong(PREF_KEY_LAST_CRASH_TIME, value).commit(); 378 } catch (Throwable e) { 379 logWtf("Error writing last crash time", e); 380 } 381 } 382 383 private void log(@NonNull String message) { 384 Slog.d(TAG, message); 385 synchronized (mLog) { 386 mLog.log(message); 387 } 388 } 389 390 private void logWtf(@NonNull String message, @Nullable Throwable e) { 391 Slog.wtf(TAG, message, e); 392 synchronized (mLog) { 393 mLog.e(message); 394 } 395 } 396 397 private void loge(@NonNull String message, @Nullable Throwable e) { 398 Slog.e(TAG, message, e); 399 synchronized (mLog) { 400 mLog.e(message); 401 } 402 } 403 404 private void logi(@NonNull String message) { 405 Slog.i(TAG, message); 406 synchronized (mLog) { 407 mLog.i(message); 408 } 409 } 410 411 /** 412 * Dump ConnectivityModuleConnector logs to the specified {@link PrintWriter}. 413 */ 414 public void dump(PrintWriter pw) { 415 // dump is thread-safe on SharedLog 416 mLog.dump(null, pw, null); 417 } 418 } 419