| /* |
| * Copyright (C) 2016 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 static android.net.wifi.WifiManager.WIFI_STATE_DISABLED; |
| import static android.net.wifi.WifiManager.WIFI_STATE_DISABLING; |
| import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED; |
| import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING; |
| import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN; |
| |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.net.ConnectivityManager; |
| import android.net.ConnectivityManager.NetworkCallback; |
| import android.net.DhcpResultsParcelable; |
| import android.net.Network; |
| import android.net.NetworkCapabilities; |
| import android.net.NetworkRequest; |
| import android.net.wifi.IWifiConnectedNetworkScorer; |
| import android.net.wifi.WifiAnnotations; |
| import android.net.wifi.WifiConfiguration; |
| import android.net.wifi.WifiInfo; |
| import android.net.wifi.WifiManager; |
| import android.net.wifi.hotspot2.IProvisioningCallback; |
| import android.net.wifi.hotspot2.OsuProvider; |
| import android.net.wifi.nl80211.DeviceWiphyCapabilities; |
| import android.os.Handler; |
| import android.os.HandlerExecutor; |
| import android.os.IBinder; |
| import android.os.Looper; |
| import android.os.Message; |
| import android.os.PersistableBundle; |
| import android.os.UserHandle; |
| import android.os.WorkSource; |
| import android.telephony.AccessNetworkConstants; |
| import android.telephony.CarrierConfigManager; |
| import android.telephony.SubscriptionInfo; |
| import android.telephony.SubscriptionManager; |
| import android.telephony.ims.ImsException; |
| import android.telephony.ims.ImsMmTelManager; |
| import android.telephony.ims.ImsReasonInfo; |
| import android.telephony.ims.RegistrationManager; |
| import android.telephony.ims.feature.MmTelFeature; |
| import android.telephony.ims.stub.ImsRegistrationImplBase; |
| import android.text.TextUtils; |
| import android.util.Log; |
| |
| import com.android.internal.util.IState; |
| import com.android.internal.util.State; |
| import com.android.internal.util.StateMachine; |
| import com.android.server.wifi.WifiNative.InterfaceCallback; |
| import com.android.server.wifi.WifiNative.RxFateReport; |
| import com.android.server.wifi.WifiNative.TxFateReport; |
| import com.android.server.wifi.util.ActionListenerWrapper; |
| import com.android.server.wifi.util.StateMachineObituary; |
| import com.android.server.wifi.util.WifiHandler; |
| import com.android.wifi.resources.R; |
| |
| import java.io.FileDescriptor; |
| import java.io.PrintWriter; |
| import java.util.ArrayDeque; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.concurrent.atomic.AtomicInteger; |
| |
| /** |
| * Manage WiFi in Client Mode where we connect to configured networks and in Scan Only Mode where |
| * we do not connect to configured networks but do perform scanning. |
| * |
| * An instance of this class is active to manage each client interface. This is in contrast to |
| * {@link DefaultClientModeManager} which handles calls when no client interfaces are active. |
| * |
| * This class will dynamically instantiate {@link ClientModeImpl} when it enters client mode, and |
| * tear it down when it exits client mode. No instance of ClientModeImpl will be active in |
| * scan-only mode, instead {@link ScanOnlyModeImpl} will be used to respond to calls. |
| * |
| * <pre> |
| * ActiveModeWarden |
| * / \ |
| * / \ |
| * ConcreteClientModeManager DefaultClientModeManager |
| * (Client Mode + Scan Only Mode) (Wifi off) |
| * / \ |
| * / \ |
| * ClientModeImpl ScanOnlyModeImpl |
| * </pre> |
| */ |
| public class ConcreteClientModeManager implements ClientModeManager { |
| private static final String TAG = "WifiClientModeManager"; |
| |
| private final ClientModeStateMachine mStateMachine; |
| |
| private final Context mContext; |
| private final Clock mClock; |
| private final WifiNative mWifiNative; |
| private final WifiMetrics mWifiMetrics; |
| private final WakeupController mWakeupController; |
| private final WifiInjector mWifiInjector; |
| private final SelfRecovery mSelfRecovery; |
| private final WifiGlobals mWifiGlobals; |
| private final DefaultClientModeManager mDefaultClientModeManager; |
| private final ClientModeManagerBroadcastQueue mBroadcastQueue; |
| private final long mId; |
| private final Graveyard mGraveyard = new Graveyard(); |
| |
| private String mClientInterfaceName; |
| private boolean mIfaceIsUp = false; |
| private boolean mShouldReduceNetworkScore = false; |
| private final DeferStopHandler mDeferStopHandler; |
| @Nullable |
| private ClientRole mRole = null; |
| @Nullable |
| private ClientRole mPreviousRole = null; |
| private long mLastRoleChangeSinceBootMs = 0; |
| @Nullable |
| private WorkSource mRequestorWs = null; |
| @NonNull |
| private Listener<ConcreteClientModeManager> mModeListener; |
| /** Caches the latest role change request. This is needed for the IMS dereg delay */ |
| @Nullable |
| private RoleChangeInfo mTargetRoleChangeInfo; |
| private boolean mVerboseLoggingEnabled = false; |
| private int mActiveSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; |
| private boolean mWifiStateChangeBroadcastEnabled = true; |
| /** |
| * mClientModeImpl is only non-null when in {@link ClientModeStateMachine.ConnectModeState} - |
| * it will be null in all other states |
| */ |
| @Nullable |
| private ClientModeImpl mClientModeImpl = null; |
| |
| @Nullable |
| private ScanOnlyModeImpl mScanOnlyModeImpl = null; |
| |
| /** |
| * One of {@link WifiManager#WIFI_STATE_DISABLED}, |
| * {@link WifiManager#WIFI_STATE_DISABLING}, |
| * {@link WifiManager#WIFI_STATE_ENABLED}, |
| * {@link WifiManager#WIFI_STATE_ENABLING}, |
| * {@link WifiManager#WIFI_STATE_UNKNOWN} |
| */ |
| private final AtomicInteger mWifiState = new AtomicInteger(WIFI_STATE_DISABLED); |
| |
| private boolean mIsStopped = true; |
| |
| ConcreteClientModeManager( |
| Context context, @NonNull Looper looper, Clock clock, |
| WifiNative wifiNative, @NonNull Listener<ConcreteClientModeManager> listener, |
| WifiMetrics wifiMetrics, |
| WakeupController wakeupController, WifiInjector wifiInjector, |
| SelfRecovery selfRecovery, WifiGlobals wifiGlobals, |
| DefaultClientModeManager defaultClientModeManager, long id, |
| @NonNull WorkSource requestorWs, @NonNull ClientRole role, |
| @NonNull ClientModeManagerBroadcastQueue broadcastQueue, |
| boolean verboseLoggingEnabled) { |
| mContext = context; |
| mClock = clock; |
| mWifiNative = wifiNative; |
| mModeListener = listener; |
| mWifiMetrics = wifiMetrics; |
| mWakeupController = wakeupController; |
| mWifiInjector = wifiInjector; |
| mStateMachine = new ClientModeStateMachine(looper); |
| mDeferStopHandler = new DeferStopHandler(looper); |
| mSelfRecovery = selfRecovery; |
| mWifiGlobals = wifiGlobals; |
| mDefaultClientModeManager = defaultClientModeManager; |
| mId = id; |
| mTargetRoleChangeInfo = new RoleChangeInfo(role, requestorWs, listener); |
| mBroadcastQueue = broadcastQueue; |
| enableVerboseLogging(verboseLoggingEnabled); |
| mStateMachine.sendMessage(ClientModeStateMachine.CMD_START, mTargetRoleChangeInfo); |
| } |
| |
| private String getTag() { |
| return TAG + "[" + (mClientInterfaceName == null ? "unknown" : mClientInterfaceName) + "]"; |
| } |
| |
| /** |
| * Sets whether to send WIFI_STATE_CHANGED broadcast for this ClientModeManager. |
| * @param enabled |
| */ |
| public void setWifiStateChangeBroadcastEnabled(boolean enabled) { |
| mWifiStateChangeBroadcastEnabled = enabled; |
| } |
| |
| /** |
| * Disconnect from any currently connected networks and stop client mode. |
| */ |
| @Override |
| public void stop() { |
| Log.d(getTag(), " currentstate: " + getCurrentStateName()); |
| mTargetRoleChangeInfo = null; |
| if (mIfaceIsUp) { |
| updateConnectModeState(mRole, WifiManager.WIFI_STATE_DISABLING, |
| WifiManager.WIFI_STATE_ENABLED); |
| } else { |
| updateConnectModeState(mRole, WifiManager.WIFI_STATE_DISABLING, |
| WifiManager.WIFI_STATE_ENABLING); |
| } |
| mDeferStopHandler.start(getWifiOffDeferringTimeMs()); |
| } |
| |
| private class DeferStopHandler extends WifiHandler { |
| private boolean mIsDeferring = false; |
| private ImsMmTelManager mImsMmTelManager = null; |
| private Looper mLooper = null; |
| private final Runnable mRunnable = () -> continueToStopWifi(); |
| private int mMaximumDeferringTimeMillis = 0; |
| private long mDeferringStartTimeMillis = 0; |
| private ConnectivityManager mConnectivityManager = null; |
| private boolean mIsImsNetworkLost = false; |
| private boolean mIsImsNetworkUnregistered = false; |
| |
| private final RegistrationManager.RegistrationCallback mImsRegistrationCallback = |
| new RegistrationManager.RegistrationCallback() { |
| @Override |
| public void onRegistered(int imsRadioTech) { |
| Log.d(getTag(), "on IMS registered on type " + imsRadioTech); |
| if (!mIsDeferring) return; |
| |
| if (imsRadioTech != AccessNetworkConstants.TRANSPORT_TYPE_WLAN) { |
| continueToStopWifi(); |
| } |
| } |
| |
| @Override |
| public void onUnregistered(ImsReasonInfo imsReasonInfo) { |
| Log.d(getTag(), "on IMS unregistered"); |
| mIsImsNetworkUnregistered = true; |
| checkAndContinueToStopWifi(); |
| } |
| }; |
| |
| private final class ImsNetworkCallback extends NetworkCallback { |
| private int mRegisteredImsNetworkCount = 0; |
| |
| @Override |
| public void onAvailable(Network network) { |
| synchronized (this) { |
| Log.d(getTag(), "IMS network available: " + network); |
| mRegisteredImsNetworkCount++; |
| } |
| } |
| |
| @Override |
| public void onLost(Network network) { |
| synchronized (this) { |
| Log.d(getTag(), "IMS network lost: " + network |
| + " ,isDeferring: " + mIsDeferring |
| + " ,registered IMS network count: " + mRegisteredImsNetworkCount); |
| mRegisteredImsNetworkCount--; |
| if (mIsDeferring && mRegisteredImsNetworkCount <= 0) { |
| mRegisteredImsNetworkCount = 0; |
| mIsImsNetworkLost = true; |
| checkAndContinueToStopWifi(); |
| } |
| } |
| } |
| } |
| |
| private NetworkCallback mImsNetworkCallback = null; |
| |
| DeferStopHandler(Looper looper) { |
| super(TAG, looper); |
| mLooper = looper; |
| } |
| |
| public void start(int delayMs) { |
| if (mIsDeferring) return; |
| |
| mMaximumDeferringTimeMillis = delayMs; |
| mDeferringStartTimeMillis = mClock.getElapsedSinceBootMillis(); |
| // Most cases don't need delay, check it first to avoid unnecessary work. |
| if (delayMs == 0) { |
| continueToStopWifi(); |
| return; |
| } |
| |
| mImsMmTelManager = ImsMmTelManager.createForSubscriptionId(mActiveSubId); |
| if (mImsMmTelManager == null || !postDelayed(mRunnable, delayMs)) { |
| // if no delay or failed to add runnable, stop Wifi immediately. |
| continueToStopWifi(); |
| return; |
| } |
| |
| mIsDeferring = true; |
| Log.d(getTag(), "Start DeferWifiOff handler with deferring time " |
| + delayMs + " ms for subId: " + mActiveSubId); |
| try { |
| mImsMmTelManager.registerImsRegistrationCallback( |
| new HandlerExecutor(new Handler(mLooper)), |
| mImsRegistrationCallback); |
| } catch (RuntimeException | ImsException e) { |
| Log.e(getTag(), "registerImsRegistrationCallback failed", e); |
| continueToStopWifi(); |
| return; |
| } |
| |
| NetworkRequest imsRequest = new NetworkRequest.Builder() |
| .addCapability(NetworkCapabilities.NET_CAPABILITY_IMS) |
| .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) |
| .build(); |
| |
| mConnectivityManager = |
| (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); |
| |
| mImsNetworkCallback = new ImsNetworkCallback(); |
| mConnectivityManager.registerNetworkCallback(imsRequest, mImsNetworkCallback, |
| new Handler(mLooper)); |
| } |
| |
| private void checkAndContinueToStopWifi() { |
| if (mIsImsNetworkLost && mIsImsNetworkUnregistered) { |
| // Add delay for targets where IMS PDN down at modem takes additional delay. |
| int delay = mContext.getResources() |
| .getInteger(R.integer.config_wifiDelayDisconnectOnImsLostMs); |
| if (delay == 0 || !postDelayed(mRunnable, delay)) { |
| continueToStopWifi(); |
| } |
| } |
| } |
| |
| private void continueToStopWifi() { |
| Log.d(getTag(), "The target role change info " + mTargetRoleChangeInfo); |
| |
| int deferringDurationMillis = |
| (int) (mClock.getElapsedSinceBootMillis() - mDeferringStartTimeMillis); |
| boolean isTimedOut = mMaximumDeferringTimeMillis > 0 |
| && deferringDurationMillis >= mMaximumDeferringTimeMillis; |
| if (mTargetRoleChangeInfo == null) { |
| Log.d(getTag(), "Continue to stop wifi"); |
| mStateMachine.captureObituaryAndQuitNow(); |
| mWifiMetrics.noteWifiOff(mIsDeferring, isTimedOut, deferringDurationMillis); |
| } else if (mTargetRoleChangeInfo.role == ROLE_CLIENT_SCAN_ONLY) { |
| if (!mWifiNative.switchClientInterfaceToScanMode( |
| mClientInterfaceName, mTargetRoleChangeInfo.requestorWs)) { |
| mModeListener.onStartFailure(ConcreteClientModeManager.this); |
| } else { |
| mStateMachine.sendMessage( |
| ClientModeStateMachine.CMD_SWITCH_TO_SCAN_ONLY_MODE_CONTINUE, |
| mTargetRoleChangeInfo); |
| mWifiMetrics.noteWifiOff(mIsDeferring, isTimedOut, deferringDurationMillis); |
| } |
| } else { |
| updateConnectModeState(mRole, WifiManager.WIFI_STATE_ENABLED, |
| WifiManager.WIFI_STATE_DISABLING); |
| } |
| |
| if (!mIsDeferring) return; |
| |
| Log.d(getTag(), "Stop DeferWifiOff handler."); |
| removeCallbacks(mRunnable); |
| if (mImsMmTelManager != null) { |
| try { |
| mImsMmTelManager.unregisterImsRegistrationCallback(mImsRegistrationCallback); |
| } catch (RuntimeException e) { |
| Log.e(getTag(), "unregisterImsRegistrationCallback failed", e); |
| } |
| } |
| |
| if (mConnectivityManager != null && mImsNetworkCallback != null) { |
| mConnectivityManager.unregisterNetworkCallback(mImsNetworkCallback); |
| mImsNetworkCallback = null; |
| } |
| |
| mIsDeferring = false; |
| mIsImsNetworkLost = false; |
| mIsImsNetworkUnregistered = false; |
| } |
| } |
| |
| /** |
| * Get deferring time before turning off WiFi. |
| */ |
| private int getWifiOffDeferringTimeMs() { |
| SubscriptionManager subscriptionManager = (SubscriptionManager) mContext.getSystemService( |
| Context.TELEPHONY_SUBSCRIPTION_SERVICE); |
| if (subscriptionManager == null) { |
| Log.d(getTag(), "SubscriptionManager not found"); |
| return 0; |
| } |
| |
| List<SubscriptionInfo> subInfoList = subscriptionManager.getActiveSubscriptionInfoList(); |
| if (subInfoList == null) { |
| Log.d(getTag(), "Active SubscriptionInfo list not found"); |
| return 0; |
| } |
| |
| // Get the maximum delay for the active subscription latched on IWLAN. |
| int maxDelay = 0; |
| for (SubscriptionInfo subInfo : subInfoList) { |
| int curDelay = getWifiOffDeferringTimeMs(subInfo.getSubscriptionId()); |
| if (curDelay > maxDelay) { |
| maxDelay = curDelay; |
| mActiveSubId = subInfo.getSubscriptionId(); |
| } |
| } |
| return maxDelay; |
| } |
| |
| private int getWifiOffDeferringTimeMs(int subId) { |
| if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { |
| Log.d(getTag(), "Invalid Subscription ID: " + subId); |
| return 0; |
| } |
| |
| ImsMmTelManager imsMmTelManager = ImsMmTelManager.createForSubscriptionId(subId); |
| // If no wifi calling, no delay |
| try { |
| if (!imsMmTelManager.isAvailable( |
| MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE, |
| ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN)) { |
| Log.d(getTag(), "IMS not registered over IWLAN for subId: " + subId); |
| return 0; |
| } |
| } catch (RuntimeException ex) { |
| Log.e(TAG, "IMS Manager is not available.", ex); |
| return 0; |
| } |
| |
| CarrierConfigManager configManager = |
| (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE); |
| PersistableBundle config = configManager.getConfigForSubId(subId); |
| return (config != null) |
| ? config.getInt(CarrierConfigManager.Ims.KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT) |
| : 0; |
| } |
| |
| @Override |
| @Nullable public ClientRole getRole() { |
| return mRole; |
| } |
| |
| @Override |
| @Nullable public ClientRole getPreviousRole() { |
| return mPreviousRole; |
| } |
| |
| @Override |
| public long getLastRoleChangeSinceBootMs() { |
| return mLastRoleChangeSinceBootMs; |
| } |
| |
| /** |
| * Class to hold info needed for role change. |
| */ |
| private static class RoleChangeInfo { |
| @Nullable public final ClientRole role; |
| @Nullable public final WorkSource requestorWs; |
| @Nullable public final Listener<ConcreteClientModeManager> modeListener; |
| |
| RoleChangeInfo(@Nullable ClientRole role) { |
| this(role, null, null); |
| } |
| |
| RoleChangeInfo(@Nullable ClientRole role, @Nullable WorkSource requestorWs, |
| @Nullable Listener<ConcreteClientModeManager> modeListener) { |
| this.role = role; |
| this.requestorWs = requestorWs; |
| this.modeListener = modeListener; |
| } |
| |
| @Override |
| public String toString() { |
| return "Role: " + role + ", RequestorWs: " + requestorWs |
| + ", ModeListener: " + modeListener; |
| } |
| } |
| |
| /** Set the role of this ClientModeManager */ |
| public void setRole(@NonNull ClientRole role, @NonNull WorkSource requestorWs) { |
| setRole(role, requestorWs, null); |
| } |
| |
| /** Set the role of this ClientModeManager */ |
| public void setRole(@NonNull ClientRole role, @NonNull WorkSource requestorWs, |
| @Nullable Listener<ConcreteClientModeManager> modeListener) { |
| mTargetRoleChangeInfo = new RoleChangeInfo(role, requestorWs, modeListener); |
| if (role == ROLE_CLIENT_SCAN_ONLY) { |
| // Switch client mode manager to scan only mode. |
| mStateMachine.sendMessage( |
| ClientModeStateMachine.CMD_SWITCH_TO_SCAN_ONLY_MODE); |
| } else { |
| // Switch client mode manager to connect mode. |
| mStateMachine.sendMessage( |
| ClientModeStateMachine.CMD_SWITCH_TO_CONNECT_MODE, |
| mTargetRoleChangeInfo); |
| } |
| } |
| |
| @Override |
| public String getInterfaceName() { |
| return mClientInterfaceName; |
| } |
| |
| @Override |
| public WorkSource getRequestorWs() { |
| return mRequestorWs; |
| } |
| |
| /** |
| * Keep stopped {@link ClientModeImpl} instances so that they can be dumped to aid debugging. |
| * |
| * TODO(b/160283853): Find a smarter way to evict old ClientModeImpls |
| */ |
| private static class Graveyard { |
| private static final int INSTANCES_TO_KEEP = 3; |
| |
| private final ArrayDeque<ClientModeImpl> mClientModeImpls = new ArrayDeque<>(); |
| |
| /** |
| * Add this stopped {@link ClientModeImpl} to the graveyard, and evict the oldest |
| * ClientModeImpl if the graveyard is full. |
| */ |
| void inter(ClientModeImpl clientModeImpl) { |
| if (mClientModeImpls.size() == INSTANCES_TO_KEEP) { |
| mClientModeImpls.removeFirst(); |
| } |
| mClientModeImpls.addLast(clientModeImpl); |
| } |
| |
| /** Dump the contents of the graveyard. */ |
| void dump(FileDescriptor fd, PrintWriter pw, String[] args) { |
| pw.println("Dump of ConcreteClientModeManager.Graveyard"); |
| pw.println("Stopped ClientModeImpls: " + mClientModeImpls.size() + " total"); |
| for (ClientModeImpl clientModeImpl : mClientModeImpls) { |
| clientModeImpl.dump(fd, pw, args); |
| } |
| pw.println(); |
| } |
| |
| boolean hasAllClientModeImplsQuit() { |
| for (ClientModeImpl cmi : mClientModeImpls) { |
| if (!cmi.hasQuit()) return false; |
| } |
| return true; |
| } |
| } |
| |
| /** |
| * Dump info about this ClientMode manager. |
| */ |
| @Override |
| public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { |
| pw.println("Dump of ClientModeManager id=" + mId); |
| pw.println("current StateMachine mode: " + getCurrentStateName()); |
| pw.println("mRole: " + mRole); |
| pw.println("mTargetRoleChangeInfo: " + mTargetRoleChangeInfo); |
| pw.println("mClientInterfaceName: " + mClientInterfaceName); |
| pw.println("mIfaceIsUp: " + mIfaceIsUp); |
| mStateMachine.dump(fd, pw, args); |
| pw.println(); |
| pw.println("Wi-Fi is " + syncGetWifiStateByName()); |
| if (mClientModeImpl == null) { |
| pw.println("No active ClientModeImpl instance"); |
| } else { |
| mClientModeImpl.dump(fd, pw, args); |
| } |
| mGraveyard.dump(fd, pw, args); |
| pw.println(); |
| } |
| |
| private String getCurrentStateName() { |
| IState currentState = mStateMachine.getCurrentState(); |
| |
| if (currentState != null) { |
| return currentState.getName(); |
| } |
| |
| return "StateMachine not active"; |
| } |
| |
| /** |
| * Update Wifi state and send the broadcast. |
| * |
| * @param role Target/Set role for this client mode manager instance. |
| * @param newState new Wifi state |
| * @param currentState current wifi state |
| */ |
| private void updateConnectModeState(ClientRole role, int newState, int currentState) { |
| setWifiStateForApiCalls(newState); |
| |
| if (newState == WifiManager.WIFI_STATE_UNKNOWN) { |
| // do not need to broadcast failure to system |
| return; |
| } |
| if (role != ROLE_CLIENT_PRIMARY || !mWifiStateChangeBroadcastEnabled) { |
| // do not raise public broadcast unless this is the primary client mode manager |
| return; |
| } |
| |
| // TODO(b/175839153): this broadcast should only be sent out when wifi is toggled on/off, |
| // not per CMM |
| final Intent intent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION); |
| intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); |
| intent.putExtra(WifiManager.EXTRA_WIFI_STATE, newState); |
| intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, currentState); |
| String summary = "broadcast=WIFI_STATE_CHANGED_ACTION" |
| + " EXTRA_WIFI_STATE=" + newState |
| + " EXTRA_PREVIOUS_WIFI_STATE=" + currentState; |
| if (mVerboseLoggingEnabled) Log.d(getTag(), "Queuing " + summary); |
| ClientModeManagerBroadcastQueue.QueuedBroadcast broadcast = |
| () -> { |
| if (mVerboseLoggingEnabled) Log.d(getTag(), "Sending " + summary); |
| mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); |
| }; |
| if (mRole == null && role == ROLE_CLIENT_PRIMARY) { |
| // This CMM is intended to be the primary, but has not completed the mode transition |
| // yet. Need to force broadcast to be sent. |
| broadcast.send(); |
| } else { |
| mBroadcastQueue.queueOrSendBroadcast(this, broadcast); |
| } |
| } |
| |
| private void setWifiStateForApiCalls(int newState) { |
| switch (newState) { |
| case WIFI_STATE_DISABLING: |
| case WIFI_STATE_DISABLED: |
| case WIFI_STATE_ENABLING: |
| case WIFI_STATE_ENABLED: |
| case WIFI_STATE_UNKNOWN: |
| if (mVerboseLoggingEnabled) { |
| Log.d(getTag(), "setting wifi state to: " + newState); |
| } |
| mWifiState.set(newState); |
| break; |
| default: |
| Log.d(getTag(), "attempted to set an invalid state: " + newState); |
| break; |
| } |
| } |
| |
| private String syncGetWifiStateByName() { |
| switch (mWifiState.get()) { |
| case WIFI_STATE_DISABLING: |
| return "disabling"; |
| case WIFI_STATE_DISABLED: |
| return "disabled"; |
| case WIFI_STATE_ENABLING: |
| return "enabling"; |
| case WIFI_STATE_ENABLED: |
| return "enabled"; |
| case WIFI_STATE_UNKNOWN: |
| return "unknown state"; |
| default: |
| return "[invalid state]"; |
| } |
| } |
| |
| private class ClientModeStateMachine extends StateMachine { |
| // Commands for the state machine. |
| public static final int CMD_START = 0; |
| public static final int CMD_SWITCH_TO_SCAN_ONLY_MODE = 1; |
| public static final int CMD_SWITCH_TO_CONNECT_MODE = 2; |
| public static final int CMD_INTERFACE_STATUS_CHANGED = 3; |
| public static final int CMD_INTERFACE_DESTROYED = 4; |
| public static final int CMD_INTERFACE_DOWN = 5; |
| public static final int CMD_SWITCH_TO_SCAN_ONLY_MODE_CONTINUE = 6; |
| private final State mIdleState = new IdleState(); |
| private final State mStartedState = new StartedState(); |
| private final State mScanOnlyModeState = new ScanOnlyModeState(); |
| private final State mConnectModeState = new ConnectModeState(); |
| // Workaround since we cannot use transitionTo(mScanOnlyModeState, RoleChangeInfo) |
| private RoleChangeInfo mScanRoleChangeInfoToSetOnTransition = null; |
| // Workaround since we cannot use transitionTo(mConnectModeState, RoleChangeInfo) |
| private RoleChangeInfo mConnectRoleChangeInfoToSetOnTransition = null; |
| |
| @Nullable |
| private StateMachineObituary mObituary = null; |
| |
| private final InterfaceCallback mWifiNativeInterfaceCallback = new InterfaceCallback() { |
| @Override |
| public void onDestroyed(String ifaceName) { |
| if (mClientInterfaceName != null && mClientInterfaceName.equals(ifaceName)) { |
| Log.d(getTag(), "STA iface " + ifaceName + " was destroyed, " |
| + "stopping client mode"); |
| |
| // we must immediately clean up state in ClientModeImpl to unregister |
| // all client mode related objects |
| // Note: onDestroyed is only called from the main Wifi thread |
| if (mClientModeImpl == null) { |
| Log.w(getTag(), "Received mWifiNativeInterfaceCallback.onDestroyed " |
| + "callback when no ClientModeImpl instance is active."); |
| } else { |
| mClientModeImpl.handleIfaceDestroyed(); |
| } |
| |
| sendMessage(CMD_INTERFACE_DESTROYED); |
| } |
| } |
| |
| @Override |
| public void onUp(String ifaceName) { |
| if (mClientInterfaceName != null && mClientInterfaceName.equals(ifaceName)) { |
| sendMessage(CMD_INTERFACE_STATUS_CHANGED, 1); |
| } |
| } |
| |
| @Override |
| public void onDown(String ifaceName) { |
| if (mClientInterfaceName != null && mClientInterfaceName.equals(ifaceName)) { |
| sendMessage(CMD_INTERFACE_STATUS_CHANGED, 0); |
| } |
| } |
| }; |
| |
| ClientModeStateMachine(Looper looper) { |
| super(TAG, looper); |
| |
| // CHECKSTYLE:OFF IndentationCheck |
| addState(mIdleState); |
| addState(mStartedState, mIdleState); |
| addState(mScanOnlyModeState, mStartedState); |
| addState(mConnectModeState, mStartedState); |
| // CHECKSTYLE:ON IndentationCheck |
| |
| setInitialState(mIdleState); |
| start(); |
| } |
| |
| void captureObituaryAndQuitNow() { |
| // capture StateMachine LogRecs since we will lose them after we call quitNow() |
| // This is used for debugging. |
| mObituary = new StateMachineObituary(this); |
| |
| quitNow(); |
| } |
| |
| @Override |
| public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { |
| if (mObituary == null) { |
| // StateMachine hasn't quit yet, dump `this` via StateMachineObituary's dump() |
| // method for consistency with `else` branch. |
| new StateMachineObituary(this).dump(fd, pw, args); |
| } else { |
| // StateMachine has quit and cleared all LogRecs. |
| // Get them from the obituary instead. |
| mObituary.dump(fd, pw, args); |
| } |
| } |
| |
| /** |
| * Reset this ConcreteClientModeManager when its role changes, so that it can be reused for |
| * another purpose. |
| */ |
| private void reset() { |
| // Therefore, the caller must ensure that the role change has been completed and these |
| // settings have already reset before setting them, otherwise the new setting would be |
| // lost. |
| setShouldReduceNetworkScore(false); |
| } |
| |
| private void setRoleInternal(@NonNull RoleChangeInfo roleChangeInfo) { |
| mPreviousRole = mRole; |
| mLastRoleChangeSinceBootMs = mClock.getElapsedSinceBootMillis(); |
| mRole = roleChangeInfo.role; |
| if (roleChangeInfo.requestorWs != null) { |
| mRequestorWs = roleChangeInfo.requestorWs; |
| } |
| if (roleChangeInfo.modeListener != null) { |
| mModeListener = roleChangeInfo.modeListener; |
| } |
| } |
| |
| private void setRoleInternalAndInvokeCallback(@NonNull RoleChangeInfo roleChangeInfo) { |
| if (roleChangeInfo.role == mRole) return; |
| if (mRole == null) { |
| Log.v(getTag(), "ClientModeManager started in role: " + roleChangeInfo); |
| setRoleInternal(roleChangeInfo); |
| mModeListener.onStarted(ConcreteClientModeManager.this); |
| } else { |
| Log.v(getTag(), "ClientModeManager role changed: " + roleChangeInfo); |
| setRoleInternal(roleChangeInfo); |
| reset(); |
| mModeListener.onRoleChanged(ConcreteClientModeManager.this); |
| } |
| if (mClientModeImpl != null) { |
| mClientModeImpl.onRoleChanged(); |
| } |
| } |
| |
| private class IdleState extends State { |
| @Override |
| public void enter() { |
| Log.d(getTag(), "entering IdleState"); |
| mClientInterfaceName = null; |
| mIfaceIsUp = false; |
| } |
| |
| @Override |
| public void exit() { |
| // Sometimes the wifi handler thread may become blocked that the statemachine |
| // will exit in the IdleState without first entering StartedState. Trigger a |
| // cleanup here in case the above sequence happens. This the statemachine was |
| // started normally this will will not send a duplicate broadcast since mIsStopped |
| // will get set to false the first time the exit happens. |
| cleanupOnQuitIfApplicable(); |
| } |
| |
| @Override |
| public boolean processMessage(Message message) { |
| switch (message.what) { |
| case CMD_START: |
| // Always start in scan mode first. |
| RoleChangeInfo roleChangeInfo = (RoleChangeInfo) message.obj; |
| mClientInterfaceName = mWifiNative.setupInterfaceForClientInScanMode( |
| mWifiNativeInterfaceCallback, roleChangeInfo.requestorWs); |
| if (TextUtils.isEmpty(mClientInterfaceName)) { |
| Log.e(getTag(), "Failed to create ClientInterface. Sit in Idle"); |
| mModeListener.onStartFailure(ConcreteClientModeManager.this); |
| break; |
| } |
| if (roleChangeInfo.role instanceof ClientConnectivityRole) { |
| sendMessage(CMD_SWITCH_TO_CONNECT_MODE, roleChangeInfo); |
| transitionTo(mStartedState); |
| } else { |
| mScanRoleChangeInfoToSetOnTransition = roleChangeInfo; |
| transitionTo(mScanOnlyModeState); |
| } |
| break; |
| default: |
| Log.d(getTag(), "received an invalid message: " + message); |
| return NOT_HANDLED; |
| } |
| return HANDLED; |
| } |
| } |
| |
| private class StartedState extends State { |
| |
| private void onUpChanged(boolean isUp) { |
| if (isUp == mIfaceIsUp) { |
| return; // no change |
| } |
| mIfaceIsUp = isUp; |
| if (!isUp) { |
| // if the interface goes down we should exit and go back to idle state. |
| Log.d(getTag(), "interface down!"); |
| mStateMachine.sendMessage(CMD_INTERFACE_DOWN); |
| } |
| } |
| |
| @Override |
| public void enter() { |
| Log.d(getTag(), "entering StartedState"); |
| mIfaceIsUp = false; |
| mIsStopped = false; |
| onUpChanged(mWifiNative.isInterfaceUp(mClientInterfaceName)); |
| } |
| |
| @Override |
| public boolean processMessage(Message message) { |
| switch (message.what) { |
| case CMD_START: |
| // Already started, ignore this command. |
| break; |
| case CMD_SWITCH_TO_CONNECT_MODE: { |
| RoleChangeInfo roleChangeInfo = (RoleChangeInfo) message.obj; |
| updateConnectModeState(roleChangeInfo.role, |
| WifiManager.WIFI_STATE_ENABLING, |
| WifiManager.WIFI_STATE_DISABLED); |
| if (!mWifiNative.switchClientInterfaceToConnectivityMode( |
| mClientInterfaceName, roleChangeInfo.requestorWs)) { |
| updateConnectModeState(roleChangeInfo.role, |
| WifiManager.WIFI_STATE_UNKNOWN, |
| WifiManager.WIFI_STATE_ENABLING); |
| updateConnectModeState(roleChangeInfo.role, |
| WifiManager.WIFI_STATE_DISABLED, |
| WifiManager.WIFI_STATE_UNKNOWN); |
| mModeListener.onStartFailure(ConcreteClientModeManager.this); |
| break; |
| } |
| // Role set in the enter of ConnectModeState. |
| mConnectRoleChangeInfoToSetOnTransition = roleChangeInfo; |
| transitionTo(mConnectModeState); |
| break; |
| } |
| case CMD_SWITCH_TO_SCAN_ONLY_MODE: |
| updateConnectModeState(mRole, WifiManager.WIFI_STATE_DISABLING, |
| WifiManager.WIFI_STATE_ENABLED); |
| mDeferStopHandler.start(getWifiOffDeferringTimeMs()); |
| break; |
| case CMD_SWITCH_TO_SCAN_ONLY_MODE_CONTINUE: { |
| RoleChangeInfo roleChangeInfo = (RoleChangeInfo) message.obj; |
| mScanRoleChangeInfoToSetOnTransition = roleChangeInfo; |
| transitionTo(mScanOnlyModeState); |
| break; |
| } |
| case CMD_INTERFACE_DOWN: |
| Log.e(getTag(), "Detected an interface down, reporting failure to " |
| + "SelfRecovery"); |
| mSelfRecovery.trigger(SelfRecovery.REASON_STA_IFACE_DOWN); |
| // once interface down, nothing else to do... stop the state machine |
| captureObituaryAndQuitNow(); |
| break; |
| case CMD_INTERFACE_STATUS_CHANGED: |
| boolean isUp = message.arg1 == 1; |
| onUpChanged(isUp); |
| break; |
| case CMD_INTERFACE_DESTROYED: |
| Log.e(getTag(), "interface destroyed - client mode stopping"); |
| mClientInterfaceName = null; |
| // once interface destroyed, nothing else to do... stop the state machine |
| captureObituaryAndQuitNow(); |
| break; |
| default: |
| return NOT_HANDLED; |
| } |
| return HANDLED; |
| } |
| |
| /** |
| * Clean up state, unregister listeners and update wifi state. |
| */ |
| @Override |
| public void exit() { |
| if (mClientInterfaceName != null) { |
| mWifiNative.teardownInterface(mClientInterfaceName); |
| mClientInterfaceName = null; |
| mIfaceIsUp = false; |
| } |
| |
| Log.i(getTag(), "StartedState#exit(), setting mRole = null"); |
| mIsStopped = true; |
| cleanupOnQuitIfApplicable(); |
| } |
| } |
| |
| private class ScanOnlyModeState extends State { |
| @Override |
| public void enter() { |
| Log.d(getTag(), "entering ScanOnlyModeState"); |
| |
| if (mClientInterfaceName != null) { |
| mScanOnlyModeImpl = mWifiInjector.makeScanOnlyModeImpl( |
| mClientInterfaceName); |
| } else { |
| Log.e(getTag(), "Entered ScanOnlyModeState with a null interface name!"); |
| } |
| |
| if (mScanRoleChangeInfoToSetOnTransition == null |
| || (mScanRoleChangeInfoToSetOnTransition.role != ROLE_CLIENT_SCAN_ONLY)) { |
| Log.wtf(TAG, "Unexpected mScanRoleChangeInfoToSetOnTransition: " |
| + mScanRoleChangeInfoToSetOnTransition); |
| // Should never happen, but fallback to scan only to avoid a crash. |
| mScanRoleChangeInfoToSetOnTransition = |
| new RoleChangeInfo(ROLE_CLIENT_SCAN_ONLY); |
| } |
| |
| setRoleInternalAndInvokeCallback(mScanRoleChangeInfoToSetOnTransition); |
| // If we're in ScanOnlyModeState, there is only 1 CMM. So it's ok to call |
| // WakeupController directly, there won't be multiple CMMs trampling over each other |
| mWakeupController.start(); |
| mWifiNative.setScanMode(mClientInterfaceName, true); |
| } |
| |
| @Override |
| public boolean processMessage(Message message) { |
| switch (message.what) { |
| case CMD_SWITCH_TO_SCAN_ONLY_MODE: |
| // Already in scan only mode, ignore this command. |
| break; |
| default: |
| return NOT_HANDLED; |
| } |
| return HANDLED; |
| } |
| |
| @Override |
| public void exit() { |
| mScanOnlyModeImpl = null; |
| mScanRoleChangeInfoToSetOnTransition = null; |
| |
| // If we're in ScanOnlyModeState, there is only 1 CMM. So it's ok to call |
| // WakeupController directly, there won't be multiple CMMs trampling over each other |
| mWakeupController.stop(); |
| mWifiNative.setScanMode(mClientInterfaceName, false); |
| } |
| } |
| |
| private class ConnectModeState extends State { |
| @Override |
| public void enter() { |
| Log.d(getTag(), "entering ConnectModeState, starting ClientModeImpl"); |
| if (mClientInterfaceName == null) { |
| Log.e(getTag(), "Supposed to start ClientModeImpl, but iface is null!"); |
| } else { |
| if (mClientModeImpl != null) { |
| Log.e(getTag(), "ConnectModeState.enter(): mClientModeImpl is already " |
| + "instantiated?!"); |
| } |
| mClientModeImpl = mWifiInjector.makeClientModeImpl( |
| mClientInterfaceName, ConcreteClientModeManager.this, |
| mVerboseLoggingEnabled); |
| mClientModeImpl.setShouldReduceNetworkScore(mShouldReduceNetworkScore); |
| } |
| if (mConnectRoleChangeInfoToSetOnTransition == null |
| || !(mConnectRoleChangeInfoToSetOnTransition.role |
| instanceof ClientConnectivityRole)) { |
| Log.wtf(TAG, "Unexpected mConnectRoleChangeInfoToSetOnTransition: " |
| + mConnectRoleChangeInfoToSetOnTransition); |
| // Should never happen, but fallback to primary to avoid a crash. |
| mConnectRoleChangeInfoToSetOnTransition = |
| new RoleChangeInfo(ROLE_CLIENT_PRIMARY); |
| } |
| |
| // Could be any one of possible connect mode roles. |
| setRoleInternalAndInvokeCallback(mConnectRoleChangeInfoToSetOnTransition); |
| updateConnectModeState(mConnectRoleChangeInfoToSetOnTransition.role, |
| WIFI_STATE_ENABLED, WIFI_STATE_ENABLING); |
| } |
| |
| @Override |
| public boolean processMessage(Message message) { |
| switch (message.what) { |
| case CMD_SWITCH_TO_CONNECT_MODE: |
| RoleChangeInfo roleChangeInfo = (RoleChangeInfo) message.obj; |
| // switching to connect mode when already in connect mode, just update the |
| // requestor WorkSource. |
| boolean success = mWifiNative.replaceStaIfaceRequestorWs( |
| mClientInterfaceName, roleChangeInfo.requestorWs); |
| if (success) { |
| setRoleInternalAndInvokeCallback(roleChangeInfo); |
| } else { |
| // If this call failed, the iface would be torn down. |
| // Thus, simply abort and let the iface down handling take care of the |
| // rest. |
| Log.e(getTag(), "Failed to switch ClientModeManager=" |
| + ConcreteClientModeManager.this + "'s requestorWs"); |
| } |
| break; |
| case CMD_SWITCH_TO_SCAN_ONLY_MODE: |
| case CMD_INTERFACE_DESTROYED: |
| updateConnectModeState(mRole, WifiManager.WIFI_STATE_DISABLING, |
| WifiManager.WIFI_STATE_ENABLED); |
| return NOT_HANDLED; // Handled in StartedState. |
| case CMD_INTERFACE_DOWN: |
| updateConnectModeState(mRole, WifiManager.WIFI_STATE_DISABLING, |
| WifiManager.WIFI_STATE_UNKNOWN); |
| return NOT_HANDLED; // Handled in StartedState. |
| case CMD_INTERFACE_STATUS_CHANGED: |
| boolean isUp = message.arg1 == 1; |
| if (isUp == mIfaceIsUp) { |
| break; // no change |
| } |
| if (!isUp) { |
| if (mWifiGlobals.isConnectedMacRandomizationEnabled() |
| && getClientMode().isConnecting()) { |
| return HANDLED; // For MAC randomization, ignore... |
| } else { |
| // Handle the error case where our underlying interface went down if |
| // we do not have mac randomization enabled (b/72459123). |
| // if the interface goes down we should exit and go back to idle |
| // state. |
| updateConnectModeState(mRole, WifiManager.WIFI_STATE_UNKNOWN, |
| WifiManager.WIFI_STATE_ENABLED); |
| } |
| } |
| return NOT_HANDLED; // Handled in StartedState. |
| default: |
| return NOT_HANDLED; |
| } |
| return HANDLED; |
| } |
| |
| @Override |
| public void exit() { |
| updateConnectModeState(mRole, WifiManager.WIFI_STATE_DISABLED, |
| WifiManager.WIFI_STATE_DISABLING); |
| |
| if (mClientModeImpl == null) { |
| Log.w(getTag(), "ConnectModeState.exit(): mClientModeImpl is already null?!"); |
| } else { |
| Log.d(getTag(), "Stopping ClientModeImpl"); |
| mClientModeImpl.stop(); |
| mGraveyard.inter(mClientModeImpl); |
| mClientModeImpl = null; |
| } |
| |
| mConnectRoleChangeInfoToSetOnTransition = null; |
| } |
| } |
| } |
| |
| /** Called by a ClientModeImpl owned by this CMM informing it has fully stopped. */ |
| public void onClientModeImplQuit() { |
| cleanupOnQuitIfApplicable(); |
| } |
| |
| /** |
| * Only clean up this CMM once the CMM and all associated ClientModeImpls have been stopped. |
| * This is necessary because ClientModeImpl sends broadcasts during stop, and the role must |
| * remain primary for {@link ClientModeManagerBroadcastQueue} to send them out. |
| */ |
| private void cleanupOnQuitIfApplicable() { |
| if (mIsStopped && mGraveyard.hasAllClientModeImplsQuit()) { |
| mPreviousRole = mRole; |
| mLastRoleChangeSinceBootMs = mClock.getElapsedSinceBootMillis(); |
| mRole = null; |
| // only call onStopped() after role has been reset to null since ActiveModeWarden |
| // expects the CMM to be fully stopped before onStopped(). |
| mModeListener.onStopped(ConcreteClientModeManager.this); |
| |
| // reset to false so that onStopped() won't be triggered again. |
| mIsStopped = false; |
| } |
| } |
| |
| @Override |
| public int syncGetWifiState() { |
| return mWifiState.get(); |
| } |
| |
| @NonNull |
| private ClientMode getClientMode() { |
| if (mClientModeImpl != null) { |
| return mClientModeImpl; |
| } |
| if (mScanOnlyModeImpl != null) { |
| return mScanOnlyModeImpl; |
| } |
| return mDefaultClientModeManager; |
| } |
| |
| /* |
| * Note: These are simple wrappers over methods to {@link ClientModeImpl}. |
| */ |
| |
| @Override |
| public void connectNetwork(NetworkUpdateResult result, ActionListenerWrapper wrapper, |
| int callingUid) { |
| getClientMode().connectNetwork(result, wrapper, callingUid); |
| } |
| |
| @Override |
| public void saveNetwork(NetworkUpdateResult result, ActionListenerWrapper wrapper, |
| int callingUid) { |
| getClientMode().saveNetwork(result, wrapper, callingUid); |
| } |
| |
| @Override |
| public void disconnect() { |
| getClientMode().disconnect(); |
| } |
| |
| @Override |
| public void reconnect(WorkSource ws) { |
| getClientMode().reconnect(ws); |
| } |
| |
| @Override |
| public void reassociate() { |
| getClientMode().reassociate(); |
| } |
| |
| @Override |
| public void startConnectToNetwork(int networkId, int uid, String bssid) { |
| getClientMode().startConnectToNetwork(networkId, uid, bssid); |
| } |
| |
| @Override |
| public void startRoamToNetwork(int networkId, String bssid) { |
| getClientMode().startRoamToNetwork(networkId, bssid); |
| } |
| |
| @Override |
| public boolean setWifiConnectedNetworkScorer( |
| IBinder binder, IWifiConnectedNetworkScorer scorer) { |
| return getClientMode().setWifiConnectedNetworkScorer(binder, scorer); |
| } |
| |
| @Override |
| public void clearWifiConnectedNetworkScorer() { |
| getClientMode().clearWifiConnectedNetworkScorer(); |
| } |
| |
| @Override |
| public void resetSimAuthNetworks(@ClientModeImpl.ResetSimReason int resetReason) { |
| getClientMode().resetSimAuthNetworks(resetReason); |
| } |
| |
| @Override |
| public void onBluetoothConnectionStateChanged() { |
| getClientMode().onBluetoothConnectionStateChanged(); |
| } |
| |
| @Override |
| public WifiInfo syncRequestConnectionInfo() { |
| return getClientMode().syncRequestConnectionInfo(); |
| } |
| |
| @Override |
| public boolean syncQueryPasspointIcon(long bssid, String fileName) { |
| return getClientMode().syncQueryPasspointIcon(bssid, fileName); |
| } |
| |
| @Override |
| public Network syncGetCurrentNetwork() { |
| return getClientMode().syncGetCurrentNetwork(); |
| } |
| |
| @Override |
| public DhcpResultsParcelable syncGetDhcpResultsParcelable() { |
| return getClientMode().syncGetDhcpResultsParcelable(); |
| } |
| |
| @Override |
| public long getSupportedFeatures() { |
| return getClientMode().getSupportedFeatures(); |
| } |
| |
| @Override |
| public boolean syncStartSubscriptionProvisioning(int callingUid, OsuProvider provider, |
| IProvisioningCallback callback) { |
| return getClientMode().syncStartSubscriptionProvisioning( |
| callingUid, provider, callback); |
| } |
| |
| @Override |
| public boolean isWifiStandardSupported(@WifiAnnotations.WifiStandard int standard) { |
| return getClientMode().isWifiStandardSupported(standard); |
| } |
| |
| @Override |
| public void enableTdls(String remoteMacAddress, boolean enable) { |
| getClientMode().enableTdls(remoteMacAddress, enable); |
| } |
| |
| @Override |
| public void dumpIpClient(FileDescriptor fd, PrintWriter pw, String[] args) { |
| getClientMode().dumpIpClient(fd, pw, args); |
| } |
| |
| @Override |
| public void dumpWifiScoreReport(FileDescriptor fd, PrintWriter pw, String[] args) { |
| getClientMode().dumpWifiScoreReport(fd, pw, args); |
| } |
| |
| @Override |
| public void enableVerboseLogging(boolean verbose) { |
| mVerboseLoggingEnabled = verbose; |
| getClientMode().enableVerboseLogging(verbose); |
| } |
| |
| @Override |
| public String getFactoryMacAddress() { |
| return getClientMode().getFactoryMacAddress(); |
| } |
| |
| @Override |
| public WifiConfiguration getConnectedWifiConfiguration() { |
| return getClientMode().getConnectedWifiConfiguration(); |
| } |
| |
| @Override |
| public WifiConfiguration getConnectingWifiConfiguration() { |
| return getClientMode().getConnectingWifiConfiguration(); |
| } |
| |
| @Override |
| public String getConnectedBssid() { |
| return getClientMode().getConnectedBssid(); |
| } |
| |
| @Override |
| public String getConnectingBssid() { |
| return getClientMode().getConnectingBssid(); |
| } |
| |
| @Override |
| public WifiLinkLayerStats getWifiLinkLayerStats() { |
| return getClientMode().getWifiLinkLayerStats(); |
| } |
| |
| @Override |
| public boolean setPowerSave(boolean ps) { |
| return getClientMode().setPowerSave(ps); |
| } |
| |
| @Override |
| public boolean setLowLatencyMode(boolean enabled) { |
| return getClientMode().setLowLatencyMode(enabled); |
| } |
| |
| @Override |
| public WifiMulticastLockManager.FilterController getMcastLockManagerFilterController() { |
| return getClientMode().getMcastLockManagerFilterController(); |
| } |
| |
| @Override |
| public boolean isConnected() { |
| return getClientMode().isConnected(); |
| } |
| |
| @Override |
| public boolean isConnecting() { |
| return getClientMode().isConnecting(); |
| } |
| |
| @Override |
| public boolean isRoaming() { |
| return getClientMode().isRoaming(); |
| } |
| |
| @Override |
| public boolean isDisconnected() { |
| return getClientMode().isDisconnected(); |
| } |
| |
| @Override |
| public boolean isSupplicantTransientState() { |
| return getClientMode().isSupplicantTransientState(); |
| } |
| |
| @Override |
| public void onCellularConnectivityChanged(@WifiDataStall.CellularDataStatusCode int status) { |
| getClientMode().onCellularConnectivityChanged(status); |
| } |
| |
| @Override |
| public void probeLink(LinkProbeCallback callback, int mcs) { |
| getClientMode().probeLink(callback, mcs); |
| } |
| |
| @Override |
| public void sendMessageToClientModeImpl(Message msg) { |
| getClientMode().sendMessageToClientModeImpl(msg); |
| } |
| |
| @Override |
| public long getId() { |
| return mId; |
| } |
| |
| @Override |
| public void setMboCellularDataStatus(boolean available) { |
| getClientMode().setMboCellularDataStatus(available); |
| } |
| |
| @Override |
| public WifiNative.RoamingCapabilities getRoamingCapabilities() { |
| return getClientMode().getRoamingCapabilities(); |
| } |
| |
| @Override |
| public boolean configureRoaming(WifiNative.RoamingConfig config) { |
| return getClientMode().configureRoaming(config); |
| } |
| |
| @Override |
| public boolean enableRoaming(boolean enabled) { |
| return getClientMode().enableRoaming(enabled); |
| } |
| |
| @Override |
| public boolean setCountryCode(String countryCode) { |
| return getClientMode().setCountryCode(countryCode); |
| } |
| |
| @Override |
| public List<TxFateReport> getTxPktFates() { |
| return getClientMode().getTxPktFates(); |
| } |
| |
| @Override |
| public List<RxFateReport> getRxPktFates() { |
| return getClientMode().getRxPktFates(); |
| } |
| |
| @Override |
| public DeviceWiphyCapabilities getDeviceWiphyCapabilities() { |
| return getClientMode().getDeviceWiphyCapabilities(); |
| } |
| |
| @Override |
| public boolean requestAnqp(String bssid, Set<Integer> anqpIds, Set<Integer> hs20Subtypes) { |
| return getClientMode().requestAnqp(bssid, anqpIds, hs20Subtypes); |
| } |
| |
| @Override |
| public boolean requestVenueUrlAnqp(String bssid) { |
| return getClientMode().requestVenueUrlAnqp(bssid); |
| } |
| |
| @Override |
| public boolean requestIcon(String bssid, String fileName) { |
| return getClientMode().requestIcon(bssid, fileName); |
| } |
| |
| @Override |
| public void setShouldReduceNetworkScore(boolean shouldReduceNetworkScore) { |
| mShouldReduceNetworkScore = shouldReduceNetworkScore; |
| getClientMode().setShouldReduceNetworkScore(shouldReduceNetworkScore); |
| } |
| |
| @Override |
| public String toString() { |
| return "ConcreteClientModeManager{id=" + getId() |
| + " iface=" + getInterfaceName() |
| + " role=" + getRole() |
| + "}"; |
| } |
| } |