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.os.Build; 29 import android.os.Environment; 30 import android.os.IBinder; 31 import android.os.Process; 32 import android.os.SystemClock; 33 import android.os.UserHandle; 34 import android.provider.DeviceConfig; 35 import android.text.format.DateUtils; 36 import android.util.ArraySet; 37 import android.util.Log; 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 45 /** 46 * Class used to communicate to the various networking mainline modules running in the network stack 47 * process from {@link com.android.server.SystemServer}. 48 * @hide 49 */ 50 public class ConnectivityModuleConnector { 51 private static final String TAG = ConnectivityModuleConnector.class.getSimpleName(); 52 private static final String IN_PROCESS_SUFFIX = ".InProcess"; 53 54 private static final String PREFS_FILE = "ConnectivityModuleConnector.xml"; 55 private static final String PREF_KEY_LAST_CRASH_TIME = "lastcrash_time"; 56 private static final String CONFIG_MIN_CRASH_INTERVAL_MS = "min_crash_interval"; 57 private static final String CONFIG_MIN_UPTIME_BEFORE_CRASH_MS = "min_uptime_before_crash"; 58 private static final String CONFIG_ALWAYS_RATELIMIT_NETWORKSTACK_CRASH = 59 "always_ratelimit_networkstack_crash"; 60 61 // Even if the network stack is lost, do not crash the system more often than this. 62 // Connectivity would be broken, but if the user needs the device for something urgent 63 // (like calling emergency services) we should not bootloop the device. 64 // This is the default value: the actual value can be adjusted via device config. 65 private static final long DEFAULT_MIN_CRASH_INTERVAL_MS = 6 * DateUtils.HOUR_IN_MILLIS; 66 67 // Even if the network stack is lost, do not crash the system server if it was less than 68 // this much after boot. This avoids bootlooping the device, and crashes should address very 69 // infrequent failures, not failures on boot. 70 private static final long DEFAULT_MIN_UPTIME_BEFORE_CRASH_MS = 30 * DateUtils.MINUTE_IN_MILLIS; 71 72 private static ConnectivityModuleConnector sInstance; 73 74 private Context mContext; 75 @GuardedBy("mHealthListeners") 76 private final ArraySet<ConnectivityModuleHealthListener> mHealthListeners = new ArraySet<>(); 77 @NonNull 78 private final Dependencies mDeps; 79 ConnectivityModuleConnector()80 private ConnectivityModuleConnector() { 81 this(new DependenciesImpl()); 82 } 83 84 @VisibleForTesting ConnectivityModuleConnector(@onNull Dependencies deps)85 ConnectivityModuleConnector(@NonNull Dependencies deps) { 86 mDeps = deps; 87 } 88 89 /** 90 * Get the {@link ConnectivityModuleConnector} singleton instance. 91 */ getInstance()92 public static synchronized ConnectivityModuleConnector getInstance() { 93 if (sInstance == null) { 94 sInstance = new ConnectivityModuleConnector(); 95 } 96 return sInstance; 97 } 98 99 /** 100 * Initialize the network stack connector. Should be called only once on device startup, before 101 * any client attempts to use the network stack. 102 */ init(Context context)103 public void init(Context context) { 104 log("Network stack init"); 105 mContext = context; 106 } 107 108 /** 109 * Callback interface for severe failures of the NetworkStack. 110 * 111 * <p>Useful for health monitors such as PackageWatchdog. 112 */ 113 public interface ConnectivityModuleHealthListener { 114 /** 115 * Called when there is a severe failure of the network stack. 116 * @param packageName Package name of the network stack. 117 */ onNetworkStackFailure(@onNull String packageName)118 void onNetworkStackFailure(@NonNull String packageName); 119 } 120 121 /** 122 * Callback invoked by the connector once the connection to the corresponding module is 123 * established. 124 */ 125 public interface ModuleServiceCallback { 126 /** 127 * Invoked when the corresponding service has connected. 128 * 129 * @param iBinder Binder object for the service. 130 */ onModuleServiceConnected(@onNull IBinder iBinder)131 void onModuleServiceConnected(@NonNull IBinder iBinder); 132 } 133 134 /** 135 * Interface used to determine the intent to use to bind to the module. Useful for testing. 136 */ 137 @VisibleForTesting 138 protected interface Dependencies { 139 /** 140 * Determine the intent to use to bind to the module. 141 * @return null if the intent could not be resolved, the intent otherwise. 142 */ 143 @Nullable getModuleServiceIntent( @onNull PackageManager pm, @NonNull String serviceIntentBaseAction, @NonNull String servicePermissionName, boolean inSystemProcess)144 Intent getModuleServiceIntent( 145 @NonNull PackageManager pm, @NonNull String serviceIntentBaseAction, 146 @NonNull String servicePermissionName, boolean inSystemProcess); 147 } 148 149 private static class DependenciesImpl implements Dependencies { 150 @Nullable 151 @Override getModuleServiceIntent( @onNull PackageManager pm, @NonNull String serviceIntentBaseAction, @NonNull String servicePermissionName, boolean inSystemProcess)152 public Intent getModuleServiceIntent( 153 @NonNull PackageManager pm, @NonNull String serviceIntentBaseAction, 154 @NonNull String servicePermissionName, boolean inSystemProcess) { 155 final Intent intent = 156 new Intent(inSystemProcess 157 ? serviceIntentBaseAction + IN_PROCESS_SUFFIX 158 : serviceIntentBaseAction); 159 final ComponentName comp = intent.resolveSystemService(pm, 0); 160 if (comp == null) { 161 return null; 162 } 163 intent.setComponent(comp); 164 165 final int uid; 166 try { 167 uid = pm.getPackageUidAsUser(comp.getPackageName(), UserHandle.USER_SYSTEM); 168 } catch (PackageManager.NameNotFoundException e) { 169 throw new SecurityException( 170 "Could not check network stack UID; package not found.", e); 171 } 172 173 final int expectedUid = 174 inSystemProcess ? Process.SYSTEM_UID : Process.NETWORK_STACK_UID; 175 if (uid != expectedUid) { 176 throw new SecurityException("Invalid network stack UID: " + uid); 177 } 178 179 if (!inSystemProcess) { 180 checkModuleServicePermission(pm, comp, servicePermissionName); 181 } 182 183 return intent; 184 } 185 } 186 187 188 /** 189 * Add a {@link ConnectivityModuleHealthListener} to listen to network stack health events. 190 */ registerHealthListener(@onNull ConnectivityModuleHealthListener listener)191 public void registerHealthListener(@NonNull ConnectivityModuleHealthListener listener) { 192 synchronized (mHealthListeners) { 193 mHealthListeners.add(listener); 194 } 195 } 196 197 /** 198 * Start a module running in the network stack or system_server process. Should be called only 199 * once for each module per device startup. 200 * 201 * <p>This method will start a networking module either in the network stack 202 * process, or inside the system server on devices that do not support the corresponding 203 * mainline network . The corresponding networking module service's binder 204 * object will then be delivered asynchronously via the provided {@link ModuleServiceCallback}. 205 * 206 * @param serviceIntentBaseAction Base action to use for constructing the intent needed to 207 * bind to the corresponding module. 208 * @param servicePermissionName Permission to be held by the corresponding module. 209 */ startModuleService( @onNull String serviceIntentBaseAction, @NonNull String servicePermissionName, @NonNull ModuleServiceCallback callback)210 public void startModuleService( 211 @NonNull String serviceIntentBaseAction, 212 @NonNull String servicePermissionName, 213 @NonNull ModuleServiceCallback callback) { 214 log("Starting networking module " + serviceIntentBaseAction); 215 216 final PackageManager pm = mContext.getPackageManager(); 217 218 // Try to bind in-process if the device was shipped with an in-process version 219 Intent intent = mDeps.getModuleServiceIntent(pm, serviceIntentBaseAction, 220 servicePermissionName, true /* inSystemProcess */); 221 222 // Otherwise use the updatable module version 223 if (intent == null) { 224 intent = mDeps.getModuleServiceIntent(pm, serviceIntentBaseAction, 225 servicePermissionName, false /* inSystemProcess */); 226 log("Starting networking module in network_stack process"); 227 } else { 228 log("Starting networking module in system_server process"); 229 } 230 231 if (intent == null) { 232 maybeCrashWithTerribleFailure("Could not resolve the networking module", null); 233 return; 234 } 235 236 final String packageName = intent.getComponent().getPackageName(); 237 238 // Start the network stack. The service will be added to the service manager by the 239 // corresponding client in ModuleServiceCallback.onModuleServiceConnected(). 240 if (!mContext.bindServiceAsUser( 241 intent, new ModuleServiceConnection(packageName, callback), 242 Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM)) { 243 maybeCrashWithTerribleFailure( 244 "Could not bind to networking module in-process, or in app with " 245 + intent, packageName); 246 return; 247 } 248 249 log("Networking module service start requested"); 250 } 251 252 private class ModuleServiceConnection implements ServiceConnection { 253 @NonNull 254 private final String mPackageName; 255 @NonNull 256 private final ModuleServiceCallback mModuleServiceCallback; 257 ModuleServiceConnection( @onNull String packageName, @NonNull ModuleServiceCallback moduleCallback)258 private ModuleServiceConnection( 259 @NonNull String packageName, 260 @NonNull ModuleServiceCallback moduleCallback) { 261 mPackageName = packageName; 262 mModuleServiceCallback = moduleCallback; 263 } 264 265 @Override onServiceConnected(ComponentName name, IBinder service)266 public void onServiceConnected(ComponentName name, IBinder service) { 267 logi("Networking module service connected"); 268 mModuleServiceCallback.onModuleServiceConnected(service); 269 } 270 271 @Override onServiceDisconnected(ComponentName name)272 public void onServiceDisconnected(ComponentName name) { 273 // onServiceDisconnected is not being called on device shutdown, so this method being 274 // called always indicates a bad state for the system server. 275 // This code path is only run by the system server: only the system server binds 276 // to the NetworkStack as a service. Other processes get the NetworkStack from 277 // the ServiceManager. 278 maybeCrashWithTerribleFailure( 279 "Lost network stack. This is not the root cause of any issue, it is a side " 280 + "effect of a crash that happened earlier. Earlier logs should point to the " 281 + "actual issue.", 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 Log.d(TAG, message); 385 } 386 387 private void logWtf(@NonNull String message, @Nullable Throwable e) { 388 Slog.wtf(TAG, message, e); 389 Log.e(TAG, message, e); 390 } 391 392 private void loge(@NonNull String message, @Nullable Throwable e) { 393 Log.e(TAG, message, e); 394 } 395 396 private void logi(@NonNull String message) { 397 Log.i(TAG, message); 398 } 399 } 400