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