diff options
author | Roshan Pius <rpius@google.com> | 2019-09-26 19:32:05 +0000 |
---|---|---|
committer | David Su <dysu@google.com> | 2019-09-26 12:39:07 -0700 |
commit | af908ff619accf068bcdca827687d1fe2d863395 (patch) | |
tree | 9a39186b3ca3a5541a84ad75e82f9fcd5e63520d /service | |
parent | 00d5009b0280a2e9854a3d2f35a1613a9b61a1f6 (diff) |
Revert "Revert "Start Wifi only after boot completes""
This reverts commit 144a530c6303f0dfb06a092bef3125a13d3c96f7.
Reason for revert: b/141624112
Change-Id: I94eb83a006a726fed43d48f3bca2039b3bf801ff
Diffstat (limited to 'service')
-rw-r--r-- | service/AndroidManifest.xml | 8 | ||||
-rw-r--r-- | service/AndroidManifest_InProcess.xml | 6 | ||||
-rw-r--r-- | service/java/com/android/server/wifi/BootCompleteReceiver.java | 98 | ||||
-rw-r--r-- | service/java/com/android/server/wifi/WifiStackService.java | 68 |
4 files changed, 150 insertions, 30 deletions
diff --git a/service/AndroidManifest.xml b/service/AndroidManifest.xml index ee05237ed..878b7d51b 100644 --- a/service/AndroidManifest.xml +++ b/service/AndroidManifest.xml @@ -69,7 +69,8 @@ <!-- TODO(b/135691051): Need to move to network stack process. "android:process="com.google.android.networkstack" This is not possible currently because hidden API access is denied when run on network - stack process --> + stack process. + <receiver> also needs to run on the networkstack process. --> <application android:persistent="true" android:directBootAware="true" @@ -81,5 +82,10 @@ <action android:name="android.net.wifi.IWifiStackConnector"/> </intent-filter> </service> + <receiver android:name="com.android.server.wifi.BootCompleteReceiver"> + <intent-filter> + <action android:name="android.intent.action.BOOT_COMPLETED" /> + </intent-filter> + </receiver> </application> </manifest> diff --git a/service/AndroidManifest_InProcess.xml b/service/AndroidManifest_InProcess.xml index 866f129fb..c480a464a 100644 --- a/service/AndroidManifest_InProcess.xml +++ b/service/AndroidManifest_InProcess.xml @@ -37,5 +37,11 @@ <action android:name="android.net.wifi.IWifiStackConnector.InProcess"/> </intent-filter> </service> + <receiver android:name="com.android.server.wifi.BootCompleteReceiver" + android:process="system"> + <intent-filter> + <action android:name="android.intent.action.BOOT_COMPLETED" /> + </intent-filter> + </receiver> </application> </manifest> diff --git a/service/java/com/android/server/wifi/BootCompleteReceiver.java b/service/java/com/android/server/wifi/BootCompleteReceiver.java new file mode 100644 index 000000000..86f263a2d --- /dev/null +++ b/service/java/com/android/server/wifi/BootCompleteReceiver.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wifi; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.concurrent.GuardedBy; + +/** + * Receives boot complete broadcast (registered in AndroidManifest.xml). + * + * Ensures that if WifiStack is initialized after boot complete, we can check whether boot was + * already completed, since if we start listening for the boot complete broadcast now it will be too + * late and we will never get the broadcast. + * + * This BroadcastReceiver can be registered multiple times in different places, and it will ensure + * that all registered callbacks are triggered exactly once. + */ +public class BootCompleteReceiver extends BroadcastReceiver { + private static final String TAG = "WifiBootCompleteReceiver"; + + private static final Object sLock = new Object(); + @GuardedBy("sLock") + private static boolean sIsBootCompleted = false; + @GuardedBy("sLock") + private static final List<Runnable> sCallbacks = new ArrayList<>(1); + + public BootCompleteReceiver() { + Log.d(TAG, "Constructed BootCompleteReceiver"); + } + + /** + * Registers a callback that will be triggered when boot is completed. Note that if boot has + * already been completed when the callback is registered, the callback will be triggered + * immediately. + * + * No guarantees are made about which thread the callback is triggered on. Please do not + * perform expensive operations in the callback, instead post to other threads. + */ + public static void registerCallback(Runnable callback) { + boolean runImmediately = false; + + synchronized (sLock) { + if (sIsBootCompleted) { + runImmediately = true; + } else { + sCallbacks.add(callback); + } + } + + // run callback outside of synchronized block + if (runImmediately) { + Log.d(TAG, "Triggering callback immediately since boot is already complete."); + callback.run(); + } else { + Log.d(TAG, "Enqueuing callback since boot is not yet complete."); + } + } + + @Override + public void onReceive(Context context, Intent intent) { + Log.d(TAG, "Received boot complete broadcast"); + + List<Runnable> callbacks = new ArrayList<>(1); + + synchronized (sLock) { + sIsBootCompleted = true; + callbacks.addAll(sCallbacks); + sCallbacks.clear(); + } + + // run callbacks outside of synchronized block + for (Runnable r : callbacks) { + Log.d(TAG, "Triggered callback"); + r.run(); + } + } +} diff --git a/service/java/com/android/server/wifi/WifiStackService.java b/service/java/com/android/server/wifi/WifiStackService.java index dbf3882eb..8887005b0 100644 --- a/service/java/com/android/server/wifi/WifiStackService.java +++ b/service/java/com/android/server/wifi/WifiStackService.java @@ -31,8 +31,11 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.net.wifi.IWifiStackConnector; +import android.net.wifi.WifiApiServiceInfo; import android.os.Binder; +import android.os.Handler; import android.os.IBinder; +import android.os.Looper; import android.os.UserHandle; import android.os.storage.StorageManager; import android.util.Log; @@ -47,6 +50,7 @@ import com.android.server.wifi.scanner.WifiScanningService; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; +import java.util.stream.Collectors; /** * Android service used to start the wifi stack when bound to via an intent. @@ -67,41 +71,21 @@ public class WifiStackService extends Service { } @Override - public IBinder retrieveApiServiceImpl(@NonNull String serviceName) { + public List<WifiApiServiceInfo> getWifiApiServiceInfos() { // Ensure this is being invoked from system_server only. mContext.enforceCallingOrSelfPermission( android.Manifest.permission.NETWORK_STACK, "WifiStackService"); long ident = Binder.clearCallingIdentity(); try { synchronized (mApiServices) { - WifiServiceBase serviceBase = mApiServices.get(serviceName); - if (serviceBase == null) return null; - return serviceBase.retrieveImpl(); - } - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - @Override - public boolean startApiService(@NonNull String serviceName) { - // Ensure this is being invoked from system_server only. - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.NETWORK_STACK, "WifiStackService"); - long ident = Binder.clearCallingIdentity(); - try { - synchronized (mApiServices) { - WifiServiceBase serviceBase = mApiServices.get(serviceName); - if (serviceBase == null) return false; - serviceBase.onStart(); - // The current active user might have switched before the wifi services - // started up. So, send a onSwitchUser callback just after onStart callback is - // invoked. - int currentUser = ActivityManager.getCurrentUser(); - if (currentUser != UserHandle.USER_SYSTEM) { - serviceBase.onSwitchUser(currentUser); - } - return true; + return mApiServices.entrySet().stream() + .map(entry -> { + WifiApiServiceInfo service = new WifiApiServiceInfo(); + service.name = entry.getKey(); + service.binder = entry.getValue().retrieveImpl(); + return service; + }) + .collect(Collectors.toList()); } } finally { Binder.restoreCallingIdentity(ident); @@ -191,6 +175,16 @@ public class WifiStackService extends Service { return false; } + // BootCompleteReceiver is registered in AndroidManifest.xml and here. The receiver + // registered here is triggered earlier, while the receiver registered in the manifest + // is more reliable since it is registered earlier, so we are guaranteed to get the + // broadcast (if we register too late the broadcast may have already triggered and we + // would have missed it). Register in both places and BootCompleteReceiver will ensure that + // callbacks are called exactly once. + Log.d(TAG, "Registering BootCompleteReceiver to listen for ACTION_BOOT_COMPLETED"); + context.registerReceiver(new BootCompleteReceiver(), + new IntentFilter(Intent.ACTION_BOOT_COMPLETED)); + synchronized (mApiServices) { // Top level wifi feature flag. if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) { @@ -219,6 +213,22 @@ public class WifiStackService extends Service { } } + Handler handler = new Handler(Looper.myLooper()); + // register callback to start Wifi services after boot completes + BootCompleteReceiver.registerCallback(() -> handler.post(() -> { + int currentUser = ActivityManager.getCurrentUser(); + synchronized (mApiServices) { + for (WifiServiceBase service : mApiServices.values()) { + service.onStart(); + // The current active user might have switched before the wifi services started + // up. So, send a onSwitchUser callback just after onStart callback is invoked. + if (currentUser != UserHandle.USER_SYSTEM) { + service.onSwitchUser(currentUser); + } + } + } + })); + // Register broadcast receiver for system events. IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_USER_SWITCHED); |