• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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