diff options
406 files changed, 31124 insertions, 6530 deletions
diff --git a/WifiDialog/src/com/android/wifi/dialog/WifiDialogActivity.java b/WifiDialog/src/com/android/wifi/dialog/WifiDialogActivity.java index 4dab628bbc..cf1b4cc884 100644 --- a/WifiDialog/src/com/android/wifi/dialog/WifiDialogActivity.java +++ b/WifiDialog/src/com/android/wifi/dialog/WifiDialogActivity.java @@ -33,9 +33,6 @@ import android.net.wifi.WifiContext; import android.net.wifi.WifiManager; import android.os.Bundle; import android.os.CountDownTimer; -import android.os.Handler; -import android.os.Looper; -import android.os.SystemClock; import android.os.Vibrator; import android.text.Editable; import android.text.SpannableString; @@ -62,8 +59,12 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.os.BuildCompat; +import com.android.wifi.x.com.android.wifi.flags.Flags; + import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Set; /** @@ -72,12 +73,9 @@ import java.util.Set; public class WifiDialogActivity extends Activity { private static final String TAG = "WifiDialog"; private static final String KEY_DIALOG_INTENTS = "KEY_DIALOG_INTENTS"; - private static final String EXTRA_DIALOG_EXPIRATION_TIME_MS = - "com.android.wifi.dialog.DIALOG_START_TIME_MS"; private static final String EXTRA_DIALOG_P2P_PIN_INPUT = "com.android.wifi.dialog.DIALOG_P2P_PIN_INPUT"; - private @NonNull Handler mHandler = new Handler(Looper.getMainLooper()); private @Nullable WifiContext mWifiContext; private @Nullable WifiManager mWifiManager; private boolean mIsVerboseLoggingEnabled; @@ -104,6 +102,10 @@ public class WifiDialogActivity extends Activity { return getWifiContext().getString(getWifiResourceId(name, "string")); } + private String getWifiString(@NonNull String name, @Nullable String arg) { + return getWifiContext().getString(getWifiResourceId(name, "string"), arg); + } + private int getWifiInteger(@NonNull String name) { return getWifiContext().getResources().getInteger(getWifiResourceId(name, "integer")); } @@ -354,7 +356,8 @@ public class WifiDialogActivity extends Activity { dialogId, intent.getStringExtra(WifiManager.EXTRA_P2P_DEVICE_NAME), intent.getBooleanExtra(WifiManager.EXTRA_P2P_PIN_REQUESTED, false), - intent.getStringExtra(WifiManager.EXTRA_P2P_DISPLAY_PIN)); + intent.getStringExtra(WifiManager.EXTRA_P2P_DISPLAY_PIN), + intent.getIntExtra(WifiManager.EXTRA_DIALOG_TIMEOUT_MS, 0)); break; default: if (mIsVerboseLoggingEnabled) { @@ -378,50 +381,6 @@ public class WifiDialogActivity extends Activity { dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG); } mActiveDialogsPerId.put(dialogId, dialog); - long timeoutMs = intent.getLongExtra(WifiManager.EXTRA_DIALOG_TIMEOUT_MS, 0); - if (timeoutMs > 0) { - // Use the original expiration time in case we've reloaded this dialog after a - // configuration change. - long expirationTimeMs = intent.getLongExtra(EXTRA_DIALOG_EXPIRATION_TIME_MS, 0); - if (expirationTimeMs > 0) { - timeoutMs = expirationTimeMs - SystemClock.uptimeMillis(); - if (timeoutMs < 0) { - timeoutMs = 0; - } - } else { - intent.putExtra( - EXTRA_DIALOG_EXPIRATION_TIME_MS, SystemClock.uptimeMillis() + timeoutMs); - } - CountDownTimer countDownTimer = new CountDownTimer(timeoutMs, 100) { - @Override - public void onTick(long millisUntilFinished) { - if (dialogType == WifiManager.DIALOG_TYPE_P2P_INVITATION_RECEIVED) { - int secondsRemaining = (int) millisUntilFinished / 1000; - if (millisUntilFinished % 1000 != 0) { - // Round up to the nearest whole second. - secondsRemaining++; - } - TextView timeRemaining = dialog.getWindow().findViewById( - getWifiViewId("time_remaining")); - timeRemaining.setText(MessageFormat.format( - getWifiString("wifi_p2p_invitation_seconds_remaining"), - secondsRemaining)); - timeRemaining.setVisibility(View.VISIBLE); - } - } - - @Override - public void onFinish() { - removeIntentAndPossiblyFinish(dialogId); - } - }.start(); - mActiveCountDownTimersPerId.put(dialogId, countDownTimer); - } else { - if (dialogType == WifiManager.DIALOG_TYPE_P2P_INVITATION_RECEIVED) { - // Set the message back to null if we aren't using a timeout. - dialog.setMessage(null); - } - } dialog.show(); if (mIsVerboseLoggingEnabled) { Log.v(TAG, "Showing dialog " + dialogId); @@ -540,6 +499,10 @@ public class WifiDialogActivity extends Activity { final int dialogId, @Nullable final String deviceName, @Nullable final String displayPin) { + if (Flags.p2pDialog2()) { + return createP2pInvitationSentDialog2(dialogId, deviceName, displayPin); + } + final View textEntryView = getWifiLayoutInflater() .inflate(getWifiLayoutId("wifi_p2p_dialog"), null); ViewGroup group = textEntryView.findViewById(getWifiViewId("info")); @@ -577,18 +540,78 @@ public class WifiDialogActivity extends Activity { } /** + * Returns a P2P Invitation Sent Dialog2 for the given Intent. + */ + private @NonNull AlertDialog createP2pInvitationSentDialog2( + final int dialogId, + @Nullable final String deviceName, + @Nullable final String displayPin) { + if (TextUtils.isEmpty(deviceName)) { + Log.w(TAG, "P2P Invitation Sent dialog device name is null or empty." + + " id=" + dialogId + + " deviceName=" + deviceName + + " displayPin=" + displayPin); + } + final View customView; + final String title; + final String message; + if (displayPin != null) { + customView = getWifiLayoutInflater() + .inflate(getWifiLayoutId("wifi_p2p_dialog2_display_pin"), null); + ((TextView) customView.findViewById(getWifiViewId("wifi_p2p_dialog2_display_pin"))) + .setText(displayPin); + title = getWifiString("wifi_p2p_dialog2_sent_title_display_pin", deviceName); + message = getWifiString("wifi_p2p_dialog2_sent_message_display_pin", deviceName); + } else { + customView = null; + title = getWifiString("wifi_p2p_dialog2_sent_title", deviceName); + message = getWifiString("wifi_p2p_dialog2_sent_message", deviceName); + } + + AlertDialog.Builder builder = getWifiAlertDialogBuilder("wifi_p2p_dialog2") + .setTitle(title) + .setMessage(message) + .setPositiveButton(getWifiString("wifi_p2p_dialog2_sent_positive_button"), + (dialogPositive, which) -> { + // No-op + if (mIsVerboseLoggingEnabled) { + Log.v(TAG, "P2P Invitation Sent Dialog id=" + dialogId + + " accepted."); + } + }); + if (customView != null) { + builder.setView(customView); + } + AlertDialog dialog = builder.create(); + if (mIsVerboseLoggingEnabled) { + Log.v(TAG, "Created P2P Invitation Sent dialog." + + " id=" + dialogId + + " deviceName=" + deviceName + + " displayPin=" + displayPin); + } + return dialog; + } + + /** * Returns a P2P Invitation Received Dialog for the given Intent. */ private @NonNull AlertDialog createP2pInvitationReceivedDialog( final int dialogId, @Nullable final String deviceName, final boolean isPinRequested, - @Nullable final String displayPin) { + @Nullable final String displayPin, + int timeoutMs) { + if (Flags.p2pDialog2()) { + return createP2pInvitationReceivedDialog2(dialogId, deviceName, isPinRequested, + displayPin, timeoutMs); + } + if (TextUtils.isEmpty(deviceName)) { Log.w(TAG, "P2P Invitation Received dialog device name is null or empty." + " id=" + dialogId + " deviceName=" + deviceName - + " displayPin=" + displayPin); + + " displayPin=" + displayPin + + " timeoutMs=" + timeoutMs); } final View textEntryView = getWifiLayoutInflater() .inflate(getWifiLayoutId("wifi_p2p_dialog"), null); @@ -702,6 +725,41 @@ public class WifiDialogActivity extends Activity { return true; }); } + if (timeoutMs > 0) { + CountDownTimer countDownTimer = new CountDownTimer(timeoutMs, 100) { + @Override + public void onTick(long millisUntilFinished) { + Intent intent = mLaunchIntentsPerId.get(dialogId); + if (intent != null) { + // Store the updated timeout to use if we reload this dialog after a + // configuration change + intent.putExtra(WifiManager.EXTRA_DIALOG_TIMEOUT_MS, + (int) millisUntilFinished); + } + + if (!getWifiBoolean("config_p2pInvitationReceivedDialogShowRemainingTime")) { + return; + } + int secondsRemaining = (int) millisUntilFinished / 1000; + if (millisUntilFinished % 1000 != 0) { + // Round up to the nearest whole second. + secondsRemaining++; + } + TextView timeRemaining = textEntryView.findViewById( + getWifiViewId("time_remaining")); + timeRemaining.setText(MessageFormat.format( + getWifiString("wifi_p2p_invitation_seconds_remaining"), + secondsRemaining)); + timeRemaining.setVisibility(View.VISIBLE); + } + + @Override + public void onFinish() { + removeIntentAndPossiblyFinish(dialogId); + } + }.start(); + mActiveCountDownTimersPerId.put(dialogId, countDownTimer); + } if (mIsVerboseLoggingEnabled) { Log.v(TAG, "Created P2P Invitation Received dialog." + " id=" + dialogId @@ -722,4 +780,195 @@ public class WifiDialogActivity extends Activity { ((TextView) row.findViewById(getWifiViewId("value"))).setText(value); group.addView(row); } + + /** + * Returns a P2P Invitation Received Dialog2 for the given Intent. + */ + private @NonNull AlertDialog createP2pInvitationReceivedDialog2( + final int dialogId, + @Nullable final String deviceName, + final boolean isPinRequested, + @Nullable final String displayPin, + final int timeoutMs) { + if (TextUtils.isEmpty(deviceName)) { + Log.w(TAG, "P2P Invitation Received dialog device name is null or empty." + + " id=" + dialogId + + " deviceName=" + deviceName + + " displayPin=" + displayPin); + } + final View customView; + String title = getWifiString("wifi_p2p_dialog2_received_title", deviceName); + final String message; + final String timeoutMessage; + final EditText pinEditText; + if (isPinRequested) { + customView = getWifiLayoutInflater() + .inflate(getWifiLayoutId("wifi_p2p_dialog2_enter_pin"), null); + pinEditText = customView.findViewById(getWifiViewId("wifi_p2p_dialog2_enter_pin")); + title = getWifiString("wifi_p2p_dialog2_received_title_enter_pin", deviceName); + message = getWifiString("wifi_p2p_dialog2_received_message_enter_pin", deviceName); + timeoutMessage = getWifiString("wifi_p2p_dialog2_received_message_enter_pin_countdown", + deviceName); + } else { + pinEditText = null; + if (!TextUtils.isEmpty(displayPin)) { + customView = getWifiLayoutInflater() + .inflate(getWifiLayoutId("wifi_p2p_dialog2_display_pin"), null); + ((TextView) customView.findViewById(getWifiViewId("wifi_p2p_dialog2_display_pin"))) + .setText(displayPin); + title = getWifiString("wifi_p2p_dialog2_received_title_display_pin", deviceName); + message = getWifiString("wifi_p2p_dialog2_received_message_display_pin", + deviceName); + timeoutMessage = getWifiString( + "wifi_p2p_dialog2_received_message_display_pin_countdown", deviceName); + } else { + customView = null; + message = getWifiString("wifi_p2p_dialog2_received_message", deviceName); + timeoutMessage = getWifiString("wifi_p2p_dialog2_received_message_countdown", + deviceName); + } + } + + AlertDialog.Builder builder = getWifiAlertDialogBuilder("wifi_p2p_dialog2") + .setTitle(title) + .setMessage(message) + .setPositiveButton(getWifiString("wifi_p2p_dialog2_received_positive_button"), + (dialogPositive, which) -> { + String pin = null; + if (pinEditText != null) { + pin = pinEditText.getText().toString(); + } + if (mIsVerboseLoggingEnabled) { + Log.v(TAG, "P2P Invitation Received Dialog id=" + dialogId + + " accepted with pin=" + pin); + } + getWifiManager().replyToP2pInvitationReceivedDialog(dialogId, true, + pin); + }) + .setNegativeButton(getWifiString("wifi_p2p_dialog2_received_negative_button"), + (dialogNegative, which) -> { + if (mIsVerboseLoggingEnabled) { + Log.v(TAG, "P2P Invitation Received dialog id=" + dialogId + + " declined."); + } + getWifiManager().replyToP2pInvitationReceivedDialog(dialogId, false, + null); + }) + .setOnCancelListener((dialogCancel) -> { + if (mIsVerboseLoggingEnabled) { + Log.v(TAG, "P2P Invitation Received dialog id=" + dialogId + + " cancelled."); + } + getWifiManager().replyToP2pInvitationReceivedDialog(dialogId, false, null); + }); + if (customView != null) { + builder.setView(customView); + } + AlertDialog dialog = builder.create(); + if (isPinRequested) { + dialog.getWindow().setSoftInputMode( + WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); + dialog.setOnShowListener(dialogShow -> { + Intent intent = mLaunchIntentsPerId.get(dialogId); + if (intent != null) { + // Populate the pin EditText with the previous user input if we're recreating + // the dialog after a configuration change. + CharSequence previousPin = + intent.getCharSequenceExtra(EXTRA_DIALOG_P2P_PIN_INPUT); + if (previousPin != null) { + pinEditText.setText(previousPin); + } + } + if (getResources().getConfiguration().orientation + == Configuration.ORIENTATION_PORTRAIT + || (getResources().getConfiguration().screenLayout + & Configuration.SCREENLAYOUT_SIZE_MASK) + >= Configuration.SCREENLAYOUT_SIZE_LARGE) { + pinEditText.requestFocus(); + pinEditText.setSelection(pinEditText.getText().length()); + } + dialog.getButton(Dialog.BUTTON_POSITIVE).setEnabled( + pinEditText.length() == 4 || pinEditText.length() == 8); + }); + pinEditText.addTextChangedListener(new TextWatcher() { + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + // No-op. + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + // No-op. + } + + @Override + public void afterTextChanged(Editable s) { + Intent intent = mLaunchIntentsPerId.get(dialogId); + if (intent != null) { + // Store the current input in the Intent in case we need to reload from a + // configuration change. + intent.putExtra(EXTRA_DIALOG_P2P_PIN_INPUT, s); + } + dialog.getButton(Dialog.BUTTON_POSITIVE).setEnabled( + s.length() == 4 || s.length() == 8); + } + }); + } else { + dialog.setOnShowListener(dialogShow -> { + dialog.getButton(Dialog.BUTTON_NEGATIVE).requestFocus(); + }); + } + if ((getResources().getConfiguration().uiMode & Configuration.UI_MODE_TYPE_APPLIANCE) + == Configuration.UI_MODE_TYPE_APPLIANCE) { + // For appliance devices, add a key listener which accepts. + dialog.setOnKeyListener((dialogKey, keyCode, event) -> { + if (keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) { + dialog.dismiss(); + getWifiManager().replyToP2pInvitationReceivedDialog(dialogId, true, null); + } + return true; + }); + } + if (timeoutMs > 0) { + CountDownTimer countDownTimer = new CountDownTimer(timeoutMs, 100) { + @Override + public void onTick(long millisUntilFinished) { + Intent intent = mLaunchIntentsPerId.get(dialogId); + if (intent != null) { + // Store the updated timeout to use if we reload this dialog after a + // configuration change + intent.putExtra(WifiManager.EXTRA_DIALOG_TIMEOUT_MS, + (int) millisUntilFinished); + } + + if (!getWifiBoolean("config_p2pInvitationReceivedDialogShowRemainingTime")) { + return; + } + int secondsRemaining = (int) millisUntilFinished / 1000; + if (millisUntilFinished % 1000 != 0) { + // Round up to the nearest whole second. + secondsRemaining++; + } + Map<String, Object> messageArgs = new HashMap<>(); + messageArgs.put("device", deviceName); + messageArgs.put("countdown", secondsRemaining); + dialog.setMessage(MessageFormat.format(timeoutMessage, messageArgs)); + } + + @Override + public void onFinish() { + removeIntentAndPossiblyFinish(dialogId); + } + }.start(); + mActiveCountDownTimersPerId.put(dialogId, countDownTimer); + } + if (mIsVerboseLoggingEnabled) { + Log.v(TAG, "Created P2P Invitation Received dialog." + + " id=" + dialogId + + " deviceName=" + deviceName + + " isPinRequested=" + isPinRequested + + " displayPin=" + displayPin); + } + return dialog; + } } diff --git a/aidl/mainline_supplicant/Android.bp b/aidl/mainline_supplicant/Android.bp new file mode 100644 index 0000000000..00600440eb --- /dev/null +++ b/aidl/mainline_supplicant/Android.bp @@ -0,0 +1,47 @@ +// Copyright (C) 2024 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. + +aidl_interface { + name: "android.system.wifi.mainline_supplicant", + unstable: true, + srcs: [ + "android/system/wifi/mainline_supplicant/*.aidl", + ], + backend: { + java: { + enabled: true, + apex_available: [ + "//apex_available:platform", + "com.android.wifi", + ], + min_sdk_version: "30", + lint: { + // Disable linter to avoid error about fixed size arrays. + // Interface will only be accessed on devices >= B. + enabled: false, + }, + }, + ndk: { + enabled: true, + apex_available: [ + "//apex_available:platform", + "com.android.wifi", + ], + min_sdk_version: "30", + }, + cpp: { + enabled: false, + }, + }, +} diff --git a/aidl/mainline_supplicant/android/system/wifi/mainline_supplicant/IMainlineSupplicant.aidl b/aidl/mainline_supplicant/android/system/wifi/mainline_supplicant/IMainlineSupplicant.aidl new file mode 100644 index 0000000000..4179b06279 --- /dev/null +++ b/aidl/mainline_supplicant/android/system/wifi/mainline_supplicant/IMainlineSupplicant.aidl @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2024 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 android.system.wifi.mainline_supplicant; + +/** + * Root of the mainline supplicant interface. This is an unstable AIDL interface used + * to interact with the supplicant binary stored in the mainline module. + */ +interface IMainlineSupplicant { + /** + * Register an interface for use by USD. + * + * @param ifaceName Name of the interface (ex. wlan0) + * @throws ServiceSpecificException with one of the following values: + * |SupplicantStatusCode.FAILURE_UNKNOWN| + * |SupplicantStatusCode.FAILURE_ARGS_INVALID| + */ + void addUsdInterface(String ifaceName); + + /** + * Remove an interface that is being used for USD. + * + * @param ifaceName Name of the interface (ex. wlan0) + * @throws ServiceSpecificException with one of the following values: + * |SupplicantStatusCode.FAILURE_UNKNOWN| + * |SupplicantStatusCode.FAILURE_ARGS_INVALID| + * |SupplicantStatusCode.FAILURE_IFACE_UNKNOWN| + */ + void removeUsdInterface(String ifaceName); + + /** + * Terminate the service. + */ + oneway void terminate(); +} diff --git a/aidl/mainline_supplicant/android/system/wifi/mainline_supplicant/IUsdCallback.aidl b/aidl/mainline_supplicant/android/system/wifi/mainline_supplicant/IUsdCallback.aidl new file mode 100644 index 0000000000..c5752a3765 --- /dev/null +++ b/aidl/mainline_supplicant/android/system/wifi/mainline_supplicant/IUsdCallback.aidl @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2024 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 android.system.wifi.mainline_supplicant; + +import android.system.wifi.mainline_supplicant.UsdMessageInfo; +import android.system.wifi.mainline_supplicant.UsdServiceProtoType; + +/** + * Callbacks for Unsynchronized Service Discovery (USD) operations. + */ +interface IUsdCallback { + /** + * Information about a USD discovery session with a specific peer. + */ + @VintfStability + parcelable UsdServiceDiscoveryInfo { + /** + * Identifier for this device. + */ + int ownId; + + /** + * Identifier for the discovered peer device. + */ + int peerId; + + /** + * MAC address of the discovered peer device. + */ + byte[6] peerMacAddress; + + /** + * Match filter from the discovery packet (publish or subscribe) which caused service + * discovery. + */ + byte[] matchFilter; + + /** + * Service protocol that is being used (ex. Generic, CSA Matter). + */ + UsdServiceProtoType serviceProtoType; + + /** + * Arbitrary service specific information communicated in discovery packets. + * There is no semantic meaning to these bytes. They are passed-through from publisher to + * subscriber as-is with no parsing. + */ + byte[] serviceSpecificInfo; + + /** + * Whether Further Service Discovery (FSD) is enabled. + */ + boolean isFsd; + } + + /** + * Codes indicating the status of USD operations. + */ + @Backing(type="int") + enum UsdReasonCode { + /** + * Unknown failure occurred. + */ + FAILURE_UNKNOWN = 0, + + /** + * The operation timed out. + */ + TIMEOUT = 1, + + /** + * The operation was requested by the user. + */ + USER_REQUESTED = 2, + + /** + * Invalid arguments were provided. + */ + INVALID_ARGS = 3 + } + + /** + * Called in response to |IUsdInterface.startPublish| to indicate that the + * publish session was started successfully. + * + * @param cmdId Identifier for the original request. + * @param publishId Identifier for the publish session. + */ + void onPublishStarted(in int cmdId, in int publishId); + + /** + * Called in response to |IUsdInterface.startSubscribe| to indicate that the + * subscribe session was started successfully. + * + * @param cmdId Identifier for the original request. + * @param subscribeId Identifier for the subscribe session. + */ + void onSubscribeStarted(in int cmdId, in int subscribeId); + + /** + * Called in response to |IUsdInterface.startPublish| to indicate that the + * publish session could not be configured. + * + * @param cmdId Identifier for the original request. + */ + void onPublishConfigFailed(in int cmdId); + + /** + * Called in response to |IUsdInterface.startSubscribe| to indicate that the + * subscribe session could not be configured. + * + * @param cmdId Identifier for the original request. + */ + void onSubscribeConfigFailed(in int cmdId); + + /** + * Called in response to |IUsdInterface.cancelPublish| to indicate that the session + * was cancelled successfully. May also be called unsolicited if the session terminated + * by supplicant. + * + * @param publishId Identifier for the publish session. + * @param reasonCode Code indicating the reason for the session cancellation. + */ + void onPublishTerminated(in int publishId, in UsdReasonCode reasonCode); + + /** + * Called in response to |IUsdInterface.cancelSubscribe| to indicate that the session + * was cancelled successfully. May also be called unsolicited if the session terminated + * by supplicant. + * + * @param subscribeId Identifier for the subscribe session. + * @param reasonCode Code indicating the reason for the session cancellation. + */ + void onSubscribeTerminated(in int subscribeId, in UsdReasonCode reasonCode); + + /** + * Indicates that the publisher sent solicited publish message to the subscriber. + * + * @param info Instance of |UsdServiceDiscoveryInfo| containing information about the reply. + */ + void onPublishReplied(in UsdServiceDiscoveryInfo info); + + /** + * Indicates that a publisher was discovered. Only called if this device is acting as a + * subscriber. + * + * @param info Instance of |UsdServiceDiscoveryInfo| containing information about the service. + */ + void onServiceDiscovered(in UsdServiceDiscoveryInfo info); + + /** + * Indicates that a message was received on an active USD link. + * + * @param messageInfo Information about the message that was received. + */ + void onMessageReceived(in UsdMessageInfo messageInfo); +} diff --git a/aidl/mainline_supplicant/android/system/wifi/mainline_supplicant/IUsdInterface.aidl b/aidl/mainline_supplicant/android/system/wifi/mainline_supplicant/IUsdInterface.aidl new file mode 100644 index 0000000000..50a0141117 --- /dev/null +++ b/aidl/mainline_supplicant/android/system/wifi/mainline_supplicant/IUsdInterface.aidl @@ -0,0 +1,319 @@ +/* + * Copyright (C) 2024 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 android.system.wifi.mainline_supplicant; + +import android.system.wifi.mainline_supplicant.UsdMessageInfo; +import android.system.wifi.mainline_supplicant.UsdServiceProtoType; + +/** + * Interface for performing Unsynchronized Service Discovery (USD) operations. + */ +interface IUsdInterface { + /** + * Capabilities supported by USD. Values are only valid if |isUsdPublisherSupported| + * and/or |isUsdSubscriberSupported| are true. + */ + parcelable UsdCapabilities { + /** + * Whether USD Publisher is supported on this device. + */ + boolean isUsdPublisherSupported; + + /** + * Whether USD Subscriber is supported on this device. + */ + boolean isUsdSubscriberSupported; + + /** + * Maximum allowed length (in bytes) for the Service Specific Info (SSI). + */ + int maxLocalSsiLengthBytes; + + /** + * Maximum allowed length (in bytes) for the service name. + */ + int maxServiceNameLengthBytes; + + /** + * Maximum allowed length (in bytes) for a match filter. + */ + int maxMatchFilterLengthBytes; + + /** + * Maximum number of allowed publish sessions. + */ + int maxNumPublishSessions; + + /** + * Maximum number of allowed subscribe sessions. + */ + int maxNumSubscribeSessions; + } + + /** + * Data used in both publish and subscribe configurations. + */ + parcelable BaseConfig { + /** + * Service name of the USD session. A UTF-8 encoded string from 1 to 255 bytes in length. + * The only acceptable single-byte UTF-8 symbols for a Service Name are alphanumeric + * values (A-Z, a-z, 0-9), hyphen ('-'), period ('.'), and underscore ('_'). All + * valid multi-byte UTF-8 characters are acceptable in a Service Name. + */ + @utf8InCpp String serviceName; + + /** + * Service protocol type for the USD session (ex. Generic, CSA Matter). + */ + UsdServiceProtoType serviceProtoType; + + /** + * Details about the service being offered or being looked for. This information is + * transmitted within Service Discovery frames, and is used to help devices find each other + * and establish connections. The format and content of the service specific information are + * flexible and can be determined by the application. + */ + byte[] serviceSpecificInfo; + + /** + * Ordered sequence of <length, value> pairs (|length| uses 1 byte and contains the number + * of bytes in the |value| field) which specify further match criteria (beyond the service + * name). + * + * The match behavior is specified in details in the NAN spec. + * Publisher: used if provided. + * Subscriber: used (if provided) only in ACTIVE sessions. + * + * Max length: |UsdCapabilities.maxMatchFilterLength|. + * NAN Spec: matching_filter_tx and Service Descriptor Attribute (SDA) / Matching Filter + */ + @nullable byte[] txMatchFilter; + + /** + * Ordered sequence of <length, value> pairs (|length| uses 1 byte and contains the number + * of bytes in the |value| field) which specify further match criteria (beyond the service + * name). + * + * The match behavior is specified in details in the NAN spec. + * Publisher: used in SOLICITED or SOLICITED_UNSOLICITED sessions. + * Subscriber: used in ACTIVE or PASSIVE sessions. + * + * Max length: |UsdCapabilities.maxMatchFilterLength|. + * NAN Spec: matching_filter_rx + */ + @nullable byte[] rxMatchfilter; + + /** + * Time interval (in seconds) that a USD session will be alive. + * The session will be terminated when the time to live (TTL) is reached, triggering either + * |IUsdCallback.onPublishTerminated| for Publish, or |IUsdCallback.onSubscribeTerminated| + * for Subscribe. + */ + int ttlSec; + + /** + * Frequency where the device should begin to dwell. Default value is channel 6 (2.437 GHz), + * but other values may be selected per regulation in the geographical location. + */ + int defaultFreqMhz; + + /** + * Channels which can be switched to. May contain any of the 20 MHz channels in the + * 2.4 Ghz and/or 5 Ghz bands, per regulation in the geographical location. + */ + int[] freqsMhz; + } + + /** + * Subscribe modes that this session can be configured in. + */ + enum SubscribeType { + /** + * Subscribe function does not request transmission of any Subscribe messages, but checks + * for matches in received Publish messages. + */ + PASSIVE_MODE = 0, + /** + * Subscribe function additionally requests transmission of Subscribe messages and processes + * Publish messages. + */ + ACTIVE_MODE = 1, + } + + /** + * Parameters for configuring a USD subscribe session. + */ + parcelable SubscribeConfig { + /** + * Base USD session parameters. + */ + BaseConfig baseConfig; + + /** + * Subscribe mode that this session should be configured in. + */ + SubscribeType subscribeType; + + /** + * Recommended periodicity (in milliseconds) of query transmissions for the session. + */ + int queryPeriodMillis; + } + + /** + * Type of USD publishing. + */ + enum PublishType { + /** + * Only transmissions that are triggered by a specific event. + */ + SOLICITED_ONLY = 0, + + /** + * Only transmissions that are not requested. + */ + UNSOLICITED_ONLY = 1, + + /** + * Both solicited and unsolicited transmissions. + */ + SOLICITED_AND_UNSOLICITED = 2, + } + + /** + * Types of USD publish transmissions. + */ + @Backing(type="int") + enum PublishTransmissionType { + /** + * Sends data from one device to a single, specific destination device. + */ + UNICAST = 0, + + /** + * Sends data from one device to a group of devices on the network simultaneously. + */ + MULTICAST = 1, + } + + /** + * Parameters for configuring a USD publish session. + */ + parcelable PublishConfig { + /** + * Base USD session parameters. + */ + BaseConfig baseConfig; + + /** + * Types of transmissions (solicited vs. unsolicited) which should be generated. + */ + PublishType publishType; + + /** + * Whether Further Service Discovery (FSD) is enabled. + */ + boolean isFsd; + + /** + * Interval (in milliseconds) for sending unsolicited publish transmissions. + */ + int announcementPeriodMillis; + + /** + * Type of the publish transmission (ex. unicast, multicast). + */ + PublishTransmissionType transmissionType; + } + + /** + * Retrieve capabilities related to Unsynchronized Service Discovery (USD). + * + * @return Instance of |UsdCapabilities| containing the capability info. + */ + UsdCapabilities getUsdCapabilities(); + + /** + * Start a USD publish session. Triggers a response via |IUsdCallback.onPublishStarted| + * if successful, or |IUsdCallback.onPublishConfigFailed| if failed. + * + * @param cmdId Identifier for this request. Will be returned in the callback to identify + * the request. + * @param usdPublishConfig Parameters for the requested publish session. + * @throws ServiceSpecificException with one of the following values: + * |SupplicantStatusCode.FAILURE_UNKNOWN| + * |SupplicantStatusCode.FAILURE_UNSUPPORTED| + */ + void startUsdPublish(in int cmdId, in PublishConfig usdPublishConfig); + + /** + * Start a USD subscribe session. Triggers a response via |IUsdCallback.onSubscribeStarted| + * if successful, or |IUsdCallback.onSubscribeConfigFailed| if failed. + * + * @param cmdId Identifier for this request. Will be returned in the callback to identify + * the request. + * @param usdSubscribeConfig Parameters for the requested subscribe session. + * @throws ServiceSpecificException with one of the following values: + * |SupplicantStatusCode.FAILURE_UNKNOWN| + * |SupplicantStatusCode.FAILURE_UNSUPPORTED| + */ + void startUsdSubscribe(in int cmdId, in SubscribeConfig usdSubscribeConfig); + + /** + * Update the service-specific info for an active publish session. + * + * @param publishId Identifier for the active publish session. + * @param serviceSpecificInfo Byte array containing the service-specific info. Note that the + * maximum info length is |UsdCapabilities.maxLocalSsiLengthBytes|. + * @throws ServiceSpecificException with one of the following values: + * |SupplicantStatusCode.FAILURE_UNKNOWN| + * |SupplicantStatusCode.FAILURE_UNSUPPORTED| + */ + void updateUsdPublish(in int publishId, in byte[] serviceSpecificInfo); + + /** + * Cancel an existing USD publish session. |IUsdCallback.onPublishTerminated| + * will be called upon completion. + * + * @param publishId Identifier for the publish session to cancel. + * @throws ServiceSpecificException with one of the following values: + * |SupplicantStatusCode.FAILURE_UNKNOWN| + * |SupplicantStatusCode.FAILURE_UNSUPPORTED| + */ + void cancelUsdPublish(in int publishId); + + /** + * Cancel an existing USD subscribe session. + * |IUsdCallback.onSubscribeTerminated| will be called upon completion. + * + * @param subscribeId Identifier for the subscribe session to cancel. + * @throws ServiceSpecificException with one of the following values: + * |SupplicantStatusCode.FAILURE_UNKNOWN| + * |SupplicantStatusCode.FAILURE_UNSUPPORTED| + */ + void cancelUsdSubscribe(in int subscribeId); + + /** + * Send a message to a peer device across an active USD link. + * + * @param messageInfo Information for the message to be sent. + * @throws ServiceSpecificException with one of the following values: + * |SupplicantStatusCode.FAILURE_UNKNOWN| + * |SupplicantStatusCode.FAILURE_UNSUPPORTED| + */ + void sendUsdMessage(in UsdMessageInfo messageInfo); +} diff --git a/aidl/mainline_supplicant/android/system/wifi/mainline_supplicant/SupplicantStatusCode.aidl b/aidl/mainline_supplicant/android/system/wifi/mainline_supplicant/SupplicantStatusCode.aidl new file mode 100644 index 0000000000..11bd6ca937 --- /dev/null +++ b/aidl/mainline_supplicant/android/system/wifi/mainline_supplicant/SupplicantStatusCode.aidl @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2024 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 android.system.wifi.mainline_supplicant; + +/** + * Enum values indicating the status of any supplicant operation. + */ +enum SupplicantStatusCode { + /** + * No errors. + */ + SUCCESS = 0, + /** + * Unknown failure occurred. + */ + FAILURE_UNKNOWN = 1, + /** + * One of the provided arguments is invalid. + */ + FAILURE_ARGS_INVALID = 2, + /** + * Interface with the provided name already exists. + */ + FAILURE_IFACE_EXISTS = 3, + /** + * Interface with the provided name does not exist. + */ + FAILURE_IFACE_UNKNOWN = 4, + /** + * Operation is not supported by the service. + */ + FAILURE_UNSUPPORTED = 5, +} diff --git a/aidl/mainline_supplicant/android/system/wifi/mainline_supplicant/UsdMessageInfo.aidl b/aidl/mainline_supplicant/android/system/wifi/mainline_supplicant/UsdMessageInfo.aidl new file mode 100644 index 0000000000..0b26d6540c --- /dev/null +++ b/aidl/mainline_supplicant/android/system/wifi/mainline_supplicant/UsdMessageInfo.aidl @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2024 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 android.system.wifi.mainline_supplicant; + +/** + * Information for sending a USD message. + */ +parcelable UsdMessageInfo { + /** + * Identifier for this device, retrieved from |ServiceDiscoveryInfo|. + */ + int ownId; + + /** + * Identifier for the peer device, retrieved from |ServiceDiscoveryInfo|. + */ + int peerId; + + /** + * MAC address for the peer device. + */ + byte[6] peerMacAddress; + + /** + * Message contents. Note that the maximum message length is + * |UsdCapabilities.maxLocalSsiLengthBytes|. + */ + byte[] message; +} diff --git a/aidl/mainline_supplicant/android/system/wifi/mainline_supplicant/UsdServiceProtoType.aidl b/aidl/mainline_supplicant/android/system/wifi/mainline_supplicant/UsdServiceProtoType.aidl new file mode 100644 index 0000000000..4e6bbe7392 --- /dev/null +++ b/aidl/mainline_supplicant/android/system/wifi/mainline_supplicant/UsdServiceProtoType.aidl @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2024 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 android.system.wifi.mainline_supplicant; + +/** + * Service protocols that use USD. + */ +@Backing(type="int") +enum UsdServiceProtoType { + /** + * Unknown service type. + */ + UNKNOWN = 0, + + /** + * Generic service. + */ + GENERIC = 1, + + /** + * CSA (Connectivity Standards Alliance) Matter. + * + * Note: CSA Matter is an open-source, royalty-free standard for smart home technology that + * allows devices to work with any Matter-certified ecosystem. + */ + CSA_MATTER = 2, +} diff --git a/apex/Android.bp b/apex/Android.bp index a878695458..614777e5dd 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -27,6 +27,8 @@ apex_defaults { // from build rule `cacerts_wfa` prebuilts: [ "cacerts_wfa", + "mainline_supplicant_conf", + "mainline_supplicant_rc", ], key: "com.android.wifi.key", certificate: ":com.android.wifi.certificate", @@ -35,6 +37,13 @@ apex_defaults { "ServiceWifiResources", "WifiDialog", ], + binaries: select(release_flag("RELEASE_WIFI_MAINLINE_SUPPLICANT_BINARY"), { + true: ["wpa_supplicant_mainline"], + default: [], + }), + jni_libs: [ + "libservice-wifi-jni", + ], defaults: ["r-launched-apex-module"], // Indicates that pre-installed version of this apex can be compressed. // Whether it actually will be compressed is controlled on per-device basis. @@ -122,6 +131,7 @@ bootclasspath_fragment { "android.net.wifi.p2p", "android.net.wifi.rtt", "android.net.wifi.twt", + "android.net.wifi.usd", "android.net.wifi.util", "com.android.wifi", ], diff --git a/flags/Android.bp b/flags/Android.bp index 5deabc5f54..99ba39a590 100644 --- a/flags/Android.bp +++ b/flags/Android.bp @@ -54,3 +54,9 @@ java_defaults { "aconfig_storage_stub", ], } + +cc_aconfig_library { + name: "wifi_aconfig_flags_c_lib", + aconfig_declarations: "wifi_aconfig_flags", + vendor_available: true, +} diff --git a/flags/wifi_flags.aconfig b/flags/wifi_flags.aconfig index 7b4decfefe..edaf96cd37 100644 --- a/flags/wifi_flags.aconfig +++ b/flags/wifi_flags.aconfig @@ -9,22 +9,6 @@ flag { } flag { - name: "p2p_ownership" - namespace: "wifi" - description: "Control the P2P group ownership feature" - bug: "215045910" - is_fixed_read_only: true -} - -flag { - name: "p2p_dual" - namespace: "wifi" - description: "Control the dual P2P group feature" - bug: "296063280" - is_fixed_read_only: true -} - -flag { name: "delay_save_to_store" namespace: "wifi" description: "Control the feature delay the save to store in batch to reduce the blocking time" @@ -84,6 +68,7 @@ flag { description: "Add new API to get channel width" bug: "335358378" is_fixed_read_only: true + is_exported: true } flag { @@ -123,6 +108,7 @@ flag { description: "optimize local-only connection API" bug: "347117408" is_fixed_read_only: true + is_exported: true } flag { @@ -137,6 +123,15 @@ flag { } flag { + name: "autojoin_restriction_security_types_api" + is_exported: true + namespace: "wifi" + description: "Add new API to set or get Autojoin Restriction security types" + bug: "340351634" + is_fixed_read_only: true +} + +flag { name: "rsn_overriding" namespace: "wifi" description: "Wi-Fi Alliance RSN Overriding feature" @@ -169,4 +164,121 @@ flag { namespace: "wifi" description: "Gate WiFi Scorer new stats collection" bug: "367362809" -}
\ No newline at end of file +} + +flag { + name: "softap_disconnect_reason" + is_exported: true + namespace: "wifi" + description: "Add SoftAP disconnect reason code" + bug: "361651437" + is_fixed_read_only: true +} + +flag { + name: "secure_ranging" + is_exported: true + namespace: "wifi" + description: "Add secure ranging support" + bug: "364722301" + is_fixed_read_only: true +} + +flag { + name: "mainline_supplicant" + namespace: "wifi" + description: "Use the secondary supplicant binary stored in the mainline module" + bug: "365585450" + is_fixed_read_only: true +} + +flag { + name: "mlo_sap" + is_exported: true + namespace: "wifi" + description: "Mlo SoftAp" + bug: "362355566" + is_fixed_read_only: true +} + +flag { + name: "wifi_direct_r2" + is_exported: true + namespace: "wifi" + description: "Wi-Fi Alliance Wi-Fi Direct R2 feature" + bug: "341971059" + is_fixed_read_only: true +} + +flag { + name: "wep_disabled_in_apm" + is_exported: true + namespace: "wifi" + description: "Wep should be disabled in advanced protection mode" + bug: "362586268" + is_fixed_read_only: true +} + +flag { + name: "bssid_blocklist_for_suggestion" + is_exported: true + namespace: "wifi" + description: "Add BSSID to blocklist for network suggestion" + bug: "340263610" + is_fixed_read_only: true +} + +flag { + name: "ap_isolate" + is_exported: true + namespace: "wifi" + description: "Supports Soft AP client isolation configuration" + bug: "364333929" + is_fixed_read_only: true +} + +flag { + name: "public_bands_for_lohs" + is_exported: true + namespace: "wifi" + description: "Public bands api for local only hotspot from system api" + bug: "362355566" + is_fixed_read_only: true +} + +flag { + name: "wifi_state_changed_listener" + is_exported: true + namespace: "wifi" + description: "Wifi state changed listener API" + bug: "349530934" + is_fixed_read_only: true +} + +flag { + name: "p2p_dialog2" + is_exported: true + namespace: "wifi" + description: "Updated P2P dialogs" + bug: "349253691" + is_fixed_read_only: true +} + + +flag { + name: "multiple_mld_on_sap_supported" + is_exported: false + namespace: "wifi" + description: "Check driver capability when determining the number of supported MLD" + bug: "382023801" + is_fixed_read_only: true +} + +flag { + name: "monitor_intent_for_all_users" + is_exported: false + namespace: "wifi" + description: "IntentReceiver should monitor intent from all users" + bug: "390257834" + is_fixed_read_only: true +} diff --git a/framework/Android.bp b/framework/Android.bp index f1186f4854..40e1003792 100644 --- a/framework/Android.bp +++ b/framework/Android.bp @@ -109,6 +109,9 @@ java_defaults { "android.hardware.wifi-V1.0-java-constants", "wifi-modules-utils", "PlatformProperties", + "android.net.wifi.flags-aconfig-java", + "ranging_aconfig_flags_lib", + "android.security.flags-aconfig-java-export", ], libs: [ "androidx.annotation_annotation", diff --git a/framework/aidl-export/android/net/wifi/BlockingOption.aidl b/framework/aidl-export/android/net/wifi/BlockingOption.aidl new file mode 100644 index 0000000000..47d4627114 --- /dev/null +++ b/framework/aidl-export/android/net/wifi/BlockingOption.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2024, 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 android.net.wifi; + +parcelable BlockingOption; diff --git a/framework/aidl-export/android/net/wifi/usd/Characteristics.aidl b/framework/aidl-export/android/net/wifi/usd/Characteristics.aidl new file mode 100644 index 0000000000..818426bda2 --- /dev/null +++ b/framework/aidl-export/android/net/wifi/usd/Characteristics.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2024, 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 android.net.wifi.usd; + +parcelable Characteristics; diff --git a/framework/aidl-export/android/net/wifi/usd/PublishConfig.aidl b/framework/aidl-export/android/net/wifi/usd/PublishConfig.aidl new file mode 100644 index 0000000000..80e5421eab --- /dev/null +++ b/framework/aidl-export/android/net/wifi/usd/PublishConfig.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2024, 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 android.net.wifi.usd; + +parcelable PublishConfig; diff --git a/framework/aidl-export/android/net/wifi/usd/SubscribeConfig.aidl b/framework/aidl-export/android/net/wifi/usd/SubscribeConfig.aidl new file mode 100644 index 0000000000..71a168423b --- /dev/null +++ b/framework/aidl-export/android/net/wifi/usd/SubscribeConfig.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2024, 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 android.net.wifi.usd; + +parcelable SubscribeConfig; diff --git a/framework/api/current.txt b/framework/api/current.txt index 45ec47ea32..c8d8685bf8 100644 --- a/framework/api/current.txt +++ b/framework/api/current.txt @@ -1,6 +1,20 @@ // Signature format: 2.0 package android.net.wifi { + @FlaggedApi("com.android.wifi.flags.bssid_blocklist_for_suggestion") public final class BlockingOption implements android.os.Parcelable { + method public int describeContents(); + method public int getBlockingTimeSeconds(); + method public boolean isBlockingBssidOnly(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.BlockingOption> CREATOR; + } + + @FlaggedApi("com.android.wifi.flags.bssid_blocklist_for_suggestion") public static final class BlockingOption.Builder { + ctor public BlockingOption.Builder(@IntRange(from=1, to=86400) int); + method @NonNull public android.net.wifi.BlockingOption build(); + method @NonNull public android.net.wifi.BlockingOption.Builder setBlockingBssidOnly(boolean); + } + public abstract class EasyConnectStatusCallback { field public static final int EASY_CONNECT_EVENT_FAILURE_AUTHENTICATION = -2; // 0xfffffffe field public static final int EASY_CONNECT_EVENT_FAILURE_BUSY = -5; // 0xfffffffb @@ -55,6 +69,8 @@ package android.net.wifi { method @FlaggedApi("com.android.wifi.flags.android_v_wifi_api") public boolean is80211azNtbResponder(); method public boolean is80211mcResponder(); method public boolean isPasspointNetwork(); + method @FlaggedApi("com.android.wifi.flags.secure_ranging") public boolean isRangingFrameProtectionRequired(); + method @FlaggedApi("com.android.wifi.flags.secure_ranging") public boolean isSecureHeLtfSupported(); method @FlaggedApi("com.android.wifi.flags.android_v_wifi_api") public boolean isTwtResponder(); method public void writeToParcel(android.os.Parcel, int); field public String BSSID; @@ -108,12 +124,17 @@ package android.net.wifi { public final class SoftApConfiguration implements android.os.Parcelable { method public int describeContents(); method @Nullable public android.net.MacAddress getBssid(); + method @FlaggedApi("com.android.wifi.flags.public_bands_for_lohs") @NonNull public android.util.SparseIntArray getChannels(); method @Nullable public String getPassphrase(); method public int getSecurityType(); method @Deprecated @Nullable public String getSsid(); method @Nullable public android.net.wifi.WifiSsid getWifiSsid(); method public boolean isHiddenSsid(); method public void writeToParcel(@NonNull android.os.Parcel, int); + field @FlaggedApi("com.android.wifi.flags.public_bands_for_lohs") public static final int BAND_2GHZ = 1; // 0x1 + field @FlaggedApi("com.android.wifi.flags.public_bands_for_lohs") public static final int BAND_5GHZ = 2; // 0x2 + field @FlaggedApi("com.android.wifi.flags.public_bands_for_lohs") public static final int BAND_60GHZ = 8; // 0x8 + field @FlaggedApi("com.android.wifi.flags.public_bands_for_lohs") public static final int BAND_6GHZ = 4; // 0x4 field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.SoftApConfiguration> CREATOR; field public static final int SECURITY_TYPE_OPEN = 0; // 0x0 field public static final int SECURITY_TYPE_WPA2_PSK = 1; // 0x1 @@ -123,6 +144,12 @@ package android.net.wifi { field public static final int SECURITY_TYPE_WPA3_SAE_TRANSITION = 2; // 0x2 } + @FlaggedApi("com.android.wifi.flags.public_bands_for_lohs") public static final class SoftApConfiguration.Builder { + ctor public SoftApConfiguration.Builder(); + method @NonNull public android.net.wifi.SoftApConfiguration build(); + method @NonNull public android.net.wifi.SoftApConfiguration.Builder setChannels(@NonNull android.util.SparseIntArray); + } + public enum SupplicantState implements android.os.Parcelable { method public int describeContents(); method public static boolean isValidState(android.net.wifi.SupplicantState); @@ -442,6 +469,7 @@ package android.net.wifi { method public void addOrUpdatePasspointConfiguration(android.net.wifi.hotspot2.PasspointConfiguration); method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_WIFI_STATE}) public void addSuggestionConnectionStatusListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.SuggestionConnectionStatusListener); method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public void addSuggestionUserApprovalStatusListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.SuggestionUserApprovalStatusListener); + method @FlaggedApi("com.android.wifi.flags.wifi_state_changed_listener") @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public void addWifiStateChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.WifiStateChangedListener); method public void allowAutojoinGlobal(boolean); method @Deprecated public static int calculateSignalLevel(int, int); method @IntRange(from=0) public int calculateSignalLevel(int); @@ -451,6 +479,7 @@ package android.net.wifi { method public android.net.wifi.WifiManager.WifiLock createWifiLock(int, String); method @Deprecated public android.net.wifi.WifiManager.WifiLock createWifiLock(String); method @Deprecated public boolean disableNetwork(int); + method @FlaggedApi("com.android.wifi.flags.bssid_blocklist_for_suggestion") @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) public void disallowCurrentSuggestedNetwork(@NonNull android.net.wifi.BlockingOption); method @Deprecated public boolean disconnect(); method @Deprecated public boolean enableNetwork(int, boolean); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_MANAGED_PROVISIONING, android.Manifest.permission.NETWORK_CARRIER_PROVISIONING}, conditional=true) public void flushPasspointAnqpCache(); @@ -532,6 +561,7 @@ package android.net.wifi { method @FlaggedApi("com.android.wifi.flags.android_v_wifi_api") public void removePerSsidRoamingMode(@NonNull android.net.wifi.WifiSsid); method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public void removeSuggestionConnectionStatusListener(@NonNull android.net.wifi.WifiManager.SuggestionConnectionStatusListener); method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public void removeSuggestionUserApprovalStatusListener(@NonNull android.net.wifi.WifiManager.SuggestionUserApprovalStatusListener); + method @FlaggedApi("com.android.wifi.flags.wifi_state_changed_listener") @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public void removeWifiStateChangedListener(@NonNull android.net.wifi.WifiManager.WifiStateChangedListener); method @RequiresPermission(allOf={android.Manifest.permission.MANAGE_WIFI_INTERFACES, android.Manifest.permission.ACCESS_WIFI_STATE}) public void reportCreateInterfaceImpact(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.BiConsumer<java.lang.Boolean,java.util.Set<android.net.wifi.WifiManager.InterfaceCreationImpact>>); method @Deprecated public boolean saveConfiguration(); method @FlaggedApi("com.android.wifi.flags.android_v_wifi_api") public void setPerSsidRoamingMode(@NonNull android.net.wifi.WifiSsid, int); @@ -542,6 +572,7 @@ package android.net.wifi { method public void setTdlsEnabledWithMacAddress(@NonNull String, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); method @Deprecated public boolean setWifiEnabled(boolean); method @RequiresPermission(allOf={android.Manifest.permission.CHANGE_WIFI_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.NEARBY_WIFI_DEVICES}, conditional=true) public void startLocalOnlyHotspot(android.net.wifi.WifiManager.LocalOnlyHotspotCallback, @Nullable android.os.Handler); + method @FlaggedApi("com.android.wifi.flags.public_bands_for_lohs") @RequiresPermission(allOf={android.Manifest.permission.CHANGE_WIFI_STATE, android.Manifest.permission.NEARBY_WIFI_DEVICES}) public void startLocalOnlyHotspotWithConfiguration(@NonNull android.net.wifi.SoftApConfiguration, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.LocalOnlyHotspotCallback); method @Deprecated public boolean startScan(); method @Deprecated public void startWps(android.net.wifi.WpsInfo, android.net.wifi.WifiManager.WpsCallback); method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public void unregisterScanResultsCallback(@NonNull android.net.wifi.WifiManager.ScanResultsCallback); @@ -711,6 +742,10 @@ package android.net.wifi { method public void setWorkSource(android.os.WorkSource); } + @FlaggedApi("com.android.wifi.flags.wifi_state_changed_listener") public static interface WifiManager.WifiStateChangedListener { + method public void onWifiStateChanged(); + } + @Deprecated public abstract static class WifiManager.WpsCallback { ctor @Deprecated public WifiManager.WpsCallback(); method @Deprecated public abstract void onFailed(int); @@ -1254,19 +1289,30 @@ package android.net.wifi.p2p { method public int describeContents(); method public int getGroupClientIpProvisioningMode(); method public int getGroupOwnerBand(); + method @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") public int getGroupOwnerVersion(); method public int getNetworkId(); method @Nullable public String getNetworkName(); + method @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") @Nullable public android.net.wifi.p2p.WifiP2pPairingBootstrappingConfig getPairingBootstrappingConfig(); method @Nullable public String getPassphrase(); + method @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") public int getPccModeConnectionType(); + method @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") public boolean isAuthorizeConnectionFromPeerEnabled(); + method @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") public void setGroupOwnerVersion(int); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.p2p.WifiP2pConfig> CREATOR; field public static final int GROUP_CLIENT_IP_PROVISIONING_MODE_IPV4_DHCP = 0; // 0x0 field public static final int GROUP_CLIENT_IP_PROVISIONING_MODE_IPV6_LINK_LOCAL = 1; // 0x1 field public static final int GROUP_OWNER_BAND_2GHZ = 1; // 0x1 field public static final int GROUP_OWNER_BAND_5GHZ = 2; // 0x2 + field @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") public static final int GROUP_OWNER_BAND_6GHZ = 3; // 0x3 field public static final int GROUP_OWNER_BAND_AUTO = 0; // 0x0 field public static final int GROUP_OWNER_INTENT_AUTO = -1; // 0xffffffff field public static final int GROUP_OWNER_INTENT_MAX = 15; // 0xf field public static final int GROUP_OWNER_INTENT_MIN = 0; // 0x0 + field @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") public static final int P2P_VERSION_1 = 0; // 0x0 + field @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") public static final int P2P_VERSION_2 = 1; // 0x1 + field @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") public static final int PCC_MODE_CONNECTION_TYPE_LEGACY_ONLY = 0; // 0x0 + field @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") public static final int PCC_MODE_CONNECTION_TYPE_LEGACY_OR_R2 = 1; // 0x1 + field @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") public static final int PCC_MODE_CONNECTION_TYPE_R2_ONLY = 2; // 0x2 field public String deviceAddress; field @IntRange(from=0, to=15) public int groupOwnerIntent; field public android.net.wifi.WpsInfo wps; @@ -1276,12 +1322,15 @@ package android.net.wifi.p2p { ctor public WifiP2pConfig.Builder(); method @NonNull public android.net.wifi.p2p.WifiP2pConfig build(); method @NonNull public android.net.wifi.p2p.WifiP2pConfig.Builder enablePersistentMode(boolean); + method @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") @NonNull public android.net.wifi.p2p.WifiP2pConfig.Builder setAuthorizeConnectionFromPeerEnabled(boolean); method @NonNull public android.net.wifi.p2p.WifiP2pConfig.Builder setDeviceAddress(@Nullable android.net.MacAddress); method @NonNull public android.net.wifi.p2p.WifiP2pConfig.Builder setGroupClientIpProvisioningMode(int); method @NonNull public android.net.wifi.p2p.WifiP2pConfig.Builder setGroupOperatingBand(int); method @NonNull public android.net.wifi.p2p.WifiP2pConfig.Builder setGroupOperatingFrequency(int); method @NonNull public android.net.wifi.p2p.WifiP2pConfig.Builder setNetworkName(@NonNull String); + method @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") @NonNull public android.net.wifi.p2p.WifiP2pConfig.Builder setPairingBootstrappingConfig(@NonNull android.net.wifi.p2p.WifiP2pPairingBootstrappingConfig); method @NonNull public android.net.wifi.p2p.WifiP2pConfig.Builder setPassphrase(@NonNull String); + method @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") @NonNull public android.net.wifi.p2p.WifiP2pConfig.Builder setPccModeConnectionType(int); } public class WifiP2pDevice implements android.os.Parcelable { @@ -1292,6 +1341,11 @@ package android.net.wifi.p2p { method @NonNull public java.util.List<android.net.wifi.ScanResult.InformationElement> getVendorElements(); method @Nullable public android.net.wifi.p2p.WifiP2pWfdInfo getWfdInfo(); method public boolean isGroupOwner(); + method @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") public boolean isOpportunisticBootstrappingMethodSupported(); + method @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") public boolean isPassphraseDisplayBootstrappingMethodSupported(); + method @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") public boolean isPassphraseKeypadBootstrappingMethodSupported(); + method @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") public boolean isPinCodeDisplayBootstrappingMethodSupported(); + method @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") public boolean isPinCodeKeypadBootstrappingMethodSupported(); method public boolean isServiceDiscoveryCapable(); method public void update(@NonNull android.net.wifi.p2p.WifiP2pDevice); method public boolean wpsDisplaySupported(); @@ -1321,6 +1375,16 @@ package android.net.wifi.p2p { field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.p2p.WifiP2pDeviceList> CREATOR; } + @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") public final class WifiP2pDirInfo implements android.os.Parcelable { + ctor public WifiP2pDirInfo(@NonNull android.net.MacAddress, @NonNull byte[], @NonNull byte[]); + method public int describeContents(); + method @NonNull public byte[] getDirTag(); + method @NonNull public android.net.MacAddress getMacAddress(); + method @NonNull public byte[] getNonce(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.p2p.WifiP2pDirInfo> CREATOR; + } + @FlaggedApi("com.android.wifi.flags.android_v_wifi_api") public final class WifiP2pDiscoveryConfig implements android.os.Parcelable { method @FlaggedApi("com.android.wifi.flags.android_v_wifi_api") public int describeContents(); method @FlaggedApi("com.android.wifi.flags.android_v_wifi_api") public int getFrequencyMhz(); @@ -1341,16 +1405,22 @@ package android.net.wifi.p2p { method public int describeContents(); method public java.util.Collection<android.net.wifi.p2p.WifiP2pDevice> getClientList(); method public int getFrequency(); + method @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") @Nullable public android.net.MacAddress getGroupOwnerBssid(); method public String getInterface(); method public int getNetworkId(); method public String getNetworkName(); method public android.net.wifi.p2p.WifiP2pDevice getOwner(); method public String getPassphrase(); + method @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") public int getSecurityType(); method public boolean isGroupOwner(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.p2p.WifiP2pGroup> CREATOR; field public static final int NETWORK_ID_PERSISTENT = -2; // 0xfffffffe field public static final int NETWORK_ID_TEMPORARY = -1; // 0xffffffff + field @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") public static final int SECURITY_TYPE_UNKNOWN = -1; // 0xffffffff + field @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") public static final int SECURITY_TYPE_WPA2_PSK = 0; // 0x0 + field @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") public static final int SECURITY_TYPE_WPA3_COMPATIBILITY = 1; // 0x1 + field @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") public static final int SECURITY_TYPE_WPA3_SAE = 2; // 0x2 } public class WifiP2pInfo implements android.os.Parcelable { @@ -1378,13 +1448,16 @@ package android.net.wifi.p2p { method @RequiresPermission(allOf={android.Manifest.permission.NEARBY_WIFI_DEVICES, android.Manifest.permission.ACCESS_FINE_LOCATION}, conditional=true) public void discoverPeersOnSocialChannels(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener); method @RequiresPermission(allOf={android.Manifest.permission.NEARBY_WIFI_DEVICES, android.Manifest.permission.ACCESS_FINE_LOCATION}, conditional=true) public void discoverPeersOnSpecificFrequency(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, int, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener); method @RequiresPermission(allOf={android.Manifest.permission.NEARBY_WIFI_DEVICES, android.Manifest.permission.ACCESS_FINE_LOCATION}, conditional=true) public void discoverServices(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener); + method @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") @RequiresPermission(allOf={android.Manifest.permission.NEARBY_WIFI_DEVICES, android.Manifest.permission.ACCESS_FINE_LOCATION}, conditional=true) public void discoverUsdBasedServices(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @NonNull android.net.wifi.p2p.WifiP2pUsdBasedServiceDiscoveryConfig, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener); method @RequiresPermission(allOf={android.Manifest.permission.NEARBY_WIFI_DEVICES, android.Manifest.permission.ACCESS_FINE_LOCATION}, conditional=true) public void getListenState(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); method public static int getP2pMaxAllowedVendorElementsLengthBytes(); method public android.net.wifi.p2p.WifiP2pManager.Channel initialize(android.content.Context, android.os.Looper, android.net.wifi.p2p.WifiP2pManager.ChannelListener); method public boolean isChannelConstrainedDiscoverySupported(); method public boolean isGroupClientRemovalSupported(); method public boolean isGroupOwnerIPv6LinkLocalAddressProvided(); + method @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") public boolean isPccModeSupported(); method public boolean isSetVendorElementsSupported(); + method @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") public boolean isWiFiDirectR2Supported(); method @FlaggedApi("com.android.wifi.flags.android_v_wifi_api") @RequiresPermission(allOf={android.Manifest.permission.NEARBY_WIFI_DEVICES, android.Manifest.permission.ACCESS_WIFI_STATE}, conditional=true) public void registerWifiP2pListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.p2p.WifiP2pManager.WifiP2pListener); method public void removeClient(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @NonNull android.net.MacAddress, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener); method @RequiresPermission(android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION) public void removeExternalApprover(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @NonNull android.net.MacAddress, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener); @@ -1393,6 +1466,7 @@ package android.net.wifi.p2p { method public void removeServiceRequest(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.nsd.WifiP2pServiceRequest, android.net.wifi.p2p.WifiP2pManager.ActionListener); method public void requestConnectionInfo(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ConnectionInfoListener); method @RequiresPermission(allOf={android.Manifest.permission.NEARBY_WIFI_DEVICES, android.Manifest.permission.ACCESS_FINE_LOCATION}, conditional=true) public void requestDeviceInfo(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @NonNull android.net.wifi.p2p.WifiP2pManager.DeviceInfoListener); + method @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") @RequiresPermission(allOf={android.Manifest.permission.NEARBY_WIFI_DEVICES, android.Manifest.permission.ACCESS_FINE_LOCATION}, conditional=true) public void requestDirInfo(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.net.wifi.p2p.WifiP2pDirInfo,java.lang.Exception>); method public void requestDiscoveryState(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @NonNull android.net.wifi.p2p.WifiP2pManager.DiscoveryStateListener); method @RequiresPermission(allOf={android.Manifest.permission.NEARBY_WIFI_DEVICES, android.Manifest.permission.ACCESS_FINE_LOCATION}, conditional=true) public void requestGroupInfo(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.GroupInfoListener); method public void requestNetworkInfo(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @NonNull android.net.wifi.p2p.WifiP2pManager.NetworkInfoListener); @@ -1407,9 +1481,11 @@ package android.net.wifi.p2p { method @RequiresPermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY) public void setWfdInfo(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @NonNull android.net.wifi.p2p.WifiP2pWfdInfo, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener); method @RequiresPermission(allOf={android.Manifest.permission.NEARBY_WIFI_DEVICES, android.Manifest.permission.ACCESS_FINE_LOCATION}, conditional=true) public void startListening(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener); method @FlaggedApi("com.android.wifi.flags.android_v_wifi_api") @RequiresPermission(allOf={android.Manifest.permission.NEARBY_WIFI_DEVICES, android.Manifest.permission.ACCESS_FINE_LOCATION}, conditional=true) public void startPeerDiscovery(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @NonNull android.net.wifi.p2p.WifiP2pDiscoveryConfig, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener); + method @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") @RequiresPermission(allOf={android.Manifest.permission.NEARBY_WIFI_DEVICES, android.Manifest.permission.ACCESS_FINE_LOCATION}, conditional=true) public void startUsdBasedLocalServiceAdvertisement(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @NonNull android.net.wifi.p2p.nsd.WifiP2pServiceInfo, @NonNull android.net.wifi.p2p.WifiP2pUsdBasedLocalServiceAdvertisementConfig, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener); method public void stopListening(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener); method public void stopPeerDiscovery(android.net.wifi.p2p.WifiP2pManager.Channel, android.net.wifi.p2p.WifiP2pManager.ActionListener); method @FlaggedApi("com.android.wifi.flags.android_v_wifi_api") public void unregisterWifiP2pListener(@NonNull android.net.wifi.p2p.WifiP2pManager.WifiP2pListener); + method @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") @RequiresPermission(allOf={android.Manifest.permission.NEARBY_WIFI_DEVICES, android.Manifest.permission.ACCESS_FINE_LOCATION}, conditional=true) public void validateDirInfo(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @NonNull android.net.wifi.p2p.WifiP2pDirInfo, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,java.lang.Exception>); field public static final String ACTION_WIFI_P2P_LISTEN_STATE_CHANGED = "android.net.wifi.p2p.action.WIFI_P2P_LISTEN_STATE_CHANGED"; field public static final String ACTION_WIFI_P2P_REQUEST_RESPONSE_CHANGED = "android.net.wifi.p2p.action.WIFI_P2P_REQUEST_RESPONSE_CHANGED"; field public static final int BUSY = 2; // 0x2 @@ -1434,6 +1510,7 @@ package android.net.wifi.p2p { field @FlaggedApi("com.android.wifi.flags.android_v_wifi_api") public static final int GROUP_CREATION_FAILURE_REASON_PROVISION_DISCOVERY_FAILED = 3; // 0x3 field @FlaggedApi("com.android.wifi.flags.android_v_wifi_api") public static final int GROUP_CREATION_FAILURE_REASON_TIMED_OUT = 1; // 0x1 field @FlaggedApi("com.android.wifi.flags.android_v_wifi_api") public static final int GROUP_CREATION_FAILURE_REASON_USER_REJECTED = 2; // 0x2 + field @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") public static final int NO_PERMISSION = 4; // 0x4 field public static final int NO_SERVICE_REQUESTS = 3; // 0x3 field public static final int P2P_UNSUPPORTED = 1; // 0x1 field public static final String WIFI_P2P_CONNECTION_CHANGED_ACTION = "android.net.wifi.p2p.CONNECTION_STATE_CHANGE"; @@ -1517,6 +1594,7 @@ package android.net.wifi.p2p { public static interface WifiP2pManager.ServiceResponseListener { method public void onServiceAvailable(int, byte[], android.net.wifi.p2p.WifiP2pDevice); + method @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") public default void onUsdBasedServiceAvailable(@NonNull android.net.wifi.p2p.WifiP2pDevice, @NonNull android.net.wifi.p2p.nsd.WifiP2pUsdBasedServiceResponse); } public static interface WifiP2pManager.UpnpServiceResponseListener { @@ -1539,6 +1617,47 @@ package android.net.wifi.p2p { method @FlaggedApi("com.android.wifi.flags.android_v_wifi_api") public default void onPeerListChanged(@NonNull android.net.wifi.p2p.WifiP2pDeviceList); } + @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") public final class WifiP2pPairingBootstrappingConfig implements android.os.Parcelable { + ctor public WifiP2pPairingBootstrappingConfig(int, @Nullable String); + method public int describeContents(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.p2p.WifiP2pPairingBootstrappingConfig> CREATOR; + field public static final int PAIRING_BOOTSTRAPPING_METHOD_DISPLAY_PASSPHRASE = 4; // 0x4 + field public static final int PAIRING_BOOTSTRAPPING_METHOD_DISPLAY_PINCODE = 2; // 0x2 + field public static final int PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PASSPHRASE = 16; // 0x10 + field public static final int PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PINCODE = 8; // 0x8 + field public static final int PAIRING_BOOTSTRAPPING_METHOD_OPPORTUNISTIC = 1; // 0x1 + field public static final int PAIRING_BOOTSTRAPPING_METHOD_OUT_OF_BAND = 32; // 0x20 + } + + @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") public final class WifiP2pUsdBasedLocalServiceAdvertisementConfig implements android.os.Parcelable { + method public int describeContents(); + method @IntRange(from=0) public int getFrequencyMhz(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.p2p.WifiP2pUsdBasedLocalServiceAdvertisementConfig> CREATOR; + } + + public static final class WifiP2pUsdBasedLocalServiceAdvertisementConfig.Builder { + ctor public WifiP2pUsdBasedLocalServiceAdvertisementConfig.Builder(); + method @NonNull public android.net.wifi.p2p.WifiP2pUsdBasedLocalServiceAdvertisementConfig build(); + method @NonNull public android.net.wifi.p2p.WifiP2pUsdBasedLocalServiceAdvertisementConfig.Builder setFrequencyMhz(@IntRange(from=1) int); + } + + @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") public final class WifiP2pUsdBasedServiceDiscoveryConfig implements android.os.Parcelable { + method public int describeContents(); + method public int getBand(); + method @Nullable public int[] getFrequenciesMhz(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.p2p.WifiP2pUsdBasedServiceDiscoveryConfig> CREATOR; + } + + public static final class WifiP2pUsdBasedServiceDiscoveryConfig.Builder { + ctor public WifiP2pUsdBasedServiceDiscoveryConfig.Builder(); + method @NonNull public android.net.wifi.p2p.WifiP2pUsdBasedServiceDiscoveryConfig build(); + method @NonNull public android.net.wifi.p2p.WifiP2pUsdBasedServiceDiscoveryConfig.Builder setBand(int); + method @NonNull public android.net.wifi.p2p.WifiP2pUsdBasedServiceDiscoveryConfig.Builder setFrequenciesMhz(@NonNull int[]); + } + public final class WifiP2pWfdInfo implements android.os.Parcelable { ctor public WifiP2pWfdInfo(); ctor public WifiP2pWfdInfo(@Nullable android.net.wifi.p2p.WifiP2pWfdInfo); @@ -1601,7 +1720,9 @@ package android.net.wifi.p2p.nsd { } public class WifiP2pServiceInfo implements android.os.Parcelable { + ctor @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") public WifiP2pServiceInfo(@NonNull android.net.wifi.p2p.nsd.WifiP2pUsdBasedServiceConfig); method public int describeContents(); + method @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") @Nullable public android.net.wifi.p2p.nsd.WifiP2pUsdBasedServiceConfig getWifiP2pUsdBasedServiceConfig(); method public void writeToParcel(android.os.Parcel, int); field public static final int SERVICE_TYPE_ALL = 0; // 0x0 field public static final int SERVICE_TYPE_BONJOUR = 1; // 0x1 @@ -1610,7 +1731,9 @@ package android.net.wifi.p2p.nsd { } public class WifiP2pServiceRequest implements android.os.Parcelable { + ctor @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") public WifiP2pServiceRequest(@NonNull android.net.wifi.p2p.nsd.WifiP2pUsdBasedServiceConfig); method public int describeContents(); + method @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") @Nullable public android.net.wifi.p2p.nsd.WifiP2pUsdBasedServiceConfig getWifiP2pUsdBasedServiceConfig(); method public static android.net.wifi.p2p.nsd.WifiP2pServiceRequest newInstance(int, String); method public static android.net.wifi.p2p.nsd.WifiP2pServiceRequest newInstance(int); method public void writeToParcel(android.os.Parcel, int); @@ -1625,6 +1748,34 @@ package android.net.wifi.p2p.nsd { method public static android.net.wifi.p2p.nsd.WifiP2pUpnpServiceRequest newInstance(String); } + @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") public final class WifiP2pUsdBasedServiceConfig implements android.os.Parcelable { + ctor public WifiP2pUsdBasedServiceConfig(); + method public int describeContents(); + method public static int getMaxAllowedServiceSpecificInfoLength(); + method @NonNull public String getServiceName(); + method @IntRange(from=0, to=255) public int getServiceProtocolType(); + method @Nullable public byte[] getServiceSpecificInfo(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.p2p.nsd.WifiP2pUsdBasedServiceConfig> CREATOR; + field public static final int SERVICE_PROTOCOL_TYPE_BONJOUR = 1; // 0x1 + field public static final int SERVICE_PROTOCOL_TYPE_GENERIC = 2; // 0x2 + } + + public static final class WifiP2pUsdBasedServiceConfig.Builder { + ctor public WifiP2pUsdBasedServiceConfig.Builder(@NonNull @Size(min=1) String); + method @NonNull public android.net.wifi.p2p.nsd.WifiP2pUsdBasedServiceConfig build(); + method @NonNull public android.net.wifi.p2p.nsd.WifiP2pUsdBasedServiceConfig.Builder setServiceProtocolType(@IntRange(from=0, to=255) int); + method @NonNull public android.net.wifi.p2p.nsd.WifiP2pUsdBasedServiceConfig.Builder setServiceSpecificInfo(@Nullable @Size(min=1) byte[]); + } + + @FlaggedApi("com.android.wifi.flags.wifi_direct_r2") public final class WifiP2pUsdBasedServiceResponse implements android.os.Parcelable { + method public int describeContents(); + method @IntRange(from=0, to=255) public int getServiceProtocolType(); + method @Nullable public byte[] getServiceSpecificInfo(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.p2p.nsd.WifiP2pUsdBasedServiceResponse> CREATOR; + } + } package android.net.wifi.rtt { @@ -1664,6 +1815,39 @@ package android.net.wifi.rtt { field public static final int TYPE_OF_PLACE = 29; // 0x1d } + @FlaggedApi("com.android.wifi.flags.secure_ranging") public final class PasnConfig implements android.os.Parcelable { + method public int describeContents(); + method public int getBaseAkms(); + method public int getCiphers(); + method @Nullable public byte[] getPasnComebackCookie(); + method @Nullable public String getPassword(); + method @Nullable public android.net.wifi.WifiSsid getWifiSsid(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field public static final int AKM_FILS_EAP_SHA256 = 64; // 0x40 + field public static final int AKM_FILS_EAP_SHA384 = 128; // 0x80 + field public static final int AKM_FT_EAP_SHA256 = 4; // 0x4 + field public static final int AKM_FT_EAP_SHA384 = 16; // 0x10 + field public static final int AKM_FT_PSK_SHA256 = 8; // 0x8 + field public static final int AKM_FT_PSK_SHA384 = 32; // 0x20 + field public static final int AKM_NONE = 0; // 0x0 + field public static final int AKM_PASN = 1; // 0x1 + field public static final int AKM_SAE = 2; // 0x2 + field public static final int CIPHER_CCMP_128 = 1; // 0x1 + field public static final int CIPHER_CCMP_256 = 2; // 0x2 + field public static final int CIPHER_GCMP_128 = 4; // 0x4 + field public static final int CIPHER_GCMP_256 = 8; // 0x8 + field public static final int CIPHER_NONE = 0; // 0x0 + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.rtt.PasnConfig> CREATOR; + } + + @FlaggedApi("com.android.wifi.flags.secure_ranging") public static final class PasnConfig.Builder { + ctor public PasnConfig.Builder(int, int); + method @NonNull public android.net.wifi.rtt.PasnConfig build(); + method @NonNull public android.net.wifi.rtt.PasnConfig.Builder setPasnComebackCookie(@NonNull byte[]); + method @NonNull public android.net.wifi.rtt.PasnConfig.Builder setPassword(@NonNull String); + method @NonNull public android.net.wifi.rtt.PasnConfig.Builder setWifiSsid(@NonNull android.net.wifi.WifiSsid); + } + public final class RangingRequest implements android.os.Parcelable { method public int describeContents(); method public static int getDefaultRttBurstSize(); @@ -1671,8 +1855,12 @@ package android.net.wifi.rtt { method public static int getMaxRttBurstSize(); method public static int getMinRttBurstSize(); method public int getRttBurstSize(); + method @FlaggedApi("com.android.wifi.flags.secure_ranging") public int getSecurityMode(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.rtt.RangingRequest> CREATOR; + field @FlaggedApi("com.android.wifi.flags.secure_ranging") public static final int SECURITY_MODE_OPEN = 0; // 0x0 + field @FlaggedApi("com.android.wifi.flags.secure_ranging") public static final int SECURITY_MODE_OPPORTUNISTIC = 1; // 0x1 + field @FlaggedApi("com.android.wifi.flags.secure_ranging") public static final int SECURITY_MODE_SECURE_AUTH = 2; // 0x2 } public static final class RangingRequest.Builder { @@ -1687,6 +1875,7 @@ package android.net.wifi.rtt { method public android.net.wifi.rtt.RangingRequest.Builder addWifiAwarePeer(@NonNull android.net.wifi.aware.PeerHandle); method public android.net.wifi.rtt.RangingRequest build(); method @NonNull public android.net.wifi.rtt.RangingRequest.Builder setRttBurstSize(int); + method @FlaggedApi("com.android.wifi.flags.secure_ranging") @NonNull public android.net.wifi.rtt.RangingRequest.Builder setSecurityMode(int); } public final class RangingResult implements android.os.Parcelable { @@ -1706,13 +1895,19 @@ package android.net.wifi.rtt { method @FlaggedApi("com.android.wifi.flags.android_v_wifi_api") public long getMinTimeBetweenNtbMeasurementsMicros(); method public int getNumAttemptedMeasurements(); method public int getNumSuccessfulMeasurements(); + method @FlaggedApi("com.android.wifi.flags.secure_ranging") public long getPasnComebackAfterMillis(); + method @FlaggedApi("com.android.wifi.flags.secure_ranging") @Nullable public byte[] getPasnComebackCookie(); method @Nullable public android.net.wifi.aware.PeerHandle getPeerHandle(); method public long getRangingTimestampMillis(); method public int getRssi(); + method @FlaggedApi("com.android.wifi.flags.secure_ranging") @IntRange(from=0, to=7) public int getSecureHeLtfProtocolVersion(); method public int getStatus(); method @Nullable public android.net.wifi.rtt.ResponderLocation getUnverifiedResponderLocation(); method @FlaggedApi("com.android.wifi.flags.android_v_wifi_api") public boolean is80211azNtbMeasurement(); method public boolean is80211mcMeasurement(); + method @FlaggedApi("com.android.wifi.flags.secure_ranging") public boolean isRangingAuthenticated(); + method @FlaggedApi("com.android.wifi.flags.secure_ranging") public boolean isRangingFrameProtected(); + method @FlaggedApi("com.android.wifi.flags.secure_ranging") public boolean isSecureHeLtfEnabled(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.rtt.RangingResult> CREATOR; field public static final int STATUS_FAIL = 1; // 0x1 @@ -1741,9 +1936,15 @@ package android.net.wifi.rtt { method @FlaggedApi("com.android.wifi.flags.android_v_wifi_api") @NonNull public android.net.wifi.rtt.RangingResult.Builder setMinTimeBetweenNtbMeasurementsMicros(long); method @FlaggedApi("com.android.wifi.flags.android_v_wifi_api") @NonNull public android.net.wifi.rtt.RangingResult.Builder setNumAttemptedMeasurements(int); method @FlaggedApi("com.android.wifi.flags.android_v_wifi_api") @NonNull public android.net.wifi.rtt.RangingResult.Builder setNumSuccessfulMeasurements(int); + method @FlaggedApi("com.android.wifi.flags.secure_ranging") @NonNull public android.net.wifi.rtt.RangingResult.Builder setPasnComebackAfterMillis(long); + method @FlaggedApi("com.android.wifi.flags.secure_ranging") @NonNull public android.net.wifi.rtt.RangingResult.Builder setPasnComebackCookie(@NonNull byte[]); method @FlaggedApi("com.android.wifi.flags.android_v_wifi_api") @NonNull public android.net.wifi.rtt.RangingResult.Builder setPeerHandle(@Nullable android.net.wifi.aware.PeerHandle); + method @FlaggedApi("com.android.wifi.flags.secure_ranging") @NonNull public android.net.wifi.rtt.RangingResult.Builder setRangingAuthenticated(boolean); + method @FlaggedApi("com.android.wifi.flags.secure_ranging") @NonNull public android.net.wifi.rtt.RangingResult.Builder setRangingFrameProtected(boolean); method @FlaggedApi("com.android.wifi.flags.android_v_wifi_api") @NonNull public android.net.wifi.rtt.RangingResult.Builder setRangingTimestampMillis(long); method @FlaggedApi("com.android.wifi.flags.android_v_wifi_api") @NonNull public android.net.wifi.rtt.RangingResult.Builder setRssi(int); + method @FlaggedApi("com.android.wifi.flags.secure_ranging") @NonNull public android.net.wifi.rtt.RangingResult.Builder setSecureHeLtfEnabled(boolean); + method @FlaggedApi("com.android.wifi.flags.secure_ranging") @NonNull public android.net.wifi.rtt.RangingResult.Builder setSecureHeLtfProtocolVersion(@IntRange(from=0, to=7) int); method @FlaggedApi("com.android.wifi.flags.android_v_wifi_api") @NonNull public android.net.wifi.rtt.RangingResult.Builder setStatus(int); method @FlaggedApi("com.android.wifi.flags.android_v_wifi_api") @NonNull public android.net.wifi.rtt.RangingResult.Builder setUnverifiedResponderLocation(@Nullable android.net.wifi.rtt.ResponderLocation); } @@ -1766,6 +1967,7 @@ package android.net.wifi.rtt { method @Nullable public android.net.MacAddress getMacAddress(); method public int getPreamble(); method public int getResponderType(); + method @FlaggedApi("com.android.wifi.flags.secure_ranging") @Nullable public android.net.wifi.rtt.SecureRangingConfig getSecureRangingConfig(); method @FlaggedApi("com.android.wifi.flags.android_v_wifi_api") public boolean is80211azNtbSupported(); method public boolean is80211mcSupported(); method public void writeToParcel(@NonNull android.os.Parcel, int); @@ -1786,6 +1988,7 @@ package android.net.wifi.rtt { method @NonNull public android.net.wifi.rtt.ResponderConfig.Builder setMacAddress(@NonNull android.net.MacAddress); method @NonNull public android.net.wifi.rtt.ResponderConfig.Builder setPreamble(int); method @NonNull public android.net.wifi.rtt.ResponderConfig.Builder setResponderType(int); + method @FlaggedApi("com.android.wifi.flags.secure_ranging") @NonNull public android.net.wifi.rtt.ResponderConfig.Builder setSecureRangingConfig(@NonNull android.net.wifi.rtt.SecureRangingConfig); } public final class ResponderLocation implements android.os.Parcelable { @@ -1828,6 +2031,22 @@ package android.net.wifi.rtt { field public static final int LOCATION_VARIABLE = 1; // 0x1 } + @FlaggedApi("com.android.wifi.flags.secure_ranging") public final class SecureRangingConfig implements android.os.Parcelable { + method @FlaggedApi("com.android.wifi.flags.secure_ranging") public int describeContents(); + method @FlaggedApi("com.android.wifi.flags.secure_ranging") @NonNull public android.net.wifi.rtt.PasnConfig getPasnConfig(); + method @FlaggedApi("com.android.wifi.flags.secure_ranging") public boolean isRangingFrameProtectionEnabled(); + method @FlaggedApi("com.android.wifi.flags.secure_ranging") public boolean isSecureHeLtfEnabled(); + method @FlaggedApi("com.android.wifi.flags.secure_ranging") public void writeToParcel(@NonNull android.os.Parcel, int); + field @FlaggedApi("com.android.wifi.flags.secure_ranging") @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.rtt.SecureRangingConfig> CREATOR; + } + + @FlaggedApi("com.android.wifi.flags.secure_ranging") public static final class SecureRangingConfig.Builder { + ctor @FlaggedApi("com.android.wifi.flags.secure_ranging") public SecureRangingConfig.Builder(@NonNull android.net.wifi.rtt.PasnConfig); + method @FlaggedApi("com.android.wifi.flags.secure_ranging") @NonNull public android.net.wifi.rtt.SecureRangingConfig build(); + method @FlaggedApi("com.android.wifi.flags.secure_ranging") @NonNull public android.net.wifi.rtt.SecureRangingConfig.Builder setRangingFrameProtectionEnabled(boolean); + method @FlaggedApi("com.android.wifi.flags.secure_ranging") @NonNull public android.net.wifi.rtt.SecureRangingConfig.Builder setSecureHeLtfEnabled(boolean); + } + public class WifiRttManager { method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public android.os.Bundle getRttCharacteristics(); method public boolean isAvailable(); @@ -1837,7 +2056,10 @@ package android.net.wifi.rtt { field public static final String CHARACTERISTICS_KEY_BOOLEAN_LCR = "key_lcr"; field @FlaggedApi("com.android.wifi.flags.android_v_wifi_api") public static final String CHARACTERISTICS_KEY_BOOLEAN_NTB_INITIATOR = "key_ntb_initiator"; field public static final String CHARACTERISTICS_KEY_BOOLEAN_ONE_SIDED_RTT = "key_one_sided_rtt"; + field @FlaggedApi("com.android.wifi.flags.secure_ranging") public static final String CHARACTERISTICS_KEY_BOOLEAN_RANGING_FRAME_PROTECTION_SUPPORTED = "key_rnm_mfp_supported"; + field @FlaggedApi("com.android.wifi.flags.secure_ranging") public static final String CHARACTERISTICS_KEY_BOOLEAN_SECURE_HE_LTF_SUPPORTED = "key_secure_he_ltf_supported"; field public static final String CHARACTERISTICS_KEY_BOOLEAN_STA_RESPONDER = "key_sta_responder"; + field @FlaggedApi("com.android.wifi.flags.secure_ranging") public static final String CHARACTERISTICS_KEY_INT_MAX_SUPPORTED_SECURE_HE_LTF_PROTO_VERSION = "key_max_supported_secure_he_ltf_proto_ver"; } } diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt index d06e48ac5e..937fbe95bc 100644 --- a/framework/api/system-current.txt +++ b/framework/api/system-current.txt @@ -13,6 +13,71 @@ package android.net.wifi { field public static final int POWER_CAP_NONE = 2147483647; // 0x7fffffff } + @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public final class DeauthenticationReasonCode { + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_AKMP_NOT_VALID = 20; // 0x14 + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_AUTHORIZED_ACCESS_LIMIT_REACHED = 46; // 0x2e + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_BAD_CIPHER_OR_AKM = 29; // 0x1d + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_BSS_TRANSITION_DISASSOC = 12; // 0xc + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_CIPHER_SUITE_REJECTED = 24; // 0x18 + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_CLASS2_FRAME_FROM_NONAUTH_STA = 6; // 0x6 + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_CLASS3_FRAME_FROM_NONASSOC_STA = 7; // 0x7 + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_DEAUTH_LEAVING = 3; // 0x3 + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_DISASSOC_AP_BUSY = 5; // 0x5 + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_DISASSOC_DUE_TO_INACTIVITY = 4; // 0x4 + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_DISASSOC_LOW_ACK = 34; // 0x22 + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_DISASSOC_STA_HAS_LEFT = 8; // 0x8 + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_END_TS_BA_DLS = 37; // 0x25 + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_EXCEEDED_TXOP = 35; // 0x23 + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_EXTERNAL_SERVICE_REQUIREMENTS = 47; // 0x2f + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_FOURWAY_HANDSHAKE_TIMEOUT = 15; // 0xf + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_GROUP_CIPHER_NOT_VALID = 18; // 0x12 + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_GROUP_KEY_UPDATE_TIMEOUT = 16; // 0x10 + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_IEEE_802_1X_AUTH_FAILED = 23; // 0x17 + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_IE_IN_4WAY_DIFFERS = 17; // 0x11 + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_INVALID_FTE = 51; // 0x33 + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_INVALID_FT_ACTION_FRAME_COUNT = 48; // 0x30 + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_INVALID_IE = 13; // 0xd + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_INVALID_MDE = 50; // 0x32 + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_INVALID_PMKID = 49; // 0x31 + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_INVALID_RSN_IE_CAPAB = 22; // 0x16 + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_MAC_ADDRESS_ALREADY_EXISTS_IN_MBSS = 64; // 0x40 + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_MESH_CHANNEL_SWITCH_REGULATORY_REQ = 65; // 0x41 + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_MESH_CHANNEL_SWITCH_UNSPECIFIED = 66; // 0x42 + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_MESH_CLOSE_RCVD = 55; // 0x37 + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_MESH_CONFIG_POLICY_VIOLATION = 54; // 0x36 + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_MESH_CONFIRM_TIMEOUT = 57; // 0x39 + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_MESH_INCONSISTENT_PARAMS = 59; // 0x3b + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_MESH_INVALID_GTK = 58; // 0x3a + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_MESH_INVALID_SECURITY_CAP = 60; // 0x3c + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_MESH_MAX_PEERS = 53; // 0x35 + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_MESH_MAX_RETRIES = 56; // 0x38 + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_MESH_PATH_ERROR_DEST_UNREACHABLE = 63; // 0x3f + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_MESH_PATH_ERROR_NO_FORWARDING_INFO = 62; // 0x3e + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_MESH_PATH_ERROR_NO_PROXY_INFO = 61; // 0x3d + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_MESH_PEERING_CANCELLED = 52; // 0x34 + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_MICHAEL_MIC_FAILURE = 14; // 0xe + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_NOT_AUTHORIZED_THIS_LOCATION = 30; // 0x1e + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_NOT_ENOUGH_BANDWIDTH = 33; // 0x21 + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_NO_SSP_ROAMING_AGREEMENT = 28; // 0x1c + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_PAIRWISE_CIPHER_NOT_VALID = 19; // 0x13 + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_PEERKEY_MISMATCH = 45; // 0x2d + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_PREV_AUTH_NOT_VALID = 2; // 0x2 + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_PWR_CAPABILITY_NOT_VALID = 10; // 0xa + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_SERVICE_CHANGE_PRECLUDES_TS = 31; // 0x1f + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_SSP_REQUESTED_DISASSOC = 27; // 0x1b + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_STA_LEAVING = 36; // 0x24 + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_STA_REQ_ASSOC_WITHOUT_AUTH = 9; // 0x9 + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_SUPPORTED_CHANNEL_NOT_VALID = 11; // 0xb + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_TDLS_TEARDOWN_UNREACHABLE = 25; // 0x19 + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_TDLS_TEARDOWN_UNSPECIFIED = 26; // 0x1a + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_TIMEOUT = 39; // 0x27 + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_UNKNOWN = 0; // 0x0 + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_UNKNOWN_TS_BA = 38; // 0x26 + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_UNSPECIFIED = 1; // 0x1 + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_UNSPECIFIED_QOS_REASON = 32; // 0x20 + field @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public static final int REASON_UNSUPPORTED_RSN_IE_VERSION = 21; // 0x15 + } + public abstract class EasyConnectStatusCallback { ctor public EasyConnectStatusCallback(); method public void onBootstrapUriGenerated(@NonNull android.net.Uri); @@ -411,6 +476,7 @@ package android.net.wifi { field public static final long SOFTAP_FEATURE_IEEE80211_AX = 16L; // 0x10L field public static final long SOFTAP_FEATURE_IEEE80211_BE = 512L; // 0x200L field public static final long SOFTAP_FEATURE_MAC_ADDRESS_CUSTOMIZATION = 8L; // 0x8L + field @FlaggedApi("com.android.wifi.flags.mlo_sap") public static final long SOFTAP_FEATURE_MLO = 4096L; // 0x1000L field public static final long SOFTAP_FEATURE_WPA3_OWE = 2048L; // 0x800L field public static final long SOFTAP_FEATURE_WPA3_OWE_TRANSITION = 1024L; // 0x400L field public static final long SOFTAP_FEATURE_WPA3_SAE = 4L; // 0x4L @@ -423,7 +489,6 @@ package android.net.wifi { method @NonNull public java.util.List<android.net.MacAddress> getBlockedClientList(); method public long getBridgedModeOpportunisticShutdownTimeoutMillis(); method @Deprecated public int getChannel(); - method @NonNull public android.util.SparseIntArray getChannels(); method public int getMacRandomizationSetting(); method public int getMaxChannelBandwidth(); method public int getMaxNumberOfClients(); @@ -434,14 +499,11 @@ package android.net.wifi { method public boolean isAutoShutdownEnabled(); method public boolean isBridgedModeOpportunisticShutdownEnabled(); method public boolean isClientControlByUserEnabled(); + method @FlaggedApi("com.android.wifi.flags.ap_isolate") public boolean isClientIsolationEnabled(); method public boolean isIeee80211axEnabled(); method public boolean isIeee80211beEnabled(); method public boolean isUserConfiguration(); method @Nullable public android.net.wifi.WifiConfiguration toWifiConfiguration(); - field public static final int BAND_2GHZ = 1; // 0x1 - field public static final int BAND_5GHZ = 2; // 0x2 - field public static final int BAND_60GHZ = 8; // 0x8 - field public static final int BAND_6GHZ = 4; // 0x4 field @Deprecated public static final int BAND_ANY = 7; // 0x7 field public static final long DEFAULT_TIMEOUT = -1L; // 0xffffffffffffffffL field public static final int RANDOMIZATION_NONE = 0; // 0x0 @@ -449,10 +511,8 @@ package android.net.wifi { field public static final int RANDOMIZATION_PERSISTENT = 1; // 0x1 } - public static final class SoftApConfiguration.Builder { - ctor public SoftApConfiguration.Builder(); + @FlaggedApi("com.android.wifi.flags.public_bands_for_lohs") public static final class SoftApConfiguration.Builder { ctor public SoftApConfiguration.Builder(@NonNull android.net.wifi.SoftApConfiguration); - method @NonNull public android.net.wifi.SoftApConfiguration build(); method @NonNull public android.net.wifi.SoftApConfiguration.Builder setAllowedAcsChannels(int, @NonNull int[]); method @NonNull public android.net.wifi.SoftApConfiguration.Builder setAllowedClientList(@NonNull java.util.List<android.net.MacAddress>); method @NonNull public android.net.wifi.SoftApConfiguration.Builder setAutoShutdownEnabled(boolean); @@ -463,8 +523,8 @@ package android.net.wifi { method @NonNull public android.net.wifi.SoftApConfiguration.Builder setBridgedModeOpportunisticShutdownTimeoutMillis(@IntRange(from=0xffffffff) long); method @NonNull public android.net.wifi.SoftApConfiguration.Builder setBssid(@Nullable android.net.MacAddress); method @NonNull public android.net.wifi.SoftApConfiguration.Builder setChannel(int, int); - method @NonNull public android.net.wifi.SoftApConfiguration.Builder setChannels(@NonNull android.util.SparseIntArray); method @NonNull public android.net.wifi.SoftApConfiguration.Builder setClientControlByUserEnabled(boolean); + method @FlaggedApi("com.android.wifi.flags.ap_isolate") @NonNull public android.net.wifi.SoftApConfiguration.Builder setClientIsolationEnabled(boolean); method @NonNull public android.net.wifi.SoftApConfiguration.Builder setHiddenSsid(boolean); method @NonNull public android.net.wifi.SoftApConfiguration.Builder setIeee80211axEnabled(boolean); method @NonNull public android.net.wifi.SoftApConfiguration.Builder setIeee80211beEnabled(boolean); @@ -485,6 +545,7 @@ package android.net.wifi { method public int getBandwidth(); method @Nullable public android.net.MacAddress getBssid(); method public int getFrequency(); + method @FlaggedApi("com.android.wifi.flags.mlo_sap") @Nullable public android.net.MacAddress getMldAddress(); method @FlaggedApi("com.android.wifi.flags.android_v_wifi_api") @NonNull public java.util.List<android.net.wifi.OuiKeyedData> getVendorData(); method public int getWifiStandard(); method @FlaggedApi("com.android.wifi.flags.android_v_wifi_api") public void setVendorData(@NonNull java.util.List<android.net.wifi.OuiKeyedData>); @@ -529,6 +590,7 @@ package android.net.wifi { public final class WifiClient implements android.os.Parcelable { method public int describeContents(); + method @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public int getDisconnectReason(); method @NonNull public android.net.MacAddress getMacAddress(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiClient> CREATOR; @@ -719,6 +781,8 @@ package android.net.wifi { method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void factoryReset(); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void forget(int, @Nullable android.net.wifi.WifiManager.ActionListener); method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.List<android.util.Pair<android.net.wifi.WifiConfiguration,java.util.Map<java.lang.Integer,java.util.List<android.net.wifi.ScanResult>>>> getAllMatchingWifiConfigs(@NonNull java.util.List<android.net.wifi.ScanResult>); + method @FlaggedApi("com.android.wifi.flags.autojoin_restriction_security_types_api") @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION}) public void getAutojoinDisallowedSecurityTypes(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<int[]>); + method @FlaggedApi("android.security.aapm_api") @NonNull public java.util.List<android.security.advancedprotection.AdvancedProtectionFeature> getAvailableAdvancedProtectionFeatures(); method @FlaggedApi("com.android.wifi.flags.get_bssid_blocklist_api") @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void getBssidBlocklist(@NonNull java.util.List<android.net.wifi.WifiSsid>, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.List<android.net.MacAddress>>); method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.ACCESS_COARSE_LOCATION}) public String getCountryCode(); method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public android.net.Network getCurrentNetwork(); @@ -753,6 +817,8 @@ package android.net.wifi { method public boolean isPortableHotspotSupported(); method public boolean isStaConcurrencyForRestrictedConnectionsSupported(); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public boolean isThirdPartyAppEnablingWifiConfirmationDialogEnabled(); + method @FlaggedApi("android.net.wifi.flags.usd") @RequiresPermission(android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION) public boolean isUsdPublisherSupported(); + method @FlaggedApi("android.net.wifi.flags.usd") @RequiresPermission(android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION) public boolean isUsdSubscriberSupported(); method public boolean isVerboseLoggingEnabled(); method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean isWifiApEnabled(); method public boolean isWifiScannerSupported(); @@ -784,6 +850,7 @@ package android.net.wifi { method @FlaggedApi("com.android.wifi.flags.android_v_wifi_api") @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void retrieveWifiBackupData(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<byte[]>); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void save(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener); method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setAutoWakeupEnabled(boolean); + method @FlaggedApi("com.android.wifi.flags.autojoin_restriction_security_types_api") @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION}) public void setAutojoinDisallowedSecurityTypes(@NonNull int[]); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void setCarrierNetworkOffloadEnabled(int, boolean, boolean); method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS) public void setCoexUnsafeChannels(@NonNull java.util.List<android.net.wifi.CoexUnsafeChannel>, int); method @FlaggedApi("com.android.wifi.flags.android_v_wifi_api") @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void setD2dAllowedWhenInfraStaDisabled(boolean); @@ -1024,6 +1091,7 @@ package android.net.wifi { public static interface WifiManager.SoftApCallback { method public default void onBlockedClientConnecting(@NonNull android.net.wifi.WifiClient, int); method public default void onCapabilityChanged(@NonNull android.net.wifi.SoftApCapability); + method @FlaggedApi("com.android.wifi.flags.softap_disconnect_reason") public default void onClientsDisconnected(@NonNull android.net.wifi.SoftApInfo, @NonNull java.util.List<android.net.wifi.WifiClient>); method @Deprecated public default void onConnectedClientsChanged(@NonNull java.util.List<android.net.wifi.WifiClient>); method public default void onConnectedClientsChanged(@NonNull android.net.wifi.SoftApInfo, @NonNull java.util.List<android.net.wifi.WifiClient>); method @Deprecated public default void onInfoChanged(@NonNull android.net.wifi.SoftApInfo); @@ -1459,6 +1527,17 @@ package android.net.wifi.aware { field public static final int UNSET_PARAMETER = -1; // 0xffffffff } + public final class Characteristics implements android.os.Parcelable { + method @FlaggedApi("com.android.ranging.flags.ranging_rtt_enabled") public int getMaxSupportedRangingPacketBandwidth(); + method @FlaggedApi("com.android.ranging.flags.ranging_rtt_enabled") public int getMaxSupportedRxChains(); + method @FlaggedApi("com.android.ranging.flags.ranging_rtt_enabled") public boolean isPeriodicRangingSupported(); + field @FlaggedApi("com.android.ranging.flags.ranging_rtt_enabled") public static final int SUPPORTED_RX_CHAINS_1 = 1; // 0x1 + field @FlaggedApi("com.android.ranging.flags.ranging_rtt_enabled") public static final int SUPPORTED_RX_CHAINS_2 = 2; // 0x2 + field @FlaggedApi("com.android.ranging.flags.ranging_rtt_enabled") public static final int SUPPORTED_RX_CHAINS_3 = 3; // 0x3 + field @FlaggedApi("com.android.ranging.flags.ranging_rtt_enabled") public static final int SUPPORTED_RX_CHAINS_4 = 4; // 0x4 + field @FlaggedApi("com.android.ranging.flags.ranging_rtt_enabled") public static final int SUPPORTED_RX_CHAINS_UNSPECIFIED = 0; // 0x0 + } + @FlaggedApi("com.android.wifi.flags.android_v_wifi_api") public final class ConfigRequest implements android.os.Parcelable { method @FlaggedApi("com.android.wifi.flags.android_v_wifi_api") public int describeContents(); method @FlaggedApi("com.android.wifi.flags.android_v_wifi_api") @NonNull public java.util.List<android.net.wifi.OuiKeyedData> getVendorData(); @@ -1479,6 +1558,7 @@ package android.net.wifi.aware { } public class DiscoverySessionCallback { + method @FlaggedApi("com.android.ranging.flags.ranging_rtt_enabled") public void onRangingResultsReceived(@NonNull java.util.List<android.net.wifi.rtt.RangingResult>); method public void onSessionResumeFailed(int); method public void onSessionResumeSucceeded(); method public void onSessionSuspendFailed(int); @@ -1487,10 +1567,12 @@ package android.net.wifi.aware { public final class PublishConfig implements android.os.Parcelable { method @FlaggedApi("com.android.wifi.flags.android_v_wifi_api") @NonNull public java.util.List<android.net.wifi.OuiKeyedData> getVendorData(); + method @FlaggedApi("com.android.ranging.flags.ranging_rtt_enabled") public boolean isPeriodicRangingResultsEnabled(); method public boolean isSuspendable(); } public static final class PublishConfig.Builder { + method @FlaggedApi("com.android.ranging.flags.ranging_rtt_enabled") @NonNull public android.net.wifi.aware.PublishConfig.Builder setPeriodicRangingResultsEnabled(boolean); method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION) public android.net.wifi.aware.PublishConfig.Builder setSuspendable(boolean); method @FlaggedApi("com.android.wifi.flags.android_v_wifi_api") @NonNull public android.net.wifi.aware.PublishConfig.Builder setVendorData(@NonNull java.util.List<android.net.wifi.OuiKeyedData>); } @@ -1500,11 +1582,35 @@ package android.net.wifi.aware { } public final class SubscribeConfig implements android.os.Parcelable { + method @FlaggedApi("com.android.ranging.flags.ranging_rtt_enabled") @IntRange(from=0) public int getCenterFreq0Mhz(); + method @FlaggedApi("com.android.ranging.flags.ranging_rtt_enabled") @IntRange(from=0) public int getCenterFreq1Mhz(); + method @FlaggedApi("com.android.ranging.flags.ranging_rtt_enabled") public int getChannelWidth(); + method @FlaggedApi("com.android.ranging.flags.ranging_rtt_enabled") @IntRange(from=0) public int getFrequencyMhz(); + method @FlaggedApi("com.android.ranging.flags.ranging_rtt_enabled") public int getPeriodicRangingInterval(); + method @FlaggedApi("com.android.ranging.flags.ranging_rtt_enabled") public int getPreamble(); + method @FlaggedApi("com.android.ranging.flags.ranging_rtt_enabled") public int getRttBurstSize(); method @FlaggedApi("com.android.wifi.flags.android_v_wifi_api") @NonNull public java.util.List<android.net.wifi.OuiKeyedData> getVendorData(); + method @FlaggedApi("com.android.ranging.flags.ranging_rtt_enabled") public boolean isPeriodicRangingEnabled(); method public boolean isSuspendable(); + field @FlaggedApi("com.android.ranging.flags.ranging_rtt_enabled") public static final int PERIODIC_RANGING_INTERVAL_1024TU = 1024; // 0x400 + field @FlaggedApi("com.android.ranging.flags.ranging_rtt_enabled") public static final int PERIODIC_RANGING_INTERVAL_128TU = 128; // 0x80 + field @FlaggedApi("com.android.ranging.flags.ranging_rtt_enabled") public static final int PERIODIC_RANGING_INTERVAL_2048TU = 2048; // 0x800 + field @FlaggedApi("com.android.ranging.flags.ranging_rtt_enabled") public static final int PERIODIC_RANGING_INTERVAL_256TU = 256; // 0x100 + field @FlaggedApi("com.android.ranging.flags.ranging_rtt_enabled") public static final int PERIODIC_RANGING_INTERVAL_4096TU = 4096; // 0x1000 + field @FlaggedApi("com.android.ranging.flags.ranging_rtt_enabled") public static final int PERIODIC_RANGING_INTERVAL_512TU = 512; // 0x200 + field @FlaggedApi("com.android.ranging.flags.ranging_rtt_enabled") public static final int PERIODIC_RANGING_INTERVAL_8192TU = 8192; // 0x2000 + field @FlaggedApi("com.android.ranging.flags.ranging_rtt_enabled") public static final int PERIODIC_RANGING_INTERVAL_NONE = 0; // 0x0 } public static final class SubscribeConfig.Builder { + method @FlaggedApi("com.android.ranging.flags.ranging_rtt_enabled") @NonNull public android.net.wifi.aware.SubscribeConfig.Builder setCenterFreq0Mhz(@IntRange(from=0) int); + method @FlaggedApi("com.android.ranging.flags.ranging_rtt_enabled") @NonNull public android.net.wifi.aware.SubscribeConfig.Builder setCenterFreq1Mhz(@IntRange(from=0) int); + method @FlaggedApi("com.android.ranging.flags.ranging_rtt_enabled") @NonNull public android.net.wifi.aware.SubscribeConfig.Builder setChannelWidth(int); + method @FlaggedApi("com.android.ranging.flags.ranging_rtt_enabled") @NonNull public android.net.wifi.aware.SubscribeConfig.Builder setFrequencyMhz(@IntRange(from=0) int); + method @FlaggedApi("com.android.ranging.flags.ranging_rtt_enabled") @NonNull public android.net.wifi.aware.SubscribeConfig.Builder setPeriodicRangingEnabled(boolean); + method @FlaggedApi("com.android.ranging.flags.ranging_rtt_enabled") @NonNull public android.net.wifi.aware.SubscribeConfig.Builder setPeriodicRangingInterval(int); + method @FlaggedApi("com.android.ranging.flags.ranging_rtt_enabled") @NonNull public android.net.wifi.aware.SubscribeConfig.Builder setPreamble(int); + method @FlaggedApi("com.android.ranging.flags.ranging_rtt_enabled") @NonNull public android.net.wifi.aware.SubscribeConfig.Builder setRttBurstSize(int); method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION) public android.net.wifi.aware.SubscribeConfig.Builder setSuspendable(boolean); method @FlaggedApi("com.android.wifi.flags.android_v_wifi_api") @NonNull public android.net.wifi.aware.SubscribeConfig.Builder setVendorData(@NonNull java.util.List<android.net.wifi.OuiKeyedData>); } @@ -1779,3 +1885,143 @@ package android.net.wifi.twt { } +package android.net.wifi.usd { + + @FlaggedApi("android.net.wifi.flags.usd") public final class Characteristics implements android.os.Parcelable { + method public int describeContents(); + method public int getMaxMatchFilterLength(); + method public int getMaxNumberOfPublishSessions(); + method public int getMaxNumberOfSubscribeSessions(); + method public int getMaxServiceNameLength(); + method public int getMaxServiceSpecificInfoLength(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.usd.Characteristics> CREATOR; + } + + @FlaggedApi("android.net.wifi.flags.usd") public abstract class Config { + method @Nullable public int[] getOperatingFrequenciesMhz(); + method @NonNull public java.util.List<byte[]> getRxMatchFilter(); + method @NonNull public byte[] getServiceName(); + method public int getServiceProtoType(); + method @Nullable public byte[] getServiceSpecificInfo(); + method @IntRange(from=0) public int getTtlSeconds(); + method @NonNull public java.util.List<byte[]> getTxMatchFilter(); + field public static final int SERVICE_PROTO_TYPE_CSA_MATTER = 1; // 0x1 + field public static final int SERVICE_PROTO_TYPE_GENERIC = 0; // 0x0 + field public static final int SUBSCRIBE_TYPE_ACTIVE = 1; // 0x1 + field public static final int SUBSCRIBE_TYPE_PASSIVE = 0; // 0x0 + field public static final int TRANSMISSION_TYPE_MULTICAST = 1; // 0x1 + field public static final int TRANSMISSION_TYPE_UNICAST = 0; // 0x0 + } + + @FlaggedApi("android.net.wifi.flags.usd") public class DiscoveryResult { + method public int getPeerId(); + method public int getServiceProtoType(); + method @Nullable public byte[] getServiceSpecificInfo(); + method public boolean isFsdEnabled(); + } + + @FlaggedApi("android.net.wifi.flags.usd") public static final class DiscoveryResult.Builder { + ctor public DiscoveryResult.Builder(int); + method @NonNull public android.net.wifi.usd.DiscoveryResult build(); + method @NonNull public android.net.wifi.usd.DiscoveryResult.Builder setFsdEnabled(boolean); + method @NonNull public android.net.wifi.usd.DiscoveryResult.Builder setServiceProtoType(int); + method @NonNull public android.net.wifi.usd.DiscoveryResult.Builder setServiceSpecificInfo(@NonNull byte[]); + } + + @FlaggedApi("android.net.wifi.flags.usd") public final class PublishConfig extends android.net.wifi.usd.Config implements android.os.Parcelable { + method public int describeContents(); + method @IntRange(from=0) public int getAnnouncementPeriodMillis(); + method public int getSolicitedTransmissionType(); + method public boolean isEventsEnabled(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.usd.PublishConfig> CREATOR; + } + + public static final class PublishConfig.Builder { + ctor public PublishConfig.Builder(@NonNull String); + method @NonNull public android.net.wifi.usd.PublishConfig build(); + method @NonNull public android.net.wifi.usd.PublishConfig.Builder setAnnouncementPeriodMillis(@IntRange(from=0) int); + method @NonNull public android.net.wifi.usd.PublishConfig.Builder setEventsEnabled(boolean); + method @NonNull public android.net.wifi.usd.PublishConfig.Builder setOperatingFrequenciesMhz(@NonNull int[]); + method @NonNull public android.net.wifi.usd.PublishConfig.Builder setRxMatchFilter(@NonNull java.util.List<byte[]>); + method @NonNull public android.net.wifi.usd.PublishConfig.Builder setServiceProtoType(int); + method @NonNull public android.net.wifi.usd.PublishConfig.Builder setServiceSpecificInfo(@NonNull byte[]); + method @NonNull public android.net.wifi.usd.PublishConfig.Builder setSolicitedTransmissionType(int); + method @NonNull public android.net.wifi.usd.PublishConfig.Builder setTtlSeconds(@IntRange(from=0) int); + method @NonNull public android.net.wifi.usd.PublishConfig.Builder setTxMatchFilter(@NonNull java.util.List<byte[]>); + } + + @FlaggedApi("android.net.wifi.flags.usd") public class PublishSession { + method @RequiresPermission(android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION) public void cancel(); + method @RequiresPermission(android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION) public void sendMessage(int, @NonNull byte[], @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); + method @RequiresPermission(android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION) public void updatePublish(@NonNull byte[]); + } + + @FlaggedApi("android.net.wifi.flags.usd") public class PublishSessionCallback extends android.net.wifi.usd.SessionCallback { + ctor public PublishSessionCallback(); + method public void onPublishFailed(int); + method public void onPublishReplied(@NonNull android.net.wifi.usd.DiscoveryResult); + method public void onPublishStarted(@NonNull android.net.wifi.usd.PublishSession); + } + + @FlaggedApi("android.net.wifi.flags.usd") public abstract class SessionCallback { + ctor public SessionCallback(); + method public void onMessageReceived(int, @Nullable byte[]); + method public void onSessionTerminated(int); + field public static final int FAILURE_MAX_SESSIONS_REACHED = 3; // 0x3 + field public static final int FAILURE_NOT_AVAILABLE = 2; // 0x2 + field public static final int FAILURE_TIMEOUT = 1; // 0x1 + field public static final int FAILURE_UNKNOWN = 0; // 0x0 + field public static final int TERMINATION_REASON_NOT_AVAILABLE = 1; // 0x1 + field public static final int TERMINATION_REASON_UNKNOWN = 0; // 0x0 + field public static final int TERMINATION_REASON_USER_INITIATED = 2; // 0x2 + } + + @FlaggedApi("android.net.wifi.flags.usd") public final class SubscribeConfig extends android.net.wifi.usd.Config implements android.os.Parcelable { + method public int describeContents(); + method @IntRange(from=0) public int getQueryPeriodMillis(); + method @Nullable public int[] getRecommendedOperatingFrequenciesMhz(); + method public int getSubscribeType(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.usd.SubscribeConfig> CREATOR; + } + + @FlaggedApi("android.net.wifi.flags.usd") public static final class SubscribeConfig.Builder { + ctor public SubscribeConfig.Builder(@NonNull String); + method @NonNull public android.net.wifi.usd.SubscribeConfig build(); + method @NonNull public android.net.wifi.usd.SubscribeConfig.Builder setOperatingFrequenciesMhz(@NonNull int[]); + method @NonNull public android.net.wifi.usd.SubscribeConfig.Builder setQueryPeriodMillis(@IntRange(from=0) int); + method @NonNull public android.net.wifi.usd.SubscribeConfig.Builder setRecommendedOperatingFrequenciesMhz(@NonNull int[]); + method @NonNull public android.net.wifi.usd.SubscribeConfig.Builder setRxMatchFilter(@NonNull java.util.List<byte[]>); + method @NonNull public android.net.wifi.usd.SubscribeConfig.Builder setServiceProtoType(int); + method @NonNull public android.net.wifi.usd.SubscribeConfig.Builder setServiceSpecificInfo(@NonNull byte[]); + method @NonNull public android.net.wifi.usd.SubscribeConfig.Builder setSubscribeType(int); + method @NonNull public android.net.wifi.usd.SubscribeConfig.Builder setTtlSeconds(@IntRange(from=0) int); + method @NonNull public android.net.wifi.usd.SubscribeConfig.Builder setTxMatchFilter(@NonNull java.util.List<byte[]>); + } + + @FlaggedApi("android.net.wifi.flags.usd") public class SubscribeSession { + method @RequiresPermission(android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION) public void cancel(); + method @RequiresPermission(android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION) public void sendMessage(int, @NonNull byte[], @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); + } + + @FlaggedApi("android.net.wifi.flags.usd") public class SubscribeSessionCallback extends android.net.wifi.usd.SessionCallback { + ctor public SubscribeSessionCallback(); + method public void onServiceDiscovered(@NonNull android.net.wifi.usd.DiscoveryResult); + method public void onSubscribeFailed(int); + method public void onSubscribeStarted(@NonNull android.net.wifi.usd.SubscribeSession); + } + + @FlaggedApi("android.net.wifi.flags.usd") public class UsdManager { + method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION) public android.net.wifi.usd.Characteristics getCharacteristics(); + method @RequiresPermission(android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION) public void publish(@NonNull android.net.wifi.usd.PublishConfig, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.usd.PublishSessionCallback); + method @RequiresPermission(android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION) public void registerPublisherStatusListener(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); + method @RequiresPermission(android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION) public void registerSubscriberStatusListener(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); + method @RequiresPermission(android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION) public void subscribe(@NonNull android.net.wifi.usd.SubscribeConfig, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.usd.SubscribeSessionCallback); + method @RequiresPermission(android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION) public void unregisterPublisherStatusListener(@NonNull java.util.function.Consumer<java.lang.Boolean>); + method @RequiresPermission(android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION) public void unregisterSubscriberStatusListener(@NonNull java.util.function.Consumer<java.lang.Boolean>); + } + +} + diff --git a/framework/jarjar-rules.txt b/framework/jarjar-rules.txt index 598c014b71..dbd5811d7d 100644 --- a/framework/jarjar-rules.txt +++ b/framework/jarjar-rules.txt @@ -86,6 +86,10 @@ rule android.util.BackupUtils* com.android.wifi.x.@0 rule android.util.LocalLog* com.android.wifi.x.@0 rule android.util.Rational* com.android.wifi.x.@0 +# Statically included ranging flags. +rule com.android.ranging.flags.Flags com.android.wifi.x.@0 +rule com.android.ranging.flags.*Flags* com.android.wifi.x.@0 + # Repackage generated flag classes. # Need to specify the rule on classes to avoid transform the literals rule com.android.wifi.flags.*FeatureFlags* com.android.wifi.x.@0 @@ -96,6 +100,10 @@ rule android.net.wifi.flags.*FeatureFlags* com.android.wifi.x.@0 rule android.net.wifi.flags.FeatureFlags* com.android.wifi.x.@0 rule android.net.wifi.flags.Flags com.android.wifi.x.@0 +rule android.security.*FeatureFlags* com.android.wifi.x.@0 +rule android.security.FeatureFlags* com.android.wifi.x.@0 +rule android.security.Flags com.android.wifi.x.@0 + # Use our statically linked bouncy castle library rule org.bouncycastle.** com.android.wifi.x.@0 # Use our statically linked protobuf library diff --git a/framework/java/android/net/wifi/BaseWifiService.java b/framework/java/android/net/wifi/BaseWifiService.java deleted file mode 100644 index 0d3ae0a753..0000000000 --- a/framework/java/android/net/wifi/BaseWifiService.java +++ /dev/null @@ -1,1223 +0,0 @@ -/* - * Copyright (c) 2018 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 android.net.wifi; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.net.DhcpInfo; -import android.net.DhcpOption; -import android.net.Network; -import android.net.TetheringManager; -import android.net.wifi.hotspot2.IProvisioningCallback; -import android.net.wifi.hotspot2.OsuProvider; -import android.net.wifi.hotspot2.PasspointConfiguration; -import android.net.wifi.twt.TwtRequest; -import android.os.Bundle; -import android.os.IBinder; -import android.os.RemoteException; -import android.os.WorkSource; - -import com.android.modules.utils.ParceledListSlice; -import com.android.modules.utils.StringParceledListSlice; - -import java.util.List; -import java.util.Map; - -/** - * Empty concrete class implementing IWifiManager with stub methods throwing runtime exceptions. - * - * This class is meant to be extended by real implementations of IWifiManager in order to facilitate - * cross-repo changes to WiFi internal APIs, including the introduction of new APIs, the removal of - * deprecated APIs, or the migration of existing API signatures. - * - * When an existing API is scheduled for removal, it can be removed from IWifiManager.aidl - * immediately and marked as @Deprecated first in this class. Children inheriting this class are - * then given a short grace period to update themselves before the @Deprecated stub is removed for - * good. If the API scheduled for removal has a replacement or an overload (signature change), - * these should be introduced before the stub is removed to allow children to migrate. - * - * When a new API is added to IWifiManager.aidl, a stub should be added in BaseWifiService as - * well otherwise compilation will fail. - * - * @hide - */ -public class BaseWifiService extends IWifiManager.Stub { - - private static final String TAG = BaseWifiService.class.getSimpleName(); - - @Override - public long getSupportedFeatures() { - throw new UnsupportedOperationException(); - } - - @Override - public void getWifiActivityEnergyInfoAsync(IOnWifiActivityEnergyInfoListener listener) { - throw new UnsupportedOperationException(); - } - - @Override - public ParceledListSlice getConfiguredNetworks(String packageName, String featureId, - boolean callerNetworksOnly) { - throw new UnsupportedOperationException(); - } - - @Override - public ParceledListSlice getPrivilegedConfiguredNetworks(String packageName, String featureId, - Bundle extras) { - throw new UnsupportedOperationException(); - } - - @Override - public WifiConfiguration getPrivilegedConnectedNetwork( - String packageName, String featureId, Bundle extras) { - throw new UnsupportedOperationException(); - } - - @Override - public void setNetworkSelectionConfig(WifiNetworkSelectionConfig nsConfig) { - throw new UnsupportedOperationException(); - } - - @Override - public void getNetworkSelectionConfig(@NonNull IWifiNetworkSelectionConfigListener listener) { - throw new UnsupportedOperationException(); - } - - @Override - public void setThirdPartyAppEnablingWifiConfirmationDialogEnabled(boolean enable) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isThirdPartyAppEnablingWifiConfirmationDialogEnabled() { - throw new UnsupportedOperationException(); - } - - @Override - public void setScreenOnScanSchedule(int[] scanScheduleSeconds, int[] scanType) { - throw new UnsupportedOperationException(); - } - - @Override - public void setOneShotScreenOnConnectivityScanDelayMillis(int delayMs) { - throw new UnsupportedOperationException(); - } - - @Override - public Map<String, Map<Integer, List<ScanResult>>> getAllMatchingFqdnsForScanResults( - List<ScanResult> scanResults) { - throw new UnsupportedOperationException(); - } - - @Override - public void setSsidsAllowlist(String packageName, ParceledListSlice<WifiSsid> ssids) { - throw new UnsupportedOperationException(); - } - - @Override - public ParceledListSlice<WifiSsid> getSsidsAllowlist(String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public Map<OsuProvider, List<ScanResult>> getMatchingOsuProviders( - ParceledListSlice<ScanResult> scanResults) { - throw new UnsupportedOperationException(); - } - - @Override - public Map<OsuProvider, PasspointConfiguration> getMatchingPasspointConfigsForOsuProviders( - ParceledListSlice<OsuProvider> osuProviders) { - throw new UnsupportedOperationException(); - } - - @Override - public int addOrUpdateNetwork(WifiConfiguration config, String packageName, Bundle extras) { - throw new UnsupportedOperationException(); - } - - @Override - public WifiManager.AddNetworkResult addOrUpdateNetworkPrivileged(WifiConfiguration config, - String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean addOrUpdatePasspointConfiguration( - PasspointConfiguration config, String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean removePasspointConfiguration(String fqdn, String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public ParceledListSlice<PasspointConfiguration> getPasspointConfigurations( - String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public ParceledListSlice<WifiConfiguration> getWifiConfigsForPasspointProfiles( - StringParceledListSlice fqdnList) { - throw new UnsupportedOperationException(); - } - - @Override - public void queryPasspointIcon(long bssid, String fileName) { - throw new UnsupportedOperationException(); - } - - @Override - public int matchProviderWithCurrentNetwork(String fqdn) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean removeNetwork(int netId, String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean removeNonCallerConfiguredNetworks(String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean enableNetwork(int netId, boolean disableOthers, String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean disableNetwork(int netId, String packageName) { - throw new UnsupportedOperationException(); - } - - /** TO BE REMOVED */ - public void allowAutojoinGlobal(boolean choice) { - throw new UnsupportedOperationException(); - } - - @Override - public void allowAutojoinGlobal(boolean choice, String packageName, Bundle extras) { - throw new UnsupportedOperationException(); - } - - @Override - public void queryAutojoinGlobal(@NonNull IBooleanListener listener) { - throw new UnsupportedOperationException(); - } - - @Override - public void allowAutojoin(int netId, boolean choice) { - throw new UnsupportedOperationException(); - } - - @Override - public void allowAutojoinPasspoint(String fqdn, boolean enableAutoJoin) { - throw new UnsupportedOperationException(); - } - - @Override - public void getBssidBlocklist(ParceledListSlice<WifiSsid> ssids, - IMacAddressListListener listener) { - throw new UnsupportedOperationException(); - } - - @Override - public void setMacRandomizationSettingPasspointEnabled(String fqdn, boolean enable) { - throw new UnsupportedOperationException(); - } - - @Override - public void setPasspointMeteredOverride(String fqdn, int meteredOverride) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean startScan(String packageName, String featureId) { - throw new UnsupportedOperationException(); - } - - @Override - public ParceledListSlice getScanResults(String callingPackage, String callingFeatureId) { - throw new UnsupportedOperationException(); - } - - @Override - public void getChannelData(@NonNull IListListener listener, String packageName, - Bundle extras) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean disconnect(String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean reconnect(String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean reassociate(String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public WifiInfo getConnectionInfo(String callingPackage, String callingFeatureId) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean setWifiEnabled(String packageName, boolean enable) { - throw new UnsupportedOperationException(); - } - - @Override - public void registerSubsystemRestartCallback(ISubsystemRestartCallback callback) { - throw new UnsupportedOperationException(); - } - - @Override - public void unregisterSubsystemRestartCallback(ISubsystemRestartCallback callback) { - throw new UnsupportedOperationException(); - } - - @Override - public void addWifiNetworkStateChangedListener(IWifiNetworkStateChangedListener listener) { - throw new UnsupportedOperationException(); - } - - @Override - public void removeWifiNetworkStateChangedListener(IWifiNetworkStateChangedListener listener) { - throw new UnsupportedOperationException(); - } - - @Override - public void restartWifiSubsystem() { - throw new UnsupportedOperationException(); - } - - @Override - public int getWifiEnabledState() { - throw new UnsupportedOperationException(); - } - - @Override - public void registerDriverCountryCodeChangedListener( - @NonNull IOnWifiDriverCountryCodeChangedListener listener, - @Nullable String packageName, @Nullable String featureId) { - throw new UnsupportedOperationException(); - } - - @Override - public void unregisterDriverCountryCodeChangedListener( - @NonNull IOnWifiDriverCountryCodeChangedListener listener) { - throw new UnsupportedOperationException(); - } - - @Override - public String getCountryCode(String packageName, String featureId) { - throw new UnsupportedOperationException(); - } - - @Override - public void setOverrideCountryCode(@NonNull String countryCode) { - throw new UnsupportedOperationException(); - } - - @Override - public void clearOverrideCountryCode() { - throw new UnsupportedOperationException(); - } - - @Override - public void setDefaultCountryCode(@NonNull String countryCode) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean is24GHzBandSupported() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean is5GHzBandSupported() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean is6GHzBandSupported() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean is60GHzBandSupported() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isWifiStandardSupported(int standard) { - throw new UnsupportedOperationException(); - } - - @Override - public DhcpInfo getDhcpInfo(String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public void setScanAlwaysAvailable(boolean isAvailable, String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isScanAlwaysAvailable() { - throw new UnsupportedOperationException(); - } - - /** - * Following method is deprecated with - * {@link BaseWifiService#acquireWifiLock(IBinder, int, String, WorkSource, String, Bundle)} - * @deprecated This is no longer supported. - */ - @Deprecated - public boolean acquireWifiLock(IBinder lock, int lockType, String tag, WorkSource ws) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean acquireWifiLock(IBinder lock, int lockType, String tag, WorkSource ws, - String packageName, Bundle extras) { - throw new UnsupportedOperationException(); - } - - /** - * Following method is deprecated with - * {@link BaseWifiService#updateWifiLockWorkSource(IBinder, WorkSource, String, Bundle)} - * @deprecated This is no longer supported. - */ - @Deprecated - public void updateWifiLockWorkSource(IBinder lock, WorkSource ws) { - throw new UnsupportedOperationException(); - } - - @Override - public void updateWifiLockWorkSource(IBinder lock, WorkSource ws, String packageName, - Bundle extras) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean releaseWifiLock(IBinder lock) { - throw new UnsupportedOperationException(); - } - - @Override - public void initializeMulticastFiltering() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isMulticastEnabled() { - throw new UnsupportedOperationException(); - } - - @Override - public void acquireMulticastLock(IBinder binder, String tag) { - throw new UnsupportedOperationException(); - } - - @Override - public void releaseMulticastLock(String tag) { - throw new UnsupportedOperationException(); - } - - @Override - public void updateInterfaceIpState(String ifaceName, int mode) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isDefaultCoexAlgorithmEnabled() { - throw new UnsupportedOperationException(); - } - - @Override - public void setCoexUnsafeChannels(List<CoexUnsafeChannel> unsafeChannels, int restrictions) { - throw new UnsupportedOperationException(); - } - - @Override - public void registerCoexCallback(ICoexCallback callback) { - throw new UnsupportedOperationException(); - } - - @Override - public void unregisterCoexCallback(ICoexCallback callback) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean startSoftAp(WifiConfiguration wifiConfig, String packageName) { - throw new UnsupportedOperationException(); - } - - /** - * Following method is deprecated with - * {@link #startTetheredHotspotRequest(TetheringManager.TetheringRequest, String)} - * @deprecated This is no longer supported. - */ - @Deprecated - @Override - public boolean startTetheredHotspot(SoftApConfiguration softApConfig, String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public void startTetheredHotspotRequest(TetheringManager.TetheringRequest request, - ISoftApCallback callback, String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean stopSoftAp() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean validateSoftApConfiguration(SoftApConfiguration config) { - throw new UnsupportedOperationException(); - } - - @Override - public int startLocalOnlyHotspot(ILocalOnlyHotspotCallback callback, String packageName, - String featureId, SoftApConfiguration customConfig, Bundle extras) { - throw new UnsupportedOperationException(); - } - - @Override - public void stopLocalOnlyHotspot() { - throw new UnsupportedOperationException(); - } - - @Override - public void registerLocalOnlyHotspotSoftApCallback(ISoftApCallback callback, Bundle extras) { - throw new UnsupportedOperationException(); - } - - @Override - public void unregisterLocalOnlyHotspotSoftApCallback(ISoftApCallback callback, Bundle extras) { - throw new UnsupportedOperationException(); - } - - @Override - public void startWatchLocalOnlyHotspot(ILocalOnlyHotspotCallback callback) { - throw new UnsupportedOperationException(); - } - - @Override - public void stopWatchLocalOnlyHotspot() { - throw new UnsupportedOperationException(); - } - - @Override - public int getWifiApEnabledState() { - throw new UnsupportedOperationException(); - } - - @Override - public WifiConfiguration getWifiApConfiguration() { - throw new UnsupportedOperationException(); - } - - @Override - public SoftApConfiguration getSoftApConfiguration() { - throw new UnsupportedOperationException(); - } - - @Override - public void queryLastConfiguredTetheredApPassphraseSinceBoot(IStringListener listener) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean setWifiApConfiguration(WifiConfiguration wifiConfig, String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean setSoftApConfiguration(SoftApConfiguration softApConfig, String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public void notifyUserOfApBandConversion(String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public void enableTdls(String remoteIPAddress, boolean enable) { - throw new UnsupportedOperationException(); - } - - @Override - public void enableTdlsWithRemoteIpAddress(String remoteIPAddress, boolean enable, - @NonNull IBooleanListener listener) { - throw new UnsupportedOperationException(); - } - - @Override - public void enableTdlsWithMacAddress(String remoteMacAddress, boolean enable) { - throw new UnsupportedOperationException(); - } - - @Override - public void enableTdlsWithRemoteMacAddress(String remoteMacAddress, boolean enable, - @NonNull IBooleanListener listener) { - throw new UnsupportedOperationException(); - } - - @Override - public void isTdlsOperationCurrentlyAvailable(@NonNull IBooleanListener listener) { - throw new UnsupportedOperationException(); - } - - @Override - public void getMaxSupportedConcurrentTdlsSessions(@NonNull IIntegerListener listener) { - throw new UnsupportedOperationException(); - } - - @Override - public void getNumberOfEnabledTdlsSessions(@NonNull IIntegerListener listener) { - throw new UnsupportedOperationException(); - } - - @Override - public String getCurrentNetworkWpsNfcConfigurationToken() { - throw new UnsupportedOperationException(); - } - - @Override - public void enableVerboseLogging(int verbose) { - throw new UnsupportedOperationException(); - } - - @Override - public int getVerboseLoggingLevel() { - throw new UnsupportedOperationException(); - } - - @Override - public void disableEphemeralNetwork(String SSID, String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public void factoryReset(String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public Network getCurrentNetwork() { - throw new UnsupportedOperationException(); - } - - @Override - public void retrieveWifiBackupData(@NonNull IByteArrayListener listener) { - throw new UnsupportedOperationException(); - } - - @Override - public void restoreWifiBackupData(byte[] data) { - throw new UnsupportedOperationException(); - } - - @Override - public byte[] retrieveBackupData() { - throw new UnsupportedOperationException(); - } - - @Override - public void restoreBackupData(byte[] data) { - throw new UnsupportedOperationException(); - } - - @Override - public byte[] retrieveSoftApBackupData() { - throw new UnsupportedOperationException(); - } - - @Override - public SoftApConfiguration restoreSoftApBackupData(byte[] data) { - throw new UnsupportedOperationException(); - } - - @Override - public void restoreSupplicantBackupData(byte[] supplicantData, byte[] ipConfigData) { - throw new UnsupportedOperationException(); - } - - @Override - public void startSubscriptionProvisioning( - OsuProvider provider, IProvisioningCallback callback) { - throw new UnsupportedOperationException(); - } - - @Override - public void addWifiVerboseLoggingStatusChangedListener( - IWifiVerboseLoggingStatusChangedListener callback) { - throw new UnsupportedOperationException(); - } - - @Override - public void removeWifiVerboseLoggingStatusChangedListener( - IWifiVerboseLoggingStatusChangedListener callback) { - throw new UnsupportedOperationException(); - } - - @Override - public void registerSoftApCallback(ISoftApCallback callback) { - throw new UnsupportedOperationException(); - } - - @Override - public void unregisterSoftApCallback(ISoftApCallback callback) { - throw new UnsupportedOperationException(); - } - - @Override - public void registerTrafficStateCallback(ITrafficStateCallback callback) { - throw new UnsupportedOperationException(); - } - - @Override - public void unregisterTrafficStateCallback(ITrafficStateCallback callback) { - throw new UnsupportedOperationException(); - } - - @Override - public void registerNetworkRequestMatchCallback(INetworkRequestMatchCallback callback) { - throw new UnsupportedOperationException(); - } - - @Override - public void unregisterNetworkRequestMatchCallback(INetworkRequestMatchCallback callback) { - throw new UnsupportedOperationException(); - } - - @Override - public int addNetworkSuggestions( - ParceledListSlice<WifiNetworkSuggestion> networkSuggestions, String callingPackageName, - String callingFeatureId) { - throw new UnsupportedOperationException(); - } - - @Override - public int removeNetworkSuggestions( - ParceledListSlice<WifiNetworkSuggestion> networkSuggestions, String callingPackageName, - int action) { - throw new UnsupportedOperationException(); - } - - @Override - public ParceledListSlice<WifiNetworkSuggestion> getNetworkSuggestions(String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public void setCarrierNetworkOffloadEnabled(int subId, boolean merged, boolean enabled) - throws RemoteException { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isCarrierNetworkOffloadEnabled(int subId, boolean merged) - throws RemoteException { - throw new UnsupportedOperationException(); - } - - @Override - public String[] getFactoryMacAddresses() { - throw new UnsupportedOperationException(); - } - - @Override - public void setDeviceMobilityState(int state) { - throw new UnsupportedOperationException(); - } - - @Override - public void startDppAsConfiguratorInitiator(IBinder binder, String packageName, - String enrolleeUri, int selectedNetworkId, int netRole, IDppCallback callback) { - throw new UnsupportedOperationException(); - } - - @Override - public void startDppAsEnrolleeInitiator(IBinder binder, String configuratorUri, - IDppCallback callback) { - throw new UnsupportedOperationException(); - } - - @Override - public void startDppAsEnrolleeResponder(IBinder binder, String deviceInfo, - int curve, IDppCallback callback) { - throw new UnsupportedOperationException(); - } - - @Override - public void stopDppSession() throws RemoteException { - throw new UnsupportedOperationException(); - } - - @Override - public void addOnWifiUsabilityStatsListener(IOnWifiUsabilityStatsListener listener) { - throw new UnsupportedOperationException(); - } - - @Override - public void removeOnWifiUsabilityStatsListener(IOnWifiUsabilityStatsListener listener) { - throw new UnsupportedOperationException(); - } - - @Override - public void updateWifiUsabilityScore(int seqNum, int score, int predictionHorizonSec) { - throw new UnsupportedOperationException(); - } - - /** TO BE REMOVED */ - public void connect(WifiConfiguration config, int netId, IActionListener callback) { - throw new UnsupportedOperationException(); - } - - /** TO BE REMOVED */ - public void connect(WifiConfiguration config, int netId, IActionListener callback, - @NonNull String packageName) { - throw new UnsupportedOperationException(); - } - @Override - public void connect(WifiConfiguration config, int netId, IActionListener callback, - @NonNull String packageName, Bundle extras) { - throw new UnsupportedOperationException(); - } - - @Override - public void startRestrictingAutoJoinToSubscriptionId(int subId) { - throw new UnsupportedOperationException(); - } - - @Override - public void stopRestrictingAutoJoinToSubscriptionId() { - throw new UnsupportedOperationException(); - } - - /** TO BE REMOVED */ - public void save(WifiConfiguration config, IActionListener callback) { - throw new UnsupportedOperationException(); - } - - @Override - public void save(WifiConfiguration config, IActionListener callback, - @NonNull String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public void forget(int netId, IActionListener callback) { - throw new UnsupportedOperationException(); - } - - @Override - public void registerScanResultsCallback(IScanResultsCallback callback) { - throw new UnsupportedOperationException(); - } - - @Override - public void unregisterScanResultsCallback(IScanResultsCallback callback) { - throw new UnsupportedOperationException(); - } - - @Override - public void registerSuggestionConnectionStatusListener( - ISuggestionConnectionStatusListener listener, String packageName, String featureId) { - throw new UnsupportedOperationException(); - } - - @Override - public void unregisterSuggestionConnectionStatusListener( - ISuggestionConnectionStatusListener listener, String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public int calculateSignalLevel(int rssi) { - throw new UnsupportedOperationException(); - } - - @Override - public void setPnoScanEnabled(boolean enabled, boolean enablePnoScanAfterWifiToggle, - String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public ParceledListSlice<WifiConfiguration> - getWifiConfigForMatchedNetworkSuggestionsSharedWithUser( - ParceledListSlice<ScanResult> scanResults) { - throw new UnsupportedOperationException(); - } - - @Override - public void setExternalPnoScanRequest(@NonNull IBinder binder, - @NonNull IPnoScanResultsCallback callback, - @NonNull List<WifiSsid> ssids, @NonNull int[] frequencies, - @NonNull String packageName, @NonNull String featureId) { - throw new UnsupportedOperationException(); - } - - @Override - public void clearExternalPnoScanRequest() { - throw new UnsupportedOperationException(); - } - - @Override - public void getLastCallerInfoForApi(int apiType, @NonNull ILastCallerListener listener) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean setWifiConnectedNetworkScorer(IBinder binder, - IWifiConnectedNetworkScorer scorer) { - throw new UnsupportedOperationException(); - } - - @Override - public void clearWifiConnectedNetworkScorer() { - throw new UnsupportedOperationException(); - } - - @Override - public Map<WifiNetworkSuggestion, List<ScanResult>> getMatchingScanResults( - ParceledListSlice<WifiNetworkSuggestion> networkSuggestions, - ParceledListSlice<ScanResult> scanResults, - String callingPackage, String callingFeatureId) { - throw new UnsupportedOperationException(); - } - - @Override - public void setScanThrottleEnabled(boolean enable) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isScanThrottleEnabled() { - throw new UnsupportedOperationException(); - } - - @Override - public Map<String, Map<Integer, List<ScanResult>>> - getAllMatchingPasspointProfilesForScanResults( - ParceledListSlice<ScanResult> scanResults) { - throw new UnsupportedOperationException(); - } - - @Override - public void setAutoWakeupEnabled(boolean enable) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isAutoWakeupEnabled() { - throw new UnsupportedOperationException(); - } - - @Override - public void addSuggestionUserApprovalStatusListener( - ISuggestionUserApprovalStatusListener listener, String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public void removeSuggestionUserApprovalStatusListener( - ISuggestionUserApprovalStatusListener listener, String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public void setEmergencyScanRequestInProgress(boolean inProgress) { - throw new UnsupportedOperationException(); - } - - @Override - public void removeAppState(int targetAppUid, @NonNull String targetAppPackageName) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean setWifiScoringEnabled(boolean enabled) { - throw new UnsupportedOperationException(); - } - - @Override - public void flushPasspointAnqpCache(@NonNull String packageName) { - throw new UnsupportedOperationException(); - } - - /** TO BE REMOVED */ - public List<WifiAvailableChannel> getUsableChannels( - int band, int mode, int filter) { - throw new UnsupportedOperationException(); - } - - @Override - public List<WifiAvailableChannel> getUsableChannels( - int band, int mode, int filter, String packageName, Bundle extras) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isWifiPasspointEnabled() { - throw new UnsupportedOperationException(); - } - - @Override - public void setWifiPasspointEnabled(boolean enabled) { - throw new UnsupportedOperationException(); - } - - @Override - public @WifiManager.WifiMultiInternetMode int getStaConcurrencyForMultiInternetMode() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean setStaConcurrencyForMultiInternetMode( - @WifiManager.WifiMultiInternetMode int mode) { - throw new UnsupportedOperationException(); - } - - @Override - public void notifyMinimumRequiredWifiSecurityLevelChanged(int level) { - throw new UnsupportedOperationException(); - } - - @Override - public void notifyWifiSsidPolicyChanged(int policyType, - @NonNull ParceledListSlice<WifiSsid> ssids) { - throw new UnsupportedOperationException(); - } - - @Override - public String[] getOemPrivilegedWifiAdminPackages() { - throw new UnsupportedOperationException(); - } - - @Override - public void replyToP2pInvitationReceivedDialog( - int dialogId, boolean accepted, @Nullable String optionalPin) { - throw new UnsupportedOperationException(); - } - - @Override - public void replyToSimpleDialog(int dialogId, int button) { - throw new UnsupportedOperationException(); - } - - @Override - public void addCustomDhcpOptions(WifiSsid ssid, byte[] oui, - @NonNull ParceledListSlice<DhcpOption> options) { - throw new UnsupportedOperationException(); - } - - @Override - public void removeCustomDhcpOptions(WifiSsid ssid, byte[] oui) { - throw new UnsupportedOperationException(); - } - - @Override - public void reportCreateInterfaceImpact(String packageName, int interfaceType, - boolean requireNewInterface, IInterfaceCreationInfoCallback callback) { - throw new UnsupportedOperationException(); - } - - @Override - public int getMaxNumberOfChannelsPerRequest() { - throw new UnsupportedOperationException(); - } - - @Override - public void addQosPolicies(@NonNull ParceledListSlice<QosPolicyParams> policyParamsList, - @NonNull IBinder binder, @NonNull String packageName, @NonNull IListListener listener) { - throw new UnsupportedOperationException(); - } - - @Override - public void removeQosPolicies(@NonNull int[] policyIdList, @NonNull String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public void removeAllQosPolicies(@NonNull String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public void addLocalOnlyConnectionStatusListener(ILocalOnlyConnectionStatusListener listener, - String packageName, String featureId) { - throw new UnsupportedOperationException(); - } - - @Override - public void removeLocalOnlyConnectionStatusListener(ILocalOnlyConnectionStatusListener listener, - String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public void setLinkLayerStatsPollingInterval(int intervalMs) { - throw new UnsupportedOperationException(); - } - - @Override - public void getLinkLayerStatsPollingInterval(@NonNull IIntegerListener listener) { - throw new UnsupportedOperationException(); - } - - @Override - public void setMloMode(@WifiManager.MloMode int mode, IBooleanListener listener) { - throw new UnsupportedOperationException(); - } - - @Override - public void getMloMode(IIntegerListener listener) { - throw new UnsupportedOperationException(); - } - - @Override - public void addWifiLowLatencyLockListener(IWifiLowLatencyLockListener listener) { - throw new UnsupportedOperationException(); - } - - @Override - public void removeWifiLowLatencyLockListener(IWifiLowLatencyLockListener listener) { - throw new UnsupportedOperationException(); - } - - @Override - public void getMaxMloAssociationLinkCount(IIntegerListener listener, Bundle extras) { - throw new UnsupportedOperationException(); - } - - @Override - public void getMaxMloStrLinkCount(IIntegerListener listener, Bundle extras) { - throw new UnsupportedOperationException(); - } - - @Override - public void getSupportedSimultaneousBandCombinations(IWifiBandsListener listener, - Bundle extras) { - throw new UnsupportedOperationException(); - } - - @Override - public void setWepAllowed(boolean isAllowed) { - throw new UnsupportedOperationException(); - } - - @Override - public void queryWepAllowed(@NonNull IBooleanListener listener) { - throw new UnsupportedOperationException(); - } - - @Override - public void enableMscs(@NonNull MscsParams mscsParams) { - throw new UnsupportedOperationException(); - } - - @Override - public void disableMscs() { - throw new UnsupportedOperationException(); - } - - @Override - public void setSendDhcpHostnameRestriction(@NonNull String packageName, - @WifiManager.SendDhcpHostnameRestriction int restriction) { - throw new UnsupportedOperationException(); - } - - @Override - public void querySendDhcpHostnameRestriction(@NonNull String packageName, - @NonNull IIntegerListener listener) { - throw new UnsupportedOperationException(); - } - - @Override - public void setPerSsidRoamingMode(WifiSsid ssid, @WifiManager.RoamingMode int roamingMode, - @NonNull String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public void removePerSsidRoamingMode(WifiSsid ssid, @NonNull String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public void getPerSsidRoamingModes(@NonNull String packageName, IMapListener listener) { - throw new UnsupportedOperationException(); - } - - @Override - public void setupTwtSession(TwtRequest twtRequest, ITwtCallback iTwtCallback, Bundle extras) { - throw new UnsupportedOperationException(); - } - - @Override - public void getTwtCapabilities(ITwtCapabilitiesListener listener, Bundle extras) { - throw new UnsupportedOperationException(); - } - - @Override - public void getStatsTwtSession(int sessionId, ITwtStatsListener iTwtStatsListener, - Bundle extras) { - throw new UnsupportedOperationException(); - } - - @Override - public void teardownTwtSession(int sessionId, Bundle extras) { - throw new UnsupportedOperationException(); - } - - @Override - public void setD2dAllowedWhenInfraStaDisabled(boolean isAllowed) { - throw new UnsupportedOperationException(); - } - - @Override - public void queryD2dAllowedWhenInfraStaDisabled(@NonNull IBooleanListener listener) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isPnoSupported() { - throw new UnsupportedOperationException(); - } -} diff --git a/framework/java/android/net/wifi/BlockingOption.java b/framework/java/android/net/wifi/BlockingOption.java new file mode 100644 index 0000000000..ee152d4398 --- /dev/null +++ b/framework/java/android/net/wifi/BlockingOption.java @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2024 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 android.net.wifi; + +import android.annotation.FlaggedApi; +import android.annotation.IntRange; +import android.os.Parcel; +import android.os.Parcelable; + +import androidx.annotation.NonNull; + +import com.android.wifi.flags.Flags; + +import java.util.Objects; + +/** + * Options for blocking a network through + * {@link WifiManager#disallowCurrentSuggestedNetwork(BlockingOption)} + */ +@FlaggedApi(Flags.FLAG_BSSID_BLOCKLIST_FOR_SUGGESTION) +public final class BlockingOption implements Parcelable { + private final int mDisableTime; + private final boolean mBSSIDOnly; + + /** + * @hide + */ + public BlockingOption(int disableTime, boolean bssidOnly) { + mDisableTime = disableTime; + mBSSIDOnly = bssidOnly; + } + + /** + * @hide + */ + public BlockingOption(Parcel in) { + mDisableTime = in.readInt(); + mBSSIDOnly = in.readBoolean(); + } + + /** + * Get the blocking time which is set by {@link Builder#Builder(int)} + * @return Blocking time in seconds + */ + public int getBlockingTimeSeconds() { + return mDisableTime; + } + + /** + * Return whether or not a single BSSID is being blocked, which is set by + * {@link Builder#setBlockingBssidOnly(boolean)} + * @return True for blocking single BSSID, false otherwise. + */ + public boolean isBlockingBssidOnly() { + return mBSSIDOnly; + } + + /** + * Builder used to create {@link BlockingOption} objects. + */ + @FlaggedApi(Flags.FLAG_BSSID_BLOCKLIST_FOR_SUGGESTION) + public static final class Builder { + private int mDisableTime; + private boolean mBSSIDOnly; + + /** + * Create a {@link Builder} with blocking time for the network + * + * @param blockingTimeSec Time period to block the network in seconds + * @throws IllegalArgumentException if input is invalid. + */ + public Builder(@IntRange(from = 1, to = 86400) int blockingTimeSec) { + if (blockingTimeSec < 1 || blockingTimeSec > 86400) { + throw new IllegalArgumentException("blockingTimeSec should between 1 to 86400"); + } + mDisableTime = blockingTimeSec; + mBSSIDOnly = false; + } + + /** + * Set to configure blocking the whole network or a single BSSID. By default, the whole + * network will be blocked. + * @param bssidOnly True for a single BSSID, otherwise the whole network will be blocked + * @return Instance of {@link Builder} to enable chaining of the builder method. + */ + @NonNull public Builder setBlockingBssidOnly(boolean bssidOnly) { + mBSSIDOnly = bssidOnly; + return this; + } + + /** + * Create a BlockingOption object for use in + * {@link WifiManager#disallowCurrentSuggestedNetwork(BlockingOption)}. + */ + @NonNull public BlockingOption build() { + return new BlockingOption(mDisableTime, mBSSIDOnly); + } + } + + @NonNull + public static final Creator<BlockingOption> CREATOR = new Creator<BlockingOption>() { + @Override + public BlockingOption createFromParcel(Parcel in) { + return new BlockingOption(in); + } + + @Override + public BlockingOption[] newArray(int size) { + return new BlockingOption[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mDisableTime); + dest.writeBoolean(mBSSIDOnly); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof BlockingOption lhs)) { + return false; + } + return mBSSIDOnly == lhs.mBSSIDOnly && mDisableTime == lhs.mDisableTime; + } + + @Override + public int hashCode() { + return Objects.hash(mBSSIDOnly, mDisableTime); + } + + @Override + public String toString() { + return "BlockingOption[ " + + "DisableTime=" + mDisableTime + + ", BSSIDOnly=" + mBSSIDOnly; + } +} diff --git a/framework/java/android/net/wifi/DeauthenticationReasonCode.java b/framework/java/android/net/wifi/DeauthenticationReasonCode.java new file mode 100644 index 0000000000..229ab45709 --- /dev/null +++ b/framework/java/android/net/wifi/DeauthenticationReasonCode.java @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2024 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 android.net.wifi; + +import android.annotation.FlaggedApi; +import android.annotation.SystemApi; + +import com.android.wifi.flags.Flags; + +/** + * Defines integer constants for Soft AP deauthentication reason codes. + * + * <p>These reason codes provide information about why a client was disconnected from a Soft AP. + * Refer to Section 9.4.1.7 and Table 9-45 of the IEEE 802.11-2016 standard for more information. + * @hide + */ +@SystemApi +@FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) +public final class DeauthenticationReasonCode { + /** Disconnected for an unknown reason. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_UNKNOWN = 0; + + /** Disconnected for an unspecified reason. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_UNSPECIFIED = 1; + + /** Disconnected because the previous authentication is no longer valid. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_PREV_AUTH_NOT_VALID = 2; + + /** Disconnected because the client is being de-authenticated. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_DEAUTH_LEAVING = 3; + + /** Disconnected due to inactivity. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_DISASSOC_DUE_TO_INACTIVITY = 4; + + /** Disconnected because the AP is unable to handle all currently associated stations. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_DISASSOC_AP_BUSY = 5; + + /** Disconnected because of a Class 2 frame received from a non-authenticated station. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_CLASS2_FRAME_FROM_NONAUTH_STA = 6; + + /** Disconnected because of a Class 3 frame received from a non-associated station. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_CLASS3_FRAME_FROM_NONASSOC_STA = 7; + + /** Disconnected because the STA has left the network. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_DISASSOC_STA_HAS_LEFT = 8; + + /** Disconnected because the STA requested association without authentication. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_STA_REQ_ASSOC_WITHOUT_AUTH = 9; + + /** Disconnected because the power capability element is not valid. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_PWR_CAPABILITY_NOT_VALID = 10; + + /** Disconnected because the supported channel element is not valid. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_SUPPORTED_CHANNEL_NOT_VALID = 11; + + /** Disconnected due to a BSS transition disassociation. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_BSS_TRANSITION_DISASSOC = 12; + + /** Disconnected because of an invalid information element. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_INVALID_IE = 13; + + /** Disconnected because of a message integrity code failure. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_MICHAEL_MIC_FAILURE = 14; + + /** Disconnected due to a four-way handshake timeout. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_FOURWAY_HANDSHAKE_TIMEOUT = 15; + + /** Disconnected due to a group key handshake timeout. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_GROUP_KEY_UPDATE_TIMEOUT = 16; + + /** Disconnected because an information element in the 4-way handshake differs. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_IE_IN_4WAY_DIFFERS = 17; + + /** Disconnected because the group cipher is not valid. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_GROUP_CIPHER_NOT_VALID = 18; + + /** Disconnected because the pairwise cipher is not valid. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_PAIRWISE_CIPHER_NOT_VALID = 19; + + /** Disconnected because the authentication and key management protocol is not valid. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_AKMP_NOT_VALID = 20; + + /** Disconnected because the robust security network IE version is not supported. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_UNSUPPORTED_RSN_IE_VERSION = 21; + + /** Disconnected because the robust security network IE capabilities are invalid. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_INVALID_RSN_IE_CAPAB = 22; + + /** Disconnected because the IEEE 802.1X authentication failed. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_IEEE_802_1X_AUTH_FAILED = 23; + + /** Disconnected because the cipher suite was rejected. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_CIPHER_SUITE_REJECTED = 24; + + /** Disconnected because the Tunneled Direct Link Setup teardown is unreachable. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_TDLS_TEARDOWN_UNREACHABLE = 25; + + /** Disconnected because of a Tunneled Direct Link Setup teardown for an unspecified reason. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_TDLS_TEARDOWN_UNSPECIFIED = 26; + + /** Disconnected because an session security protocol requested disassociation. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_SSP_REQUESTED_DISASSOC = 27; + + /** Disconnected because there is no session security protocol roaming agreement. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_NO_SSP_ROAMING_AGREEMENT = 28; + + /** Disconnected because of an unsupported cipher or authentication key management method. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_BAD_CIPHER_OR_AKM = 29; + + /** Disconnected because the client is not authorized at this location. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_NOT_AUTHORIZED_THIS_LOCATION = 30; + + /** Disconnected because a service change precludes traffic specification. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_SERVICE_CHANGE_PRECLUDES_TS = 31; + + /** Disconnected for an unspecified quality of service related reason. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_UNSPECIFIED_QOS_REASON = 32; + + /** Disconnected because there is not enough bandwidth. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_NOT_ENOUGH_BANDWIDTH = 33; + + /** Disconnected due to low acknowledgment rate. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_DISASSOC_LOW_ACK = 34; + + /** Disconnected for exceeding the transmission opportunity. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_EXCEEDED_TXOP = 35; + + /** Disconnected because the station is leaving the network. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_STA_LEAVING = 36; + + /** Disconnected because of the end of a traffic specification, block ack, or DLS session. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_END_TS_BA_DLS = 37; + + /** Disconnected because of an unknown traffic specification or block ack. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_UNKNOWN_TS_BA = 38; + + /** Disconnected due to a timeout. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_TIMEOUT = 39; + + /** Disconnected because of a peerkey mismatch. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_PEERKEY_MISMATCH = 45; + + /** Disconnected because the authorized access limit has been reached. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_AUTHORIZED_ACCESS_LIMIT_REACHED = 46; + + /** Disconnected due to external service requirements. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_EXTERNAL_SERVICE_REQUIREMENTS = 47; + + /** Disconnected because of an invalid fast transition action frame count. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_INVALID_FT_ACTION_FRAME_COUNT = 48; + + /** Disconnected because of an invalid pairwise master key identifier. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_INVALID_PMKID = 49; + + /** Disconnected because of an invalid management downlink endpoint. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_INVALID_MDE = 50; + + /** Disconnected because of an invalid fast transition endpoint. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_INVALID_FTE = 51; + + /** Disconnected because mesh peering was cancelled. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_MESH_PEERING_CANCELLED = 52; + + /** Disconnected because the maximum number of mesh peers has been reached. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_MESH_MAX_PEERS = 53; + + /** Disconnected because of a mesh configuration policy violation. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_MESH_CONFIG_POLICY_VIOLATION = 54; + + /** Disconnected because a mesh close message was received. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_MESH_CLOSE_RCVD = 55; + + /** Disconnected because the maximum number of mesh retries has been reached. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_MESH_MAX_RETRIES = 56; + + /** Disconnected due to a mesh confirmation timeout. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_MESH_CONFIRM_TIMEOUT = 57; + + /** Disconnected because of an invalid mesh group temporal key. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_MESH_INVALID_GTK = 58; + + /** Disconnected because of inconsistent mesh parameters. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_MESH_INCONSISTENT_PARAMS = 59; + + /** Disconnected because of an invalid mesh security capability. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_MESH_INVALID_SECURITY_CAP = 60; + + /** Disconnected because of a mesh path error: no proxy information. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_MESH_PATH_ERROR_NO_PROXY_INFO = 61; + + /** Disconnected because of a mesh path error: no forwarding information. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_MESH_PATH_ERROR_NO_FORWARDING_INFO = 62; + + /** Disconnected because of a mesh path error: destination unreachable. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_MESH_PATH_ERROR_DEST_UNREACHABLE = 63; + + /** Disconnected because the MAC address already exists in the mesh basic service set. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_MAC_ADDRESS_ALREADY_EXISTS_IN_MBSS = 64; + + /** Disconnected due to a mesh channel switch due to regulatory requirements. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_MESH_CHANNEL_SWITCH_REGULATORY_REQ = 65; + + /** Disconnected due to a mesh channel switch for an unspecified reason. */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + public static final int REASON_MESH_CHANNEL_SWITCH_UNSPECIFIED = 66; + + private DeauthenticationReasonCode() {} // Private constructor to prevent instantiation +} diff --git a/framework/java/android/net/wifi/ISoftApCallback.aidl b/framework/java/android/net/wifi/ISoftApCallback.aidl index 4190e4f3ad..17104166d7 100644 --- a/framework/java/android/net/wifi/ISoftApCallback.aidl +++ b/framework/java/android/net/wifi/ISoftApCallback.aidl @@ -18,16 +18,15 @@ package android.net.wifi; import android.net.wifi.SoftApCapability; import android.net.wifi.SoftApInfo; -import android.net.wifi.WifiClient; import android.net.wifi.SoftApState; +import android.net.wifi.WifiClient; /** * Interface for Soft AP callback. * * @hide */ -oneway interface ISoftApCallback -{ +oneway interface ISoftApCallback { /** * Service to manager callback providing current soft AP state. The possible * parameter values listed are defined in WifiManager.java @@ -45,8 +44,7 @@ oneway interface ISoftApCallback * @param isRegistration whether or not the callbackk was triggered when register. */ void onConnectedClientsOrInfoChanged(in Map<String, SoftApInfo> infos, - in Map<String, List<WifiClient>> clients, boolean isBridged, - boolean isRegistration); + in Map<String, List<WifiClient>> clients, boolean isBridged, boolean isRegistration); /** * Service to manager callback providing capability of softap. @@ -62,4 +60,12 @@ oneway interface ISoftApCallback * @param blockedReason one of blocked reason from {@link WifiManager.SapClientBlockedReason} */ void onBlockedClientConnecting(in WifiClient client, int blockedReason); + + /** + * Service to manager callback providing clients that disconnected from the softap. + * + * @param info information about the AP instance + * @param clients the disconnected clients of the AP instance + */ + void onClientsDisconnected(in SoftApInfo info, in List<WifiClient> clients); } diff --git a/framework/java/android/net/wifi/IWifiManager.aidl b/framework/java/android/net/wifi/IWifiManager.aidl index dc8a44407f..2382f94382 100644 --- a/framework/java/android/net/wifi/IWifiManager.aidl +++ b/framework/java/android/net/wifi/IWifiManager.aidl @@ -16,14 +16,11 @@ package android.net.wifi; -import android.net.wifi.hotspot2.OsuProvider; -import android.net.wifi.hotspot2.PasspointConfiguration; -import android.net.wifi.hotspot2.IProvisioningCallback; - import android.net.DhcpInfo; import android.net.DhcpOption; import android.net.Network; import android.net.TetheringManager.TetheringRequest; +import android.net.wifi.BlockingOption; import android.net.wifi.CoexUnsafeChannel; import android.net.wifi.IActionListener; import android.net.wifi.IBooleanListener; @@ -34,14 +31,13 @@ import android.net.wifi.IIntegerListener; import android.net.wifi.IInterfaceCreationInfoCallback; import android.net.wifi.ILastCallerListener; import android.net.wifi.IListListener; -import android.net.wifi.ILocalOnlyHotspotCallback; import android.net.wifi.ILocalOnlyConnectionStatusListener; +import android.net.wifi.ILocalOnlyHotspotCallback; +import android.net.wifi.IMacAddressListListener; import android.net.wifi.IMapListener; import android.net.wifi.INetworkRequestMatchCallback; import android.net.wifi.IOnWifiActivityEnergyInfoListener; import android.net.wifi.IOnWifiDriverCountryCodeChangedListener; -import android.net.wifi.IWifiNetworkStateChangedListener; -import android.net.wifi.IMacAddressListListener; import android.net.wifi.IOnWifiUsabilityStatsListener; import android.net.wifi.IPnoScanResultsCallback; import android.net.wifi.IScanResultsCallback; @@ -58,6 +54,8 @@ import android.net.wifi.IWifiBandsListener; import android.net.wifi.IWifiConnectedNetworkScorer; import android.net.wifi.IWifiLowLatencyLockListener; import android.net.wifi.IWifiNetworkSelectionConfigListener; +import android.net.wifi.IWifiNetworkStateChangedListener; +import android.net.wifi.IWifiStateChangedListener; import android.net.wifi.IWifiVerboseLoggingStatusChangedListener; import android.net.wifi.MscsParams; import android.net.wifi.QosPolicyParams; @@ -70,14 +68,14 @@ import android.net.wifi.WifiManager; import android.net.wifi.WifiNetworkSelectionConfig; import android.net.wifi.WifiNetworkSuggestion; import android.net.wifi.WifiSsid; - +import android.net.wifi.hotspot2.IProvisioningCallback; +import android.net.wifi.hotspot2.OsuProvider; +import android.net.wifi.hotspot2.PasspointConfiguration; import android.net.wifi.twt.TwtRequest; - import android.os.Bundle; import android.os.Messenger; import android.os.ResultReceiver; import android.os.WorkSource; - import com.android.modules.utils.ParceledListSlice; import com.android.modules.utils.StringParceledListSlice; @@ -86,9 +84,8 @@ import com.android.modules.utils.StringParceledListSlice; * * {@hide} */ -interface IWifiManager -{ - long getSupportedFeatures(); +interface IWifiManager { + boolean isFeatureSupported(int feature); oneway void getWifiActivityEnergyInfoAsync(in IOnWifiActivityEnergyInfoListener listener); @@ -104,13 +101,14 @@ interface IWifiManager void setOneShotScreenOnConnectivityScanDelayMillis(int delayMs); - ParceledListSlice getConfiguredNetworks(String packageName, String featureId, boolean callerNetworksOnly); - - ParceledListSlice getPrivilegedConfiguredNetworks(String packageName, String featureId, in Bundle extras); + ParceledListSlice getConfiguredNetworks( + String packageName, String featureId, boolean callerNetworksOnly); - WifiConfiguration getPrivilegedConnectedNetwork(String packageName, String featureId, in Bundle extras); + ParceledListSlice getPrivilegedConfiguredNetworks( + String packageName, String featureId, in Bundle extras); - Map getAllMatchingFqdnsForScanResults(in List<ScanResult> scanResult); + WifiConfiguration getPrivilegedConnectedNetwork( + String packageName, String featureId, in Bundle extras); void setSsidsAllowlist(String packageName, in ParceledListSlice<WifiSsid> ssids); @@ -122,7 +120,8 @@ interface IWifiManager int addOrUpdateNetwork(in WifiConfiguration config, String packageName, in Bundle extras); - WifiManager.AddNetworkResult addOrUpdateNetworkPrivileged(in WifiConfiguration config, String packageName); + WifiManager.AddNetworkResult addOrUpdateNetworkPrivileged( + in WifiConfiguration config, String packageName); boolean addOrUpdatePasspointConfiguration(in PasspointConfiguration config, String packageName); @@ -130,7 +129,8 @@ interface IWifiManager ParceledListSlice<PasspointConfiguration> getPasspointConfigurations(in String packageName); - ParceledListSlice<WifiConfiguration> getWifiConfigsForPasspointProfiles(in StringParceledListSlice fqdnList); + ParceledListSlice<WifiConfiguration> getWifiConfigsForPasspointProfiles( + in StringParceledListSlice fqdnList); void queryPasspointIcon(long bssid, String fileName); @@ -162,7 +162,8 @@ interface IWifiManager void getChannelData(in IListListener listener, String packageName, in Bundle extras); - void getBssidBlocklist(in ParceledListSlice<WifiSsid> ssids, in IMacAddressListListener listener); + void getBssidBlocklist( + in ParceledListSlice<WifiSsid> ssids, in IMacAddressListListener listener); boolean disconnect(String packageName); @@ -176,6 +177,10 @@ interface IWifiManager int getWifiEnabledState(); + void addWifiStateChangedListener(in IWifiStateChangedListener listener); + + void removeWifiStateChangedListener(in IWifiStateChangedListener listener); + void registerDriverCountryCodeChangedListener( in IOnWifiDriverCountryCodeChangedListener listener, String packageName, String featureId); @@ -211,9 +216,11 @@ interface IWifiManager boolean isScanAlwaysAvailable(); - boolean acquireWifiLock(IBinder lock, int lockType, String tag, in WorkSource ws, in String packageName, in Bundle extras); + boolean acquireWifiLock(IBinder lock, int lockType, String tag, in WorkSource ws, + in String packageName, in Bundle extras); - void updateWifiLockWorkSource(IBinder lock, in WorkSource ws, in String packageName, in Bundle extras); + void updateWifiLockWorkSource( + IBinder lock, in WorkSource ws, in String packageName, in Bundle extras); boolean releaseWifiLock(IBinder lock); @@ -223,13 +230,14 @@ interface IWifiManager void acquireMulticastLock(IBinder binder, String tag); - void releaseMulticastLock(String tag); + void releaseMulticastLock(IBinder binder, String tag); void updateInterfaceIpState(String ifaceName, int mode); boolean isDefaultCoexAlgorithmEnabled(); - void setCoexUnsafeChannels(in List<CoexUnsafeChannel> unsafeChannels, int mandatoryRestrictions); + void setCoexUnsafeChannels( + in List<CoexUnsafeChannel> unsafeChannels, int mandatoryRestrictions); void registerCoexCallback(in ICoexCallback callback); @@ -239,14 +247,16 @@ interface IWifiManager boolean startTetheredHotspot(in SoftApConfiguration softApConfig, String packageName); - void startTetheredHotspotRequest(in TetheringRequest request, in ISoftApCallback callback, String packageName); + void startTetheredHotspotRequest( + in TetheringRequest request, in ISoftApCallback callback, String packageName); boolean stopSoftAp(); boolean validateSoftApConfiguration(in SoftApConfiguration config); int startLocalOnlyHotspot(in ILocalOnlyHotspotCallback callback, String packageName, - String featureId, in SoftApConfiguration customConfig, in Bundle extras); + String featureId, in SoftApConfiguration customConfig, in Bundle extras, + boolean isCalledFromSystemApi); void stopLocalOnlyHotspot(); @@ -258,11 +268,9 @@ interface IWifiManager void stopWatchLocalOnlyHotspot(); - @UnsupportedAppUsage - int getWifiApEnabledState(); + @UnsupportedAppUsage int getWifiApEnabledState(); - @UnsupportedAppUsage - WifiConfiguration getWifiApConfiguration(); + @UnsupportedAppUsage WifiConfiguration getWifiApConfiguration(); SoftApConfiguration getSoftApConfiguration(); @@ -272,15 +280,15 @@ interface IWifiManager boolean setSoftApConfiguration(in SoftApConfiguration softApConfig, String packageName); - void notifyUserOfApBandConversion(String packageName); - void enableTdls(String remoteIPAddress, boolean enable); - void enableTdlsWithRemoteIpAddress(String remoteIPAddress, boolean enable, in IBooleanListener listener); + void enableTdlsWithRemoteIpAddress( + String remoteIPAddress, boolean enable, in IBooleanListener listener); void enableTdlsWithMacAddress(String remoteMacAddress, boolean enable); - void enableTdlsWithRemoteMacAddress(String remoteMacAddress, boolean enable, in IBooleanListener listener); + void enableTdlsWithRemoteMacAddress( + String remoteMacAddress, boolean enable, in IBooleanListener listener); void isTdlsOperationCurrentlyAvailable(in IBooleanListener listener); @@ -298,8 +306,7 @@ interface IWifiManager void factoryReset(String packageName); - @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) - Network getCurrentNetwork(); + @UnsupportedAppUsage(maxTargetSdk=30, trackingBug=170729553) Network getCurrentNetwork(); byte[] retrieveBackupData(); @@ -317,9 +324,11 @@ interface IWifiManager void unregisterSoftApCallback(in ISoftApCallback callback); - void addWifiVerboseLoggingStatusChangedListener(in IWifiVerboseLoggingStatusChangedListener listener); + void addWifiVerboseLoggingStatusChangedListener( + in IWifiVerboseLoggingStatusChangedListener listener); - void removeWifiVerboseLoggingStatusChangedListener(in IWifiVerboseLoggingStatusChangedListener listener); + void removeWifiVerboseLoggingStatusChangedListener( + in IWifiVerboseLoggingStatusChangedListener listener); void addOnWifiUsabilityStatsListener(in IOnWifiUsabilityStatsListener listener); @@ -333,10 +342,11 @@ interface IWifiManager void unregisterNetworkRequestMatchCallback(in INetworkRequestMatchCallback callback); - int addNetworkSuggestions(in ParceledListSlice<WifiNetworkSuggestion> networkSuggestions, in String packageName, - in String featureId); + int addNetworkSuggestions(in ParceledListSlice<WifiNetworkSuggestion> networkSuggestions, + in String packageName, in String featureId); - int removeNetworkSuggestions(in ParceledListSlice<WifiNetworkSuggestion> networkSuggestions, in String packageName, int action); + int removeNetworkSuggestions(in ParceledListSlice<WifiNetworkSuggestion> networkSuggestions, + in String packageName, int action); ParceledListSlice<WifiNetworkSuggestion> getNetworkSuggestions(in String packageName); @@ -345,21 +355,23 @@ interface IWifiManager void setDeviceMobilityState(int state); void startDppAsConfiguratorInitiator(in IBinder binder, in String packageName, - in String enrolleeUri, int selectedNetworkId, int netRole, in IDppCallback callback); + in String enrolleeUri, int selectedNetworkId, int netRole, in IDppCallback callback); - void startDppAsEnrolleeInitiator(in IBinder binder, in String configuratorUri, - in IDppCallback callback); + void startDppAsEnrolleeInitiator( + in IBinder binder, in String configuratorUri, in IDppCallback callback); - void startDppAsEnrolleeResponder(in IBinder binder, in String deviceInfo, int curve, - in IDppCallback callback); + void startDppAsEnrolleeResponder( + in IBinder binder, in String deviceInfo, int curve, in IDppCallback callback); void stopDppSession(); void updateWifiUsabilityScore(int seqNum, int score, int predictionHorizonSec); - oneway void connect(in WifiConfiguration config, int netId, in IActionListener listener, in String packageName, in Bundle extras); + oneway void connect(in WifiConfiguration config, int netId, in IActionListener listener, + in String packageName, in Bundle extras); - oneway void save(in WifiConfiguration config, in IActionListener listener, in String packageName); + oneway void save( + in WifiConfiguration config, in IActionListener listener, in String packageName); oneway void forget(int netId, in IActionListener listener); @@ -367,25 +379,32 @@ interface IWifiManager void unregisterScanResultsCallback(in IScanResultsCallback callback); - void registerSuggestionConnectionStatusListener(in ISuggestionConnectionStatusListener listener, String packageName, String featureId); + void registerSuggestionConnectionStatusListener( + in ISuggestionConnectionStatusListener listener, String packageName, String featureId); - void unregisterSuggestionConnectionStatusListener(in ISuggestionConnectionStatusListener listener, String packageName); + void unregisterSuggestionConnectionStatusListener( + in ISuggestionConnectionStatusListener listener, String packageName); - void addLocalOnlyConnectionStatusListener(in ILocalOnlyConnectionStatusListener listener, String packageName, String featureId); + void addLocalOnlyConnectionStatusListener( + in ILocalOnlyConnectionStatusListener listener, String packageName, String featureId); - void removeLocalOnlyConnectionStatusListener(in ILocalOnlyConnectionStatusListener listener, String packageName); + void removeLocalOnlyConnectionStatusListener( + in ILocalOnlyConnectionStatusListener listener, String packageName); int calculateSignalLevel(int rssi); - ParceledListSlice<WifiConfiguration> getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(in ParceledListSlice<ScanResult> scanResults); + ParceledListSlice<WifiConfiguration> getWifiConfigForMatchedNetworkSuggestionsSharedWithUser( + in ParceledListSlice<ScanResult> scanResults); boolean setWifiConnectedNetworkScorer(in IBinder binder, in IWifiConnectedNetworkScorer scorer); void clearWifiConnectedNetworkScorer(); - void setExternalPnoScanRequest(in IBinder binder, in IPnoScanResultsCallback callback, in List<WifiSsid> ssids, in int[] frequencies, String packageName, String featureId); + void setExternalPnoScanRequest(in IBinder binder, in IPnoScanResultsCallback callback, + in List<WifiSsid> ssids, in int[] frequencies, String packageName, String featureId); - void setPnoScanEnabled(boolean enabled, boolean enablePnoScanAfterWifiToggle, String packageName); + void setPnoScanEnabled( + boolean enabled, boolean enablePnoScanAfterWifiToggle, String packageName); void clearExternalPnoScanRequest(); @@ -394,7 +413,9 @@ interface IWifiManager /** * Return the Map of {@link WifiNetworkSuggestion} and the list of <ScanResult> */ - Map getMatchingScanResults(in ParceledListSlice<WifiNetworkSuggestion> networkSuggestions, in ParceledListSlice<ScanResult> scanResults, String callingPackage, String callingFeatureId); + Map getMatchingScanResults(in ParceledListSlice<WifiNetworkSuggestion> networkSuggestions, + in ParceledListSlice<ScanResult> scanResults, String callingPackage, + String callingFeatureId); void setScanThrottleEnabled(boolean enable); @@ -420,9 +441,11 @@ interface IWifiManager void restartWifiSubsystem(); - void addSuggestionUserApprovalStatusListener(in ISuggestionUserApprovalStatusListener listener, String packageName); + void addSuggestionUserApprovalStatusListener( + in ISuggestionUserApprovalStatusListener listener, String packageName); - void removeSuggestionUserApprovalStatusListener(in ISuggestionUserApprovalStatusListener listener, String packageName); + void removeSuggestionUserApprovalStatusListener( + in ISuggestionUserApprovalStatusListener listener, String packageName); void setEmergencyScanRequestInProgress(boolean inProgress); @@ -432,7 +455,8 @@ interface IWifiManager void flushPasspointAnqpCache(String packageName); - List<WifiAvailableChannel> getUsableChannels(int band, int mode, int filter, String packageName, in Bundle extras); + List<WifiAvailableChannel> getUsableChannels( + int band, int mode, int filter, String packageName, in Bundle extras); boolean isWifiPasspointEnabled(); @@ -452,15 +476,18 @@ interface IWifiManager void replyToSimpleDialog(int dialogId, int reply); - void addCustomDhcpOptions(in WifiSsid ssid, in byte[] oui, in ParceledListSlice<DhcpOption> options); + void addCustomDhcpOptions( + in WifiSsid ssid, in byte[] oui, in ParceledListSlice<DhcpOption> options); void removeCustomDhcpOptions(in WifiSsid ssid, in byte[] oui); - void reportCreateInterfaceImpact(String packageName, int interfaceType, boolean requireNewInterface, in IInterfaceCreationInfoCallback callback); + void reportCreateInterfaceImpact(String packageName, int interfaceType, + boolean requireNewInterface, in IInterfaceCreationInfoCallback callback); int getMaxNumberOfChannelsPerRequest(); - void addQosPolicies(in ParceledListSlice<QosPolicyParams> policyParamsList, in IBinder binder, String packageName, in IListListener callback); + void addQosPolicies(in ParceledListSlice<QosPolicyParams> policyParamsList, in IBinder binder, + String packageName, in IListListener callback); void removeQosPolicies(in int[] policyIdList, String packageName); @@ -500,7 +527,7 @@ interface IWifiManager void removePerSsidRoamingMode(in WifiSsid ssid, String packageName); - void getPerSsidRoamingModes(String packageName,in IMapListener listener); + void getPerSsidRoamingModes(String packageName, in IMapListener listener); void getTwtCapabilities(in ITwtCapabilitiesListener listener, in Bundle extras); @@ -519,4 +546,15 @@ interface IWifiManager void restoreWifiBackupData(in byte[] data); boolean isPnoSupported(); + + void setAutojoinDisallowedSecurityTypes(int restrictions, in Bundle extras); + + void getAutojoinDisallowedSecurityTypes(in IIntegerListener listener, in Bundle extras); + + void disallowCurrentSuggestedNetwork(in BlockingOption option, String packageName); + + void storeCapturedData(int triggerType, boolean isFullCapture, long triggerStartTimeMillis, + long triggerStopTimeMillis, in IIntegerListener listener); + boolean isUsdSubscriberSupported(); + boolean isUsdPublisherSupported(); } diff --git a/framework/java/android/net/wifi/IWifiStateChangedListener.aidl b/framework/java/android/net/wifi/IWifiStateChangedListener.aidl new file mode 100644 index 0000000000..2b5cdfdf24 --- /dev/null +++ b/framework/java/android/net/wifi/IWifiStateChangedListener.aidl @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2024 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 android.net.wifi; + +/** + * Interface for Wi-Fi state changed listener. + * @hide + */ +oneway interface IWifiStateChangedListener { + void onWifiStateChanged(); +} diff --git a/framework/java/android/net/wifi/ScanResult.java b/framework/java/android/net/wifi/ScanResult.java index 161928fac2..6954590ae9 100644 --- a/framework/java/android/net/wifi/ScanResult.java +++ b/framework/java/android/net/wifi/ScanResult.java @@ -32,6 +32,8 @@ import android.os.Parcel; import android.os.Parcelable; import android.util.Log; +import androidx.annotation.Keep; + import com.android.modules.utils.build.SdkLevel; import com.android.wifi.flags.Flags; @@ -360,9 +362,26 @@ public final class ScanResult implements Parcelable { public static final int KEY_MGMT_FT_SAE_EXT_KEY = 19; /** * @hide + * Security key management scheme: PASN. + */ + public static final int KEY_MGMT_PASN = 20; + + /** + * Security key management scheme: FT authentication negotiated over IEEE Std 802.1X using + * SHA-384. + * @hide + */ + public static final int KEY_MGMT_EAP_FT_SHA384 = 21; + /** + * Security key management scheme: FT authentication using PSK (SHA-384). + * @hide + */ + public static final int KEY_MGMT_FT_PSK_SHA384 = 22; + /** + * @hide * Security key management scheme: any unknown AKM. */ - public static final int KEY_MGMT_UNKNOWN = 20; + public static final int KEY_MGMT_UNKNOWN = 23; /** * @hide * No cipher suite. @@ -736,6 +755,13 @@ public final class ScanResult implements Parcelable { /** @hide */ public static final long FLAG_TWT_RESPONDER = 0x0000000000000008; + + /** @hide */ + public static final long FLAG_SECURE_HE_LTF_SUPPORTED = 0x0000000000000010; + + /** @hide */ + public static final long FLAG_RANGING_FRAME_PROTECTION_REQUIRED = 0x0000000000000020; + /* * These flags are specific to the ScanResult class, and are not related to the |flags| * field of the per-BSS scan results from WPA supplicant. @@ -790,6 +816,33 @@ public final class ScanResult implements Parcelable { } /** + * Returns whether the AP supports secure HE-LTF for ranging measurement. + * + * Secure HE-LTF (High Efficiency Long Training Field), is a security enhancement for the HE + * PHY (High Efficiency Physical Layer) that aims to protect ranging measurements by using + * randomized HE-LTF sequences. This prevents attackers from exploiting predictable HE-LTF + * patterns. IEEE 802.11az adds secure HE-LTF support in Extended RSN capabilities. + */ + @FlaggedApi(Flags.FLAG_SECURE_RANGING) + public boolean isSecureHeLtfSupported() { + return (flags & FLAG_SECURE_HE_LTF_SUPPORTED) != 0; + } + + /** + * Returns whether the AP requires frame protection for ranging measurement. + * + * Ranging frame protection in IEEE 802.11az, also known as URNM-MFPR (Unassociated Range + * Negotiation and Measurement Management Frame Protection Required), is a security policy + * that dictates whether ranging frames are required to be protected even without a formal + * association between the Initiating STA (ISTA) and Responding STA (RSTA) + */ + @FlaggedApi(Flags.FLAG_SECURE_RANGING) + public boolean isRangingFrameProtectionRequired() { + return (flags & FLAG_RANGING_FRAME_PROTECTION_REQUIRED) != 0; + } + + + /** * Indicates venue name (such as 'San Francisco Airport') published by access point; only * available on Passpoint network and if published by access point. * @deprecated - This information is not provided @@ -928,6 +981,7 @@ public final class ScanResult implements Parcelable { * * @hide */ + @Keep public static boolean is24GHz(int freqMhz) { return freqMhz >= BAND_24_GHZ_START_FREQ_MHZ && freqMhz <= BAND_24_GHZ_END_FREQ_MHZ; } @@ -939,6 +993,7 @@ public final class ScanResult implements Parcelable { * * @hide */ + @Keep public static boolean is5GHz(int freqMhz) { return freqMhz >= BAND_5_GHZ_START_FREQ_MHZ && freqMhz <= BAND_5_GHZ_END_FREQ_MHZ; } @@ -950,6 +1005,7 @@ public final class ScanResult implements Parcelable { * * @hide */ + @Keep public static boolean is6GHz(int freqMhz) { if (freqMhz == BAND_6_GHZ_OP_CLASS_136_CH_2_FREQ_MHZ) { return true; @@ -1219,6 +1275,8 @@ public final class ScanResult implements Parcelable { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final int EID_RSN = 48; /** @hide */ + public static final int EID_RSN_EXTENSION = 244; + /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final int EID_EXTENDED_SUPPORTED_RATES = 50; /** @hide */ @@ -1456,6 +1514,9 @@ public final class ScanResult implements Parcelable { private boolean mIs80211McRTTResponder = false; private boolean mIs80211azNtbRTTResponder = false; private boolean mIsTwtResponder = false; + private boolean mIsSecureHeLtfSupported = false; + private boolean mIsRangingFrameProtectionRequired = false; + /** @hide */ @NonNull public Builder setHessid(long hessid) { @@ -1618,6 +1679,18 @@ public final class ScanResult implements Parcelable { public ScanResult build() { return new ScanResult(this); } + + /** @hide */ + public Builder setSecureHeLtfSupported(boolean supported) { + mIsSecureHeLtfSupported = supported; + return this; + } + + /** @hide */ + public Builder setRangingFrameProtectionRequired(boolean required) { + mIsRangingFrameProtectionRequired = required; + return this; + } } /** @@ -1656,6 +1729,9 @@ public final class ScanResult implements Parcelable { this.flags |= (builder.mIsTwtResponder) ? FLAG_TWT_RESPONDER : 0; this.radioChainInfos = null; this.mApMldMacAddress = null; + this.flags |= (builder.mIsSecureHeLtfSupported) ? FLAG_SECURE_HE_LTF_SUPPORTED : 0; + this.flags |= (builder.mIsRangingFrameProtectionRequired) + ? FLAG_RANGING_FRAME_PROTECTION_REQUIRED : 0; } /** diff --git a/framework/java/android/net/wifi/SoftApCapability.java b/framework/java/android/net/wifi/SoftApCapability.java index 0607db4a28..151c07c63e 100644 --- a/framework/java/android/net/wifi/SoftApCapability.java +++ b/framework/java/android/net/wifi/SoftApCapability.java @@ -16,6 +16,7 @@ package android.net.wifi; +import android.annotation.FlaggedApi; import android.annotation.LongDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -25,6 +26,10 @@ import android.net.wifi.SoftApConfiguration.BandType; import android.os.Parcel; import android.os.Parcelable; +import androidx.annotation.Keep; + +import com.android.wifi.flags.Flags; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; @@ -142,6 +147,14 @@ public final class SoftApCapability implements Parcelable { */ public static final long SOFTAP_FEATURE_WPA3_OWE = 1 << 11; + /* + * Support for multiple link operation on a single multiple link device. + * Flag when {@code R.Integer.config_wifiSoftApMaxNumberMLDSupported} is configured + * to non zero value and chip report MLO SoftAP is supported. + */ + @FlaggedApi(Flags.FLAG_MLO_SAP) + public static final long SOFTAP_FEATURE_MLO = 1 << 12; + /** @hide */ @Retention(RetentionPolicy.SOURCE) @LongDef(flag = true, prefix = { "SOFTAP_FEATURE_" }, value = { @@ -157,6 +170,7 @@ public final class SoftApCapability implements Parcelable { SOFTAP_FEATURE_BAND_60G_SUPPORTED, SOFTAP_FEATURE_WPA3_OWE_TRANSITION, SOFTAP_FEATURE_WPA3_OWE, + SOFTAP_FEATURE_MLO, }) public @interface HotspotFeatures {} @@ -259,6 +273,7 @@ public final class SoftApCapability implements Parcelable { * @throws IllegalArgumentException when band type is invalid. * @hide */ + @Keep public boolean setSupportedChannelList(@BandType int band, @Nullable int[] supportedChannelList) { if (supportedChannelList == null) return false; diff --git a/framework/java/android/net/wifi/SoftApConfiguration.java b/framework/java/android/net/wifi/SoftApConfiguration.java index 5432484b6a..92efcf72cb 100644 --- a/framework/java/android/net/wifi/SoftApConfiguration.java +++ b/framework/java/android/net/wifi/SoftApConfiguration.java @@ -26,6 +26,7 @@ import android.app.compat.CompatChanges; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; import android.net.MacAddress; +import android.net.wifi.util.Environment; import android.net.wifi.util.HexEncoding; import android.os.Build; import android.os.Parcel; @@ -34,6 +35,7 @@ import android.text.TextUtils; import android.util.Log; import android.util.SparseIntArray; +import androidx.annotation.Keep; import androidx.annotation.RequiresApi; import androidx.annotation.VisibleForTesting; @@ -74,30 +76,26 @@ public final class SoftApConfiguration implements Parcelable { /** * 2GHz band. - * @hide */ - @SystemApi + @FlaggedApi(Flags.FLAG_PUBLIC_BANDS_FOR_LOHS) public static final int BAND_2GHZ = 1 << 0; /** * 5GHz band. - * @hide */ - @SystemApi + @FlaggedApi(Flags.FLAG_PUBLIC_BANDS_FOR_LOHS) public static final int BAND_5GHZ = 1 << 1; /** * 6GHz band. - * @hide */ - @SystemApi + @FlaggedApi(Flags.FLAG_PUBLIC_BANDS_FOR_LOHS) public static final int BAND_6GHZ = 1 << 2; /** * 60GHz band. - * @hide */ - @SystemApi + @FlaggedApi(Flags.FLAG_PUBLIC_BANDS_FOR_LOHS) public static final int BAND_60GHZ = 1 << 3; /** @@ -406,6 +404,11 @@ public final class SoftApConfiguration implements Parcelable { private @NonNull List<OuiKeyedData> mVendorData; /** + * Whether connected clients can communicate with each other or not. + */ + private boolean mIsClientIsolationEnabled; + + /** * THe definition of security type OPEN. */ public static final int SECURITY_TYPE_OPEN = 0; @@ -473,7 +476,8 @@ public final class SoftApConfiguration implements Parcelable { @NonNull Set<Integer> allowedAcsChannels5g, @NonNull Set<Integer> allowedAcsChannels6g, @WifiAnnotations.Bandwidth int maxChannelBandwidth, - @Nullable List<OuiKeyedData> vendorData) { + @Nullable List<OuiKeyedData> vendorData, + boolean isClientIsolationEnabled) { mWifiSsid = ssid; mBssid = bssid; mPassphrase = passphrase; @@ -505,6 +509,7 @@ public final class SoftApConfiguration implements Parcelable { mAllowedAcsChannels6g = new HashSet<>(allowedAcsChannels6g); mMaxChannelBandwidth = maxChannelBandwidth; mVendorData = new ArrayList<>(vendorData); + mIsClientIsolationEnabled = isClientIsolationEnabled; } @Override @@ -543,7 +548,8 @@ public final class SoftApConfiguration implements Parcelable { && Objects.equals(mAllowedAcsChannels5g, other.mAllowedAcsChannels5g) && Objects.equals(mAllowedAcsChannels6g, other.mAllowedAcsChannels6g) && mMaxChannelBandwidth == other.mMaxChannelBandwidth - && Objects.equals(mVendorData, other.mVendorData); + && Objects.equals(mVendorData, other.mVendorData) + && mIsClientIsolationEnabled == other.mIsClientIsolationEnabled; } @Override @@ -573,7 +579,8 @@ public final class SoftApConfiguration implements Parcelable { mAllowedAcsChannels5g, mAllowedAcsChannels6g, mMaxChannelBandwidth, - mVendorData); + mVendorData, + mIsClientIsolationEnabled); } @Override @@ -608,6 +615,7 @@ public final class SoftApConfiguration implements Parcelable { sbuf.append(" \n mAllowedAcsChannels6g = ").append(mAllowedAcsChannels6g); sbuf.append(" \n mMaxChannelBandwidth = ").append(mMaxChannelBandwidth); sbuf.append(" \n mVendorData = ").append(mVendorData); + sbuf.append(" \n mIsClientIsolationEnabled = ").append(mIsClientIsolationEnabled); return sbuf.toString(); } @@ -638,6 +646,7 @@ public final class SoftApConfiguration implements Parcelable { writeHashSetInt(dest, mAllowedAcsChannels6g); dest.writeInt(mMaxChannelBandwidth); dest.writeList(mVendorData); + dest.writeBoolean(mIsClientIsolationEnabled); } /* Reference from frameworks/base/core/java/android/os/Parcel.java */ @@ -749,7 +758,8 @@ public final class SoftApConfiguration implements Parcelable { readHashSetInt(in), readHashSetInt(in), in.readInt(), - readOuiKeyedDataList(in)); + readOuiKeyedDataList(in), + in.readBoolean()); } @Override @@ -860,6 +870,7 @@ public final class SoftApConfiguration implements Parcelable { * * @hide */ + @Keep public @NonNull int[] getBands() { int[] bands = new int[mChannels.size()]; for (int i = 0; i < bands.length; i++) { @@ -895,11 +906,9 @@ public final class SoftApConfiguration implements Parcelable { * * Note: return array may only include one channel when current setting is single AP mode. * See also {@link Builder#setChannels(SparseIntArray)}. - * - * @hide */ + @FlaggedApi(Flags.FLAG_PUBLIC_BANDS_FOR_LOHS) @RequiresApi(Build.VERSION_CODES.S) - @SystemApi public @NonNull SparseIntArray getChannels() { if (!SdkLevel.isAtLeastS()) { throw new UnsupportedOperationException(); @@ -967,6 +976,8 @@ public final class SoftApConfiguration implements Parcelable { * (true: authorization required) or not (false: not required). * See also {@link Builder#setClientControlByUserEnabled(Boolean)}. * + * @return true when client isolation is enable. + * * @hide */ @SystemApi @@ -1218,6 +1229,24 @@ public final class SoftApConfiguration implements Parcelable { } /** + * Returns whether client isolation is enabled or not. + * + * Client isolation is used to disallow a connected Soft AP + * client to communicate with other connected clients. + * + * @hide + */ + @RequiresApi(Build.VERSION_CODES.BAKLAVA) + @FlaggedApi(Flags.FLAG_AP_ISOLATE) + @SystemApi + public boolean isClientIsolationEnabled() { + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + return mIsClientIsolationEnabled; + } + + /** * Returns a {@link WifiConfiguration} representation of this {@link SoftApConfiguration}. * Note that SoftApConfiguration may contain configuration which is cannot be represented * by the legacy WifiConfiguration, in such cases a null will be returned. @@ -1282,10 +1311,8 @@ public final class SoftApConfiguration implements Parcelable { * * All fields are optional. By default, SSID and BSSID are automatically chosen by the * framework, and an open network is created. - * - * @hide */ - @SystemApi + @FlaggedApi(Flags.FLAG_PUBLIC_BANDS_FOR_LOHS) public static final class Builder { private WifiSsid mWifiSsid; private MacAddress mBssid; @@ -1312,6 +1339,7 @@ public final class SoftApConfiguration implements Parcelable { private Set<Integer> mAllowedAcsChannels6g; private @WifiAnnotations.Bandwidth int mMaxChannelBandwidth; private @Nullable List<OuiKeyedData> mVendorData; + private boolean mIsClientIsolationEnabled; /** * Constructs a Builder with default values (see {@link Builder}). @@ -1347,11 +1375,15 @@ public final class SoftApConfiguration implements Parcelable { mAllowedAcsChannels6g = new HashSet<>(); mMaxChannelBandwidth = SoftApInfo.CHANNEL_WIDTH_AUTO; mVendorData = new ArrayList<>(); + mIsClientIsolationEnabled = false; } /** * Constructs a Builder initialized from an existing {@link SoftApConfiguration} instance. + * + * @hide */ + @SystemApi public Builder(@NonNull SoftApConfiguration other) { if (other == null) { Log.e(TAG, "Cannot provide a null SoftApConfiguration"); @@ -1391,6 +1423,7 @@ public final class SoftApConfiguration implements Parcelable { mMacRandomizationSetting = RANDOMIZATION_NONE; } mVendorData = new ArrayList<>(other.mVendorData); + mIsClientIsolationEnabled = other.mIsClientIsolationEnabled; } /** @@ -1443,7 +1476,8 @@ public final class SoftApConfiguration implements Parcelable { mAllowedAcsChannels5g, mAllowedAcsChannels6g, mMaxChannelBandwidth, - mVendorData); + mVendorData, + mIsClientIsolationEnabled); } /** @@ -1460,9 +1494,12 @@ public final class SoftApConfiguration implements Parcelable { * representation is longer than 32 bytes. * * @deprecated Use {@link #setWifiSsid(WifiSsid)} instead. + * + * @hide */ @NonNull @Deprecated + @SystemApi public Builder setSsid(@Nullable String ssid) { if (ssid == null) { mWifiSsid = null; @@ -1484,9 +1521,12 @@ public final class SoftApConfiguration implements Parcelable { * * @param wifiSsid SSID, or null ot have the SSID automatically chosen by the framework. * @return Builder for chaining. + * + * @hide */ @NonNull @RequiresApi(Build.VERSION_CODES.TIRAMISU) + @SystemApi public Builder setWifiSsid(@Nullable WifiSsid wifiSsid) { if (!SdkLevel.isAtLeastT()) { throw new UnsupportedOperationException(); @@ -1503,9 +1543,12 @@ public final class SoftApConfiguration implements Parcelable { * * @param vendorElements VendorElements * @return Builder for chaining. + * + * @hide */ @NonNull @RequiresApi(Build.VERSION_CODES.TIRAMISU) + @SystemApi public Builder setVendorElements( @NonNull List<ScanResult.InformationElement> vendorElements) { if (!SdkLevel.isAtLeastT()) { @@ -1561,8 +1604,11 @@ public final class SoftApConfiguration implements Parcelable { * @return Builder for chaining. * @throws IllegalArgumentException when the given BSSID is the all-zero * , multicast or broadcast MAC address. + * + * @hide */ @NonNull + @SystemApi public Builder setBssid(@Nullable MacAddress bssid) { if (bssid != null) { Preconditions.checkArgument(!bssid.equals(WifiManager.ALL_ZEROS_MAC_ADDRESS)); @@ -1602,8 +1648,11 @@ public final class SoftApConfiguration implements Parcelable { * when the passphrase is not between 8 and 63 bytes (inclusive) for * - {@link #SECURITY_TYPE_WPA2_PSK} * - {@link #SECURITY_TYPE_WPA3_SAE_TRANSITION} + * + * @hide */ @NonNull + @SystemApi public Builder setPassphrase(@Nullable String passphrase, @SecurityType int securityType) { if (!SdkLevel.isAtLeastT() && (securityType == SECURITY_TYPE_WPA3_OWE_TRANSITION @@ -1646,8 +1695,11 @@ public final class SoftApConfiguration implements Parcelable { * * @param hiddenSsid true for a hidden SSID, false otherwise. * @return Builder for chaining. + * + * @hide */ @NonNull + @SystemApi public Builder setHiddenSsid(boolean hiddenSsid) { mHiddenSsid = hiddenSsid; return this; @@ -1662,8 +1714,11 @@ public final class SoftApConfiguration implements Parcelable { * {@link #BAND_2GHZ}, {@link #BAND_5GHZ}, {@link #BAND_6GHZ}. * @return Builder for chaining. * @throws IllegalArgumentException when an invalid band type is provided. + * + * @hide */ @NonNull + @SystemApi public Builder setBand(@BandType int band) { if (!isBandValid(band)) { throw new IllegalArgumentException("Invalid band type: " + band); @@ -1694,9 +1749,12 @@ public final class SoftApConfiguration implements Parcelable { * @return Builder for chaining. * @throws IllegalArgumentException when more than 2 bands are set or an invalid band type * is provided. + * + * @hide */ @RequiresApi(Build.VERSION_CODES.S) @NonNull + @SystemApi public Builder setBands(@NonNull int[] bands) { if (!SdkLevel.isAtLeastS()) { throw new UnsupportedOperationException(); @@ -1744,8 +1802,11 @@ public final class SoftApConfiguration implements Parcelable { * @param band containing this channel. * @return Builder for chaining. * @throws IllegalArgumentException when the invalid channel or band type is configured. + * + * @hide */ @NonNull + @SystemApi public Builder setChannel(int channel, @BandType int band) { if (!isChannelBandPairValid(channel, band)) { throw new IllegalArgumentException("Invalid channel(" + channel @@ -1758,42 +1819,22 @@ public final class SoftApConfiguration implements Parcelable { /** * Specifies the channels and associated bands for the APs. - * - * When more than 1 channel is set, this will bring up concurrent APs on the requested - * channels and bands (if possible). - * - * Valid channels are country dependent. - * The {@link SoftApCapability#getSupportedChannelList(int)} can be used to obtain - * valid channels in each band. - * - * Use {@link WifiManager#isBridgedApConcurrencySupported()} to determine - * whether or not concurrent APs are supported. - * * <p> - * If not set, the default for the channel is the special value 0 which has the framework - * auto-select a valid channel from the band configured with {@link #setBands(int[])}. - * - * The channel auto selection will be offloaded to driver when - * {@link SoftApCapability#areFeaturesSupported(long)} - * with {@link SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD} - * returns true. The driver will auto select the best channel (e.g. best performance) - * based on environment interference. Check {@link SoftApCapability} for more detail. - * - * Requires the driver to support {@link SoftApCapability.SOFTAP_FEATURE_ACS_OFFLOAD} - * when multiple bands are configured without specified channel value (i.e. channel is - * the special value 0). Otherwise, - * {@link WifiManager#startTetheredHotspot(SoftApConfiguration)} will report error code - * {@link WifiManager#SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}. - * - * Note: Only supports 2.4GHz + 5GHz bands. If any other band is set, will report error - * {@link WifiManager#SAP_START_FAILURE_UNSUPPORTED_CONFIGURATION}. - * * The API contains (band, channel) input since the 6GHz band uses the same channel * numbering scheme as is used in the 2.4GHz and 5GHz band. Therefore, both are needed to * uniquely identify individual channels. - * + * <p> * Reference the Wi-Fi channel numbering and the channelization in IEEE 802.11-2016 * specifications, section 17.3.8.4.2, 17.3.8.4.3 and Table 15-6. + * <p> + * Using the special value 0 which has the framework auto-select a valid channel + * from the band configured. + * When more than 1 channel/band is set, this will bring up concurrent APs on the requested + * channels and bands (if possible). + * Use {@link WifiManager#isBridgedApConcurrencySupported()} to determine + * whether concurrent APs are supported. + * If not set, the default value is {@link #BAND_2GHZ} with the special channel value 0 + * which has the framework auto-select from {@link #BAND_2GHZ}. * * <p> * @param channels SparseIntArray (key: {@code #BandType} , value: channel) consists of @@ -1859,8 +1900,11 @@ public final class SoftApConfiguration implements Parcelable { * * @param maxNumberOfClients maximum client number of the AP. * @return Builder for chaining. + * + * @hide */ @NonNull + @SystemApi public Builder setMaxNumberOfClients(@IntRange(from = 0) int maxNumberOfClients) { if (maxNumberOfClients < 0) { throw new IllegalArgumentException("maxNumberOfClients should be not negative"); @@ -1881,8 +1925,11 @@ public final class SoftApConfiguration implements Parcelable { * @return Builder for chaining. * * @see #setShutdownTimeoutMillis(long) + * + * @hide */ @NonNull + @SystemApi public Builder setAutoShutdownEnabled(boolean enable) { mAutoShutdownEnabled = enable; return this; @@ -1907,8 +1954,11 @@ public final class SoftApConfiguration implements Parcelable { * @return Builder for chaining. * * @see #setAutoShutdownEnabled(boolean) + * + * @hide */ @NonNull + @SystemApi public Builder setShutdownTimeoutMillis(@IntRange(from = -1) long timeoutMillis) { if (CompatChanges.isChangeEnabled( REMOVE_ZERO_FOR_TIMEOUT_SETTING) && timeoutMillis < 1) { @@ -1950,8 +2000,11 @@ public final class SoftApConfiguration implements Parcelable { * * @param enabled true for enabling the control by user, false otherwise. * @return Builder for chaining. + * + * @hide */ @NonNull + @SystemApi public Builder setClientControlByUserEnabled(boolean enabled) { mClientControlByUser = enabled; return this; @@ -1975,9 +2028,12 @@ public final class SoftApConfiguration implements Parcelable { * <p> * * @return Builder for chaining. + * + * @hide */ @NonNull @RequiresApi(Build.VERSION_CODES.TIRAMISU) + @SystemApi public Builder setAllowedAcsChannels(@BandType int band, @NonNull int[] channels) { if (!SdkLevel.isAtLeastT()) { throw new UnsupportedOperationException(); @@ -2031,9 +2087,12 @@ public final class SoftApConfiguration implements Parcelable { * or {@link SoftApInfo#CHANNEL_WIDTH_320MHZ} * * @return builder for chaining + * + * @hide */ @NonNull @RequiresApi(Build.VERSION_CODES.TIRAMISU) + @SystemApi public Builder setMaxChannelBandwidth(@WifiAnnotations.Bandwidth int maxChannelBandwidth) { if (!SdkLevel.isAtLeastT()) { throw new UnsupportedOperationException(); @@ -2076,8 +2135,11 @@ public final class SoftApConfiguration implements Parcelable { * @param allowedClientList list of clients which are allowed to associate to the AP * without user pre-approval. * @return Builder for chaining. + * + * @hide */ @NonNull + @SystemApi public Builder setAllowedClientList(@NonNull List<MacAddress> allowedClientList) { mAllowedClientList = new ArrayList<>(allowedClientList); return this; @@ -2100,8 +2162,11 @@ public final class SoftApConfiguration implements Parcelable { * * @param blockedClientList list of clients which are not allowed to associate to the AP. * @return Builder for chaining. + * + * @hide */ @NonNull + @SystemApi public Builder setBlockedClientList(@NonNull List<MacAddress> blockedClientList) { mBlockedClientList = new ArrayList<>(blockedClientList); return this; @@ -2133,9 +2198,12 @@ public final class SoftApConfiguration implements Parcelable { * @return Builder for chaining. * * @see #setBssid(MacAddress) + * + * @hide */ @RequiresApi(Build.VERSION_CODES.S) @NonNull + @SystemApi public Builder setMacRandomizationSetting( @MacRandomizationSetting int macRandomizationSetting) { if (!SdkLevel.isAtLeastS()) { @@ -2175,9 +2243,11 @@ public final class SoftApConfiguration implements Parcelable { * @param enable true to enable, false to disable. * @return Builder for chaining. * + * @hide */ @RequiresApi(Build.VERSION_CODES.S) @NonNull + @SystemApi public Builder setBridgedModeOpportunisticShutdownEnabled(boolean enable) { if (!SdkLevel.isAtLeastS()) { throw new UnsupportedOperationException(); @@ -2203,9 +2273,11 @@ public final class SoftApConfiguration implements Parcelable { * @param enable true to enable, false to disable. * @return Builder for chaining. * + * @hide */ @RequiresApi(Build.VERSION_CODES.S) @NonNull + @SystemApi public Builder setIeee80211axEnabled(boolean enable) { if (!SdkLevel.isAtLeastS()) { throw new UnsupportedOperationException(); @@ -2231,9 +2303,11 @@ public final class SoftApConfiguration implements Parcelable { * @param enable true to enable, false to disable. * @return Builder for chaining. * + * @hide */ @RequiresApi(Build.VERSION_CODES.TIRAMISU) @NonNull + @SystemApi public Builder setIeee80211beEnabled(boolean enable) { if (!SdkLevel.isAtLeastT()) { throw new UnsupportedOperationException(); @@ -2276,9 +2350,12 @@ public final class SoftApConfiguration implements Parcelable { * @return Builder for chaining. * * @see #setBridgedModeOpportunisticShutdownEnabled(boolean) + * + * @hide */ @RequiresApi(Build.VERSION_CODES.TIRAMISU) @NonNull + @SystemApi public Builder setBridgedModeOpportunisticShutdownTimeoutMillis( @IntRange(from = -1) long timeoutMillis) { if (!SdkLevel.isAtLeastT()) { @@ -2311,10 +2388,13 @@ public final class SoftApConfiguration implements Parcelable { * @param vendorData List of {@link OuiKeyedData} containing the vendor-provided * configuration data. Note that multiple elements with the same OUI are allowed. * @return Builder for chaining. + * + * @hide */ @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) @NonNull + @SystemApi public Builder setVendorData(@NonNull List<OuiKeyedData> vendorData) { if (!SdkLevel.isAtLeastV()) { throw new UnsupportedOperationException(); @@ -2325,5 +2405,28 @@ public final class SoftApConfiguration implements Parcelable { mVendorData = vendorData; return this; } + + /** + * Specifies whether or not client isolation is enabled. + * + * Client isolation can be used to disallow a connected Soft AP + * client to communicate with other connected clients. + * + * @param isClientIsolationEnabled true when enabling client isolation. + * @return Builder for chaining. + * + * @hide + */ + @FlaggedApi(Flags.FLAG_AP_ISOLATE) + @RequiresApi(Build.VERSION_CODES.BAKLAVA) + @NonNull + @SystemApi + public Builder setClientIsolationEnabled(boolean isClientIsolationEnabled) { + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + mIsClientIsolationEnabled = isClientIsolationEnabled; + return this; + } } } diff --git a/framework/java/android/net/wifi/SoftApInfo.java b/framework/java/android/net/wifi/SoftApInfo.java index f4440e9e74..fac3fd428c 100644 --- a/framework/java/android/net/wifi/SoftApInfo.java +++ b/framework/java/android/net/wifi/SoftApInfo.java @@ -166,6 +166,10 @@ public final class SoftApInfo implements Parcelable { /** List of {@link OuiKeyedData} containing vendor-specific configuration data. */ private List<OuiKeyedData> mVendorData = Collections.emptyList(); + /** The multiple link device (MLD) MAC Address which Wi-Fi 7 AP resides on. */ + @Nullable + private MacAddress mMldAddress; + /** * Get the frequency which AP resides on. */ @@ -355,6 +359,28 @@ public final class SoftApInfo implements Parcelable { } /** + * Get the multiple link device MAC address which Wi-Fi AP resides on. + * Null when AP disabled or non Wi-Fi 7 AP. + */ + @FlaggedApi(Flags.FLAG_MLO_SAP) + @Nullable + public MacAddress getMldAddress() { + return mMldAddress; + } + + /** + * Set the multi-link address (MLA) of the multi-link device (MLD) on the Wi-Fi AP. + * <p> + * <li>If not set (non Wi-Fi 7 Soft AP), defaults to null.</li> + * @param mldAddress multiple link device MAC address. + * + * @hide + */ + public void setMldAddress(@Nullable MacAddress mldAddress) { + mMldAddress = mldAddress; + } + + /** * @hide */ public SoftApInfo(@Nullable SoftApInfo source) { @@ -366,6 +392,7 @@ public final class SoftApInfo implements Parcelable { mApInstanceIdentifier = source.mApInstanceIdentifier; mIdleShutdownTimeoutMillis = source.mIdleShutdownTimeoutMillis; mVendorData = new ArrayList<>(source.mVendorData); + mMldAddress = source.mMldAddress; } } @@ -391,6 +418,7 @@ public final class SoftApInfo implements Parcelable { dest.writeString(mApInstanceIdentifier); dest.writeLong(mIdleShutdownTimeoutMillis); dest.writeList(mVendorData); + dest.writeParcelable(mMldAddress, flags); } @NonNull @@ -405,6 +433,7 @@ public final class SoftApInfo implements Parcelable { info.mApInstanceIdentifier = in.readString(); info.mIdleShutdownTimeoutMillis = in.readLong(); info.mVendorData = ParcelUtil.readOuiKeyedDataList(in); + info.mMldAddress = in.readParcelable(MacAddress.class.getClassLoader()); return info; } @@ -425,6 +454,7 @@ public final class SoftApInfo implements Parcelable { sbuf.append(", mApInstanceIdentifier= ").append(mApInstanceIdentifier); sbuf.append(", mIdleShutdownTimeoutMillis= ").append(mIdleShutdownTimeoutMillis); sbuf.append(", mVendorData= ").append(mVendorData); + if (mMldAddress != null) sbuf.append(",mMldAddress=").append(mBssid.toString()); sbuf.append("}"); return sbuf.toString(); } @@ -440,12 +470,13 @@ public final class SoftApInfo implements Parcelable { && mWifiStandard == softApInfo.mWifiStandard && Objects.equals(mApInstanceIdentifier, softApInfo.mApInstanceIdentifier) && mIdleShutdownTimeoutMillis == softApInfo.mIdleShutdownTimeoutMillis - && Objects.equals(mVendorData, softApInfo.mVendorData); + && Objects.equals(mVendorData, softApInfo.mVendorData) + && Objects.equals(mMldAddress, softApInfo.mMldAddress); } @Override public int hashCode() { return Objects.hash(mFrequency, mBandwidth, mBssid, mWifiStandard, mApInstanceIdentifier, - mIdleShutdownTimeoutMillis, mVendorData); + mIdleShutdownTimeoutMillis, mVendorData, mMldAddress); } } diff --git a/framework/java/android/net/wifi/SupplicantState.java b/framework/java/android/net/wifi/SupplicantState.java index de7e2b556b..eee70a2bc8 100644 --- a/framework/java/android/net/wifi/SupplicantState.java +++ b/framework/java/android/net/wifi/SupplicantState.java @@ -19,6 +19,8 @@ package android.net.wifi; import android.os.Parcel; import android.os.Parcelable; +import androidx.annotation.Keep; + /** * From <code>defs.h</code> in <code>wpa_supplicant</code>. * <p/> @@ -195,6 +197,7 @@ public enum SupplicantState implements Parcelable { } /** @hide */ + @Keep public static boolean isConnecting(SupplicantState state) { switch(state) { case AUTHENTICATING: diff --git a/framework/java/android/net/wifi/WifiAnnotations.java b/framework/java/android/net/wifi/WifiAnnotations.java index 2a92c31361..7013b2b94d 100644 --- a/framework/java/android/net/wifi/WifiAnnotations.java +++ b/framework/java/android/net/wifi/WifiAnnotations.java @@ -176,4 +176,71 @@ public final class WifiAnnotations { UriParserResults.URI_SCHEME_DPP, }) public @interface UriScheme {} + + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"REASON_"}, value = { + DeauthenticationReasonCode.REASON_UNKNOWN, + DeauthenticationReasonCode.REASON_UNSPECIFIED, + DeauthenticationReasonCode.REASON_PREV_AUTH_NOT_VALID, + DeauthenticationReasonCode.REASON_DEAUTH_LEAVING, + DeauthenticationReasonCode.REASON_DISASSOC_DUE_TO_INACTIVITY, + DeauthenticationReasonCode.REASON_DISASSOC_AP_BUSY, + DeauthenticationReasonCode.REASON_CLASS2_FRAME_FROM_NONAUTH_STA, + DeauthenticationReasonCode.REASON_CLASS3_FRAME_FROM_NONASSOC_STA, + DeauthenticationReasonCode.REASON_DISASSOC_STA_HAS_LEFT, + DeauthenticationReasonCode.REASON_STA_REQ_ASSOC_WITHOUT_AUTH, + DeauthenticationReasonCode.REASON_PWR_CAPABILITY_NOT_VALID, + DeauthenticationReasonCode.REASON_SUPPORTED_CHANNEL_NOT_VALID, + DeauthenticationReasonCode.REASON_BSS_TRANSITION_DISASSOC, + DeauthenticationReasonCode.REASON_INVALID_IE, + DeauthenticationReasonCode.REASON_MICHAEL_MIC_FAILURE, + DeauthenticationReasonCode.REASON_FOURWAY_HANDSHAKE_TIMEOUT, + DeauthenticationReasonCode.REASON_GROUP_KEY_UPDATE_TIMEOUT, + DeauthenticationReasonCode.REASON_IE_IN_4WAY_DIFFERS, + DeauthenticationReasonCode.REASON_GROUP_CIPHER_NOT_VALID, + DeauthenticationReasonCode.REASON_PAIRWISE_CIPHER_NOT_VALID, + DeauthenticationReasonCode.REASON_AKMP_NOT_VALID, + DeauthenticationReasonCode.REASON_UNSUPPORTED_RSN_IE_VERSION, + DeauthenticationReasonCode.REASON_INVALID_RSN_IE_CAPAB, + DeauthenticationReasonCode.REASON_IEEE_802_1X_AUTH_FAILED, + DeauthenticationReasonCode.REASON_CIPHER_SUITE_REJECTED, + DeauthenticationReasonCode.REASON_TDLS_TEARDOWN_UNREACHABLE, + DeauthenticationReasonCode.REASON_TDLS_TEARDOWN_UNSPECIFIED, + DeauthenticationReasonCode.REASON_SSP_REQUESTED_DISASSOC, + DeauthenticationReasonCode.REASON_NO_SSP_ROAMING_AGREEMENT, + DeauthenticationReasonCode.REASON_BAD_CIPHER_OR_AKM, + DeauthenticationReasonCode.REASON_NOT_AUTHORIZED_THIS_LOCATION, + DeauthenticationReasonCode.REASON_SERVICE_CHANGE_PRECLUDES_TS, + DeauthenticationReasonCode.REASON_UNSPECIFIED_QOS_REASON, + DeauthenticationReasonCode.REASON_NOT_ENOUGH_BANDWIDTH, + DeauthenticationReasonCode.REASON_DISASSOC_LOW_ACK, + DeauthenticationReasonCode.REASON_EXCEEDED_TXOP, + DeauthenticationReasonCode.REASON_STA_LEAVING, + DeauthenticationReasonCode.REASON_END_TS_BA_DLS, + DeauthenticationReasonCode.REASON_UNKNOWN_TS_BA, + DeauthenticationReasonCode.REASON_TIMEOUT, + DeauthenticationReasonCode.REASON_PEERKEY_MISMATCH, + DeauthenticationReasonCode.REASON_AUTHORIZED_ACCESS_LIMIT_REACHED, + DeauthenticationReasonCode.REASON_EXTERNAL_SERVICE_REQUIREMENTS, + DeauthenticationReasonCode.REASON_INVALID_FT_ACTION_FRAME_COUNT, + DeauthenticationReasonCode.REASON_INVALID_PMKID, + DeauthenticationReasonCode.REASON_INVALID_MDE, + DeauthenticationReasonCode.REASON_INVALID_FTE, + DeauthenticationReasonCode.REASON_MESH_PEERING_CANCELLED, + DeauthenticationReasonCode.REASON_MESH_MAX_PEERS, + DeauthenticationReasonCode.REASON_MESH_CONFIG_POLICY_VIOLATION, + DeauthenticationReasonCode.REASON_MESH_CLOSE_RCVD, + DeauthenticationReasonCode.REASON_MESH_MAX_RETRIES, + DeauthenticationReasonCode.REASON_MESH_CONFIRM_TIMEOUT, + DeauthenticationReasonCode.REASON_MESH_INVALID_GTK, + DeauthenticationReasonCode.REASON_MESH_INCONSISTENT_PARAMS, + DeauthenticationReasonCode.REASON_MESH_INVALID_SECURITY_CAP, + DeauthenticationReasonCode.REASON_MESH_PATH_ERROR_NO_PROXY_INFO, + DeauthenticationReasonCode.REASON_MESH_PATH_ERROR_NO_FORWARDING_INFO, + DeauthenticationReasonCode.REASON_MESH_PATH_ERROR_DEST_UNREACHABLE, + DeauthenticationReasonCode.REASON_MAC_ADDRESS_ALREADY_EXISTS_IN_MBSS, + DeauthenticationReasonCode.REASON_MESH_CHANNEL_SWITCH_REGULATORY_REQ, + DeauthenticationReasonCode.REASON_MESH_CHANNEL_SWITCH_UNSPECIFIED, + }) + public @interface SoftApDisconnectReason {} } diff --git a/framework/java/android/net/wifi/WifiClient.java b/framework/java/android/net/wifi/WifiClient.java index c69b039e9e..cd3e8c51b9 100644 --- a/framework/java/android/net/wifi/WifiClient.java +++ b/framework/java/android/net/wifi/WifiClient.java @@ -16,6 +16,7 @@ package android.net.wifi; +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; @@ -24,6 +25,8 @@ import android.os.Parcel; import android.os.Parcelable; import android.util.Log; +import com.android.wifi.flags.Flags; + import java.util.Objects; /** @hide */ @@ -38,6 +41,15 @@ public final class WifiClient implements Parcelable { private final String mApInstanceIdentifier; /** + * Reason for disconnection, if known. + * + * <p>This field is only meaningful when a client disconnects. + * It will not be updated while a client is connected. + */ + @WifiAnnotations.SoftApDisconnectReason + private final int mDisconnectReason; + + /** * The mac address of this client. */ @NonNull @@ -58,13 +70,35 @@ public final class WifiClient implements Parcelable { return mApInstanceIdentifier; } + /** + * Get the reason the client disconnected from the AP. + * + * <p>This field is only populated when the WifiClient is returned via + * {@link WifiManager.SoftApCallback#onClientsDisconnected}. + * The value {@link DeauthenticationReasonCode#REASON_UNKNOWN} is used as the default value + * and in the case where a client connects. + * @return a disconnection reason code to provide information on why the disconnect happened. + */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + @WifiAnnotations.SoftApDisconnectReason + public int getDisconnectReason() { + return mDisconnectReason; + } + private WifiClient(Parcel in) { mMacAddress = in.readParcelable(null); mApInstanceIdentifier = in.readString(); + mDisconnectReason = in.readInt(); } /** @hide */ public WifiClient(@NonNull MacAddress macAddress, @NonNull String apInstanceIdentifier) { + this(macAddress, apInstanceIdentifier, DeauthenticationReasonCode.REASON_UNKNOWN); + } + + /** @hide */ + public WifiClient(@NonNull MacAddress macAddress, @NonNull String apInstanceIdentifier, + @WifiAnnotations.SoftApDisconnectReason int disconnectReason) { if (macAddress == null) { Log.wtf(TAG, "Null MacAddress provided"); this.mMacAddress = WifiManager.ALL_ZEROS_MAC_ADDRESS; @@ -72,6 +106,7 @@ public final class WifiClient implements Parcelable { this.mMacAddress = macAddress; } this.mApInstanceIdentifier = apInstanceIdentifier; + this.mDisconnectReason = disconnectReason; } @Override @@ -83,6 +118,7 @@ public final class WifiClient implements Parcelable { public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeParcelable(mMacAddress, flags); dest.writeString(mApInstanceIdentifier); + dest.writeInt(mDisconnectReason); } @NonNull @@ -102,20 +138,21 @@ public final class WifiClient implements Parcelable { return "WifiClient{" + "mMacAddress=" + mMacAddress + "mApInstanceIdentifier=" + mApInstanceIdentifier + + "mDisconnectReason=" + mDisconnectReason + '}'; } @Override public boolean equals(@Nullable Object o) { if (this == o) return true; - if (!(o instanceof WifiClient)) return false; - WifiClient client = (WifiClient) o; + if (!(o instanceof WifiClient client)) return false; return Objects.equals(mMacAddress, client.mMacAddress) - && mApInstanceIdentifier.equals(client.mApInstanceIdentifier); + && mApInstanceIdentifier.equals(client.mApInstanceIdentifier) + && mDisconnectReason == client.mDisconnectReason; } @Override public int hashCode() { - return Objects.hash(mMacAddress, mApInstanceIdentifier); + return Objects.hash(mMacAddress, mApInstanceIdentifier, mDisconnectReason); } } diff --git a/framework/java/android/net/wifi/WifiConfiguration.java b/framework/java/android/net/wifi/WifiConfiguration.java index 88582dde76..028756862c 100644 --- a/framework/java/android/net/wifi/WifiConfiguration.java +++ b/framework/java/android/net/wifi/WifiConfiguration.java @@ -45,6 +45,7 @@ import android.text.TextUtils; import android.util.Log; import android.util.SparseArray; +import androidx.annotation.Keep; import androidx.annotation.RequiresApi; import com.android.internal.annotations.VisibleForTesting; @@ -664,6 +665,7 @@ public class WifiConfiguration implements Parcelable { * * @hide */ + @Keep public void addSecurityParams(@SecurityType int securityType) { // This ensures that there won't be duplicate security types. for (SecurityParams params : mSecurityParamsList) { @@ -738,6 +740,7 @@ public class WifiConfiguration implements Parcelable { * If there is no security params, generate one according to legacy fields. * @hide */ + @Keep public void convertLegacyFieldsToSecurityParamsIfNeeded() { if (!mSecurityParamsList.isEmpty()) return; if (allowedKeyManagement.get(KeyMgmt.WAPI_CERT)) { @@ -886,6 +889,7 @@ public class WifiConfiguration implements Parcelable { * @return true if there is a security params matches the type. * @hide */ + @Keep public boolean isSecurityType(@SecurityType int securityType) { for (SecurityParams p : mSecurityParamsList) { if (p.isSecurityType(securityType)) { @@ -913,6 +917,7 @@ public class WifiConfiguration implements Parcelable { * @return the default security params. * @hide */ + @Keep public @NonNull SecurityParams getDefaultSecurityParams() { return new SecurityParams(mSecurityParamsList.get(0)); } @@ -2721,6 +2726,7 @@ public class WifiConfiguration implements Parcelable { } /** @hide */ + @Keep public boolean hasNeverDetectedCaptivePortal() { return mHasNeverDetectedCaptivePortal; } @@ -3943,6 +3949,7 @@ public class WifiConfiguration implements Parcelable { /** @hide */ @UnsupportedAppUsage + @Keep public void setStaticIpConfiguration(StaticIpConfiguration staticIpConfiguration) { mIpConfiguration.setStaticIpConfiguration(staticIpConfiguration); } @@ -3953,12 +3960,14 @@ public class WifiConfiguration implements Parcelable { */ @NonNull @UnsupportedAppUsage + @Keep public IpConfiguration.IpAssignment getIpAssignment() { return mIpConfiguration.getIpAssignment(); } /** @hide */ @UnsupportedAppUsage + @Keep public void setIpAssignment(IpConfiguration.IpAssignment ipAssignment) { mIpConfiguration.setIpAssignment(ipAssignment); } @@ -4466,6 +4475,7 @@ public class WifiConfiguration implements Parcelable { * @return true if preSharedKey is needed, false otherwise. * @hide */ + @Keep public boolean needsPreSharedKey() { for (SecurityParams params : mSecurityParamsList) { if (params.isSecurityType(SECURITY_TYPE_PSK) @@ -4526,6 +4536,7 @@ public class WifiConfiguration implements Parcelable { * @return true if preSharedKey encryption is needed, false otherwise. * @hide */ + @Keep public boolean hasPreSharedKeyChanged() { return mHasPreSharedKeyChanged; } diff --git a/framework/java/android/net/wifi/WifiFrameworkInitializer.java b/framework/java/android/net/wifi/WifiFrameworkInitializer.java index 8b59393769..e942d74f74 100644 --- a/framework/java/android/net/wifi/WifiFrameworkInitializer.java +++ b/framework/java/android/net/wifi/WifiFrameworkInitializer.java @@ -21,14 +21,14 @@ import android.content.Context; import android.content.pm.PackageManager; import android.net.wifi.aware.IWifiAwareManager; import android.net.wifi.aware.WifiAwareManager; +import android.net.wifi.flags.Flags; import android.net.wifi.p2p.IWifiP2pManager; import android.net.wifi.p2p.WifiP2pManager; import android.net.wifi.rtt.IWifiRttManager; import android.net.wifi.rtt.WifiRttManager; -import android.os.HandlerThread; -import android.os.Looper; - -import androidx.annotation.VisibleForTesting; +import android.net.wifi.usd.IUsdManager; +import android.net.wifi.usd.UsdManager; +import android.net.wifi.util.Environment; /** * Class for performing registration for all Wifi services. @@ -37,32 +37,6 @@ import androidx.annotation.VisibleForTesting; */ @SystemApi public class WifiFrameworkInitializer { - - /** - * A class implementing the lazy holder idiom: the unique static instance - * of {@link #INSTANCE} is instantiated in a thread-safe way (guaranteed by - * the language specs) the first time that NoPreloadHolder is referenced in getInstanceLooper(). - * - * This is necessary because we can't spawn a new thread in {@link #registerServiceWrappers()}. - * {@link #registerServiceWrappers()} is called during the Zygote phase, which disallows - * spawning new threads. Naming the class "NoPreloadHolder" ensures that the classloader will - * not preload this class, inadvertently spawning the thread too early. - */ - private static class NoPreloadHolder { - private static final HandlerThread INSTANCE = createInstance(); - - private static HandlerThread createInstance() { - HandlerThread thread = new HandlerThread("WifiManagerThread"); - thread.start(); - return thread; - } - } - /** @hide */ - @VisibleForTesting - public static Looper getInstanceLooper() { - return NoPreloadHolder.INSTANCE.getLooper(); - } - private WifiFrameworkInitializer() {} /** @@ -82,7 +56,7 @@ public class WifiFrameworkInitializer { return null; } IWifiManager service = IWifiManager.Stub.asInterface(serviceBinder); - return new WifiManager(context, service, getInstanceLooper()); + return new WifiManager(context, service); } ); SystemServiceRegistry.registerContextAwareService( @@ -118,7 +92,7 @@ public class WifiFrameworkInitializer { return null; } IWifiScanner service = IWifiScanner.Stub.asInterface(serviceBinder); - return new WifiScanner(context, service, getInstanceLooper()); + return new WifiScanner(context, service); } ); SystemServiceRegistry.registerContextAwareService( @@ -145,5 +119,19 @@ public class WifiFrameworkInitializer { return new RttManager(context, wifiRttManager); } ); + if (Flags.usd() && Environment.isSdkAtLeastB()) { + SystemServiceRegistry.registerContextAwareService( + Context.WIFI_USD_SERVICE, + UsdManager.class, (context, serviceBinder) -> { + if (!context.getResources().getBoolean( + context.getResources().getIdentifier("config_deviceSupportsWifiUsd", + "bool", "android"))) { + return null; + } + IUsdManager service = IUsdManager.Stub.asInterface(serviceBinder); + return new UsdManager(context, service); + } + ); + } } } diff --git a/framework/java/android/net/wifi/WifiInfo.java b/framework/java/android/net/wifi/WifiInfo.java index 8e233d74d6..2b34b7b80b 100644 --- a/framework/java/android/net/wifi/WifiInfo.java +++ b/framework/java/android/net/wifi/WifiInfo.java @@ -44,6 +44,7 @@ import android.telephony.SubscriptionManager; import android.text.TextUtils; import android.util.SparseArray; +import androidx.annotation.Keep; import androidx.annotation.RequiresApi; import com.android.modules.utils.build.SdkLevel; @@ -794,8 +795,9 @@ public class WifiInfo implements TransportInfo, Parcelable { * @return the SSID. */ public String getSSID() { - if (mWifiSsid != null) { - String ssidString = mWifiSsid.toString(); + WifiSsid ssid = mWifiSsid; + if (ssid != null) { + String ssidString = ssid.toString(); if (!TextUtils.isEmpty(ssidString)) { return ssidString; } @@ -1146,6 +1148,7 @@ public class WifiInfo implements TransportInfo, Parcelable { /** * @hide */ + @Keep public boolean is24GHz() { return ScanResult.is24GHz(mFrequency); } @@ -1153,6 +1156,7 @@ public class WifiInfo implements TransportInfo, Parcelable { /** * @hide */ + @Keep @UnsupportedAppUsage public boolean is5GHz() { return ScanResult.is5GHz(mFrequency); @@ -1161,6 +1165,7 @@ public class WifiInfo implements TransportInfo, Parcelable { /** * @hide */ + @Keep public boolean is6GHz() { return ScanResult.is6GHz(mFrequency); } @@ -1211,6 +1216,7 @@ public class WifiInfo implements TransportInfo, Parcelable { /** @hide */ @UnsupportedAppUsage + @Keep public boolean getMeteredHint() { return mMeteredHint; } @@ -1530,6 +1536,7 @@ public class WifiInfo implements TransportInfo, Parcelable { /** @hide */ @UnsupportedAppUsage @Nullable + @Keep public static String removeDoubleQuotes(@Nullable String string) { if (string == null) return null; final int length = string.length(); diff --git a/framework/java/android/net/wifi/WifiManager.java b/framework/java/android/net/wifi/WifiManager.java index 4ab897e730..0ebf06fa5d 100644 --- a/framework/java/android/net/wifi/WifiManager.java +++ b/framework/java/android/net/wifi/WifiManager.java @@ -46,6 +46,7 @@ import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; +import android.content.pm.PackageManager; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; import android.net.DhcpInfo; @@ -68,6 +69,7 @@ import android.net.wifi.p2p.WifiP2pManager; import android.net.wifi.twt.TwtRequest; import android.net.wifi.twt.TwtSession; import android.net.wifi.twt.TwtSessionCallback; +import android.net.wifi.util.Environment; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -79,6 +81,8 @@ import android.os.Parcelable; import android.os.RemoteException; import android.os.WorkSource; import android.os.connectivity.WifiActivityEnergyInfo; +import android.security.advancedprotection.AdvancedProtectionFeature; +import android.security.advancedprotection.AdvancedProtectionManager; import android.telephony.SubscriptionInfo; import android.text.TextUtils; import android.util.ArraySet; @@ -1130,6 +1134,17 @@ public class WifiManager { */ public static final String EXTRA_PREVIOUS_WIFI_STATE = "previous_wifi_state"; + /** @hide */ + @IntDef(flag = false, prefix = { "WIFI_STATE_" }, value = { + WIFI_STATE_DISABLING, + WIFI_STATE_DISABLED, + WIFI_STATE_ENABLING, + WIFI_STATE_ENABLED, + WIFI_STATE_UNKNOWN, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface WifiState {} + /** * Wi-Fi is currently being disabled. The state will change to {@link #WIFI_STATE_DISABLED} if * it finishes successfully. @@ -2108,6 +2123,8 @@ public class WifiManager { sOnWifiNetworkStateChangedListenerMap = new SparseArray<>(); private static final SparseArray<IWifiLowLatencyLockListener> sWifiLowLatencyLockListenerMap = new SparseArray<>(); + private static final SparseArray<IWifiStateChangedListener> + sWifiStateChangedListenerMap = new SparseArray<>(); /** * Multi-link operation (MLO) will allow Wi-Fi devices to operate on multiple links at the same @@ -2201,15 +2218,12 @@ public class WifiManager { * * @param context the application context * @param service the Binder interface - * @param looper the Looper used to deliver callbacks * @hide - hide this because it takes in a parameter of type IWifiManager, which * is a system private class. */ - public WifiManager(@NonNull Context context, @NonNull IWifiManager service, - @NonNull Looper looper) { + public WifiManager(@NonNull Context context, @NonNull IWifiManager service) { mContext = context; mService = service; - mLooper = looper; mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion; updateVerboseLoggingEnabledFromService(); } @@ -3852,221 +3866,238 @@ public class WifiManager { } /** @hide */ - public static final long WIFI_FEATURE_INFRA = 1L << 0; // Basic infrastructure mode + public static final int WIFI_FEATURE_INFRA = 0; // Basic infrastructure mode /** @hide */ - public static final long WIFI_FEATURE_PASSPOINT = 1L << 2; // Support for GAS/ANQP + public static final int WIFI_FEATURE_PASSPOINT = 2; // Support for GAS/ANQP /** @hide */ - public static final long WIFI_FEATURE_P2P = 1L << 3; // Wifi-Direct + public static final int WIFI_FEATURE_P2P = 3; // Wifi-Direct /** @hide */ - public static final long WIFI_FEATURE_MOBILE_HOTSPOT = 1L << 4; // Soft AP + public static final int WIFI_FEATURE_MOBILE_HOTSPOT = 4; // Soft AP /** @hide */ - public static final long WIFI_FEATURE_SCANNER = 1L << 5; // WifiScanner APIs + public static final int WIFI_FEATURE_SCANNER = 5; // WifiScanner APIs /** @hide */ - public static final long WIFI_FEATURE_AWARE = 1L << 6; // Wi-Fi Aware networking + public static final int WIFI_FEATURE_AWARE = 6; // Wi-Fi Aware networking /** @hide */ - public static final long WIFI_FEATURE_D2D_RTT = 1L << 7; // Device-to-device RTT + public static final int WIFI_FEATURE_D2D_RTT = 7; // Device-to-device RTT /** @hide */ - public static final long WIFI_FEATURE_D2AP_RTT = 1L << 8; // Device-to-AP RTT + public static final int WIFI_FEATURE_D2AP_RTT = 8; // Device-to-AP RTT /** @hide */ - public static final long WIFI_FEATURE_PNO = 1L << 10; // Preferred network offload + public static final int WIFI_FEATURE_PNO = 10; // Preferred network offload /** @hide */ - public static final long WIFI_FEATURE_TDLS = 1L << 12; // Tunnel directed link setup + public static final int WIFI_FEATURE_TDLS = 12; // Tunnel directed link setup /** @hide */ - public static final long WIFI_FEATURE_TDLS_OFFCHANNEL = 1L << 13; // TDLS off channel + public static final int WIFI_FEATURE_TDLS_OFFCHANNEL = 13; // TDLS off channel /** @hide */ - public static final long WIFI_FEATURE_AP_STA = 1L << 15; // AP STA Concurrency + public static final int WIFI_FEATURE_AP_STA = 15; // AP STA Concurrency /** @hide */ - public static final long WIFI_FEATURE_LINK_LAYER_STATS = 1L << 16; // Link layer stats + public static final int WIFI_FEATURE_LINK_LAYER_STATS = 16; // Link layer stats /** @hide */ - public static final long WIFI_FEATURE_LOGGER = 1L << 17; // WiFi Logger + public static final int WIFI_FEATURE_LOGGER = 17; // WiFi Logger /** @hide */ - public static final long WIFI_FEATURE_RSSI_MONITOR = 1L << 19; // RSSI Monitor + public static final int WIFI_FEATURE_RSSI_MONITOR = 19; // RSSI Monitor /** @hide */ - public static final long WIFI_FEATURE_MKEEP_ALIVE = 1L << 20; // mkeep_alive + public static final int WIFI_FEATURE_MKEEP_ALIVE = 20; // mkeep_alive /** @hide */ - public static final long WIFI_FEATURE_CONFIG_NDO = 1L << 21; // ND offload + public static final int WIFI_FEATURE_CONFIG_NDO = 21; // ND offload /** @hide */ - public static final long WIFI_FEATURE_CONTROL_ROAMING = 1L << 23; // Control firmware roaming + public static final int WIFI_FEATURE_CONTROL_ROAMING = 23; // Control firmware roaming /** @hide */ - public static final long WIFI_FEATURE_IE_WHITELIST = 1L << 24; // Probe IE white listing + public static final int WIFI_FEATURE_IE_WHITELIST = 24; // Probe IE white listing /** @hide */ - public static final long WIFI_FEATURE_SCAN_RAND = 1L << 25; // Random MAC & Probe seq + public static final int WIFI_FEATURE_SCAN_RAND = 25; // Random MAC & Probe seq /** @hide */ - public static final long WIFI_FEATURE_TX_POWER_LIMIT = 1L << 26; // Set Tx power limit + public static final int WIFI_FEATURE_TX_POWER_LIMIT = 26; // Set Tx power limit /** @hide */ - public static final long WIFI_FEATURE_WPA3_SAE = 1L << 27; // WPA3-Personal SAE + public static final int WIFI_FEATURE_WPA3_SAE = 27; // WPA3-Personal SAE /** @hide */ - public static final long WIFI_FEATURE_WPA3_SUITE_B = 1L << 28; // WPA3-Enterprise Suite-B + public static final int WIFI_FEATURE_WPA3_SUITE_B = 28; // WPA3-Enterprise Suite-B /** @hide */ - public static final long WIFI_FEATURE_OWE = 1L << 29; // Enhanced Open + public static final int WIFI_FEATURE_OWE = 29; // Enhanced Open /** @hide */ - public static final long WIFI_FEATURE_LOW_LATENCY = 1L << 30; // Low Latency modes + public static final int WIFI_FEATURE_LOW_LATENCY = 30; // Low Latency modes /** @hide */ - public static final long WIFI_FEATURE_DPP = 1L << 31; // DPP (Easy-Connect) + public static final int WIFI_FEATURE_DPP = 31; // DPP (Easy-Connect) /** @hide */ - public static final long WIFI_FEATURE_P2P_RAND_MAC = 1L << 32; // Random P2P MAC + public static final int WIFI_FEATURE_P2P_RAND_MAC = 32; // Random P2P MAC /** @hide */ - public static final long WIFI_FEATURE_CONNECTED_RAND_MAC = 1L << 33; // Random STA MAC + public static final int WIFI_FEATURE_CONNECTED_RAND_MAC = 33; // Random STA MAC /** @hide */ - public static final long WIFI_FEATURE_AP_RAND_MAC = 1L << 34; // Random AP MAC + public static final int WIFI_FEATURE_AP_RAND_MAC = 34; // Random AP MAC /** @hide */ - public static final long WIFI_FEATURE_MBO = 1L << 35; // MBO Support + public static final int WIFI_FEATURE_MBO = 35; // MBO Support /** @hide */ - public static final long WIFI_FEATURE_OCE = 1L << 36; // OCE Support + public static final int WIFI_FEATURE_OCE = 36; // OCE Support /** @hide */ - public static final long WIFI_FEATURE_WAPI = 1L << 37; // WAPI + public static final int WIFI_FEATURE_WAPI = 37; // WAPI /** @hide */ - public static final long WIFI_FEATURE_FILS_SHA256 = 1L << 38; // FILS-SHA256 + public static final int WIFI_FEATURE_FILS_SHA256 = 38; // FILS-SHA256 /** @hide */ - public static final long WIFI_FEATURE_FILS_SHA384 = 1L << 39; // FILS-SHA384 + public static final int WIFI_FEATURE_FILS_SHA384 = 39; // FILS-SHA384 /** @hide */ - public static final long WIFI_FEATURE_SAE_PK = 1L << 40; // SAE-PK + public static final int WIFI_FEATURE_SAE_PK = 40; // SAE-PK /** @hide */ - public static final long WIFI_FEATURE_STA_BRIDGED_AP = 1L << 41; // STA + Bridged AP + public static final int WIFI_FEATURE_STA_BRIDGED_AP = 41; // STA + Bridged AP /** @hide */ - public static final long WIFI_FEATURE_BRIDGED_AP = 1L << 42; // Bridged AP + public static final int WIFI_FEATURE_BRIDGED_AP = 42; // Bridged AP /** @hide */ - public static final long WIFI_FEATURE_INFRA_60G = 1L << 43; // 60 GHz Band Support + public static final int WIFI_FEATURE_INFRA_60G = 43; // 60 GHz Band Support /** * Support for 2 STA's for the local-only (peer to peer) connection + internet connection * concurrency. * @hide */ - public static final long WIFI_FEATURE_ADDITIONAL_STA_LOCAL_ONLY = 1L << 44; + public static final int WIFI_FEATURE_ADDITIONAL_STA_LOCAL_ONLY = 44; /** * Support for 2 STA's for the make before break concurrency. * @hide */ - public static final long WIFI_FEATURE_ADDITIONAL_STA_MBB = 1L << 45; + public static final int WIFI_FEATURE_ADDITIONAL_STA_MBB = 45; /** * Support for 2 STA's for the restricted connection + internet connection concurrency. * @hide */ - public static final long WIFI_FEATURE_ADDITIONAL_STA_RESTRICTED = 1L << 46; + public static final int WIFI_FEATURE_ADDITIONAL_STA_RESTRICTED = 46; /** * DPP (Easy-Connect) Enrollee Responder mode support * @hide */ - public static final long WIFI_FEATURE_DPP_ENROLLEE_RESPONDER = 1L << 47; + public static final int WIFI_FEATURE_DPP_ENROLLEE_RESPONDER = 47; /** * Passpoint Terms and Conditions feature support * @hide */ - public static final long WIFI_FEATURE_PASSPOINT_TERMS_AND_CONDITIONS = 1L << 48; + public static final int WIFI_FEATURE_PASSPOINT_TERMS_AND_CONDITIONS = 48; /** @hide */ - public static final long WIFI_FEATURE_SAE_H2E = 1L << 49; // Hash-to-Element + public static final int WIFI_FEATURE_SAE_H2E = 49; // Hash-to-Element /** @hide */ - public static final long WIFI_FEATURE_WFD_R2 = 1L << 50; // Wi-Fi Display R2 + public static final int WIFI_FEATURE_WFD_R2 = 50; // Wi-Fi Display R2 /** * RFC 7542 decorated identity support * @hide */ - public static final long WIFI_FEATURE_DECORATED_IDENTITY = 1L << 51; + public static final int WIFI_FEATURE_DECORATED_IDENTITY = 51; /** * Trust On First Use support for WPA Enterprise network * @hide */ - public static final long WIFI_FEATURE_TRUST_ON_FIRST_USE = 1L << 52; + public static final int WIFI_FEATURE_TRUST_ON_FIRST_USE = 52; /** * Support for 2 STA's multi internet concurrency. * @hide */ - public static final long WIFI_FEATURE_ADDITIONAL_STA_MULTI_INTERNET = 1L << 53; + public static final int WIFI_FEATURE_ADDITIONAL_STA_MULTI_INTERNET = 53; /** * Support for DPP (Easy-Connect) AKM. * @hide */ - public static final long WIFI_FEATURE_DPP_AKM = 1L << 54; + public static final int WIFI_FEATURE_DPP_AKM = 54; /** * Support for setting TLS minimum version. * @hide */ - public static final long WIFI_FEATURE_SET_TLS_MINIMUM_VERSION = 1L << 55; + public static final int WIFI_FEATURE_SET_TLS_MINIMUM_VERSION = 55; /** * Support for TLS v.13. * @hide */ - public static final long WIFI_FEATURE_TLS_V1_3 = 1L << 56; + public static final int WIFI_FEATURE_TLS_V1_3 = 56; /** * Support for Dual Band Simultaneous (DBS) operation. * @hide */ - public static final long WIFI_FEATURE_DUAL_BAND_SIMULTANEOUS = 1L << 57; + public static final int WIFI_FEATURE_DUAL_BAND_SIMULTANEOUS = 57; /** * Support for TID-To-Link Mapping negotiation. * @hide */ - public static final long WIFI_FEATURE_T2LM_NEGOTIATION = 1L << 58; + public static final int WIFI_FEATURE_T2LM_NEGOTIATION = 58; /** * Support for WEP Wi-Fi Network * @hide */ - public static final long WIFI_FEATURE_WEP = 1L << 59; + public static final int WIFI_FEATURE_WEP = 59; /** * Support for WPA PERSONAL Wi-Fi Network * @hide */ - public static final long WIFI_FEATURE_WPA_PERSONAL = 1L << 60; + public static final int WIFI_FEATURE_WPA_PERSONAL = 60; /** * Support for Roaming Mode * @hide */ - public static final long WIFI_FEATURE_AGGRESSIVE_ROAMING_MODE_SUPPORT = 1L << 61; + public static final int WIFI_FEATURE_AGGRESSIVE_ROAMING_MODE_SUPPORT = 61; /** * Supports device-to-device connections when infra STA is disabled. * @hide */ - public static final long WIFI_FEATURE_D2D_WHEN_INFRA_STA_DISABLED = 1L << 62; + public static final int WIFI_FEATURE_D2D_WHEN_INFRA_STA_DISABLED = 62; + + /** + * Support for Soft AP multi-links operation. + * @hide + */ + public static final int WIFI_FEATURE_SOFTAP_MLO = 63; - private long getSupportedFeatures() { + /** + * Supports multiple Wi-Fi 7 multi-link devices (MLD) on SoftAp. + * @hide + */ + public static final int WIFI_FEATURE_MULTIPLE_MLD_ON_SAP = 64; + + /** + * NOTE: When adding a new WIFI_FEATURE_ value, also be sure to update + * {@link com.android.server.wifi.util.FeatureBitsetUtils} + */ + + private boolean isFeatureSupported(int feature) { try { - return mService.getSupportedFeatures(); + return mService.isFeatureSupported(feature); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } - private boolean isFeatureSupported(long feature) { - return (getSupportedFeatures() & feature) == feature; - } - /** * @return true if this adapter supports Passpoint * @hide */ public boolean isPasspointSupported() { - return isFeatureSupported(WIFI_FEATURE_PASSPOINT); + // Both OEM and chip support are required + return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_PASSPOINT) + && isFeatureSupported(WIFI_FEATURE_PASSPOINT); } /** * @return true if this adapter supports WifiP2pManager (Wi-Fi Direct) */ public boolean isP2pSupported() { - return isFeatureSupported(WIFI_FEATURE_P2P); + // Both OEM and chip support are required + return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT) + && isFeatureSupported(WIFI_FEATURE_P2P); } /** @@ -4092,7 +4123,9 @@ public class WifiManager { * @hide */ public boolean isWifiAwareSupported() { - return isFeatureSupported(WIFI_FEATURE_AWARE); + // Both OEM and chip support are required + return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE) + && isFeatureSupported(WIFI_FEATURE_AWARE); } /** @@ -5372,6 +5405,107 @@ public class WifiManager { } /** + * Register a callback for Wi-Fi state. See {@link WifiStateChangedListener}. + * Caller will receive the event when the Wi-Fi state changes. + * Caller can remove a previously registered callback using + * {@link WifiManager#removeWifiStateChangedListener(WifiStateChangedListener)} + * + * @param executor Executor to execute listener callback on + * @param listener Listener to register + */ + @FlaggedApi(Flags.FLAG_WIFI_STATE_CHANGED_LISTENER) + @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) + public void addWifiStateChangedListener(@NonNull @CallbackExecutor Executor executor, + @NonNull WifiStateChangedListener listener) { + Objects.requireNonNull(executor); + Objects.requireNonNull(listener); + if (mVerboseLoggingEnabled) { + Log.d(TAG, "addWifiStateChangedListener: listener=" + listener + + ", executor=" + executor); + } + final int listenerIdentifier = System.identityHashCode(listener); + synchronized (sWifiStateChangedListenerMap) { + try { + if (sWifiStateChangedListenerMap.contains(listenerIdentifier)) { + Log.w(TAG, "Same listener already registered"); + return; + } + IWifiStateChangedListener.Stub listenerProxy = + new WifiStateChangedListenerProxy(executor, listener); + sWifiStateChangedListenerMap.put(listenerIdentifier, listenerProxy); + mService.addWifiStateChangedListener(listenerProxy); + } catch (RemoteException e) { + sWifiStateChangedListenerMap.remove(listenerIdentifier); + throw e.rethrowFromSystemServer(); + } + } + } + + /** + * Unregisters a WifiStateChangedListener from listening on the current Wi-Fi state. + * + * @param listener WifiStateChangedListener to unregister + */ + @FlaggedApi(Flags.FLAG_WIFI_STATE_CHANGED_LISTENER) + @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) + public void removeWifiStateChangedListener(@NonNull WifiStateChangedListener listener) { + Objects.requireNonNull(listener); + if (mVerboseLoggingEnabled) { + Log.d(TAG, "removeWifiStateChangedListener: listener=" + listener); + } + final int listenerIdentifier = System.identityHashCode(listener); + synchronized (sWifiStateChangedListenerMap) { + try { + if (!sWifiStateChangedListenerMap.contains(listenerIdentifier)) { + Log.w(TAG, "Unknown external listener " + listenerIdentifier); + return; + } + mService.removeWifiStateChangedListener( + sWifiStateChangedListenerMap.get(listenerIdentifier)); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } finally { + sWifiStateChangedListenerMap.remove(listenerIdentifier); + } + } + } + + /** + * Listener interface for applications to be notified when the Wi-Fi enabled state changes. + */ + @FlaggedApi(Flags.FLAG_WIFI_STATE_CHANGED_LISTENER) + public interface WifiStateChangedListener { + /** + * Called when the Wi-Fi enabled state changes. + * The new value can be queried via {@link WifiManager#getWifiState()}. + */ + void onWifiStateChanged(); + } + + /** + * Listener proxy for WifiStateChangedListener objects. + */ + private static class WifiStateChangedListenerProxy extends IWifiStateChangedListener.Stub { + private Executor mExecutor; + private WifiStateChangedListener mListener; + + WifiStateChangedListenerProxy(@NonNull Executor executor, + @NonNull WifiStateChangedListener listener) { + Objects.requireNonNull(executor); + Objects.requireNonNull(listener); + mExecutor = executor; + mListener = listener; + } + + @Override + public void onWifiStateChanged() { + Log.i(TAG, "WifiStateChangedListenerProxy: onWifiStateChanged"); + Binder.clearCallingIdentity(); + mExecutor.execute(() -> mListener.onWifiStateChanged()); + } + } + + /** * Calculates the level of the signal. This should be used any time a signal * is being shown. * @@ -5864,9 +5998,37 @@ public class WifiManager { public void startLocalOnlyHotspot(LocalOnlyHotspotCallback callback, @Nullable Handler handler) { Executor executor = handler == null ? null : new HandlerExecutor(handler); - startLocalOnlyHotspotInternal(null, executor, callback); + startLocalOnlyHotspotInternal(null, executor, callback, false); } + /** + * Starts a local-only hotspot with a specific configuration applied. See + * {@link #startLocalOnlyHotspot(LocalOnlyHotspotCallback, Handler)}. + * + * Since custom configuration settings may be incompatible with each other, the hotspot started + * through this method cannot coexist with another hotspot created through + * {@link #startLocalOnlyHotspot(LocalOnlyHotspotCallback, Handler)}. If this is attempted, + * the first hotspot request wins and others receive + * {@link LocalOnlyHotspotCallback#ERROR_GENERIC} through + * {@link LocalOnlyHotspotCallback#onFailed}. + * + * @param config Custom configuration for the hotspot. See {@link SoftApConfiguration}. + * @param executor Executor to run callback methods on. + * @param callback LocalOnlyHotspotCallback for the application to receive updates about + * operating status. + */ + @RequiresPermission(allOf = {CHANGE_WIFI_STATE, NEARBY_WIFI_DEVICES}) + @FlaggedApi(Flags.FLAG_PUBLIC_BANDS_FOR_LOHS) + public void startLocalOnlyHotspotWithConfiguration(@NonNull SoftApConfiguration config, + @NonNull @CallbackExecutor Executor executor, + @NonNull LocalOnlyHotspotCallback callback) { + Objects.requireNonNull(config); + Objects.requireNonNull(executor); + Objects.requireNonNull(callback); + startLocalOnlyHotspotInternal(config, executor, callback, false); + } + + /** * Starts a local-only hotspot with a specific configuration applied. See * {@link #startLocalOnlyHotspot(LocalOnlyHotspotCallback, Handler)}. @@ -5895,7 +6057,7 @@ public class WifiManager { @Nullable @CallbackExecutor Executor executor, @Nullable LocalOnlyHotspotCallback callback) { Objects.requireNonNull(config); - startLocalOnlyHotspotInternal(config, executor, callback); + startLocalOnlyHotspotInternal(config, executor, callback, true); } /** @@ -5909,7 +6071,8 @@ public class WifiManager { private void startLocalOnlyHotspotInternal( @Nullable SoftApConfiguration config, @Nullable @CallbackExecutor Executor executor, - @Nullable LocalOnlyHotspotCallback callback) { + @Nullable LocalOnlyHotspotCallback callback, + boolean isCalledFromSystemApi) { if (executor == null) { executor = mContext.getMainExecutor(); } @@ -5925,7 +6088,7 @@ public class WifiManager { mContext.getAttributionSource()); } int returnCode = mService.startLocalOnlyHotspot(proxy, packageName, featureId, - config, extras); + config, extras, isCalledFromSystemApi); if (returnCode != LocalOnlyHotspotCallback.REQUEST_REGISTERED) { // Send message to the proxy to make sure we call back on the correct thread proxy.onHotspotFailed(returnCode); @@ -6772,6 +6935,17 @@ public class WifiManager { @SapClientBlockedReason int blockedReason) { // Do nothing: can be used to ask user to update client to allowed list or blocked list. } + + /** + * Called when clients disconnect from a soft AP instance. + * + * @param info The {@link SoftApInfo} of the AP. + * @param clients The clients that have disconnected from the AP instance specified by + * {@code info}. + */ + @FlaggedApi(Flags.FLAG_SOFTAP_DISCONNECT_REASON) + default void onClientsDisconnected(@NonNull SoftApInfo info, + @NonNull List<WifiClient> clients) {} } /** @@ -6949,6 +7123,18 @@ public class WifiManager { mCallback.onBlockedClientConnecting(client, blockedReason); }); } + + @Override + public void onClientsDisconnected(SoftApInfo info, List<WifiClient> clients) { + if (mVerboseLoggingEnabled) { + Log.v(TAG, "SoftApCallbackProxy on mode " + mIpMode + + ", onClientsDisconnected: info =" + info + + " with clients = " + clients); + } + + Binder.clearCallingIdentity(); + mExecutor.execute(() -> mCallback.onClientsDisconnected(info, clients)); + } } /** @@ -7384,7 +7570,7 @@ public class WifiManager { @Nullable ActionListener listener) { ActionListenerProxy listenerProxy = null; if (listener != null) { - listenerProxy = new ActionListenerProxy("connect", mLooper, listener); + listenerProxy = new ActionListenerProxy("connect", mContext.getMainLooper(), listener); } try { Bundle extras = new Bundle(); @@ -7533,7 +7719,7 @@ public class WifiManager { if (config == null) throw new IllegalArgumentException("config cannot be null"); ActionListenerProxy listenerProxy = null; if (listener != null) { - listenerProxy = new ActionListenerProxy("save", mLooper, listener); + listenerProxy = new ActionListenerProxy("save", mContext.getMainLooper(), listener); } try { mService.save(config, listenerProxy, mContext.getOpPackageName()); @@ -7571,7 +7757,7 @@ public class WifiManager { if (netId < 0) throw new IllegalArgumentException("Network id cannot be negative"); ActionListenerProxy listenerProxy = null; if (listener != null) { - listenerProxy = new ActionListenerProxy("forget", mLooper, listener); + listenerProxy = new ActionListenerProxy("forget", mContext.getMainLooper(), listener); } try { mService.forget(netId, listenerProxy); @@ -8282,7 +8468,7 @@ public class WifiManager { mService.acquireMulticastLock(mBinder, mTag); synchronized (WifiManager.this) { if (mActiveLockCount >= MAX_ACTIVE_LOCKS) { - mService.releaseMulticastLock(mTag); + mService.releaseMulticastLock(mBinder, mTag); throw new UnsupportedOperationException( "Exceeded maximum number of wifi locks"); } @@ -8324,7 +8510,7 @@ public class WifiManager { synchronized (mBinder) { if (mRefCounted ? (--mRefCount == 0) : (mHeld)) { try { - mService.releaseMulticastLock(mTag); + mService.releaseMulticastLock(mBinder, mTag); synchronized (WifiManager.this) { mActiveLockCount--; } @@ -10157,6 +10343,46 @@ public class WifiManager { } /** + * If isFullCapture is true, capture everything in ring buffer + * + * If isFullCapture is false, extract WifiUsabilityStatsEntries from ring buffer whose + * timestamps are within [triggerStartTimeMillis, triggerStopTimeMillis) in WiFiMetrics, and + * store them as upload candidates. + * + * Source of elapsed time since boot will be android.os.SystemClock.elapsedRealtime() + * + * @param executor The executor on which callback will be invoked. + * @param resultsCallback An asynchronous callback that will return a execution result of + * mWifiMetrics.storeCapturedData + * @param triggerType data capture trigger type + * @param isFullCapture if we do full capture on ring buffer or not + * @param triggerStartTimeMillis data capture start timestamp, elapsed time since boot + * @param triggerStopTimeMillis data capture stop timestamp, elapsed time since boot + * @hide + */ + @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) + public void storeCapturedData(@NonNull @CallbackExecutor Executor executor, + @NonNull IntConsumer resultsCallback, int triggerType, boolean isFullCapture, + long triggerStartTimeMillis, long triggerStopTimeMillis) { + Objects.requireNonNull(executor, "executor cannot be null"); + Objects.requireNonNull(resultsCallback, "resultsCallback cannot be null"); + try { + mService.storeCapturedData(triggerType, isFullCapture, triggerStartTimeMillis, + triggerStopTimeMillis, new IIntegerListener.Stub() { + @Override + public void onResult(int value) { + Binder.clearCallingIdentity(); + executor.execute(() -> { + resultsCallback.accept(value); + }); + } + }); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Callback interface for framework to receive network status updates and trigger of updating * {@link WifiUsabilityStatsEntry}. * @@ -12868,4 +13094,214 @@ public class WifiManager { throw e.rethrowFromSystemServer(); } } + + /** + * Disallow Wi-Fi autojoin on ScanResults matching the selected security types. + * This does not restrict manual connections. + * + * @param restrictions An array of {@code WifiInfo.SECURITY_TYPE_*} values to disallow autojoin. + * An empty array will clear all restrictions. Note, certain combinations of + * restricted security types are not valid. + * 1. restrictions contains WifiInfo.SECURITY_TYPE_OWE, + * but not WifiInfo.SECURITY_TYPE_OPEN. + * 2. restrictions contains WifiInfo.SECURITY_TYPE_SAE, + * but not WifiInfo.SECURITY_TYPE_PSK. + * 3. restrictions contains WifiInfo.SECURITY_TYPE_EAP_WPA3_ENTERPRISE, + * but not WifiInfo.SECURITY_TYPE_EAP. + * + * Usage example: + * <pre> + * To disallow autojoin to Wi-Fi networks with security type, OPEN, WEP + * or OWE, use following argument. + * + * {@code + * int[] restrictions = { + * WifiInfo.SECURITY_TYPE_OPEN, + * WifiInfo.SECURITY_TYPE_WEP, + * WifiInfo.SECURITY_TYPE_OWE }; + * wifiManager.setAutojoinDisallowedSecurityTypes(restrictions); + * } + * + * To clear autojoin restriction on all security types, use following + * argument. + * + * {@code + * wifiManager.setAutojoinDisallowedSecurityTypes(new int[0]); + * } + * </pre> + * @throws UnsupportedOperationException if the API is not supported. + * @hide + */ + @SystemApi + @FlaggedApi(Flags.FLAG_AUTOJOIN_RESTRICTION_SECURITY_TYPES_API) + @RequiresApi(Build.VERSION_CODES.TIRAMISU) + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + MANAGE_WIFI_NETWORK_SELECTION + }) + public void setAutojoinDisallowedSecurityTypes( + @NonNull @WifiAnnotations.SecurityType int[] restrictions) { + if (!SdkLevel.isAtLeastT()) { + throw new UnsupportedOperationException(); + } + Objects.requireNonNull(restrictions, "restrictions cannot be null"); + try { + Bundle extras = new Bundle(); + extras.putParcelable(EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE, + mContext.getAttributionSource()); + int restrictionBitmap = 0; + for (int securityType : restrictions) { + restrictionBitmap |= 0x1 << securityType; + } + mService.setAutojoinDisallowedSecurityTypes(restrictionBitmap, extras); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Retrieves the autojoin disallowed Wi-Fi security types currently set for the device. + * + * @param executor The executor to run the callback on. + * @param resultsCallback The callback to receive the result. It will be called with an array + * of autojoin disallowedse security types from + * {@code WifiInfo.SECURITY_TYPE_*}. + * @throws UnsupportedOperationException if the API is not supported. + * @hide + */ + @SystemApi + @FlaggedApi(Flags.FLAG_AUTOJOIN_RESTRICTION_SECURITY_TYPES_API) + @RequiresApi(Build.VERSION_CODES.TIRAMISU) + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + MANAGE_WIFI_NETWORK_SELECTION + }) + public void getAutojoinDisallowedSecurityTypes(@NonNull @CallbackExecutor Executor executor, + @NonNull Consumer<int[]> resultsCallback) { + if (!SdkLevel.isAtLeastT()) { + throw new UnsupportedOperationException(); + } + Objects.requireNonNull(executor, "executor cannot be null"); + Objects.requireNonNull(resultsCallback, "resultsCallback cannot be null"); + try { + Bundle extras = new Bundle(); + extras.putParcelable(EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE, + mContext.getAttributionSource()); + mService.getAutojoinDisallowedSecurityTypes(new IIntegerListener.Stub() { + @Override + public void onResult(int value) { + Binder.clearCallingIdentity(); + executor.execute(() -> { + List<Integer> restrictions = new ArrayList<>(); + for (int i = 0; i < Integer.SIZE; i++) { + if (((0x1 << i) & value) != 0) { + restrictions.add(i); + } + } + int[] results = new int[restrictions.size()]; + for (int i = 0; i < restrictions.size(); i++) { + results[i] = restrictions.get(i); + } + resultsCallback.accept(results); + }); + } + }, extras); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Indicates what {@link AdvancedProtectionFeature} are supported over Wi-Fi. + * + * The {@link AdvancedProtectionFeature} is the advanced protection feature + * providing protections which works when Android Advanced Protection Mode (AAPM) + * is enabled. + * + * @return a list of the supported features. + * @hide + */ + @SystemApi + @FlaggedApi(android.security.Flags.FLAG_AAPM_API) + @RequiresApi(Build.VERSION_CODES.BAKLAVA) + @NonNull + @SuppressLint("NewApi") + public List<AdvancedProtectionFeature> getAvailableAdvancedProtectionFeatures() { + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + List<AdvancedProtectionFeature> features = new ArrayList<>(); + if (Flags.wepDisabledInApm() && android.security.Flags.aapmApi()) { + features.add(new AdvancedProtectionFeature( + AdvancedProtectionManager.FEATURE_ID_DISALLOW_WEP)); + } + return features; + } + + /** + * When the device is connected to a network suggested by calling app + * {@link #addNetworkSuggestions(List)}, this API provide a way to avoid the current connection + * without {@link #removeNetworkSuggestions(List)}. The disallowed network will be disconnected + * or roam to other networks. + * App can only use this API to control the current connected network + * which was suggested by this app. + * + * @param blockingOption Option to change for the network blocking {@link BlockingOption} + */ + @FlaggedApi(Flags.FLAG_BSSID_BLOCKLIST_FOR_SUGGESTION) + @RequiresPermission(CHANGE_WIFI_STATE) + public void disallowCurrentSuggestedNetwork(@NonNull BlockingOption blockingOption) { + Objects.requireNonNull(blockingOption, "blockingOption cannot be null"); + try { + mService.disallowCurrentSuggestedNetwork(blockingOption, mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Return whether Unsynchronized Service Discovery (USD) subscriber is supported or not. + * @hide + */ + @android.annotation.RequiresApi(Build.VERSION_CODES.BAKLAVA) + @SystemApi + @FlaggedApi(android.net.wifi.flags.Flags.FLAG_USD) + @RequiresPermission(MANAGE_WIFI_NETWORK_SELECTION) + public boolean isUsdSubscriberSupported() { + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + try { + return mService.isUsdSubscriberSupported(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Return whether Unsynchronized Service Discovery (USD) publisher is supported or not. + * <p> + * The USD publisher support is controlled by an overlay config_wifiUsdPublisherSupported. + * By default, the feature will be disabled because the publisher operation impacts other + * concurrency operation such as Station. The USD publisher switches channels and dwells a + * longer time (500 milliseconds to 1 second) on non-home channel which disrupts other + * concurrency operation. + * + * @return true if publisher feature is supported, otherwise false. + * @hide + */ + @android.annotation.RequiresApi(Build.VERSION_CODES.BAKLAVA) + @SystemApi + @FlaggedApi(android.net.wifi.flags.Flags.FLAG_USD) + @RequiresPermission(MANAGE_WIFI_NETWORK_SELECTION) + public boolean isUsdPublisherSupported() { + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + try { + return mService.isUsdPublisherSupported(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } diff --git a/framework/java/android/net/wifi/WifiNetworkSpecifier.java b/framework/java/android/net/wifi/WifiNetworkSpecifier.java index 199969d9c7..e00e7e6b7b 100644 --- a/framework/java/android/net/wifi/WifiNetworkSpecifier.java +++ b/framework/java/android/net/wifi/WifiNetworkSpecifier.java @@ -192,6 +192,8 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc private int[] mChannels; + private boolean mPreferSecondarySta; + public Builder() { mSsidPatternMatcher = null; mBssidPatternMatcher = null; @@ -480,6 +482,19 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc return this; } + /** + * Hint the Wifi service to prefer using secondary STA for this connection. + * + * @param value - true to prefer this connection to be started on a secondary STA. + * false to let the wifi framework decide + * @return Instance of {@link Builder} to enable chaining of the builder method. + * @hide + */ + @NonNull public Builder setPreferSecondarySta(boolean value) { + mPreferSecondarySta = value; + return this; + } + private void setSecurityParamsInWifiConfiguration( @NonNull WifiConfiguration configuration) { if (!TextUtils.isEmpty(mWpa2PskPassphrase)) { // WPA-PSK network. @@ -684,7 +699,8 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc mBssidPatternMatcher, mBand, buildWifiConfiguration(), - mChannels); + mChannels, + mPreferSecondarySta); } } @@ -707,6 +723,7 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc @WifiBand private final int mBand; private final int[] mChannelFreqs; + private boolean mPreferSecondarySta; /** * Security credentials for the network. @@ -729,7 +746,8 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc @NonNull Pair<MacAddress, MacAddress> bssidPatternMatcher, @WifiBand int band, @NonNull WifiConfiguration wifiConfiguration, - @NonNull int[] channelFreqs) { + @NonNull int[] channelFreqs, + boolean preferSecondarySta) { checkNotNull(ssidPatternMatcher); checkNotNull(bssidPatternMatcher); checkNotNull(wifiConfiguration); @@ -739,6 +757,7 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc this.mBand = band; this.wifiConfiguration = wifiConfiguration; this.mChannelFreqs = channelFreqs; + this.mPreferSecondarySta = preferSecondarySta; } /** @@ -756,6 +775,14 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc return mChannelFreqs.clone(); } + /** + * @see Builder#setPreferSecondarySta(boolean) + * @hide + */ + public boolean isPreferSecondarySta() { + return mPreferSecondarySta; + } + public static final @NonNull Creator<WifiNetworkSpecifier> CREATOR = new Creator<WifiNetworkSpecifier>() { @Override @@ -767,9 +794,10 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc Pair.create(baseAddress, mask); int band = in.readInt(); WifiConfiguration wifiConfiguration = in.readParcelable(null); - int[] mChannels = in.createIntArray(); + int[] channels = in.createIntArray(); + boolean preferSecondarySta = in.readBoolean(); return new WifiNetworkSpecifier(ssidPatternMatcher, bssidPatternMatcher, band, - wifiConfiguration, mChannels); + wifiConfiguration, channels, preferSecondarySta); } @Override @@ -791,13 +819,15 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc dest.writeInt(mBand); dest.writeParcelable(wifiConfiguration, flags); dest.writeIntArray(mChannelFreqs); + dest.writeBoolean(mPreferSecondarySta); } @Override public int hashCode() { return Objects.hash( ssidPatternMatcher.getPath(), ssidPatternMatcher.getType(), bssidPatternMatcher, - mBand, wifiConfiguration.allowedKeyManagement, Arrays.hashCode(mChannelFreqs)); + mBand, wifiConfiguration.allowedKeyManagement, Arrays.hashCode(mChannelFreqs), + mPreferSecondarySta); } @Override @@ -818,7 +848,8 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc && this.mBand == lhs.mBand && Objects.equals(this.wifiConfiguration.allowedKeyManagement, lhs.wifiConfiguration.allowedKeyManagement) - && Arrays.equals(mChannelFreqs, lhs.mChannelFreqs); + && Arrays.equals(mChannelFreqs, lhs.mChannelFreqs) + && mPreferSecondarySta == lhs.mPreferSecondarySta; } @Override @@ -831,6 +862,7 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc .append(", BSSID=").append(wifiConfiguration.BSSID) .append(", channels=").append(Arrays.toString(mChannelFreqs)) .append(", band=").append(mBand) + .append(", preferSecondarySta=").append(mPreferSecondarySta) .append("]") .toString(); } diff --git a/framework/java/android/net/wifi/WifiScanner.java b/framework/java/android/net/wifi/WifiScanner.java index 1dffa8a3c7..fd9676e0e4 100644 --- a/framework/java/android/net/wifi/WifiScanner.java +++ b/framework/java/android/net/wifi/WifiScanner.java @@ -34,7 +34,6 @@ import android.content.Context; import android.os.Binder; import android.os.Build; import android.os.Bundle; -import android.os.Looper; import android.os.Parcel; import android.os.Parcelable; import android.os.Process; @@ -2022,12 +2021,9 @@ public class WifiScanner { * * @param context the application context * @param service the Binder interface for {@link Context#WIFI_SCANNING_SERVICE} - * @param looper the Looper used to deliver callbacks - * * @hide */ - public WifiScanner(@NonNull Context context, @NonNull IWifiScanner service, - @NonNull Looper looper) { + public WifiScanner(@NonNull Context context, @NonNull IWifiScanner service) { mContext = context; mService = service; } diff --git a/framework/java/android/net/wifi/WifiUsabilityStatsEntry.java b/framework/java/android/net/wifi/WifiUsabilityStatsEntry.java index 435bf4fcfe..0b86c1d4e3 100644 --- a/framework/java/android/net/wifi/WifiUsabilityStatsEntry.java +++ b/framework/java/android/net/wifi/WifiUsabilityStatsEntry.java @@ -152,6 +152,42 @@ public final class WifiUsabilityStatsEntry implements Parcelable { /** @see #getTimeSliceDutyCycleInPercent() */ private final int mTimeSliceDutyCycleInPercent; + private final int mWifiLinkCount; + /** Refer to WifiManager.MloMode */ + private @WifiManager.MloMode int mMloMode; + /** The number of tx bytes transmitted on current interface */ + private final long mTxTransmittedBytes; + /** The number of rx bytes transmitted on current interface */ + private final long mRxTransmittedBytes; + /** The total number of LABEL_BAD event happens */ + private final int mLabelBadEventCount; + /** The current WiFi state in framework */ + private final int mWifiFrameworkState; + /** Downstream throughput estimation provided by Network Capabilities */ + private final int mIsNetworkCapabilitiesDownstreamSufficient; + /** Upstream throughput estimation provided by Network Capabilities */ + private final int mIsNetworkCapabilitiesUpstreamSufficient; + /** Downstream throughput estimation used in Network Selection */ + private final int mIsThroughputPredictorDownstreamSufficient; + /** Upstream throughput estimation used in Network Selection */ + private final int mIsThroughputPredictorUpstreamSufficient; + /** If bluetooth is connected */ + private final boolean mIsBluetoothConnected; + /** UWB Adapter state */ + private final int mUwbAdapterState; + /** Low Latency mode state */ + private final boolean mIsLowLatencyActivated; + /** Maximum supported tx link speed in Mbps */ + private final int mMaxSupportedTxLinkSpeed; + /** Maximum supported rx link speed in Mbps */ + private final int mMaxSupportedRxLinkSpeed; + /** WiFi Voip mode state */ + private final int mVoipMode; + /** Thread device role */ + private final int mThreadDeviceRole; + /** Data stall status */ + private final int mStatusDataStall; + /** {@hide} */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = {"WME_ACCESS_CATEGORY_"}, value = { @@ -265,6 +301,83 @@ public final class WifiUsabilityStatsEntry implements Parcelable { } private final ContentionTimeStats[] mContentionTimeStats; + /** + * Packet statistics. + */ + /** @hide */ + public static final class PacketStats implements Parcelable { + private long mTxSuccess; + private long mTxRetries; + private long mTxBad; + private long mRxSuccess; + + public PacketStats() { + } + + /** + * Constructor function + * @param txSuccess Number of successfully transmitted unicast data pkts (ACK rcvd) + * @param txRetries Number of transmitted unicast data retry pkts + * @param txBad Number of transmitted unicast data pkt losses (no ACK) + * @param rxSuccess Number of received unicast data packets + */ + public PacketStats(long txSuccess, long txRetries, long txBad, long rxSuccess) { + this.mTxSuccess = txSuccess; + this.mTxRetries = txRetries; + this.mTxBad = txBad; + this.mRxSuccess = rxSuccess; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeLong(mTxSuccess); + dest.writeLong(mTxRetries); + dest.writeLong(mTxBad); + dest.writeLong(mRxSuccess); + } + + /** Implement the Parcelable interface */ + public static final @NonNull Creator<PacketStats> CREATOR = + new Creator<PacketStats>() { + public PacketStats createFromParcel(Parcel in) { + PacketStats stats = new PacketStats(); + stats.mTxSuccess = in.readLong(); + stats.mTxRetries = in.readLong(); + stats.mTxBad = in.readLong(); + stats.mRxSuccess = in.readLong(); + return stats; + } + public PacketStats[] newArray(int size) { + return new PacketStats[size]; + } + }; + + /** Number of successfully transmitted unicast data pkts (ACK rcvd) */ + public long getTxSuccess() { + return mTxSuccess; + } + + /** Number of transmitted unicast data retry pkts */ + public long getTxRetries() { + return mTxRetries; + } + + /** Number of transmitted unicast data pkt losses (no ACK) */ + public long getTxBad() { + return mTxBad; + } + + /** Number of received unicast data packets */ + public long getRxSuccess() { + return mRxSuccess; + } + } + /** {@hide} */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = {"WIFI_PREAMBLE_"}, value = { @@ -481,6 +594,145 @@ public final class WifiUsabilityStatsEntry implements Parcelable { private final RateStats[] mRateStats; /** + * Per peer statistics for WiFi the link + */ + /** @hide */ + public static final class PeerInfo implements Parcelable { + private int mStaCount; + private int mChanUtil; + private RateStats[] mRateStats; + + public PeerInfo() { + } + + /** + * Constructor function. + * @param staCount Station count + * @param chanUtil Channel utilization + * @param rateStats Per rate statistics on this WiFi peer + */ + public PeerInfo(int staCount, int chanUtil, RateStats[] rateStats) { + this.mStaCount = staCount; + this.mChanUtil = chanUtil; + this.mRateStats = rateStats; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mStaCount); + dest.writeInt(mChanUtil); + dest.writeTypedArray(mRateStats, flags); + } + + /** Implement the Parcelable interface */ + public static final @NonNull Creator<PeerInfo> CREATOR = new Creator<PeerInfo>() { + public PeerInfo createFromParcel(Parcel in) { + PeerInfo stats = new PeerInfo(); + stats.mStaCount = in.readInt(); + stats.mChanUtil = in.readInt(); + stats.mRateStats = in.createTypedArray(RateStats.CREATOR); + return stats; + } + public PeerInfo[] newArray(int size) { + return new PeerInfo[size]; + } + }; + + /** Station count */ + public int getStaCount() { + return mStaCount; + } + + /** Channel utilization */ + public int getChanUtil() { + return mChanUtil; + } + + /** Per rate statistics */ + public List<RateStats> getRateStats() { + if (mRateStats != null) { + return Arrays.asList(mRateStats); + } + return Collections.emptyList(); + } + } + + /** + * Scan Results who have the same freq with current WiFi link + */ + /** @hide */ + public static final class ScanResultWithSameFreq implements Parcelable { + private long mScanResultTimestampMicros; + private int mRssi; + private int mFrequencyMhz; + + public ScanResultWithSameFreq() { + } + + /** + * Constructor function. + * @param scanResultTimestampMicros Timestamp in microseconds (since boot) when this result + * was last seen. + * @param rssi The detected signal level in dBm + * @param frequencyMhz The center frequency of the primary 20 MHz frequency + * (in MHz) of the channel + */ + public ScanResultWithSameFreq(long scanResultTimestampMicros, int rssi, int frequencyMhz) { + this.mScanResultTimestampMicros = scanResultTimestampMicros; + this.mRssi = rssi; + this.mFrequencyMhz = frequencyMhz; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeLong(mScanResultTimestampMicros); + dest.writeInt(mRssi); + dest.writeInt(mFrequencyMhz); + } + + /** Implement the Parcelable interface */ + public static final @NonNull Creator<ScanResultWithSameFreq> CREATOR = + new Creator<ScanResultWithSameFreq>() { + public ScanResultWithSameFreq createFromParcel(Parcel in) { + ScanResultWithSameFreq scanResultWithSameFreq = + new ScanResultWithSameFreq(); + scanResultWithSameFreq.mScanResultTimestampMicros = in.readLong(); + scanResultWithSameFreq.mRssi = in.readInt(); + scanResultWithSameFreq.mFrequencyMhz = in.readInt(); + return scanResultWithSameFreq; + } + public ScanResultWithSameFreq[] newArray(int size) { + return new ScanResultWithSameFreq[size]; + } + }; + + /** timestamp in microseconds (since boot) when this result was last seen */ + public long getScanResultTimestampMicros() { + return mScanResultTimestampMicros; + } + + /** The detected signal level in dBm */ + public int getRssi() { + return mRssi; + } + + /** The center frequency of the primary 20 MHz frequency (in MHz) of the channel */ + public int getFrequency() { + return mFrequencyMhz; + } + } + + /** * Wifi link layer radio stats. */ public static final class RadioStats implements Parcelable { @@ -499,6 +751,7 @@ public final class WifiUsabilityStatsEntry implements Parcelable { private long mTotalRoamScanTimeMillis; private long mTotalPnoScanTimeMillis; private long mTotalHotspot2ScanTimeMillis; + private int[] mTxTimeMsPerLevel; /** @hide */ public RadioStats() { @@ -542,6 +795,47 @@ public final class WifiUsabilityStatsEntry implements Parcelable { this.mTotalHotspot2ScanTimeMillis = onTimeHs20Scan; } + /** + * Constructor function + * @param radioId Firmware/Hardware implementation specific persistent value for this + * device, identifying the radio interface for which the stats are produced. + * @param onTime The total time the wifi radio is on in ms counted from the last radio + * chip reset + * @param txTime The total time the wifi radio is transmitting in ms counted from the last + * radio chip reset + * @param rxTime The total time the wifi radio is receiving in ms counted from the last + * radio chip reset + * @param onTimeScan The total time spent on all types of scans in ms counted from the + * last radio chip reset + * @param onTimeNanScan The total time spent on nan scans in ms counted from the last radio + * chip reset + * @param onTimeBackgroundScan The total time spent on background scans in ms counted from + * the last radio chip reset + * @param onTimeRoamScan The total time spent on roam scans in ms counted from the last + * radio chip reset + * @param onTimePnoScan The total time spent on pno scans in ms counted from the last radio + * chip reset + * @param onTimeHs20Scan The total time spent on hotspot2.0 scans and GAS exchange in ms + * counted from the last radio chip reset + * @param txTimeMsPerLevel Time for which the radio is in active tranmission per tx level + */ + /** @hide */ + public RadioStats(int radioId, long onTime, long txTime, long rxTime, long onTimeScan, + long onTimeNanScan, long onTimeBackgroundScan, long onTimeRoamScan, + long onTimePnoScan, long onTimeHs20Scan, int[] txTimeMsPerLevel) { + this.mRadioId = radioId; + this.mTotalRadioOnTimeMillis = onTime; + this.mTotalRadioTxTimeMillis = txTime; + this.mTotalRadioRxTimeMillis = rxTime; + this.mTotalScanTimeMillis = onTimeScan; + this.mTotalNanScanTimeMillis = onTimeNanScan; + this.mTotalBackgroundScanTimeMillis = onTimeBackgroundScan; + this.mTotalRoamScanTimeMillis = onTimeRoamScan; + this.mTotalPnoScanTimeMillis = onTimePnoScan; + this.mTotalHotspot2ScanTimeMillis = onTimeHs20Scan; + this.mTxTimeMsPerLevel = txTimeMsPerLevel; + } + @Override public int describeContents() { return 0; @@ -559,6 +853,7 @@ public final class WifiUsabilityStatsEntry implements Parcelable { dest.writeLong(mTotalRoamScanTimeMillis); dest.writeLong(mTotalPnoScanTimeMillis); dest.writeLong(mTotalHotspot2ScanTimeMillis); + dest.writeIntArray(mTxTimeMsPerLevel); } /** Implement the Parcelable interface */ @@ -576,6 +871,7 @@ public final class WifiUsabilityStatsEntry implements Parcelable { stats.mTotalRoamScanTimeMillis = in.readLong(); stats.mTotalPnoScanTimeMillis = in.readLong(); stats.mTotalHotspot2ScanTimeMillis = in.readLong(); + stats.mTxTimeMsPerLevel = in.createIntArray(); return stats; } public RadioStats[] newArray(int size) { @@ -645,6 +941,13 @@ public final class WifiUsabilityStatsEntry implements Parcelable { public long getTotalHotspot2ScanTimeMillis() { return mTotalHotspot2ScanTimeMillis; } + /** + * Time for which the radio is in active tranmission per tx level + */ + /** @hide */ + public int[] getTxTimeMsPerLevel() { + return mTxTimeMsPerLevel; + } } private final RadioStats[] mRadioStats; private final int mChannelUtilizationRatio; @@ -670,6 +973,16 @@ public final class WifiUsabilityStatsEntry implements Parcelable { private final int mRadioId; /** The RSSI (in dBm) at the sample time */ private final int mRssi; + /** Frequency of the link in MHz */ + private final int mFrequencyMhz; + /** RSSI of management frames on this WiFi link */ + private final int mRssiMgmt; + /** Channel bandwidth on this WiFi link */ + @WifiChannelBandwidth private int mChannelWidth; + /** Center frequency (MHz) first segment on this WiFi link */ + private final int mCenterFreqFirstSegment; + /** Center frequency (MHz) second segment on this WiFi link */ + private final int mCenterFreqSecondSegment; /** Tx Link speed at the sample time in Mbps */ private final int mTxLinkSpeedMbps; /** Rx link speed at the sample time in Mbps */ @@ -697,6 +1010,12 @@ public final class WifiUsabilityStatsEntry implements Parcelable { private final ContentionTimeStats[] mContentionTimeStats; /** Rate information and statistics */ private final RateStats[] mRateStats; + /** Packet statistics */ + private final PacketStats[] mPacketStats; + /** Peer statistics */ + private final PeerInfo[] mPeerInfo; + /** Scan results who have the same frequency with this WiFi link */ + private final ScanResultWithSameFreq[] mScanResultsWithSameFreq; /** @hide */ public LinkStats() { @@ -704,6 +1023,11 @@ public final class WifiUsabilityStatsEntry implements Parcelable { mState = LINK_STATE_UNKNOWN; mRssi = WifiInfo.INVALID_RSSI; mRadioId = RadioStats.INVALID_RADIO_ID; + mFrequencyMhz = 0; + mRssiMgmt = 0; + mChannelWidth = WIFI_BANDWIDTH_20_MHZ; + mCenterFreqFirstSegment = 0; + mCenterFreqSecondSegment = 0; mTxLinkSpeedMbps = WifiInfo.LINK_SPEED_UNKNOWN; mRxLinkSpeedMbps = WifiInfo.LINK_SPEED_UNKNOWN; mTotalTxSuccess = 0; @@ -716,6 +1040,9 @@ public final class WifiUsabilityStatsEntry implements Parcelable { mTotalRadioOnFreqTimeMillis = 0; mContentionTimeStats = null; mRateStats = null; + mPacketStats = null; + mPeerInfo = null; + mScanResultsWithSameFreq = null; } /** @hide @@ -725,6 +1052,11 @@ public final class WifiUsabilityStatsEntry implements Parcelable { * @param radioId Identifier of the radio on which the link is operating * currently. * @param rssi Link Rssi (in dBm) at sample time. + * @param frequencyMhz Frequency of the link in MHz + * @param rssiMgmt RSSI of management frames on this WiFi link + * @param channelWidth channel width of WiFi link + * @param centerFreqFirstSegment Center frequency (MHz) of first segment + * @param centerFreqSecondSegment Center frequency (MHz) of second segment * @param txLinkSpeedMpbs Transmit link speed in Mpbs at sample time. * @param rxLinkSpeedMpbs Receive link speed in Mbps at sample time. * @param totalTxSuccess Total number of Tx success. @@ -739,16 +1071,29 @@ public final class WifiUsabilityStatsEntry implements Parcelable { * last radio chip reset. * @param contentionTimeStats Data packet contention time statistics. * @param rateStats Rate information. + * @param packetStats Packet statistics. + * @param peerInfo Peer statistics. + * @param scanResultsWithSameFreq Scan results who have the same frequency with this + * WiFi link. */ - public LinkStats(int linkId, int state, int radioId, int rssi, int txLinkSpeedMpbs, - int rxLinkSpeedMpbs, long totalTxSuccess, long totalTxRetries, long totalTxBad, - long totalRxSuccess, long totalBeaconRx, int timeSliceDutyCycleInPercent, - long totalCcaBusyFreqTimeMillis, long totalRadioOnFreqTimeMillis, - ContentionTimeStats[] contentionTimeStats, RateStats[] rateStats) { + public LinkStats(int linkId, int state, int radioId, int rssi, int frequencyMhz, + int rssiMgmt, @WifiChannelBandwidth int channelWidth, int centerFreqFirstSegment, + int centerFreqSecondSegment, int txLinkSpeedMpbs, int rxLinkSpeedMpbs, + long totalTxSuccess, long totalTxRetries, long totalTxBad, long totalRxSuccess, + long totalBeaconRx, int timeSliceDutyCycleInPercent, + long totalCcaBusyFreqTimeMillis, long totalRadioOnFreqTimeMillis, + ContentionTimeStats[] contentionTimeStats, RateStats[] rateStats, + PacketStats[] packetStats, PeerInfo[] peerInfo, + ScanResultWithSameFreq[] scanResultsWithSameFreq) { this.mLinkId = linkId; this.mState = state; this.mRadioId = radioId; this.mRssi = rssi; + this.mFrequencyMhz = frequencyMhz; + this.mRssiMgmt = rssiMgmt; + this.mChannelWidth = channelWidth; + this.mCenterFreqFirstSegment = centerFreqFirstSegment; + this.mCenterFreqSecondSegment = centerFreqSecondSegment; this.mTxLinkSpeedMbps = txLinkSpeedMpbs; this.mRxLinkSpeedMbps = rxLinkSpeedMpbs; this.mTotalTxSuccess = totalTxSuccess; @@ -761,6 +1106,9 @@ public final class WifiUsabilityStatsEntry implements Parcelable { this.mTotalRadioOnFreqTimeMillis = totalRadioOnFreqTimeMillis; this.mContentionTimeStats = contentionTimeStats; this.mRateStats = rateStats; + this.mPacketStats = packetStats; + this.mPeerInfo = peerInfo; + this.mScanResultsWithSameFreq = scanResultsWithSameFreq; } @Override @@ -774,6 +1122,11 @@ public final class WifiUsabilityStatsEntry implements Parcelable { dest.writeInt(mState); dest.writeInt(mRadioId); dest.writeInt(mRssi); + dest.writeInt(mFrequencyMhz); + dest.writeInt(mRssiMgmt); + dest.writeInt(mChannelWidth); + dest.writeInt(mCenterFreqFirstSegment); + dest.writeInt(mCenterFreqSecondSegment); dest.writeInt(mTxLinkSpeedMbps); dest.writeInt(mRxLinkSpeedMbps); dest.writeLong(mTotalTxSuccess); @@ -786,6 +1139,9 @@ public final class WifiUsabilityStatsEntry implements Parcelable { dest.writeLong(mTotalRadioOnFreqTimeMillis); dest.writeTypedArray(mContentionTimeStats, flags); dest.writeTypedArray(mRateStats, flags); + dest.writeTypedArray(mPacketStats, flags); + dest.writeTypedArray(mPeerInfo, flags); + dest.writeTypedArray(mScanResultsWithSameFreq, flags); } /** Implement the Parcelable interface */ @@ -794,11 +1150,15 @@ public final class WifiUsabilityStatsEntry implements Parcelable { new Creator<>() { public LinkStats createFromParcel(Parcel in) { return new LinkStats(in.readInt(), in.readInt(), in.readInt(), in.readInt(), - in.readInt(), in.readInt(), in.readLong(), in.readLong(), - in.readLong(), in.readLong(), in.readLong(), in.readInt(), - in.readLong(), in.readLong(), + in.readInt(), in.readInt(), in.readInt(), in.readInt(), + in.readInt(), in.readInt(), in.readInt(), in.readLong(), + in.readLong(), in.readLong(), in.readLong(), in.readLong(), + in.readInt(), in.readLong(), in.readLong(), in.createTypedArray(ContentionTimeStats.CREATOR), - in.createTypedArray(RateStats.CREATOR)); + in.createTypedArray(RateStats.CREATOR), + in.createTypedArray(PacketStats.CREATOR), + in.createTypedArray(PeerInfo.CREATOR), + in.createTypedArray(ScanResultWithSameFreq.CREATOR)); } public LinkStats[] newArray(int size) { @@ -826,7 +1186,15 @@ public final class WifiUsabilityStatsEntry implements Parcelable { boolean isThroughputSufficient, boolean isWifiScoringEnabled, boolean isCellularDataAvailable, @NetworkType int cellularDataNetworkType, int cellularSignalStrengthDbm, int cellularSignalStrengthDb, - boolean isSameRegisteredCell, SparseArray<LinkStats> linkStats) { + boolean isSameRegisteredCell, SparseArray<LinkStats> linkStats, + int wifiLinkCount, @WifiManager.MloMode int mloMode, + long txTransmittedBytes, long rxTransmittedBytes, int labelBadEventCount, + int wifiFrameworkState, int isNetworkCapabilitiesDownstreamSufficient, + int isNetworkCapabilitiesUpstreamSufficient, + int isThroughputPredictorDownstreamSufficient, + int isThroughputPredictorUpstreamSufficient, boolean isBluetoothConnected, + int uwbAdapterState, boolean isLowLatencyActivated, int maxSupportedTxLinkSpeed, + int maxSupportedRxLinkSpeed, int voipMode, int threadDeviceRole, int statusDataStall) { mTimeStampMillis = timeStampMillis; mRssi = rssi; mLinkSpeedMbps = linkSpeedMbps; @@ -863,6 +1231,24 @@ public final class WifiUsabilityStatsEntry implements Parcelable { mCellularSignalStrengthDb = cellularSignalStrengthDb; mIsSameRegisteredCell = isSameRegisteredCell; mLinkStats = linkStats; + mWifiLinkCount = wifiLinkCount; + mMloMode = mloMode; + mTxTransmittedBytes = txTransmittedBytes; + mRxTransmittedBytes = rxTransmittedBytes; + mLabelBadEventCount = labelBadEventCount; + mWifiFrameworkState = wifiFrameworkState; + mIsNetworkCapabilitiesDownstreamSufficient = isNetworkCapabilitiesDownstreamSufficient; + mIsNetworkCapabilitiesUpstreamSufficient = isNetworkCapabilitiesUpstreamSufficient; + mIsThroughputPredictorDownstreamSufficient = isThroughputPredictorDownstreamSufficient; + mIsThroughputPredictorUpstreamSufficient = isThroughputPredictorUpstreamSufficient; + mIsBluetoothConnected = isBluetoothConnected; + mUwbAdapterState = uwbAdapterState; + mIsLowLatencyActivated = isLowLatencyActivated; + mMaxSupportedTxLinkSpeed = maxSupportedTxLinkSpeed; + mMaxSupportedRxLinkSpeed = maxSupportedRxLinkSpeed; + mVoipMode = voipMode; + mThreadDeviceRole = threadDeviceRole; + mStatusDataStall = statusDataStall; } /** Implement the Parcelable interface */ @@ -908,6 +1294,24 @@ public final class WifiUsabilityStatsEntry implements Parcelable { dest.writeInt(mCellularSignalStrengthDb); dest.writeBoolean(mIsSameRegisteredCell); dest.writeSparseArray(mLinkStats); + dest.writeInt(mWifiLinkCount); + dest.writeInt(mMloMode); + dest.writeLong(mTxTransmittedBytes); + dest.writeLong(mRxTransmittedBytes); + dest.writeInt(mLabelBadEventCount); + dest.writeInt(mWifiFrameworkState); + dest.writeInt(mIsNetworkCapabilitiesDownstreamSufficient); + dest.writeInt(mIsNetworkCapabilitiesUpstreamSufficient); + dest.writeInt(mIsThroughputPredictorDownstreamSufficient); + dest.writeInt(mIsThroughputPredictorUpstreamSufficient); + dest.writeBoolean(mIsBluetoothConnected); + dest.writeInt(mUwbAdapterState); + dest.writeBoolean(mIsLowLatencyActivated); + dest.writeInt(mMaxSupportedTxLinkSpeed); + dest.writeInt(mMaxSupportedRxLinkSpeed); + dest.writeInt(mVoipMode); + dest.writeInt(mThreadDeviceRole); + dest.writeInt(mStatusDataStall); } /** Implement the Parcelable interface */ @@ -929,7 +1333,11 @@ public final class WifiUsabilityStatsEntry implements Parcelable { in.readInt(), in.readBoolean(), in.readBoolean(), in.readBoolean(), in.readInt(), in.readInt(), in.readInt(), in.readBoolean(), - in.readSparseArray(LinkStats.class.getClassLoader()) + in.readSparseArray(LinkStats.class.getClassLoader()), in.readInt(), + in.readInt(), in.readLong(), in.readLong(), in.readInt(), in.readInt(), + in.readInt(), in.readInt(), in.readInt(), in.readInt(), in.readBoolean(), + in.readInt(), in.readBoolean(), in.readInt(), in.readInt(), in.readInt(), + in.readInt(), in.readInt() ); } @@ -1003,6 +1411,77 @@ public final class WifiUsabilityStatsEntry implements Parcelable { throw new NoSuchElementException("linkId is invalid - " + linkId); } + /** + * Get frequency of specific WiFi link + * + * @param linkId Identifier of the link. + * @return Frequency in Mhz + * @throws NoSuchElementException if linkId is invalid. + */ + /** @hide */ + public int getFrequencyMhz(int linkId) { + if (mLinkStats.contains(linkId)) return mLinkStats.get(linkId).mFrequencyMhz; + throw new NoSuchElementException("linkId is invalid - " + linkId); + } + + /** + * Get RSSI of management frames on this WiFi link + * + * @param linkId Identifier of the link. + * @return RSSI of management frames on this WiFi link + * @throws NoSuchElementException if linkId is invalid. + */ + /** @hide */ + public int getRssiMgmt(int linkId) { + if (mLinkStats.contains(linkId)) return mLinkStats.get(linkId).mRssiMgmt; + throw new NoSuchElementException("linkId is invalid - " + linkId); + } + + /** + * Get channel width of this WiFi link + * + * @param linkId Identifier of the link. + * @return channel width of this WiFi link + * @throws NoSuchElementException if linkId is invalid. + */ + /** @hide */ + public int getChannelWidth(int linkId) { + if (mLinkStats.contains(linkId)) { + return mLinkStats.get(linkId).mChannelWidth; + } + throw new NoSuchElementException("linkId is invalid - " + linkId); + } + + /** + * Get center frequency (MHz) of first segment of this WiFi link + * + * @param linkId Identifier of the link. + * @return center frequency (MHz) of first segment of this WiFi link + * @throws NoSuchElementException if linkId is invalid. + */ + /** @hide */ + public int getCenterFreqFirstSegment(int linkId) { + if (mLinkStats.contains(linkId)) { + return mLinkStats.get(linkId).mCenterFreqFirstSegment; + } + throw new NoSuchElementException("linkId is invalid - " + linkId); + } + + /** + * Get center frequency (MHz) of second segment of this WiFi link + * + * @param linkId Identifier of the link. + * @return center frequency (MHz) of second segment of this WiFi link + * @throws NoSuchElementException if linkId is invalid. + */ + /** @hide */ + public int getCenterFreqSecondSegment(int linkId) { + if (mLinkStats.contains(linkId)) { + return mLinkStats.get(linkId).mCenterFreqSecondSegment; + } + throw new NoSuchElementException("linkId is invalid - " + linkId); + } + /** Link speed at the sample time in Mbps. In case of Multi Link Operation (MLO), returned value * is the current link speed of the associated link with the highest RSSI. * @@ -1356,6 +1835,30 @@ public final class WifiUsabilityStatsEntry implements Parcelable { } /** + * Packet statistics of a WiFi link for Access Category. + * + * @param linkId Identifier of the link. + * @param ac The access category, see {@link WmeAccessCategory}. + * @return The packet statistics, see {@link PacketStats}. + * @throws NoSuchElementException if linkId is invalid. + */ + /** @hide */ + @NonNull + public PacketStats getPacketStats(int linkId, @WmeAccessCategory int ac) { + if (!mLinkStats.contains(linkId)) { + throw new NoSuchElementException("linkId is invalid - " + linkId); + } + PacketStats[] linkPacketStats = mLinkStats.get( + linkId).mPacketStats; + if (linkPacketStats != null + && linkPacketStats.length == NUM_WME_ACCESS_CATEGORIES) { + return linkPacketStats[ac]; + } + Log.e(TAG, "The PacketStats is not filled out correctly"); + return new PacketStats(); + } + + /** * Rate information and statistics, which are ordered by preamble, modulation and coding scheme * (MCS), and number of spatial streams (NSS). In case of Multi Link Operation (MLO), the * returned rate information is that of the associated link with the highest RSSI. @@ -1407,6 +1910,73 @@ public final class WifiUsabilityStatsEntry implements Parcelable { } /** + * Rate information and statistics, which are ordered by preamble, modulation and coding scheme + * (MCS), and number of spatial streams (NSS) for WiFi peer. + * + * @param linkId Identifier of the link. + * @param peerIndex Identifier of PeerInfo. + * @return A list of rate statistics in the form of a list of {@link RateStats} objects. + * Depending on the link type, the list is created following the order of: + * - HT (IEEE Std 802.11-2020, Section 19): LEGACY rates (1Mbps, ..., 54Mbps), + * HT MCS0, ..., MCS15; + * - VHT (IEEE Std 802.11-2020, Section 21): LEGACY rates (1Mbps, ..., 54Mbps), + * VHT MCS0/NSS1, ..., VHT MCS11/NSS1, VHT MCSO/NSS2, ..., VHT MCS11/NSS2; + * - HE (IEEE Std 802.11ax-2020, Section 27): LEGACY rates (1Mbps, ..., 54Mbps), + * HE MCS0/NSS1, ..., HE MCS11/NSS1, HE MCSO/NSS2, ..., HE MCS11/NSS2. + * - EHT (IEEE std 802.11be-2021, Section 36): Legacy rates (1Mbps, ..., 54Mbps), + * EHT MSC0/NSS1, ..., EHT MCS14/NSS1, EHT MCS0/NSS2, ..., EHT MCS14/NSS2. + * @throws NoSuchElementException if linkId is invalid. + */ + /** @hide */ + @NonNull + public List<RateStats> getRateStats(int linkId, int peerIndex) { + if (mLinkStats.contains(linkId)) { + if (getPeerInfo(linkId).size() > 0 && peerIndex < getPeerInfo(linkId).size()) { + RateStats[] rateStats = mLinkStats.get(linkId).mPeerInfo[peerIndex].mRateStats; + if (rateStats != null) return Arrays.asList(rateStats); + } + return Collections.emptyList(); + } + throw new NoSuchElementException("linkId is invalid - " + linkId); + } + + /** + * Peer information and statistics + * + * @param linkId Identifier of the link. + * @return A list of peer statistics in the form of a list of {@link PeerInfo} objects. + * @throws NoSuchElementException if linkId is invalid. + */ + /** @hide */ + @NonNull + public List<PeerInfo> getPeerInfo(int linkId) { + if (mLinkStats.contains(linkId)) { + PeerInfo[] peerInfo = mLinkStats.get(linkId).mPeerInfo; + if (peerInfo != null) return Arrays.asList(peerInfo); + return Collections.emptyList(); + } + throw new NoSuchElementException("linkId is invalid - " + linkId); + } + + /** + * Scan results who have the same frequency with this WiFi link + * + * @param linkId Identifier of the link. + * @return An array of Scan results who have the same frequency with this WiFi link + * @throws NoSuchElementException if linkId is invalid. + */ + /** @hide */ + @NonNull + public ScanResultWithSameFreq[] getScanResultsWithSameFreq(int linkId) { + if (mLinkStats.contains(linkId)) { + ScanResultWithSameFreq[] scanResultsWithSameFreq = + mLinkStats.get(linkId).mScanResultsWithSameFreq; + return scanResultsWithSameFreq; + } + throw new NoSuchElementException("linkId is invalid - " + linkId); + } + + /** * Radio stats from all the radios, see {@link RadioStats#getRadioId()} * @return A list of Wifi link layer radio stats, see {@link RadioStats} */ @@ -1485,4 +2055,94 @@ public final class WifiUsabilityStatsEntry implements Parcelable { public boolean isSameRegisteredCell() { return mIsSameRegisteredCell; } + + /** @hide */ + public int getWifiLinkCount() { + return mWifiLinkCount; + } + + /** @hide */ + public int getMloMode() { + return mMloMode; + } + + /** @hide */ + public long getTxTransmittedBytes() { + return mTxTransmittedBytes; + } + + /** @hide */ + public long getRxTransmittedBytes() { + return mRxTransmittedBytes; + } + + /** @hide */ + public long getLabelBadEventCount() { + return mLabelBadEventCount; + } + + /** @hide */ + public int getWifiFrameworkState() { + return mWifiFrameworkState; + } + + /** @hide */ + public int isNetworkCapabilitiesDownstreamSufficient() { + return mIsNetworkCapabilitiesDownstreamSufficient; + } + + /** @hide */ + public int isNetworkCapabilitiesUpstreamSufficient() { + return mIsNetworkCapabilitiesUpstreamSufficient; + } + + /** @hide */ + public int isThroughputPredictorDownstreamSufficient() { + return mIsThroughputPredictorDownstreamSufficient; + } + + /** @hide */ + public int isThroughputPredictorUpstreamSufficient() { + return mIsThroughputPredictorUpstreamSufficient; + } + + /** @hide */ + public boolean isBluetoothConnected() { + return mIsBluetoothConnected; + } + + /** @hide */ + public int getUwbAdapterState() { + return mUwbAdapterState; + } + + /** @hide */ + public boolean getLowLatencyModeState() { + return mIsLowLatencyActivated; + } + + /** @hide */ + public int getMaxSupportedTxLinkSpeed() { + return mMaxSupportedTxLinkSpeed; + } + + /** @hide */ + public int getMaxSupportedRxLinkSpeed() { + return mMaxSupportedRxLinkSpeed; + } + + /** @hide */ + public int getVoipMode() { + return mVoipMode; + } + + /** @hide */ + public int getThreadDeviceRole() { + return mThreadDeviceRole; + } + + /** @hide */ + public int getStatusDataStall() { + return mStatusDataStall; + } } diff --git a/framework/java/android/net/wifi/aware/Characteristics.java b/framework/java/android/net/wifi/aware/Characteristics.java index df36140c10..600739029d 100644 --- a/framework/java/android/net/wifi/aware/Characteristics.java +++ b/framework/java/android/net/wifi/aware/Characteristics.java @@ -16,8 +16,13 @@ package android.net.wifi.aware; +import static com.android.ranging.flags.Flags.FLAG_RANGING_RTT_ENABLED; + +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.IntRange; +import android.annotation.SystemApi; +import android.net.wifi.WifiAnnotations; import android.os.Build; import android.os.Bundle; import android.os.Parcel; @@ -62,6 +67,16 @@ public final class Characteristics implements Parcelable { public static final String KEY_SUPPORT_NAN_PAIRING = "key_support_nan_pairing"; /** @hide */ public static final String KEY_SUPPORT_SUSPENSION = "key_support_suspension"; + /** @hide */ + @FlaggedApi(FLAG_RANGING_RTT_ENABLED) + public static final String KEY_SUPPORT_PERIODIC_RANGING = "key_support_periodic_ranging"; + /** @hide */ + @FlaggedApi(FLAG_RANGING_RTT_ENABLED) + public static final String KEY_MAX_SUPPORTED_RANGING_PKT_BANDWIDTH = + "key_max_supported_ranging_pkt_bandwidth"; + /** @hide */ + @FlaggedApi(FLAG_RANGING_RTT_ENABLED) + public static final String KEY_MAX_SUPPORTED_RX_CHAINS = "key_max_supported_rx_chains"; private final Bundle mCharacteristics; @@ -187,6 +202,78 @@ public final class Characteristics implements Parcelable { return mCharacteristics.getBoolean(KEY_SUPPORT_SUSPENSION); } + /** + * Check if Periodic Ranging is supported. + * Periodic Ranging on Aware allows applications to get the asynchronous ranging + * report periodically. + * @return True if supported, false otherwise. + * @hide + */ + @FlaggedApi(FLAG_RANGING_RTT_ENABLED) + @SystemApi + public boolean isPeriodicRangingSupported() { + return mCharacteristics.getBoolean(KEY_SUPPORT_PERIODIC_RANGING); + } + + /** @hide */ + @IntDef(flag = true, prefix = { "SUPPORTED_RX_CHAINS_" }, value = { + SUPPORTED_RX_CHAINS_UNSPECIFIED, + SUPPORTED_RX_CHAINS_1, + SUPPORTED_RX_CHAINS_2, + SUPPORTED_RX_CHAINS_3, + SUPPORTED_RX_CHAINS_4, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SupportedRxChains {} + + /** @hide */ + @FlaggedApi(FLAG_RANGING_RTT_ENABLED) + @SystemApi + public static final int SUPPORTED_RX_CHAINS_1 = 1; + /** @hide */ + @FlaggedApi(FLAG_RANGING_RTT_ENABLED) + @SystemApi + public static final int SUPPORTED_RX_CHAINS_2 = 2; + /** @hide */ + @FlaggedApi(FLAG_RANGING_RTT_ENABLED) + @SystemApi + public static final int SUPPORTED_RX_CHAINS_3 = 3; + /** @hide */ + @FlaggedApi(FLAG_RANGING_RTT_ENABLED) + @SystemApi + public static final int SUPPORTED_RX_CHAINS_4 = 4; + /** @hide */ + @FlaggedApi(FLAG_RANGING_RTT_ENABLED) + @SystemApi + public static final int SUPPORTED_RX_CHAINS_UNSPECIFIED = 0; + + /** + * Get the supported number of receive chains. + * + * @return Number of supported receive chains. + * @hide + */ + @FlaggedApi(FLAG_RANGING_RTT_ENABLED) + @SystemApi + public @SupportedRxChains int getMaxSupportedRxChains() { + return mCharacteristics.getInt(KEY_MAX_SUPPORTED_RX_CHAINS); + } + + /** + * Get Max supported ranging per packet Bandwidth + * + * @return the bandwidth representation of the Wi-Fi channel from + * {@link ScanResult#CHANNEL_WIDTH_20MHZ}, {@link ScanResult#CHANNEL_WIDTH_40MHZ}, + * {@link ScanResult#CHANNEL_WIDTH_80MHZ}, {@link ScanResult#CHANNEL_WIDTH_160MHZ}, + * or {@link ScanResult#CHANNEL_WIDTH_320MHZ}. + * @hide + */ + @FlaggedApi(FLAG_RANGING_RTT_ENABLED) + @SystemApi + public @WifiAnnotations.ChannelWidth int getMaxSupportedRangingPacketBandwidth() { + return mCharacteristics.getInt(KEY_MAX_SUPPORTED_RANGING_PKT_BANDWIDTH); + } + /** @hide */ @IntDef(flag = true, prefix = { "WIFI_AWARE_CIPHER_SUITE_" }, value = { WIFI_AWARE_CIPHER_SUITE_NONE, diff --git a/framework/java/android/net/wifi/aware/DiscoverySessionCallback.java b/framework/java/android/net/wifi/aware/DiscoverySessionCallback.java index 80bce68d83..ad37287f2c 100644 --- a/framework/java/android/net/wifi/aware/DiscoverySessionCallback.java +++ b/framework/java/android/net/wifi/aware/DiscoverySessionCallback.java @@ -16,8 +16,12 @@ package android.net.wifi.aware; +import static com.android.ranging.flags.Flags.FLAG_RANGING_RTT_ENABLED; + +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.SystemApi; +import android.net.wifi.rtt.RangingResult; import java.util.List; @@ -382,4 +386,16 @@ public class DiscoverySessionCallback { public void onBootstrappingFailed(@NonNull PeerHandle peerHandle) { } + + /** + * Callback indicating that ranging results have been received. + * + * @param rangingResults List of range measurements. + * @hide + */ + @FlaggedApi(FLAG_RANGING_RTT_ENABLED) + @SystemApi + public void onRangingResultsReceived(@NonNull List<RangingResult> rangingResults) { + + } } diff --git a/framework/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl b/framework/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl index 392ec6cc2e..326f236f19 100644 --- a/framework/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl +++ b/framework/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl @@ -16,16 +16,16 @@ package android.net.wifi.aware; -import android.net.wifi.aware.AwarePairingConfig; import android.net.wifi.OuiKeyedData; +import android.net.wifi.aware.AwarePairingConfig; +import android.net.wifi.rtt.RangingResult; /** * Callback interface that WifiAwareManager implements * * {@hide} */ -oneway interface IWifiAwareDiscoverySessionCallback -{ +oneway interface IWifiAwareDiscoverySessionCallback { void onSessionStarted(int discoverySessionId); void onSessionConfigSuccess(); void onSessionConfigFail(int reason); @@ -50,4 +50,5 @@ oneway interface IWifiAwareDiscoverySessionCallback void onPairingSetupConfirmed(int peerId, boolean accept, String alias); void onPairingVerificationConfirmed(int peerId, boolean accept, String alias); void onBootstrappingVerificationConfirmed(int peerId, boolean accept, int method); + void onRangingResultsReceived(in List<RangingResult> rangingResults); } diff --git a/framework/java/android/net/wifi/aware/PublishConfig.java b/framework/java/android/net/wifi/aware/PublishConfig.java index b429fac63d..774708f372 100644 --- a/framework/java/android/net/wifi/aware/PublishConfig.java +++ b/framework/java/android/net/wifi/aware/PublishConfig.java @@ -18,6 +18,8 @@ package android.net.wifi.aware; import static android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION; +import static com.android.ranging.flags.Flags.FLAG_RANGING_RTT_ENABLED; + import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; @@ -112,12 +114,15 @@ public final class PublishConfig implements Parcelable { private final List<OuiKeyedData> mVendorData; /** @hide */ + public final boolean mEnablePeriodicRangingResults; + + /** @hide */ public PublishConfig(byte[] serviceName, byte[] serviceSpecificInfo, byte[] matchFilter, int publishType, int ttlSec, boolean enableTerminateNotification, boolean enableRanging, boolean enableInstantMode, @WifiScanner.WifiBand int band, WifiAwareDataPathSecurityConfig securityConfig, AwarePairingConfig pairingConfig, boolean isSuspendable, - @NonNull List<OuiKeyedData> vendorData) { + @NonNull List<OuiKeyedData> vendorData, boolean enablePeriodicRangingResults) { mServiceName = serviceName; mServiceSpecificInfo = serviceSpecificInfo; mMatchFilter = matchFilter; @@ -131,6 +136,7 @@ public final class PublishConfig implements Parcelable { mPairingConfig = pairingConfig; mIsSuspendable = isSuspendable; mVendorData = vendorData; + mEnablePeriodicRangingResults = enablePeriodicRangingResults; } @Override @@ -152,7 +158,8 @@ public final class PublishConfig implements Parcelable { + ", mSecurityConfig" + mSecurityConfig + ", mPairingConfig" + mPairingConfig + ", mIsSuspendable=" + mIsSuspendable - + ", mVendorData=" + mVendorData + "]"; + + ", mVendorData=" + mVendorData + "]" + + ", mEnablePeriodicRangingResults=" + mEnablePeriodicRangingResults; } @Override @@ -175,6 +182,7 @@ public final class PublishConfig implements Parcelable { dest.writeParcelable(mPairingConfig, flags); dest.writeBoolean(mIsSuspendable); dest.writeList(mVendorData); + dest.writeBoolean(mEnablePeriodicRangingResults); } @NonNull @@ -201,10 +209,12 @@ public final class PublishConfig implements Parcelable { .readParcelable(AwarePairingConfig.class.getClassLoader()); boolean isSuspendable = in.readBoolean(); List<OuiKeyedData> vendorData = ParcelUtil.readOuiKeyedDataList(in); + boolean enablePeriodicRangingResults = in.readBoolean(); return new PublishConfig(serviceName, ssi, matchFilter, publishType, ttlSec, enableTerminateNotification, enableRanging, enableInstantMode, - band, securityConfig, pairingConfig, isSuspendable, vendorData); + band, securityConfig, pairingConfig, isSuspendable, vendorData, + enablePeriodicRangingResults); } }; @@ -226,6 +236,7 @@ public final class PublishConfig implements Parcelable { && mTtlSec == lhs.mTtlSec && mEnableTerminateNotification == lhs.mEnableTerminateNotification && mEnableRanging == lhs.mEnableRanging + && mEnablePeriodicRangingResults == lhs.mEnablePeriodicRangingResults && mEnableInstantMode == lhs.mEnableInstantMode && mBand == lhs.mBand && mIsSuspendable == lhs.mIsSuspendable @@ -239,7 +250,7 @@ public final class PublishConfig implements Parcelable { return Objects.hash(Arrays.hashCode(mServiceName), Arrays.hashCode(mServiceSpecificInfo), Arrays.hashCode(mMatchFilter), mPublishType, mTtlSec, mEnableTerminateNotification, mEnableRanging, mEnableInstantMode, mBand, mSecurityConfig, mPairingConfig, - mIsSuspendable, mVendorData); + mIsSuspendable, mVendorData, mEnablePeriodicRangingResults); } /** @@ -310,6 +321,11 @@ public final class PublishConfig implements Parcelable { if (!rttSupported && mEnableRanging) { throw new IllegalArgumentException("Ranging is not supported"); } + + if ((!rttSupported || !characteristics.isPeriodicRangingSupported()) + && mEnablePeriodicRangingResults) { + throw new IllegalArgumentException("Periodic Ranging is not supported"); + } } /** @@ -387,6 +403,18 @@ public final class PublishConfig implements Parcelable { } /** + * Check if periodic ranging reporting is enabled for publish session + * @see Builder#setPeriodicRangingResultsEnabled(boolean) + * @return true for enabled, false otherwise. + * @hide + */ + @FlaggedApi(FLAG_RANGING_RTT_ENABLED) + @SystemApi + public boolean isPeriodicRangingResultsEnabled() { + return mEnablePeriodicRangingResults; + } + + /** * Builder used to build {@link PublishConfig} objects. */ public static final class Builder { @@ -403,6 +431,7 @@ public final class PublishConfig implements Parcelable { private AwarePairingConfig mPairingConfig = null; private boolean mIsSuspendable = false; private @NonNull List<OuiKeyedData> mVendorData = Collections.emptyList(); + private boolean mEnablePeriodicRangingResults = false; /** * Specify the service name of the publish session. The actual on-air @@ -555,6 +584,33 @@ public final class PublishConfig implements Parcelable { } /** + * Configure whether periodic ranging results need to be notified to Publisher + * <p> + * Optional. Disabled by default - i.e. any ranging result will not be notified to + * the Publisher. + * <p> + * The device must support Periodic Ranging for this feature to be used. + * Feature support check is determined by + * {@link Characteristics#isPeriodicRangingSupported()}. + * <p> + * The ranging result will be notified to Publisher via + * {@link DiscoverySessionCallback#onRangingResultsReceived(RangingResults)}. + * + * @param enable If true, ranging result will be notified to Publisher. + * + * @return The builder to facilitate chaining + * {@code builder.setXXX(..).setXXX(..)}. + * @hide + */ + @FlaggedApi(FLAG_RANGING_RTT_ENABLED) + @SystemApi + @NonNull + public Builder setPeriodicRangingResultsEnabled(boolean enable) { + mEnablePeriodicRangingResults = enable; + return this; + } + + /** * Configure whether to enable and use instant communication for this publish session. * Instant communication will speed up service discovery and any data-path set up as part of * this session. Use {@link Characteristics#isInstantCommunicationModeSupported()} to check @@ -694,7 +750,8 @@ public final class PublishConfig implements Parcelable { public PublishConfig build() { return new PublishConfig(mServiceName, mServiceSpecificInfo, mMatchFilter, mPublishType, mTtlSec, mEnableTerminateNotification, mEnableRanging, mEnableInstantMode, - mBand, mSecurityConfig, mPairingConfig, mIsSuspendable, mVendorData); + mBand, mSecurityConfig, mPairingConfig, mIsSuspendable, mVendorData, + mEnablePeriodicRangingResults); } } } diff --git a/framework/java/android/net/wifi/aware/SubscribeConfig.java b/framework/java/android/net/wifi/aware/SubscribeConfig.java index ea99d819cd..13f290ebe2 100644 --- a/framework/java/android/net/wifi/aware/SubscribeConfig.java +++ b/framework/java/android/net/wifi/aware/SubscribeConfig.java @@ -18,8 +18,11 @@ package android.net.wifi.aware; import static android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION; +import static com.android.ranging.flags.Flags.FLAG_RANGING_RTT_ENABLED; + import android.annotation.FlaggedApi; import android.annotation.IntDef; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -27,7 +30,10 @@ import android.annotation.SystemApi; import android.net.wifi.OuiKeyedData; import android.net.wifi.ParcelUtil; import android.net.wifi.ScanResult; +import android.net.wifi.WifiAnnotations; import android.net.wifi.WifiScanner; +import android.net.wifi.rtt.RangingRequest; +import android.net.wifi.rtt.ResponderConfig; import android.net.wifi.util.HexEncoding; import android.os.Build; import android.os.Parcel; @@ -76,6 +82,97 @@ public final class SubscribeConfig implements Parcelable { */ public static final int SUBSCRIBE_TYPE_ACTIVE = 1; + private static final int AWARE_BAND_2_DISCOVERY_CHANNEL = 2437; + private static final int MIN_RTT_BURST_SIZE = RangingRequest.getMinRttBurstSize(); + private static final int MAX_RTT_BURST_SIZE = RangingRequest.getMaxRttBurstSize(); + + /** + * Ranging Interval's are in binary Time Unit (TU). As per IEEE 802.11-1999 1 TU equals + * 1024 microseconds. + * + * @hide + */ + @IntDef({ + PERIODIC_RANGING_INTERVAL_NONE, PERIODIC_RANGING_INTERVAL_128TU, + PERIODIC_RANGING_INTERVAL_256TU, PERIODIC_RANGING_INTERVAL_512TU, + PERIODIC_RANGING_INTERVAL_1024TU, PERIODIC_RANGING_INTERVAL_2048TU, + PERIODIC_RANGING_INTERVAL_4096TU, PERIODIC_RANGING_INTERVAL_8192TU}) + @Retention(RetentionPolicy.SOURCE) + public @interface PeriodicRangingInterval { + } + + /** + * Ranging is not repeated + * + * @hide + */ + @FlaggedApi(FLAG_RANGING_RTT_ENABLED) + @SystemApi + public static final int PERIODIC_RANGING_INTERVAL_NONE = 0; + + /** + * Ranging interval is 128TU [= (128 * 1024) / 1000 = 131.072 ms] + * + * @hide + */ + @FlaggedApi(FLAG_RANGING_RTT_ENABLED) + @SystemApi + public static final int PERIODIC_RANGING_INTERVAL_128TU = 128; + + /** + * Ranging interval is 256TU [= (256 * 1024) / 1000 = 262.144 ms] + * + * @hide + */ + @FlaggedApi(FLAG_RANGING_RTT_ENABLED) + @SystemApi + public static final int PERIODIC_RANGING_INTERVAL_256TU = 256; + + /** + * Ranging interval is 512TU [= (512 * 1024) / 1000 = 524.288 ms] + * + * @hide + */ + @FlaggedApi(FLAG_RANGING_RTT_ENABLED) + @SystemApi + public static final int PERIODIC_RANGING_INTERVAL_512TU = 512; + + /** + * Ranging interval is 1024TU [= (1024 * 1024) / 1000 = 1048.576 ms] + * + * @hide + */ + @FlaggedApi(FLAG_RANGING_RTT_ENABLED) + @SystemApi + public static final int PERIODIC_RANGING_INTERVAL_1024TU = 1024; + + /** + * Ranging interval is 2048TU [= (2048 * 1024) / 1000 = 2097.152 ms] + * + * @hide + */ + @FlaggedApi(FLAG_RANGING_RTT_ENABLED) + @SystemApi + public static final int PERIODIC_RANGING_INTERVAL_2048TU = 2048; + + /** + * Ranging interval is 4096TU [= (4096 * 1024) / 1000 = 4194.304 ms] + * + * @hide + */ + @FlaggedApi(FLAG_RANGING_RTT_ENABLED) + @SystemApi + public static final int PERIODIC_RANGING_INTERVAL_4096TU = 4096; + + /** + * Ranging interval is 8192TU [= (8192 * 1024) / 1000 = 8388.608 ms] + * + * @hide + */ + @FlaggedApi(FLAG_RANGING_RTT_ENABLED) + @SystemApi + public static final int PERIODIC_RANGING_INTERVAL_8192TU = 8192; + /** @hide */ public final byte[] mServiceName; @@ -116,12 +213,39 @@ public final class SubscribeConfig implements Parcelable { private final List<OuiKeyedData> mVendorData; /** @hide */ + public final int mPeriodicRangingInterval; + + /** @hide */ + public final boolean mPeriodicRangingEnabled; + + /** @hide */ + public final int mRttBurstSize; + + /** @hide */ + public final int mFrequencyMhz; + + /** @hide */ + public final int mCenterFrequency0Mhz; + + /** @hide */ + public final int mCenterFrequency1Mhz; + + /** @hide */ + public final int mPreamble; + + /** @hide */ + public final int mChannelWidth; + + /** @hide */ public SubscribeConfig(byte[] serviceName, byte[] serviceSpecificInfo, byte[] matchFilter, int subscribeType, int ttlSec, boolean enableTerminateNotification, boolean minDistanceMmSet, int minDistanceMm, boolean maxDistanceMmSet, int maxDistanceMm, boolean enableInstantMode, @WifiScanner.WifiBand int band, AwarePairingConfig pairingConfig, boolean isSuspendable, - @NonNull List<OuiKeyedData> vendorData) { + @NonNull List<OuiKeyedData> vendorData, int rangingInterval, + boolean enablePeriodicRanging, int rttBurstSize, int frequencyMhz, + int centerFrequency0Mhz, int centerFrequency1Mhz, int preamble, + int channelWidth) { mServiceName = serviceName; mServiceSpecificInfo = serviceSpecificInfo; mMatchFilter = matchFilter; @@ -137,6 +261,14 @@ public final class SubscribeConfig implements Parcelable { mPairingConfig = pairingConfig; mIsSuspendable = isSuspendable; mVendorData = vendorData; + mPeriodicRangingInterval = rangingInterval; + mPeriodicRangingEnabled = enablePeriodicRanging; + mRttBurstSize = rttBurstSize; + mFrequencyMhz = frequencyMhz; + mCenterFrequency0Mhz = centerFrequency0Mhz; + mCenterFrequency1Mhz = centerFrequency1Mhz; + mPreamble = preamble; + mChannelWidth = channelWidth; } @Override @@ -160,7 +292,15 @@ public final class SubscribeConfig implements Parcelable { + ", mBand=" + mBand + ", mPairingConfig" + mPairingConfig + ", mIsSuspendable=" + mIsSuspendable - + ", mVendorData=" + mVendorData + "]"; + + ", mVendorData=" + mVendorData + "]" + + ", mPeriodicRangingInterval" + mPeriodicRangingInterval + + ", mPeriodicRangingEnabled" + mPeriodicRangingEnabled + + ", mRttBurstSize" + mRttBurstSize + + ", mFrequencyMhz" + mFrequencyMhz + + ", mCenterFrequency0Mhz" + mCenterFrequency0Mhz + + ", mCenterFrequency1Mhz" + mCenterFrequency1Mhz + + ", mPreamble" + mPreamble + + ", mChannelWidth" + mChannelWidth; } @Override @@ -185,6 +325,14 @@ public final class SubscribeConfig implements Parcelable { dest.writeParcelable(mPairingConfig, flags); dest.writeBoolean(mIsSuspendable); dest.writeList(mVendorData); + dest.writeInt(mPeriodicRangingInterval); + dest.writeBoolean(mPeriodicRangingEnabled); + dest.writeInt(mRttBurstSize); + dest.writeInt(mFrequencyMhz); + dest.writeInt(mCenterFrequency0Mhz); + dest.writeInt(mCenterFrequency1Mhz); + dest.writeInt(mPreamble); + dest.writeInt(mChannelWidth); } @NonNull @@ -212,11 +360,21 @@ public final class SubscribeConfig implements Parcelable { AwarePairingConfig.class.getClassLoader()); boolean isSuspendable = in.readBoolean(); List<OuiKeyedData> vendorData = ParcelUtil.readOuiKeyedDataList(in); + int rangingInterval = in.readInt(); + boolean enablePeriodicRanging = in.readBoolean(); + int burstSize = in.readInt(); + int frequencyMhz = in.readInt(); + int centerFrequency0Mhz = in.readInt(); + int centerFrequency1Mhz = in.readInt(); + int preamble = in.readInt(); + int channelWidth = in.readInt(); return new SubscribeConfig(serviceName, ssi, matchFilter, subscribeType, ttlSec, enableTerminateNotification, minDistanceMmSet, minDistanceMm, maxDistanceMmSet, maxDistanceMm, enableInstantMode, band, pairingConfig, isSuspendable, - vendorData); + vendorData, rangingInterval, enablePeriodicRanging, burstSize, + frequencyMhz, centerFrequency0Mhz, centerFrequency1Mhz, preamble, + channelWidth); } }; @@ -241,7 +399,14 @@ public final class SubscribeConfig implements Parcelable { && mEnableInstantMode == lhs.mEnableInstantMode && mBand == lhs.mBand && mIsSuspendable == lhs.mIsSuspendable - && Objects.equals(mVendorData, lhs.mVendorData))) { + && Objects.equals(mVendorData, lhs.mVendorData) + && mPeriodicRangingEnabled == lhs.mPeriodicRangingEnabled + && mRttBurstSize == lhs.mRttBurstSize + && mFrequencyMhz == lhs.mFrequencyMhz + && mCenterFrequency0Mhz == lhs.mCenterFrequency0Mhz + && mCenterFrequency1Mhz == lhs.mCenterFrequency1Mhz + && mPreamble == lhs.mPreamble + && mChannelWidth == lhs.mChannelWidth)) { return false; } @@ -253,6 +418,9 @@ public final class SubscribeConfig implements Parcelable { return false; } + if (mPeriodicRangingEnabled && mPeriodicRangingInterval != lhs.mPeriodicRangingInterval) { + return false; + } return true; } @@ -261,7 +429,9 @@ public final class SubscribeConfig implements Parcelable { int result = Objects.hash(Arrays.hashCode(mServiceName), Arrays.hashCode(mServiceSpecificInfo), Arrays.hashCode(mMatchFilter), mSubscribeType, mTtlSec, mEnableTerminateNotification, mMinDistanceMmSet, - mMaxDistanceMmSet, mEnableInstantMode, mBand, mIsSuspendable, mVendorData); + mMaxDistanceMmSet, mEnableInstantMode, mBand, mIsSuspendable, mVendorData, + mPeriodicRangingEnabled, mRttBurstSize, mFrequencyMhz, mCenterFrequency0Mhz, + mCenterFrequency1Mhz, mPreamble, mChannelWidth); if (mMinDistanceMmSet) { result = Objects.hash(result, mMinDistanceMm); @@ -269,6 +439,9 @@ public final class SubscribeConfig implements Parcelable { if (mMaxDistanceMmSet) { result = Objects.hash(result, mMaxDistanceMm); } + if (mPeriodicRangingEnabled) { + result = Objects.hash(result, mPeriodicRangingInterval); + } return result; } @@ -339,9 +512,39 @@ public final class SubscribeConfig implements Parcelable { "Maximum distance must be greater than minimum distance"); } + if (mPeriodicRangingEnabled && (mMinDistanceMmSet || mMaxDistanceMmSet)) { + throw new IllegalArgumentException( + "Either Periodic Ranging or Min/Max distance is allowed. Not both."); + } + if (!rttSupported && (mMinDistanceMmSet || mMaxDistanceMmSet)) { throw new IllegalArgumentException("Ranging is not supported"); } + if ((!rttSupported || !characteristics.isPeriodicRangingSupported()) + && mPeriodicRangingEnabled) { + throw new IllegalArgumentException("Periodic ranging is not supported"); + } + if (mPeriodicRangingEnabled && mPeriodicRangingInterval < 0) { + throw new IllegalArgumentException("Periodic ranging interval must be non-negative"); + } + if (mPeriodicRangingEnabled && mRttBurstSize < 0) { + throw new IllegalArgumentException("Rtt Burst size must be non-negative"); + } + if (mPeriodicRangingEnabled && mFrequencyMhz < 0) { + throw new IllegalArgumentException(" Frequency must be non-negative"); + } + if (mPeriodicRangingEnabled && mCenterFrequency0Mhz < 0) { + throw new IllegalArgumentException("Center Frequency0 must be non-negative"); + } + if (mPeriodicRangingEnabled && mCenterFrequency1Mhz < 0) { + throw new IllegalArgumentException("Center Frequency1 must be non-negative"); + } + if (mPeriodicRangingEnabled && mPreamble < 0) { + throw new IllegalArgumentException("Preamble must be non-negative"); + } + if (mPeriodicRangingEnabled && mChannelWidth < 0) { + throw new IllegalArgumentException("Channel width must be non-negative"); + } } /** @@ -409,6 +612,108 @@ public final class SubscribeConfig implements Parcelable { } /** + * Check if periodic range reporting is enabled for subscribe session + * @see Builder#setPeriodicRangingEnabled(boolean) + * @return true for enabled, false otherwise. + * @hide + */ + @FlaggedApi(FLAG_RANGING_RTT_ENABLED) + @SystemApi + public boolean isPeriodicRangingEnabled() { + return mPeriodicRangingEnabled; + } + + /** + * Get periodic range reporting interval for subscribe session + * @see Builder#setPeriodicRangingInterval(int) + * @return interval of reporting. + * @hide + */ + @FlaggedApi(FLAG_RANGING_RTT_ENABLED) + @SystemApi + public @PeriodicRangingInterval int getPeriodicRangingInterval() { + return mPeriodicRangingInterval; + } + + /** + * Get the RTT burst size used to determine the average range. + * @see Builder#setRttBurstSize(int) + * @return the RTT burst size. + * @hide + */ + @FlaggedApi(FLAG_RANGING_RTT_ENABLED) + @SystemApi + public int getRttBurstSize() { + return mRttBurstSize; + } + + /** + * Get the frequency in MHz of the Wi-Fi channel + * @see Builder#setFrequencyMhz(int) + * @return frequency in MHz. + * @hide + */ + @FlaggedApi(FLAG_RANGING_RTT_ENABLED) + @SystemApi + @IntRange(from = 0) + public int getFrequencyMhz() { + return mFrequencyMhz; + } + + /** + * Get the center frequency in MHz of the first channel segment + * @see Builder#setCenterFreq0Mhz(int) + * @return the center frequency in MHz of the first channel segment. + * @hide + */ + @FlaggedApi(FLAG_RANGING_RTT_ENABLED) + @SystemApi + @IntRange(from = 0) + public int getCenterFreq0Mhz() { + return mCenterFrequency0Mhz; + } + + /** + * Get the center frequency in MHz of the second channel segment (if used) + * @see Builder#setCenterFreq1Mhz(int) + * @return the center frequency in MHz of the second channel segment + * @hide + */ + @FlaggedApi(FLAG_RANGING_RTT_ENABLED) + @SystemApi + @IntRange(from = 0) + public int getCenterFreq1Mhz() { + return mCenterFrequency1Mhz; + } + + /** + * Get the preamble type of the channel. + * @see Builder#setPreamble(int) + * @return the preamble used for this channel. + * @hide + */ + @FlaggedApi(FLAG_RANGING_RTT_ENABLED) + @SystemApi + public @WifiAnnotations.PreambleType int getPreamble() { + return ResponderConfig.translateFromLocalToScanResultPreamble(mPreamble); + } + + /** + * Channel bandwidth; one of {@link ScanResult#CHANNEL_WIDTH_20MHZ}, + * {@link ScanResult#CHANNEL_WIDTH_40MHZ}, + * {@link ScanResult#CHANNEL_WIDTH_80MHZ}, {@link ScanResult#CHANNEL_WIDTH_160MHZ}, + * {@link ScanResult #CHANNEL_WIDTH_80MHZ_PLUS_MHZ} or {@link ScanResult#CHANNEL_WIDTH_320MHZ}. + * @see Builder#setChannelWidth(int) + * @return the bandwidth repsentation of the Wi-Fi channel + * @hide + */ + @FlaggedApi(FLAG_RANGING_RTT_ENABLED) + @SystemApi + public @WifiAnnotations.ChannelWidth int getChannelWidth() { + return ResponderConfig.translateFromLocalToScanResultChannelWidth(mChannelWidth); + } + + /** * Builder used to build {@link SubscribeConfig} objects. */ public static final class Builder { @@ -427,6 +732,14 @@ public final class SubscribeConfig implements Parcelable { private AwarePairingConfig mPairingConfig; private boolean mIsSuspendable = false; private @NonNull List<OuiKeyedData> mVendorData = Collections.emptyList(); + private boolean mPeriodicRangingEnabled = false; + private int mPeriodicRangingInterval = PERIODIC_RANGING_INTERVAL_512TU; + private int mRttBurstSize = RangingRequest.getDefaultRttBurstSize(); + private int mFrequencyMhz = AWARE_BAND_2_DISCOVERY_CHANNEL; + private int mCenterFrequency0Mhz = 0; + private int mCenterFrequency1Mhz = 0; + private int mPreamble = ResponderConfig.PREAMBLE_HT; + private int mChannelWidth = ResponderConfig.CHANNEL_WIDTH_20MHZ; /** * Specify the service name of the subscribe session. The actual on-air @@ -724,6 +1037,194 @@ public final class SubscribeConfig implements Parcelable { } /** + * Configure the interval for Wifi Aware periodic ranging. + * <p> + * To get the periodic ranging support use + * {@link Characteristics#isPeriodicRangingSupported()} + * When interval is not configured, default interval {@link PERIODIC_RANGING_INTERVAL_512TU} + * is used. + * </p> + * + * @param interval Ranging interval as described in {@link PeriodicRangingInterval} + * @return The builder to facilitate chaining + * {@code builder.setXXX(..).setXXX(..)}. + * @hide + */ + @NonNull + @FlaggedApi(FLAG_RANGING_RTT_ENABLED) + @SystemApi + public Builder setPeriodicRangingInterval(@PeriodicRangingInterval int interval) { + if (interval != PERIODIC_RANGING_INTERVAL_NONE + && interval != PERIODIC_RANGING_INTERVAL_128TU + && interval != PERIODIC_RANGING_INTERVAL_256TU + && interval != PERIODIC_RANGING_INTERVAL_512TU + && interval != PERIODIC_RANGING_INTERVAL_1024TU + && interval != PERIODIC_RANGING_INTERVAL_2048TU + && interval != PERIODIC_RANGING_INTERVAL_4096TU + && interval != PERIODIC_RANGING_INTERVAL_8192TU) { + throw new IllegalArgumentException("Invalid Ranging interval - " + interval); + } + mPeriodicRangingInterval = interval; + return this; + } + + /** + * Enable Wifi Aware periodic ranging. + * <p> + * To get the periodic ranging support use + * {@link Characteristics#isPeriodicRangingSupported()} + * + * Wifi aware based periodic ranging allows continuous ranging report based on configured + * interval through {@link #setPeriodicRangingInterval()}. To stop continuous ranging + * results, reset the {@link #setPeriodicRangingEnabled()} and reconfigure using updated + * {@link SubscribeDiscoverySession#updateSubscribe(SubscribeConfig)} + * </p> + * + * @param enable Enable or disable periodic ranging report + * @return The builder to facilitate chaining + * {@code builder.setXXX(..).setXXX(..)}. + * @hide + */ + @NonNull + @FlaggedApi(FLAG_RANGING_RTT_ENABLED) + @SystemApi + public Builder setPeriodicRangingEnabled(boolean enable) { + mPeriodicRangingEnabled = enable; + return this; + } + + /** + * Set the RTT Burst size for the Aware Periodic Ranging. + * <p> + * If not set, the default RTT burst size given by + * {@link RangingRequest#getDefaultRttBurstSize()} is used to determine the default value. + * If set, the value must be in the range {@link RangingRequest#getMinRttBurstSize()} and + * {@link RangingRequest#getMaxRttBurstSize()} inclusively, or a + * {@link java.lang.IllegalArgumentException} will be thrown. + * </p> + * + * @param burstSize The number of FTM packets used to estimate a range + * @return The builder to facilitate chaining + * {@code builder.setXXX(..).setXXX(..)}. + * @hide + */ + @NonNull + @FlaggedApi(FLAG_RANGING_RTT_ENABLED) + @SystemApi + public Builder setRttBurstSize(int burstSize) { + if (burstSize < MIN_RTT_BURST_SIZE || burstSize > MAX_RTT_BURST_SIZE) { + throw new IllegalArgumentException("RTT burst size out of range."); + } + mRttBurstSize = burstSize; + return this; + } + + /** + * Sets the frequency of the channel in MHz. + * <p> + * Note: The frequency is used as a hint, and the underlying WiFi subsystem may use it, or + * select an alternate if its own connectivity scans have determined the frequency of the + * Peer/Publisher has changed. + * </p> + * + * @param frequency the frequency of the channel in MHz + * @return The builder to facilitate chaining + * {@code builder.setXXX(..).setXXX(..)}. + * @hide + */ + @NonNull + @FlaggedApi(FLAG_RANGING_RTT_ENABLED) + @SystemApi + public Builder setFrequencyMhz(@IntRange(from = 0) int frequency) { + mFrequencyMhz = frequency; + return this; + } + + /** + * Sets the center frequency in MHz of the first segment of the channel. + * <p> + * Note: The frequency is used as a hint, and the underlying WiFi subsystem may use it, or + * select an alternate if its own connectivity scans have determined the frequency of the + * Peer/Publisher has changed. + * </p> + * + * @param centerFreq0 the center frequency in MHz of first channel segment + * @return The builder to facilitate chaining + * {@code builder.setXXX(..).setXXX(..)}. + * @hide + */ + @NonNull + @FlaggedApi(FLAG_RANGING_RTT_ENABLED) + @SystemApi + public Builder setCenterFreq0Mhz(@IntRange(from = 0) int centerFreq0) { + mCenterFrequency0Mhz = centerFreq0; + return this; + } + + /** + * Sets the center frequency in MHz of the second segment of the channel, if used. + * <p> + * Note: The frequency is used as a hint, and the underlying WiFi subsystem may use it, or + * select an alternate if its own connectivity scans have determined the frequency of the + * Peer/Publisher has changed. + * </p> + * + * @param centerFreq1 the center frequency in MHz of second channel segment + * @return The builder to facilitate chaining + * {@code builder.setXXX(..).setXXX(..)}. + * @hide + */ + @NonNull + @FlaggedApi(FLAG_RANGING_RTT_ENABLED) + @SystemApi + public Builder setCenterFreq1Mhz(@IntRange(from = 0) int centerFreq1) { + mCenterFrequency1Mhz = centerFreq1; + return this; + } + + /** + * Sets the preamble encoding for the protocol. + * <p> + * Note: The preamble is used as a hint, and the underlying WiFi subsystem may use it, or + * select an alternate based on negotiation of Peer capability or concurrency management. + * </p> + * + * @param preamble the preamble encoding + * @return The builder to facilitate chaining + * {@code builder.setXXX(..).setXXX(..)}. + * @hide + */ + @NonNull + @FlaggedApi(FLAG_RANGING_RTT_ENABLED) + @SystemApi + public Builder setPreamble(@WifiAnnotations.PreambleType int preamble) { + mPreamble = ResponderConfig.translateFromScanResultToLocalPreamble(preamble); + return this; + } + + /** + * Sets the channel bandwidth. + * <p> + * Note: The channel bandwidth is used as a hint, and the underlying WiFi subsystem may use + * it, or select an alternate based on negotiation of Peer capability or concurrency + * management. + * </p> + * + * @param channelWidth the bandwidth of the channel in MHz + * @return The builder to facilitate chaining + * {@code builder.setXXX(..).setXXX(..)}. + * @hide + */ + @NonNull + @FlaggedApi(FLAG_RANGING_RTT_ENABLED) + @SystemApi + public Builder setChannelWidth(@WifiAnnotations.ChannelWidth int channelWidth) { + mChannelWidth = + ResponderConfig.translateFromScanResultToLocalChannelWidth(channelWidth); + return this; + } + + /** * Build {@link SubscribeConfig} given the current requests made on the * builder. */ @@ -731,7 +1232,11 @@ public final class SubscribeConfig implements Parcelable { return new SubscribeConfig(mServiceName, mServiceSpecificInfo, mMatchFilter, mSubscribeType, mTtlSec, mEnableTerminateNotification, mMinDistanceMmSet, mMinDistanceMm, mMaxDistanceMmSet, mMaxDistanceMm, - mEnableInstantMode, mBand, mPairingConfig, mIsSuspendable, mVendorData); + mEnableInstantMode, mBand, mPairingConfig, mIsSuspendable, mVendorData, + mPeriodicRangingInterval, mPeriodicRangingEnabled, mRttBurstSize, + mFrequencyMhz, mCenterFrequency0Mhz, mCenterFrequency1Mhz, mPreamble, + mChannelWidth); + } } } diff --git a/framework/java/android/net/wifi/aware/WifiAwareManager.java b/framework/java/android/net/wifi/aware/WifiAwareManager.java index 2edf792b16..d74052c38f 100644 --- a/framework/java/android/net/wifi/aware/WifiAwareManager.java +++ b/framework/java/android/net/wifi/aware/WifiAwareManager.java @@ -45,6 +45,7 @@ import android.net.wifi.IIntegerListener; import android.net.wifi.IListListener; import android.net.wifi.OuiKeyedData; import android.net.wifi.WifiManager; +import android.net.wifi.rtt.RangingResult; import android.net.wifi.util.HexEncoding; import android.os.Binder; import android.os.Build; @@ -1227,6 +1228,11 @@ public class WifiAwareManager { } } + @Override + public void onRangingResultsReceived(List<RangingResult> rangingResults) { + mHandler.post(() -> mOriginalCallback.onRangingResultsReceived(rangingResults)); + } + /* * Proxies methods */ diff --git a/framework/java/android/net/wifi/hotspot2/pps/Credential.java b/framework/java/android/net/wifi/hotspot2/pps/Credential.java index 658588ae8b..25d20f14ae 100644 --- a/framework/java/android/net/wifi/hotspot2/pps/Credential.java +++ b/framework/java/android/net/wifi/hotspot2/pps/Credential.java @@ -145,7 +145,7 @@ public final class Credential implements Parcelable { * Maximum string length for username. Refer to Credential/UsernamePassword/Username * node in Hotspot 2.0 Release 2 Technical Specification Section 9.1 for more info. */ - private static final int MAX_USERNAME_BYTES = 63; + private static final int MAX_USERNAME_BYTES = 253; /** * Maximum string length for password. Refer to Credential/UsernamePassword/Password diff --git a/framework/java/android/net/wifi/p2p/WifiP2pConfig.java b/framework/java/android/net/wifi/p2p/WifiP2pConfig.java index d2775fee1b..8e0805797f 100644 --- a/framework/java/android/net/wifi/p2p/WifiP2pConfig.java +++ b/framework/java/android/net/wifi/p2p/WifiP2pConfig.java @@ -27,6 +27,7 @@ import android.net.MacAddress; import android.net.wifi.OuiKeyedData; import android.net.wifi.ParcelUtil; import android.net.wifi.WpsInfo; +import android.net.wifi.util.Environment; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -43,6 +44,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.regex.PatternSyntaxException; /** @@ -52,6 +54,10 @@ import java.util.regex.PatternSyntaxException; */ public class WifiP2pConfig implements Parcelable { + static final int PSK_PASSWORD_MIN_LEN = 8; + static final int PSK_PASSWORD_MAX_LEN = 63; + static final int SAE_PASSWORD_MAX_LEN = 128; + /** * The device MAC address uniquely identifies a Wi-Fi p2p device */ @@ -85,7 +91,8 @@ public class WifiP2pConfig implements Parcelable { * The result will be one of the following: * {@link #GROUP_OWNER_BAND_AUTO}, * {@link #GROUP_OWNER_BAND_2GHZ}, - * {@link #GROUP_OWNER_BAND_5GHZ} + * {@link #GROUP_OWNER_BAND_5GHZ}, + * {@link #GROUP_OWNER_BAND_6GHZ} */ @GroupOperatingBandType public int getGroupOwnerBand() { @@ -98,9 +105,10 @@ public class WifiP2pConfig implements Parcelable { /** @hide */ @IntDef(flag = false, prefix = { "GROUP_OWNER_BAND_" }, value = { - GROUP_OWNER_BAND_AUTO, - GROUP_OWNER_BAND_2GHZ, - GROUP_OWNER_BAND_5GHZ + GROUP_OWNER_BAND_AUTO, + GROUP_OWNER_BAND_2GHZ, + GROUP_OWNER_BAND_5GHZ, + GROUP_OWNER_BAND_6GHZ, }) @Retention(RetentionPolicy.SOURCE) public @interface GroupOperatingBandType {} @@ -127,6 +135,11 @@ public class WifiP2pConfig implements Parcelable { * Allow the system to pick the operating frequency from the 5 GHz band. */ public static final int GROUP_OWNER_BAND_5GHZ = 2; + /** + * Allow the system to pick the operating frequency from the 6 GHz band. + */ + @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) + public static final int GROUP_OWNER_BAND_6GHZ = 3; /** * The least inclination to be a group owner, to be filled in the field @@ -243,6 +256,165 @@ public class WifiP2pConfig implements Parcelable { return mVendorData; } + /** + * Default connection type used internally by the P2P service. + * + * @hide + */ + public static final int PCC_MODE_DEFAULT_CONNECTION_TYPE_LEGACY_ONLY = 0; + + /** + * Legacy connection type. + * <p>Group Owner: Configured to support WPA2-Personal connections. + * <p>Group Client: Configured to connect to Group Owner using WPA2-Personal. + */ + @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) + public static final int PCC_MODE_CONNECTION_TYPE_LEGACY_ONLY = + PCC_MODE_DEFAULT_CONNECTION_TYPE_LEGACY_ONLY; + + /** + * Wi-Fi Direct R1/R2 compatible mode connection type. + * <p>Group Owner: Configured in WPA3-Personal Compatibility Mode to support WPA3-Personal and + * WPA2-Personal connections simultaneously. + * <p>Group Client: Configured to connect to Group Owner using WPA3-Personal or WPA2-Personal. + * The system will choose WPA3-Personal if Group Owner support WPA3-Personal. + */ + @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) + public static final int PCC_MODE_CONNECTION_TYPE_LEGACY_OR_R2 = 1; + + /** + * This configuration allows only Wi-Fi Direct R2 supported devices to establish connection. + * <p>Group Owner: Configured to support WPA3-Personal connections. + * <p>Group Client: Configured to connect to Group Owner using WPA3-Personal. + */ + @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) + public static final int PCC_MODE_CONNECTION_TYPE_R2_ONLY = 2; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "PCC_MODE_CONNECTION_TYPE_" }, value = { + PCC_MODE_DEFAULT_CONNECTION_TYPE_LEGACY_ONLY, + PCC_MODE_CONNECTION_TYPE_LEGACY_ONLY, + PCC_MODE_CONNECTION_TYPE_LEGACY_OR_R2, + PCC_MODE_CONNECTION_TYPE_R2_ONLY, + }) + public @interface PccModeConnectionType {} + + @PccModeConnectionType + private int mPccModeConnectionType = PCC_MODE_DEFAULT_CONNECTION_TYPE_LEGACY_ONLY; + + /** + * Get the PCC Mode connection type. + * + * @return One of the {@code PCC_MODE_CONNECTION_TYPE_*}. + */ + @RequiresApi(Build.VERSION_CODES.BAKLAVA) + @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) + public @PccModeConnectionType int getPccModeConnectionType() { + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + return mPccModeConnectionType; + } + + /** + * Default P2P version used internally by the P2P service. + * + * @hide + */ + public static final int P2P_DEFAULT_VERSION_1 = 0; + + /** + * P2P Protocol version 1 + */ + @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) + public static final int P2P_VERSION_1 = P2P_DEFAULT_VERSION_1; + + /** + * P2P Protocol version 2 + */ + @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) + public static final int P2P_VERSION_2 = 1; + + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = false, prefix = { "P2P_VERSION_" }, value = { + P2P_DEFAULT_VERSION_1, + P2P_VERSION_1, + P2P_VERSION_2, + }) + public @interface P2pVersion {} + + @P2pVersion + private int mGroupOwnerVersion = P2P_DEFAULT_VERSION_1; + + /** + * Get the P2P Group Owner version. + * See also {@link #setGroupOwnerVersion(int)}. + * + * @return The P2P Group Owner protocol version. + */ + @RequiresApi(Build.VERSION_CODES.BAKLAVA) + @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) + public @P2pVersion int getGroupOwnerVersion() { + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + return mGroupOwnerVersion; + } + + /** + * Set the P2P Group Owner version. + * + * @param version The P2P Group Owner protocol version. + */ + @RequiresApi(Build.VERSION_CODES.BAKLAVA) + @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) + public void setGroupOwnerVersion( + @P2pVersion int version) { + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + mGroupOwnerVersion = version; + } + + private @Nullable WifiP2pPairingBootstrappingConfig mPairingBootstrappingConfig; + + /** + * Get the pairing bootstrapping configuration , or null if unset. + */ + @RequiresApi(Build.VERSION_CODES.BAKLAVA) + @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) + @Nullable + public WifiP2pPairingBootstrappingConfig getPairingBootstrappingConfig() { + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + return mPairingBootstrappingConfig; + } + + /** + * Used to authorize a connection request from the peer device. + */ + private boolean mIsAuthorizeConnectionFromPeerEnabled = false; + + /** + * Query to check if the configuration is for authorizing a connection request + * from the peer device. @see {@link Builder#setAuthorizeConnectionFromPeerEnabled(boolean)} + * + * @return true if configured to authorize a connection request from the Peer device, + * False otherwise. + */ + @RequiresApi(Build.VERSION_CODES.BAKLAVA) + @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) + public boolean isAuthorizeConnectionFromPeerEnabled() { + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + return mIsAuthorizeConnectionFromPeerEnabled; + } + public WifiP2pConfig() { //set defaults wps = new WpsInfo(); @@ -315,10 +487,19 @@ public class WifiP2pConfig implements Parcelable { sbuf.append("\n networkName: ").append(networkName); sbuf.append("\n passphrase: ").append( TextUtils.isEmpty(passphrase) ? "<empty>" : "<non-empty>"); + sbuf.append("\n pccModeConnectionType: ").append(mPccModeConnectionType); sbuf.append("\n groupOwnerBand: ").append(groupOwnerBand); sbuf.append("\n groupClientIpProvisioningMode: ").append(mGroupClientIpProvisioningMode); sbuf.append("\n joinExistingGroup: ").append(mJoinExistingGroup); sbuf.append("\n vendorData: ").append(mVendorData); + sbuf.append("\n Group Owner Version: ").append(mGroupOwnerVersion); + if (Environment.isSdkAtLeastB() && Flags.wifiDirectR2()) { + sbuf.append("\n Pairing bootstrapping config : ") + .append((mPairingBootstrappingConfig == null) + ? "<null>" : mPairingBootstrappingConfig.toString()); + } + sbuf.append("\n authorizeConnectionFromPeerEnabled: ") + .append(mIsAuthorizeConnectionFromPeerEnabled); return sbuf.toString(); } @@ -336,10 +517,14 @@ public class WifiP2pConfig implements Parcelable { netId = source.netId; networkName = source.networkName; passphrase = source.passphrase; + mPccModeConnectionType = source.mPccModeConnectionType; groupOwnerBand = source.groupOwnerBand; mGroupClientIpProvisioningMode = source.mGroupClientIpProvisioningMode; mJoinExistingGroup = source.mJoinExistingGroup; mVendorData = new ArrayList<>(source.mVendorData); + mGroupOwnerVersion = source.mGroupOwnerVersion; + mPairingBootstrappingConfig = source.mPairingBootstrappingConfig; + mIsAuthorizeConnectionFromPeerEnabled = source.mIsAuthorizeConnectionFromPeerEnabled; } } @@ -351,10 +536,16 @@ public class WifiP2pConfig implements Parcelable { dest.writeInt(netId); dest.writeString(networkName); dest.writeString(passphrase); + dest.writeInt(mPccModeConnectionType); dest.writeInt(groupOwnerBand); dest.writeInt(mGroupClientIpProvisioningMode); dest.writeBoolean(mJoinExistingGroup); dest.writeList(mVendorData); + dest.writeInt(mGroupOwnerVersion); + if (Environment.isSdkAtLeastB() && Flags.wifiDirectR2()) { + dest.writeParcelable(mPairingBootstrappingConfig, flags); + } + dest.writeBoolean(mIsAuthorizeConnectionFromPeerEnabled); } /** Implement the Parcelable interface */ @@ -362,18 +553,25 @@ public class WifiP2pConfig implements Parcelable { public static final Creator<WifiP2pConfig> CREATOR = new Creator<WifiP2pConfig>() { public WifiP2pConfig createFromParcel(Parcel in) { - WifiP2pConfig config = new WifiP2pConfig(); - config.deviceAddress = in.readString(); - config.wps = (WpsInfo) in.readParcelable(null); - config.groupOwnerIntent = in.readInt(); - config.netId = in.readInt(); - config.networkName = in.readString(); - config.passphrase = in.readString(); - config.groupOwnerBand = in.readInt(); - config.mGroupClientIpProvisioningMode = in.readInt(); - config.mJoinExistingGroup = in.readBoolean(); - config.mVendorData = ParcelUtil.readOuiKeyedDataList(in); - return config; + WifiP2pConfig config = new WifiP2pConfig(); + config.deviceAddress = in.readString(); + config.wps = (WpsInfo) in.readParcelable(WpsInfo.class.getClassLoader()); + config.groupOwnerIntent = in.readInt(); + config.netId = in.readInt(); + config.networkName = in.readString(); + config.passphrase = in.readString(); + config.mPccModeConnectionType = in.readInt(); + config.groupOwnerBand = in.readInt(); + config.mGroupClientIpProvisioningMode = in.readInt(); + config.mJoinExistingGroup = in.readBoolean(); + config.mVendorData = ParcelUtil.readOuiKeyedDataList(in); + config.mGroupOwnerVersion = in.readInt(); + if (Environment.isSdkAtLeastB() && Flags.wifiDirectR2()) { + config.mPairingBootstrappingConfig = in.readParcelable( + WifiP2pPairingBootstrappingConfig.class.getClassLoader()); + } + config.mIsAuthorizeConnectionFromPeerEnabled = in.readBoolean(); + return config; } public WifiP2pConfig[] newArray(int size) { @@ -410,6 +608,10 @@ public class WifiP2pConfig implements Parcelable { private int mNetId = WifiP2pGroup.NETWORK_ID_TEMPORARY; private int mGroupClientIpProvisioningMode = GROUP_CLIENT_IP_PROVISIONING_MODE_IPV4_DHCP; private boolean mJoinExistingGroup = false; + @PccModeConnectionType + private int mPccModeConnectionType = PCC_MODE_DEFAULT_CONNECTION_TYPE_LEGACY_ONLY; + private @Nullable WifiP2pPairingBootstrappingConfig mPairingBootstrappingConfig; + private boolean mIsAuthorizeConnectionFromPeerEnabled = false; /** * Specify the peer's MAC address. If not set, the device will @@ -477,8 +679,11 @@ public class WifiP2pConfig implements Parcelable { /** * Specify the passphrase for creating or joining a group. * <p> - * The passphrase must be an ASCII string whose length is between 8 - * and 63. + * The passphrase must be an ASCII string whose length is, + * 1. Between 8 and 63 for {@link #PCC_MODE_CONNECTION_TYPE_LEGACY_ONLY} and + * {@link #PCC_MODE_CONNECTION_TYPE_LEGACY_OR_R2}. + * 2. Less than 128 for {@link #PCC_MODE_CONNECTION_TYPE_R2_ONLY}. + * * <p> * Must be called - an empty passphrase is not valid. * @@ -488,29 +693,60 @@ public class WifiP2pConfig implements Parcelable { */ @NonNull public Builder setPassphrase(@NonNull String passphrase) { + Objects.requireNonNull(passphrase, "passphrase cannot be null"); if (TextUtils.isEmpty(passphrase)) { throw new IllegalArgumentException( "passphrase must be non-empty."); } - if (passphrase.length() < 8 || passphrase.length() > 63) { + if (passphrase.length() > SAE_PASSWORD_MAX_LEN) { throw new IllegalArgumentException( - "The length of a passphrase must be between 8 and 63."); + "The length of a passphrase must be less than 128"); } mPassphrase = passphrase; return this; } /** + * Specifies the PCC Mode connection type. + * + * @param connectionType One of the {@code PCC_MODE_CONNECTION_TYPE_*}. + * @return Builder for chaining. + * + * @throws IllegalArgumentException when the connectionType is invalid. + */ + @RequiresApi(Build.VERSION_CODES.BAKLAVA) + @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) + @NonNull + public Builder setPccModeConnectionType( + @PccModeConnectionType int connectionType) { + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + switch (connectionType) { + case PCC_MODE_CONNECTION_TYPE_LEGACY_ONLY: + case PCC_MODE_CONNECTION_TYPE_LEGACY_OR_R2: + case PCC_MODE_CONNECTION_TYPE_R2_ONLY: + mPccModeConnectionType = connectionType; + break; + default: + throw new IllegalArgumentException( + "Invalid constant for the PCC Mode connection type!"); + } + return this; + } + + /** * Specify the band to use for creating the group or joining the group. The band should * be {@link #GROUP_OWNER_BAND_2GHZ}, {@link #GROUP_OWNER_BAND_5GHZ} or - * {@link #GROUP_OWNER_BAND_AUTO}. + * {@link #GROUP_OWNER_BAND_6GHZ} or {@link #GROUP_OWNER_BAND_AUTO}. * <p> * When creating a group as Group Owner using {@link * WifiP2pManager#createGroup(WifiP2pManager.Channel, * WifiP2pConfig, WifiP2pManager.ActionListener)}, * specifying {@link #GROUP_OWNER_BAND_AUTO} allows the system to pick the operating * frequency from all supported bands. - * Specifying {@link #GROUP_OWNER_BAND_2GHZ} or {@link #GROUP_OWNER_BAND_5GHZ} + * Specifying {@link #GROUP_OWNER_BAND_2GHZ} or {@link #GROUP_OWNER_BAND_5GHZ} or + * {@link #GROUP_OWNER_BAND_6GHZ} * only allows the system to pick the operating frequency in the specified band. * If the Group Owner cannot create a group in the specified band, the operation will fail. * <p> @@ -519,7 +755,8 @@ public class WifiP2pConfig implements Parcelable { * WifiP2pManager.ActionListener)}, * specifying {@link #GROUP_OWNER_BAND_AUTO} allows the system to scan all supported * frequencies to find the desired group. Specifying {@link #GROUP_OWNER_BAND_2GHZ} or - * {@link #GROUP_OWNER_BAND_5GHZ} only allows the system to scan the specified band. + * {@link #GROUP_OWNER_BAND_5GHZ} or {@link #GROUP_OWNER_BAND_6GHZ} only allows the + * system to scan the specified band. * <p> * {@link #setGroupOperatingBand(int)} and {@link #setGroupOperatingFrequency(int)} are * mutually exclusive. Setting operating band and frequency both is invalid. @@ -528,20 +765,21 @@ public class WifiP2pConfig implements Parcelable { * * @param band the operating band of the group. * This should be one of {@link #GROUP_OWNER_BAND_AUTO}, - * {@link #GROUP_OWNER_BAND_2GHZ}, {@link #GROUP_OWNER_BAND_5GHZ}. + * {@link #GROUP_OWNER_BAND_2GHZ}, {@link #GROUP_OWNER_BAND_5GHZ}, + * {@link #GROUP_OWNER_BAND_6GHZ}. * @return The builder to facilitate chaining * {@code builder.setXXX(..).setXXX(..)}. */ @NonNull public Builder setGroupOperatingBand(@GroupOperatingBandType int band) { - switch (band) { - case GROUP_OWNER_BAND_AUTO: - case GROUP_OWNER_BAND_2GHZ: - case GROUP_OWNER_BAND_5GHZ: - mGroupOperatingBand = band; - break; - default: - throw new IllegalArgumentException( + if (GROUP_OWNER_BAND_AUTO == band + || GROUP_OWNER_BAND_2GHZ == band + || GROUP_OWNER_BAND_5GHZ == band + || (Environment.isSdkAtLeastB() && Flags.wifiDirectR2() + && GROUP_OWNER_BAND_6GHZ == band)) { + mGroupOperatingBand = band; + } else { + throw new IllegalArgumentException( "Invalid constant for the group operating band!"); } return this; @@ -682,6 +920,54 @@ public class WifiP2pConfig implements Parcelable { } /** + * Set the pairing bootstrapping configuration for connecting using P2P pairing + * Protocol. + * + * @param config See {@link WifiP2pPairingBootstrappingConfig } + * @return The builder to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. + */ + @RequiresApi(Build.VERSION_CODES.BAKLAVA) + @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) + @NonNull + public Builder setPairingBootstrappingConfig( + @NonNull WifiP2pPairingBootstrappingConfig config) { + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + Objects.requireNonNull(config, "config cannot be null"); + mPairingBootstrappingConfig = config; + return this; + } + + /** + * Specify that the configuration is to authorize a connection request from a peer device. + * The MAC address of the peer device is specified using + * {@link WifiP2pConfig.Builder#setDeviceAddress(MacAddress)}. + * <p> + * Optional. false by default. The default configuration is to join a group or to initiate + * a group formation. + * <p> + * This configuration is typically used in Bluetooth LE assisted P2P pairing protocol + * defined in Wi-Fi Direct R2 specification, section 3.9. The collocated Bluetooth Provider + * sends the pairing password to the peer device (Seeker) and direct the system to + * authorize the connection request from the peer device using {@link + * WifiP2pManager#connect(WifiP2pManager.Channel, WifiP2pConfig, + * WifiP2pManager.ActionListener)}. The device will then wait for the connection request + * from the peer device. + * + * @param enabled true to authorize a connection request from the peer device, false to + * let the device join a group or form a group. + * @return The builder to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. + */ + @RequiresApi(Build.VERSION_CODES.BAKLAVA) + @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) + @NonNull + public Builder setAuthorizeConnectionFromPeerEnabled(boolean enabled) { + mIsAuthorizeConnectionFromPeerEnabled = enabled; + return this; + } + + /** * Build {@link WifiP2pConfig} given the current requests made on the builder. * @return {@link WifiP2pConfig} constructed based on builder method calls. */ @@ -695,7 +981,21 @@ public class WifiP2pConfig implements Parcelable { if (TextUtils.isEmpty(mNetworkName) && mDeviceAddress.equals(MAC_ANY_ADDRESS)) { throw new IllegalStateException( - "peer address must be set if network name and pasphrase are not set."); + "peer address must be set if network name and passphrase are not set."); + } + + if (!TextUtils.isEmpty(mNetworkName) + && !TextUtils.isEmpty(mPassphrase)) { + if (mPccModeConnectionType == PCC_MODE_CONNECTION_TYPE_LEGACY_ONLY + || mPccModeConnectionType == PCC_MODE_CONNECTION_TYPE_LEGACY_OR_R2) { + if (mPassphrase.length() < PSK_PASSWORD_MIN_LEN + || mPassphrase.length() > PSK_PASSWORD_MAX_LEN) { + throw new IllegalArgumentException( + "The length of a passphrase must be between " + + PSK_PASSWORD_MIN_LEN + " and " + + PSK_PASSWORD_MAX_LEN + " for legacy connection type"); + } + } } if (mGroupOperatingFrequency > 0 && mGroupOperatingBand > 0) { @@ -707,6 +1007,7 @@ public class WifiP2pConfig implements Parcelable { config.deviceAddress = mDeviceAddress.toString(); config.networkName = mNetworkName; config.passphrase = mPassphrase; + config.mPccModeConnectionType = mPccModeConnectionType; config.groupOwnerBand = GROUP_OWNER_BAND_AUTO; if (mGroupOperatingFrequency > 0) { config.groupOwnerBand = mGroupOperatingFrequency; @@ -716,6 +1017,13 @@ public class WifiP2pConfig implements Parcelable { config.netId = mNetId; config.mGroupClientIpProvisioningMode = mGroupClientIpProvisioningMode; config.mJoinExistingGroup = mJoinExistingGroup; + if (mPairingBootstrappingConfig != null) { + config.mPairingBootstrappingConfig = mPairingBootstrappingConfig; + config.mGroupClientIpProvisioningMode = + GROUP_CLIENT_IP_PROVISIONING_MODE_IPV6_LINK_LOCAL; + config.wps.setup = WpsInfo.INVALID; + } + config.mIsAuthorizeConnectionFromPeerEnabled = mIsAuthorizeConnectionFromPeerEnabled; return config; } } diff --git a/framework/java/android/net/wifi/p2p/WifiP2pDevice.java b/framework/java/android/net/wifi/p2p/WifiP2pDevice.java index 75ae83d286..78a13d1217 100644 --- a/framework/java/android/net/wifi/p2p/WifiP2pDevice.java +++ b/framework/java/android/net/wifi/p2p/WifiP2pDevice.java @@ -25,6 +25,7 @@ import android.net.MacAddress; import android.net.wifi.OuiKeyedData; import android.net.wifi.ParcelUtil; import android.net.wifi.ScanResult; +import android.net.wifi.util.Environment; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -214,6 +215,11 @@ public class WifiP2pDevice implements Parcelable { } return mVendorData; } + /** + * The bitmask of supported {@code PAIRING_BOOTSTRAPPING_METHOD_*} methods used to enable + * the pairing bootstrapping between bootstrapping initiator and a bootstrapping responder. + */ + private int mPairingBootstrappingMethods; public WifiP2pDevice() { } @@ -467,6 +473,110 @@ public class WifiP2pDevice implements Parcelable { mIpAddress = ipAddress; } + /** + * Returns true if opportunistic bootstrapping method is supported. + * Defined in Wi-Fi Alliance Wi-Fi Direct R2 Specification Table 10 - Bootstrapping Methods. + */ + @RequiresApi(Build.VERSION_CODES.BAKLAVA) + @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) + public boolean isOpportunisticBootstrappingMethodSupported() { + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + return (mPairingBootstrappingMethods & WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_OPPORTUNISTIC) != 0; + } + + /** + * Returns true if pin-code display bootstrapping method is supported. + * Defined in Wi-Fi Alliance Wi-Fi Direct R2 Specification Table 10 - Bootstrapping Methods. + */ + @RequiresApi(Build.VERSION_CODES.BAKLAVA) + @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) + public boolean isPinCodeDisplayBootstrappingMethodSupported() { + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + return (mPairingBootstrappingMethods & WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_DISPLAY_PINCODE) != 0; + } + + /** + * Returns true if passphrase display bootstrapping method is supported. + * Defined in Wi-Fi Alliance Wi-Fi Direct R2 Specification Table 10 - Bootstrapping Methods. + */ + @RequiresApi(Build.VERSION_CODES.BAKLAVA) + @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) + public boolean isPassphraseDisplayBootstrappingMethodSupported() { + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + return (mPairingBootstrappingMethods & WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_DISPLAY_PASSPHRASE) != 0; + } + + /** + * Returns true if pin-code keypad bootstrapping method is supported. + * Defined in Wi-Fi Alliance Wi-Fi Direct R2 Specification Table 10 - Bootstrapping Methods. + */ + @RequiresApi(Build.VERSION_CODES.BAKLAVA) + @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) + public boolean isPinCodeKeypadBootstrappingMethodSupported() { + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + return (mPairingBootstrappingMethods & WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PINCODE) != 0; + } + + /** + * Returns true if passphrase keypad bootstrapping method is supported. + * Defined in Wi-Fi Alliance Wi-Fi Direct R2 Specification Table 10 - Bootstrapping Methods. + */ + @RequiresApi(Build.VERSION_CODES.BAKLAVA) + @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) + public boolean isPassphraseKeypadBootstrappingMethodSupported() { + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + return (mPairingBootstrappingMethods & WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PASSPHRASE) != 0; + } + + /** + * Get the supported pairing bootstrapping methods for framework internal usage. + * @hide + */ + public int getPairingBootStrappingMethods() { + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + return mPairingBootstrappingMethods; + } + + /** + * Set the supported pairing bootstrapping methods. + * + * @param methods Bitmask of supported + * {@code WifiP2pPairingBootstrappingConfig.PAIRING_BOOTSTRAPPING_METHOD_*} + * @hide + */ + public void setPairingBootStrappingMethods( + @WifiP2pPairingBootstrappingConfig.PairingBootstrappingMethod int methods) { + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + mPairingBootstrappingMethods = methods; + } + + /** + * Store the Device Identity Resolution (DIR) Info received in USD frame for framework + * internal usage. + * @hide + */ + @Nullable + public WifiP2pDirInfo dirInfo; + @Override public boolean equals(Object obj) { if (this == obj) return true; @@ -502,6 +612,7 @@ public class WifiP2pDevice implements Parcelable { sbuf.append("\n wfdInfo: ").append(wfdInfo); sbuf.append("\n vendorElements: ").append(mVendorElements); sbuf.append("\n vendorData: ").append(mVendorData); + sbuf.append("\n Pairing Bootstrapping Methods: ").append(mPairingBootstrappingMethods); return sbuf.toString(); } @@ -531,6 +642,7 @@ public class WifiP2pDevice implements Parcelable { mVendorElements = new ArrayList<>(source.mVendorElements); } mVendorData = new ArrayList<>(source.mVendorData); + mPairingBootstrappingMethods = source.mPairingBootstrappingMethods; } } @@ -560,6 +672,7 @@ public class WifiP2pDevice implements Parcelable { } dest.writeTypedList(mVendorElements); dest.writeList(mVendorData); + dest.writeInt(mPairingBootstrappingMethods); } /** Implement the Parcelable interface */ @@ -567,31 +680,33 @@ public class WifiP2pDevice implements Parcelable { new Creator<WifiP2pDevice>() { @Override public WifiP2pDevice createFromParcel(Parcel in) { - WifiP2pDevice device = new WifiP2pDevice(); - device.deviceName = in.readString(); - device.deviceAddress = in.readString(); - device.mInterfaceMacAddress = in.readParcelable(MacAddress.class.getClassLoader()); - if (in.readByte() == 1) { - try { - device.mIpAddress = InetAddress.getByAddress(in.createByteArray()); - } catch (UnknownHostException e) { - e.printStackTrace(); - return new WifiP2pDevice(); + WifiP2pDevice device = new WifiP2pDevice(); + device.deviceName = in.readString(); + device.deviceAddress = in.readString(); + device.mInterfaceMacAddress = + in.readParcelable(MacAddress.class.getClassLoader()); + if (in.readByte() == 1) { + try { + device.mIpAddress = InetAddress.getByAddress(in.createByteArray()); + } catch (UnknownHostException e) { + e.printStackTrace(); + return new WifiP2pDevice(); + } } - } - device.primaryDeviceType = in.readString(); - device.secondaryDeviceType = in.readString(); - device.wpsConfigMethodsSupported = in.readInt(); - device.deviceCapability = in.readInt(); - device.groupCapability = in.readInt(); - device.status = in.readInt(); - if (in.readInt() == 1) { - device.wfdInfo = WifiP2pWfdInfo.CREATOR.createFromParcel(in); - } - device.mVendorElements = in.createTypedArrayList( - ScanResult.InformationElement.CREATOR); - device.mVendorData = ParcelUtil.readOuiKeyedDataList(in); - return device; + device.primaryDeviceType = in.readString(); + device.secondaryDeviceType = in.readString(); + device.wpsConfigMethodsSupported = in.readInt(); + device.deviceCapability = in.readInt(); + device.groupCapability = in.readInt(); + device.status = in.readInt(); + if (in.readInt() == 1) { + device.wfdInfo = WifiP2pWfdInfo.CREATOR.createFromParcel(in); + } + device.mVendorElements = in.createTypedArrayList( + ScanResult.InformationElement.CREATOR); + device.mVendorData = ParcelUtil.readOuiKeyedDataList(in); + device.mPairingBootstrappingMethods = in.readInt(); + return device; } @Override diff --git a/framework/java/android/net/wifi/p2p/WifiP2pDeviceList.java b/framework/java/android/net/wifi/p2p/WifiP2pDeviceList.java index e7866e6180..411c2a83a7 100644 --- a/framework/java/android/net/wifi/p2p/WifiP2pDeviceList.java +++ b/framework/java/android/net/wifi/p2p/WifiP2pDeviceList.java @@ -17,6 +17,7 @@ package android.net.wifi.p2p; import android.compat.annotation.UnsupportedAppUsage; +import android.net.wifi.util.Environment; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -102,6 +103,10 @@ public class WifiP2pDeviceList implements Parcelable { d.deviceCapability = device.deviceCapability; d.groupCapability = device.groupCapability; d.wfdInfo = device.wfdInfo; + if (Environment.isSdkAtLeastB()) { + d.setPairingBootStrappingMethods(device.getPairingBootStrappingMethods()); + d.dirInfo = device.dirInfo; + } return; } //Not found, add a new one diff --git a/framework/java/android/net/wifi/p2p/WifiP2pDirInfo.java b/framework/java/android/net/wifi/p2p/WifiP2pDirInfo.java new file mode 100644 index 0000000000..0292628154 --- /dev/null +++ b/framework/java/android/net/wifi/p2p/WifiP2pDirInfo.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2024 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 android.net.wifi.p2p; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.RequiresApi; +import android.net.MacAddress; +import android.os.Build; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.wifi.flags.Flags; + +import java.util.Arrays; + +/** + * This object contains the Device Identity Resolution (DIR) Info to check if the device is a + * previously paired device. + * The device advertises this information in Bluetooth LE advertising packets + * and Unsynchronized Service Discovery (USD) frames. The device receiving DIR + * Info uses this information to identify that the peer device is a previously paired device. + * For Details, refer Wi-Fi Alliance Wi-Fi Direct R2 specification section 3.8.2 Pairing Identity + * and section 3.9.2.3.2 Optional Advertising Data Elements. + */ +@RequiresApi(Build.VERSION_CODES.BAKLAVA) +@FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) +public final class WifiP2pDirInfo implements Parcelable { + /** + * The MAC address of the P2P device interface. + */ + private MacAddress mMacAddress; + + /** + * Random number of 8 octets. + */ + private byte[] mNonce; + + /** + * A resolvable identity value of 8 octets. + */ + private byte[] mDirTag; + + /** + * @return the MAC address of the P2P device interface. + */ + @NonNull + public MacAddress getMacAddress() { + return mMacAddress; + } + + /** + * Get the nonce value used to derive DIR Tag. + * See {@link WifiP2pDirInfo} + * + * @return A byte-array of random number of size 8 octets. + */ + @NonNull + public byte[] getNonce() { + return mNonce; + } + + /** + * Get the DIR Tag value. + * See {@link WifiP2pDirInfo} + * + * @return A byte-array of Tag value of size 8 octets. + */ + @NonNull + public byte[] getDirTag() { + return mDirTag; + } + + /** + * Constructor for Device Identity Resolution (DIR) Info generated based on the 128 bit Device + * Identity key. For details, refer Wi-Fi Alliance Wi-Fi Direct R2 specification Table 8. + * + * @param macAddress The MAC address of the P2P device interface. + * @param nonce Random number of 8 octets. + * @param dirTag Resolvable identity value of 8 octets derived based on the device MAC address, + * device identity key and P2P device MAC address. + * Tag = Truncate-64(HMAC-SHA-256(DevIk, "DIR" || P2P Device Address || Nonce)) + * + */ + public WifiP2pDirInfo(@NonNull MacAddress macAddress, @NonNull byte[] nonce, + @NonNull byte[] dirTag) { + mMacAddress = macAddress; + mNonce = nonce; + mDirTag = dirTag; + } + + /** + * Generates a string of all the defined elements. + * + * @return a compiled string representing all elements + */ + public String toString() { + StringBuilder sbuf = new StringBuilder("WifiP2pDirInfo:"); + sbuf.append("\n Mac Address: ").append(mMacAddress); + sbuf.append("\n Nonce : ").append((mNonce == null) + ? "<null>" : Arrays.toString(mNonce)); + sbuf.append("\n DIR Tag : ").append((mDirTag == null) + ? "<null>" : Arrays.toString(mDirTag)); + return sbuf.toString(); + } + + /** Implement the Parcelable interface */ + public int describeContents() { + return 0; + } + + /** Implement the Parcelable interface */ + public void writeToParcel(@NonNull Parcel dest, int flags) { + mMacAddress.writeToParcel(dest, flags); + dest.writeByteArray(mNonce); + dest.writeByteArray(mDirTag); + } + + /** Implement the Parcelable interface */ + @NonNull + public static final Creator<WifiP2pDirInfo> CREATOR = + new Creator<WifiP2pDirInfo>() { + public WifiP2pDirInfo createFromParcel(Parcel in) { + return new WifiP2pDirInfo(MacAddress.CREATOR.createFromParcel(in), + in.createByteArray(), in.createByteArray()); + } + + public WifiP2pDirInfo[] newArray(int size) { + return new WifiP2pDirInfo[size]; + } + }; +} diff --git a/framework/java/android/net/wifi/p2p/WifiP2pGroup.java b/framework/java/android/net/wifi/p2p/WifiP2pGroup.java index 1edc97b422..43dc26fa04 100644 --- a/framework/java/android/net/wifi/p2p/WifiP2pGroup.java +++ b/framework/java/android/net/wifi/p2p/WifiP2pGroup.java @@ -17,6 +17,7 @@ package android.net.wifi.p2p; import android.annotation.FlaggedApi; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; @@ -24,6 +25,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.net.MacAddress; import android.net.wifi.OuiKeyedData; import android.net.wifi.ParcelUtil; +import android.net.wifi.util.Environment; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -34,6 +36,8 @@ import androidx.annotation.RequiresApi; import com.android.modules.utils.build.SdkLevel; import com.android.wifi.flags.Flags; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.net.Inet4Address; import java.net.InetAddress; import java.util.ArrayList; @@ -75,6 +79,41 @@ public class WifiP2pGroup implements Parcelable { public static final int NETWORK_ID_PERSISTENT = -2; /** + * The definition of security type unknown. It is set when framework fails to derive the + * security type from the authentication key management provided by wpa_supplicant. + */ + @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) + public static final int SECURITY_TYPE_UNKNOWN = -1; + + /** + * The definition of security type WPA2-PSK. + */ + @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) + public static final int SECURITY_TYPE_WPA2_PSK = 0; + + /** + * The definition of security type WPA3-Compatibility Mode. + */ + @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) + public static final int SECURITY_TYPE_WPA3_COMPATIBILITY = 1; + + /** + * The definition of security type WPA3-SAE. + */ + @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) + public static final int SECURITY_TYPE_WPA3_SAE = 2; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "SECURITY_TYPE_" }, value = { + SECURITY_TYPE_UNKNOWN, + SECURITY_TYPE_WPA2_PSK, + SECURITY_TYPE_WPA3_COMPATIBILITY, + SECURITY_TYPE_WPA3_SAE, + }) + public @interface SecurityType {} + + /** * Group owner P2P interface MAC address. * @hide */ @@ -96,6 +135,10 @@ public class WifiP2pGroup implements Parcelable { /** The passphrase used for WPA2-PSK */ private String mPassphrase; + /** The security type of the group */ + @SecurityType + private int mSecurityType; + private String mInterface; /** The network ID in wpa_supplicant */ @@ -363,6 +406,30 @@ public class WifiP2pGroup implements Parcelable { return mPassphrase; } + /** + * Set the security type of the group. + * + * @param securityType One of the {@code SECURITY_TYPE_*}. + * @hide + */ + public void setSecurityType(@SecurityType int securityType) { + mSecurityType = securityType; + } + + /** + * Get the security type of the group. + * + * @return One of the {@code SECURITY_TYPE_*}. + */ + @RequiresApi(Build.VERSION_CODES.BAKLAVA) + @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) + public @SecurityType int getSecurityType() { + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + return mSecurityType; + } + /** @hide */ @UnsupportedAppUsage public void setInterface(String intf) { @@ -433,6 +500,31 @@ public class WifiP2pGroup implements Parcelable { mVendorData = new ArrayList<>(vendorData); } + /** + * Returns the BSSID, if this device is the group owner of the P2P group supporting Wi-Fi + * Direct R2 protocol. + * <p> + * The interface address of a Wi-Fi Direct R2 supported device is randomized. So for every + * group owner session a randomized interface address will be returned. + * <p> + * The BSSID returned will be {@code null}, if this device is a client device or a group owner + * which doesn't support Wi-Fi Direct R2 protocol. + * @return the BSSID. + */ + @RequiresApi(Build.VERSION_CODES.BAKLAVA) + @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) + @Nullable + public MacAddress getGroupOwnerBssid() { + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + if (isGroupOwner() && getSecurityType() == SECURITY_TYPE_WPA3_SAE + && interfaceAddress != null) { + return MacAddress.fromBytes(interfaceAddress); + } + return null; + } + public String toString() { StringBuffer sbuf = new StringBuffer(); sbuf.append("network: ").append(mNetworkName); @@ -443,6 +535,8 @@ public class WifiP2pGroup implements Parcelable { } sbuf.append("\n interface: ").append(mInterface); sbuf.append("\n networkId: ").append(mNetId); + sbuf.append("\n securityType: ").append(mSecurityType); + sbuf.append("\n frequency: ").append(mFrequency); sbuf.append("\n vendorData: ").append(mVendorData); return sbuf.toString(); @@ -463,6 +557,9 @@ public class WifiP2pGroup implements Parcelable { mPassphrase = source.getPassphrase(); mInterface = source.getInterface(); mNetId = source.getNetworkId(); + if (Environment.isSdkAtLeastB() && Flags.wifiDirectR2()) { + mSecurityType = source.getSecurityType(); + } mFrequency = source.getFrequency(); if (SdkLevel.isAtLeastV()) { mVendorData = new ArrayList<>(source.getVendorData()); @@ -482,6 +579,9 @@ public class WifiP2pGroup implements Parcelable { dest.writeString(mPassphrase); dest.writeString(mInterface); dest.writeInt(mNetId); + if (Environment.isSdkAtLeastB() && Flags.wifiDirectR2()) { + dest.writeInt(mSecurityType); + } dest.writeInt(mFrequency); if (SdkLevel.isAtLeastV()) { dest.writeList(mVendorData); @@ -490,28 +590,33 @@ public class WifiP2pGroup implements Parcelable { /** Implement the Parcelable interface */ public static final @android.annotation.NonNull Creator<WifiP2pGroup> CREATOR = - new Creator<WifiP2pGroup>() { - public WifiP2pGroup createFromParcel(Parcel in) { - WifiP2pGroup group = new WifiP2pGroup(); - group.setNetworkName(in.readString()); - group.setOwner((WifiP2pDevice)in.readParcelable(null)); - group.setIsGroupOwner(in.readByte() == (byte)1); - int clientCount = in.readInt(); - for (int i=0; i<clientCount; i++) { - group.addClient((WifiP2pDevice) in.readParcelable(null)); - } - group.setPassphrase(in.readString()); - group.setInterface(in.readString()); - group.setNetworkId(in.readInt()); - group.setFrequency(in.readInt()); - if (SdkLevel.isAtLeastV()) { - group.setVendorData(ParcelUtil.readOuiKeyedDataList(in)); + new Creator<WifiP2pGroup>() { + public WifiP2pGroup createFromParcel(Parcel in) { + WifiP2pGroup group = new WifiP2pGroup(); + group.setNetworkName(in.readString()); + group.setOwner((WifiP2pDevice) in.readParcelable( + WifiP2pDevice.class.getClassLoader())); + group.setIsGroupOwner(in.readByte() == (byte) 1); + int clientCount = in.readInt(); + for (int i = 0; i < clientCount; i++) { + group.addClient((WifiP2pDevice) in.readParcelable( + WifiP2pDevice.class.getClassLoader())); + } + group.setPassphrase(in.readString()); + group.setInterface(in.readString()); + group.setNetworkId(in.readInt()); + if (Environment.isSdkAtLeastB() && Flags.wifiDirectR2()) { + group.setSecurityType(in.readInt()); + } + group.setFrequency(in.readInt()); + if (SdkLevel.isAtLeastV()) { + group.setVendorData(ParcelUtil.readOuiKeyedDataList(in)); + } + return group; } - return group; - } - public WifiP2pGroup[] newArray(int size) { - return new WifiP2pGroup[size]; - } + public WifiP2pGroup[] newArray(int size) { + return new WifiP2pGroup[size]; + } }; } diff --git a/framework/java/android/net/wifi/p2p/WifiP2pManager.java b/framework/java/android/net/wifi/p2p/WifiP2pManager.java index 0969060784..ff973dedae 100644 --- a/framework/java/android/net/wifi/p2p/WifiP2pManager.java +++ b/framework/java/android/net/wifi/p2p/WifiP2pManager.java @@ -41,6 +41,9 @@ import android.net.wifi.p2p.nsd.WifiP2pServiceRequest; import android.net.wifi.p2p.nsd.WifiP2pServiceResponse; import android.net.wifi.p2p.nsd.WifiP2pUpnpServiceInfo; import android.net.wifi.p2p.nsd.WifiP2pUpnpServiceResponse; +import android.net.wifi.p2p.nsd.WifiP2pUsdBasedServiceConfig; +import android.net.wifi.p2p.nsd.WifiP2pUsdBasedServiceResponse; +import android.net.wifi.util.Environment; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -48,6 +51,7 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.Messenger; +import android.os.OutcomeReceiver; import android.os.RemoteException; import android.text.TextUtils; import android.util.CloseGuard; @@ -170,6 +174,13 @@ public class WifiP2pManager { public static final long FEATURE_GROUP_CLIENT_REMOVAL = 1L << 2; /** @hide */ public static final long FEATURE_GROUP_OWNER_IPV6_LINK_LOCAL_ADDRESS_PROVIDED = 1L << 3; + /** @hide */ + public static final long FEATURE_WIFI_DIRECT_R2 = 1L << 4; // Wi-Fi Direct R2 Support + /** + * Wi-Fi Direct R1/R2 Compatibility Mode support. + * @hide + */ + public static final long FEATURE_PCC_MODE_ALLOW_LEGACY_AND_R2_CONNECTION = 1L << 5; /** * Extra for transporting a WifiP2pConfig @@ -183,6 +194,23 @@ public class WifiP2pManager { */ public static final String EXTRA_PARAM_KEY_SERVICE_INFO = "android.net.wifi.p2p.EXTRA_PARAM_KEY_SERVICE_INFO"; + + /** + * Extra for transporting Un-synchronized service discovery (USD) based service discovery + * configuration. + * @hide + */ + public static final String EXTRA_PARAM_KEY_USD_BASED_SERVICE_DISCOVERY_CONFIG = + "android.net.wifi.p2p.EXTRA_PARAM_KEY_USD_BASED_SERVICE_DISCOVERY_CONFIG"; + + /** + * Extra for transporting Un-synchronized service discovery (USD) based local service + * advertisement configuration. + * @hide + */ + public static final String EXTRA_PARAM_KEY_USD_BASED_LOCAL_SERVICE_ADVERTISEMENT_CONFIG = + "android.net.wifi.p2p.EXTRA_PARAM_KEY_USD_BASED_LOCAL_SERVICE_ADVERTISEMENT_CONFIG"; + /** * Extra for transporting a peer discovery frequency. * @hide @@ -590,6 +618,25 @@ public class WifiP2pManager { */ private static final int WIFI_P2P_VENDOR_ELEMENTS_MAXIMUM_LENGTH = 512; + /** + * Run USD based P2P service discovery with a service discovery configuration. + * @hide + */ + public static final int WIFI_P2P_USD_BASED_SERVICE_DISCOVERY = 1; + + /** + * Add P2P local service with advertisement configuration. + * @hide + */ + public static final int WIFI_P2P_USD_BASED_ADD_LOCAL_SERVICE = 1; + + /** + * Extra for transporting DIR Information. + * @hide + */ + public static final String EXTRA_PARAM_KEY_DIR_INFO = + "android.net.wifi.p2p.EXTRA_PARAM_KEY_DIR_INFO"; + private Context mContext; IWifiP2pManager mService; @@ -865,6 +912,20 @@ public class WifiP2pManager { /** @hide */ public static final int RESPONSE_GET_LISTEN_STATE = BASE + 118; + /** @hide */ + public static final int GET_DIR_INFO = BASE + 119; + /** @hide */ + public static final int GET_DIR_INFO_FAILED = BASE + 120; + /** @hide */ + public static final int RESPONSE_GET_DIR_INFO = BASE + 121; + + /** @hide */ + public static final int VALIDATE_DIR_INFO = BASE + 122; + /** @hide */ + public static final int VALIDATE_DIR_INFO_FAILED = BASE + 123; + /** @hide */ + public static final int RESPONSE_VALIDATE_DIR_INFO = BASE + 124; + private static final SparseArray<IWifiP2pListener> sWifiP2pListenerMap = new SparseArray<>(); /** * Create a new WifiP2pManager instance. Applications use @@ -906,6 +967,25 @@ public class WifiP2pManager { */ public static final int NO_SERVICE_REQUESTS = 3; + /** + * Passed with {@link ActionListener#onFailure}. + * Indicates that the operation failed due to calling app doesn't have permission to call the + * API. + */ + @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) + public static final int NO_PERMISSION = 4; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = { + ERROR, + P2P_UNSUPPORTED, + BUSY, + NO_PERMISSION + }) + public @interface FailureReason{} + + /** Interface for callback invocation when framework channel is lost */ public interface ChannelListener { /** @@ -971,6 +1051,15 @@ public class WifiP2pManager { */ public void onServiceAvailable(int protocolType, byte[] responseData, WifiP2pDevice srcDevice); + /** + * The requested USD based service response is available. + * @param srcDevice source device. + * @param usdResponseData {@link WifiP2pUsdBasedServiceResponse}. + */ + @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) + default void onUsdBasedServiceAvailable(@NonNull WifiP2pDevice srcDevice, + @NonNull WifiP2pUsdBasedServiceResponse usdResponseData) { + } } /** @@ -1902,6 +1991,31 @@ public class WifiP2pManager { .onPinGenerated(deviceAddress, pin); } break; + case RESPONSE_GET_DIR_INFO: + if (listener != null) { + if (Flags.wifiDirectR2()) { + ((WifiP2pDirInfoListener) listener) + .onDirInfoReceived((WifiP2pDirInfo) message.obj); + } + } + break; + case GET_DIR_INFO_FAILED: + if (listener != null) { + ((WifiP2pDirInfoListener) listener) + .onFailure(message.arg1); + } + break; + case RESPONSE_VALIDATE_DIR_INFO: + if (listener != null) { + ((WifiP2pDirInfoValidationListener) listener) + .onDirInfoValidation(message.arg1 == 1); + } + break; + case VALIDATE_DIR_INFO_FAILED: + if (listener != null) { + ((WifiP2pDirInfoValidationListener) listener) + .onFailure(message.arg1); + } default: Log.d(TAG, "Ignored " + message); break; @@ -1918,8 +2032,13 @@ public class WifiP2pManager { } } else { if (mServRspListener != null) { - mServRspListener.onServiceAvailable(resp.getServiceType(), - resp.getRawData(), resp.getSrcDevice()); + if (Flags.wifiDirectR2() && resp.getWifiP2pUsdBasedServiceResponse() != null) { + mServRspListener.onUsdBasedServiceAvailable( + resp.getSrcDevice(), resp.getWifiP2pUsdBasedServiceResponse()); + } else { + mServRspListener.onServiceAvailable(resp.getServiceType(), + resp.getRawData(), resp.getSrcDevice()); + } } } } @@ -2618,6 +2737,12 @@ public class WifiP2pManager { ActionListener listener) { checkChannel(channel); checkServiceInfo(servInfo); + if (Environment.isSdkAtLeastB()) { + if (servInfo.getWifiP2pUsdBasedServiceConfig() != null) { + throw new UnsupportedOperationException("Application must call" + + " WifiP2pManager#startUsdBasedLocalServiceAdvertisement for USD config"); + } + } Bundle extras = prepareExtrasBundle(channel); extras.putParcelable(EXTRA_PARAM_KEY_SERVICE_INFO, servInfo); channel.mAsyncChannel.sendMessage(prepareMessage(ADD_LOCAL_SERVICE, 0, @@ -2625,6 +2750,67 @@ public class WifiP2pManager { } /** + * Start a service discovery advertisement using Un-synchronized service discovery (USD). + * Once {@link #startUsdBasedLocalServiceAdvertisement(Channel, WifiP2pServiceInfo, + * WifiP2pUsdBasedLocalServiceAdvertisementConfig, ActionListener)} is called, the device will + * go to the channel frequency requested via + * {@link WifiP2pUsdBasedLocalServiceAdvertisementConfig} and responds to a service discovery + * request from a peer. + * + * <p> The service information is set through + * {@link WifiP2pServiceInfo#WifiP2pServiceInfo(WifiP2pUsdBasedServiceConfig)} + * + * <p> The function call immediately returns after sending a request to start the service + * advertisement to the framework. The application is notified of a success or failure to + * start service advertisement through listener callbacks {@link ActionListener#onSuccess} or + * {@link ActionListener#onFailure}. + * + * <p>The service information can be cleared with calls to + * {@link #removeLocalService} or {@link #clearLocalServices}. + * <p> + * Use {@link #isWiFiDirectR2Supported()} to determine whether the device supports + * this feature. If {@link #isWiFiDirectR2Supported()} return {@code false} then + * this method will throw {@link UnsupportedOperationException}. + * <p> + * The application must have {@link android.Manifest.permission#NEARBY_WIFI_DEVICES} with + * android:usesPermissionFlags="neverForLocation". If the application does not declare + * android:usesPermissionFlags="neverForLocation", then it must also have + * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. + * + * @param channel is the channel created at {@link #initialize} + * @param servInfo is a local service information. + * @param config is the configuration for this service discovery advertisement. + * @param listener for callbacks on success or failure. Can be null. + */ + @RequiresPermission(allOf = { + android.Manifest.permission.NEARBY_WIFI_DEVICES, + android.Manifest.permission.ACCESS_FINE_LOCATION + }, conditional = true) + @RequiresApi(Build.VERSION_CODES.BAKLAVA) + @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) + @SuppressLint("ExecutorRegistration") // initialize creates a channel and requires a Looper + public void startUsdBasedLocalServiceAdvertisement(@NonNull Channel channel, + @NonNull WifiP2pServiceInfo servInfo, + @NonNull WifiP2pUsdBasedLocalServiceAdvertisementConfig config, + @Nullable ActionListener listener) { + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + if (!isWiFiDirectR2Supported()) { + throw new UnsupportedOperationException(); + } + checkChannel(channel); + Objects.requireNonNull(servInfo, "service info cannot be null"); + Objects.requireNonNull(config, "Advertisement config cannot be null"); + Bundle extras = prepareExtrasBundle(channel); + extras.putParcelable(EXTRA_PARAM_KEY_SERVICE_INFO, servInfo); + extras.putParcelable(EXTRA_PARAM_KEY_USD_BASED_LOCAL_SERVICE_ADVERTISEMENT_CONFIG, config); + channel.mAsyncChannel.sendMessage(prepareMessage(ADD_LOCAL_SERVICE, + WIFI_P2P_USD_BASED_ADD_LOCAL_SERVICE, + channel.putListener(listener), extras, channel.mContext)); + } + + /** * Remove a registered local service added with {@link #addLocalService} * * <p> The function call immediately returns after sending a request to remove a @@ -2749,6 +2935,68 @@ public class WifiP2pManager { } /** + * Initiate Un-synchronized service discovery (USD) based service discovery. A discovery + * process involves scanning for requested services for the purpose of establishing a + * connection to a peer that supports an available service using USD protocol. + * + * This method accepts a {@link WifiP2pUsdBasedServiceDiscoveryConfig} object specifying the + * desired parameters for the service discovery. The configuration object allows to specify + * either a band or frequency list to scan for service. + * + * <p> The function call immediately returns after sending a request to start service + * discovery to the framework. The application is notified of a success or failure to initiate + * discovery through listener callbacks {@link ActionListener#onSuccess} or + * {@link ActionListener#onFailure}. + * + * <p> The USD based services to be discovered are specified with calls to + * {@link #addServiceRequest} with the service request information set through + * {@link WifiP2pServiceRequest#WifiP2pServiceRequest(WifiP2pUsdBasedServiceConfig)} + * + * <p>The application is notified of the response against the service discovery request + * via {@link ServiceResponseListener#onUsdBasedServiceAvailable(WifiP2pDevice, + * WifiP2pUsdBasedServiceResponse)} listener callback registered by + * {@link #setServiceResponseListener(Channel, ServiceResponseListener)} . + * + * <p> + * Use {@link #isWiFiDirectR2Supported()} to determine whether the device supports + * this feature. If {@link #isWiFiDirectR2Supported()} return {@code false} then + * this method will throw {@link UnsupportedOperationException}. + * <p> + * The application must have {@link android.Manifest.permission#NEARBY_WIFI_DEVICES} with + * android:usesPermissionFlags="neverForLocation". If the application does not declare + * android:usesPermissionFlags="neverForLocation", then it must also have + * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. + * + * @param channel is the channel created at {@link #initialize} + * @param config is the configuration for this USD based service discovery + * @param listener for callbacks on success or failure. Can be null. + */ + @RequiresPermission(allOf = { + android.Manifest.permission.NEARBY_WIFI_DEVICES, + android.Manifest.permission.ACCESS_FINE_LOCATION + }, conditional = true) + @RequiresApi(Build.VERSION_CODES.BAKLAVA) + @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) + @SuppressLint("ExecutorRegistration") // initialize creates a channel and requires a Looper + public void discoverUsdBasedServices(@NonNull Channel channel, + @NonNull WifiP2pUsdBasedServiceDiscoveryConfig config, + @Nullable ActionListener listener) { + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + if (!isWiFiDirectR2Supported()) { + throw new UnsupportedOperationException(); + } + checkChannel(channel); + Objects.requireNonNull(config, "Service discovery config cannot be null"); + Bundle extras = prepareExtrasBundle(channel); + extras.putParcelable(EXTRA_PARAM_KEY_USD_BASED_SERVICE_DISCOVERY_CONFIG, config); + channel.mAsyncChannel.sendMessage(prepareMessage(DISCOVER_SERVICES, + WIFI_P2P_USD_BASED_SERVICE_DISCOVERY, + channel.putListener(listener), extras, channel.mContext)); + } + + /** * Add a service discovery request. * * <p> The function call immediately returns after sending a request to add service @@ -2756,6 +3004,13 @@ public class WifiP2pManager { * add service through listener callbacks {@link ActionListener#onSuccess} or * {@link ActionListener#onFailure}. * + * <p> The USD based service information are set in the service request through + * {@link WifiP2pServiceRequest#WifiP2pServiceRequest(WifiP2pUsdBasedServiceConfig)}. + * Application must use {@link #isWiFiDirectR2Supported()} to determine whether the device + * supports USD based service discovery. If {@link #isWiFiDirectR2Supported()} return + * {@code false} then this method will throw {@link UnsupportedOperationException} for service + * request information containing USD service configuration. + * * <p>After service discovery request is added, you can initiate service discovery by * {@link #discoverServices}. * @@ -2771,6 +3026,11 @@ public class WifiP2pManager { WifiP2pServiceRequest req, ActionListener listener) { checkChannel(channel); checkServiceRequest(req); + if (Environment.isSdkAtLeastB()) { + if (req.getWifiP2pUsdBasedServiceConfig() != null && !isWiFiDirectR2Supported()) { + throw new UnsupportedOperationException(); + } + } channel.mAsyncChannel.sendMessage(ADD_SERVICE_REQUEST, 0, channel.putListener(listener), req); } @@ -3205,6 +3465,32 @@ public class WifiP2pManager { } /** + * Check if this device supports Wi-Fi Direct R2 (P2P2). + * + * @return true if this device supports Wi-Fi Alliance Wi-Fi Direct R2 (Support for P2P2 IE and + * establishing connection by using the P2P pairing protocol), false otherwise. + * For more details, visit <a href="https://www.wi-fi.org/">https://www.wi-fi.org/</a> and + * search for "Wi-Fi Direct" . + */ + @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) + public boolean isWiFiDirectR2Supported() { + return isFeatureSupported(FEATURE_WIFI_DIRECT_R2); + } + + /** + * Check if this device supports P2P Connection Compatibility Mode(R1/R2 compatibility mode). + * + * @return true if this device supports hosting an autonomous Group Owner which allows + * legacy P2P clients and R2 clients to join the group in PCC Mode and also supports connecting + * to a Group Owner either using legacy security mode (WPA2-PSK) or R2 mandated security + * mode(WPA3-SAE) in PCC Mode. + */ + @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) + public boolean isPccModeSupported() { + return isFeatureSupported(FEATURE_PCC_MODE_ALLOW_LEGACY_AND_R2_CONNECTION); + } + + /** * Get a handover request message for use in WFA NFC Handover transfer. * @hide */ @@ -3652,4 +3938,186 @@ public class WifiP2pManager { public static int getP2pMaxAllowedVendorElementsLengthBytes() { return WIFI_P2P_VENDOR_ELEMENTS_MAXIMUM_LENGTH; } + + private static Exception reasonCodeToException(int reason) { + if (reason == ERROR) { + return new IllegalStateException("Internal error"); + } else if (reason == BUSY) { + return new IllegalStateException("Framework is busy"); + } else if (Flags.wifiDirectR2() && reason == NO_PERMISSION) { + return new SecurityException("Application doesn't have required permission"); + } else { + return new IllegalStateException(); + } + } + + /** + * Interface for callback invocation in response to {@link #requestDirInfo}. + * @hide + */ + public interface WifiP2pDirInfoListener { + /** + * The callback to indicate that the system searched for DIR information. + * @param dirInfo {@link WifiP2pDirInfo} if exists, otherwise null. + */ + void onDirInfoReceived(@Nullable WifiP2pDirInfo dirInfo); + + /** + * The operation failed. + * @param reason The reason for failure. + */ + void onFailure(int reason); + } + + /** + * Get the Device Identity Resolution (DIR) Information. + * See {@link WifiP2pDirInfo} for details + * + * Note: The results callback returns null if the device doesn't have any persistent group + * with device identity key information. + * + * <p> + * Use {@link #isWiFiDirectR2Supported()} to determine whether the device supports + * this feature. If {@link #isWiFiDirectR2Supported()} return {@code false} then + * this method will throw {@link UnsupportedOperationException}. + * <p> + * The application must have {@link android.Manifest.permission#NEARBY_WIFI_DEVICES} with + * android:usesPermissionFlags="neverForLocation". If the application does not declare + * android:usesPermissionFlags="neverForLocation", then it must also have + * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. + * + * @param c It is the channel created at {@link #initialize}. + * @param executor The executor on which callback will be invoked. + * @param callback An OutcomeReceiver callback for receiving {@link WifiP2pDirInfo} via + * {@link OutcomeReceiver#onResult(Object)}. This callback will return + * null when DIR info doesn't exist. + * When this API call fails due to permission issues, state machine + * is busy etc., {@link OutcomeReceiver#onError(Throwable)} is called. + */ + @RequiresPermission(allOf = { + android.Manifest.permission.NEARBY_WIFI_DEVICES, + android.Manifest.permission.ACCESS_FINE_LOCATION + }, conditional = true) + @RequiresApi(Build.VERSION_CODES.BAKLAVA) + @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) + public void requestDirInfo(@NonNull Channel c, @NonNull @CallbackExecutor Executor executor, + @NonNull OutcomeReceiver<WifiP2pDirInfo, Exception> callback) { + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + if (!isWiFiDirectR2Supported()) { + throw new UnsupportedOperationException(); + } + Objects.requireNonNull(c, "channel cannot be null and needs to be initialized)"); + Objects.requireNonNull(executor, "executor cannot be null"); + Objects.requireNonNull(callback, "callback cannot be null"); + Bundle extras = prepareExtrasBundle(c); + c.mAsyncChannel.sendMessage(prepareMessage(GET_DIR_INFO, 0, + c.putListener(new WifiP2pDirInfoListener() { + @Override + public void onDirInfoReceived(WifiP2pDirInfo result) { + Binder.clearCallingIdentity(); + executor.execute(() -> { + callback.onResult(result); + }); + } + + @Override + public void onFailure(int reason) { + Binder.clearCallingIdentity(); + executor.execute(() -> { + callback.onError(reasonCodeToException(reason)); + }); + } + }), extras, c.mContext)); + } + + /** + * Interface for callback invocation when the received DIR information is validated + * in response to {@link #validateDirInfo}. + * @hide + */ + public interface WifiP2pDirInfoValidationListener { + /** + * The requested DIR information is validated. + * @param result True if a match is found, false otherwise. + */ + void onDirInfoValidation(boolean result); + + /** + * The operation failed. + * @param reason The reason for failure. + */ + void onFailure(@FailureReason int reason); + } + + /** + * Validate the Device Identity Resolution (DIR) Information of a P2P device. + * See {@link WifiP2pDirInfo} for details. + * Framework takes the {@link WifiP2pDirInfo} and derives a set of Tag values based on + * the cached Device Identity Keys (DevIK) of all paired peers saved in the device. + * If a derived Tag value matches the Tag value received in the {@link WifiP2pDirInfo}, the + * device is identified as a paired peer and returns true. + * + * <p> + * Use {@link #isWiFiDirectR2Supported()} to determine whether the device supports + * this feature. If {@link #isWiFiDirectR2Supported()} return {@code false} then + * this method will throw {@link UnsupportedOperationException}. + * <p> + * The application must have {@link android.Manifest.permission#NEARBY_WIFI_DEVICES} with + * android:usesPermissionFlags="neverForLocation". If the application does not declare + * android:usesPermissionFlags="neverForLocation", then it must also have + * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. + * + * @param c It is the channel created at {@link #initialize}. + * @param dirInfo {@link WifiP2pDirInfo} to validate. + * @param executor The executor on which callback will be invoked. + * @param callback An OutcomeReceiver callback for receiving the result via + * {@link OutcomeReceiver#onResult(Object)} indicating whether the DIR + * info of P2P device is of a paired device. {code true} for paired, + * {@code false} for not paired. + * When this API call fails due to permission issues, state machine + * is busy etc., {@link OutcomeReceiver#onError(Throwable)} is called. + */ + @RequiresPermission(allOf = { + android.Manifest.permission.NEARBY_WIFI_DEVICES, + android.Manifest.permission.ACCESS_FINE_LOCATION + }, conditional = true) + @RequiresApi(Build.VERSION_CODES.BAKLAVA) + @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) + public void validateDirInfo(@NonNull Channel c, @NonNull WifiP2pDirInfo dirInfo, + @NonNull @CallbackExecutor Executor executor, + @NonNull OutcomeReceiver<Boolean, Exception> callback) { + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + if (!isWiFiDirectR2Supported()) { + throw new UnsupportedOperationException(); + } + Objects.requireNonNull(c, "channel cannot be null and needs to be initialized)"); + Objects.requireNonNull(dirInfo, "dirInfo cannot be null"); + Objects.requireNonNull(executor, "executor cannot be null"); + Objects.requireNonNull(callback, "resultsCallback cannot be null"); + Bundle extras = prepareExtrasBundle(c); + + extras.putParcelable(EXTRA_PARAM_KEY_DIR_INFO, dirInfo); + c.mAsyncChannel.sendMessage(prepareMessage(VALIDATE_DIR_INFO, 0, + c.putListener(new WifiP2pDirInfoValidationListener() { + @Override + public void onDirInfoValidation(boolean result) { + Binder.clearCallingIdentity(); + executor.execute(() -> { + callback.onResult(result); + }); + } + + @Override + public void onFailure(@FailureReason int reason) { + Binder.clearCallingIdentity(); + executor.execute(() -> { + callback.onError(reasonCodeToException(reason)); + }); + } + }), extras, c.mContext)); + } } diff --git a/framework/java/android/net/wifi/p2p/WifiP2pPairingBootstrappingConfig.java b/framework/java/android/net/wifi/p2p/WifiP2pPairingBootstrappingConfig.java new file mode 100644 index 0000000000..7ef07cb856 --- /dev/null +++ b/framework/java/android/net/wifi/p2p/WifiP2pPairingBootstrappingConfig.java @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2024 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 android.net.wifi.p2p; + +import android.annotation.FlaggedApi; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Build; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; + +import androidx.annotation.RequiresApi; + +import com.android.wifi.flags.Flags; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * A class representing Wi-Fi Direct pairing bootstrapping configuration. + * + * @see android.net.wifi.p2p.WifiP2pConfig + */ +@RequiresApi(Build.VERSION_CODES.BAKLAVA) +@FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) +public final class WifiP2pPairingBootstrappingConfig implements Parcelable { + + /** + * Pairing bootstrapping method opportunistic + */ + public static final int PAIRING_BOOTSTRAPPING_METHOD_OPPORTUNISTIC = 1 << 0; + + /** + * Pairing bootstrapping method display pin-code - The pin-code is displayed on the connection + * initiating device. The user enters the displayed pin-code on the other device. + */ + public static final int PAIRING_BOOTSTRAPPING_METHOD_DISPLAY_PINCODE = 1 << 1; + + /** + * Pairing bootstrapping method display passphrase - The passphrase is displayed on the + * connection initiating device. The user enters the displayed passphrase on the other device. + */ + public static final int PAIRING_BOOTSTRAPPING_METHOD_DISPLAY_PASSPHRASE = 1 << 2; + + /** + * Pairing bootstrapping method keypad pin-code - The pin-code is displayed on the other + * device. The user enters the displayed pin-code on the connection initiating device. + */ + public static final int PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PINCODE = 1 << 3; + + /** + * Pairing bootstrapping method keypad passphrase - The passphrase is displayed on the other + * device. The user enters the displayed passphrase on the connection initiating device. + */ + public static final int PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PASSPHRASE = 1 << 4; + + /** + * Pairing bootstrapping done out of band (For example: Over Bluetooth LE. + * Refer Wi-Fi Alliance Wi-Fi Direct R2 specification Section 3.9 for the details). + */ + public static final int PAIRING_BOOTSTRAPPING_METHOD_OUT_OF_BAND = 1 << 5; + + + /** @hide */ + @IntDef(flag = true, prefix = {"PAIRING_BOOTSTRAPPING_METHOD_"}, value = { + PAIRING_BOOTSTRAPPING_METHOD_OPPORTUNISTIC, + PAIRING_BOOTSTRAPPING_METHOD_DISPLAY_PINCODE, + PAIRING_BOOTSTRAPPING_METHOD_DISPLAY_PASSPHRASE, + PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PINCODE, + PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PASSPHRASE, + PAIRING_BOOTSTRAPPING_METHOD_OUT_OF_BAND, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface PairingBootstrappingMethod { + } + /** One of the {@code PAIRING_BOOTSTRAPPING_METHOD_*}. */ + private int mPairingBootstrappingMethod; + + /** + * Password for pairing setup, if {@code mPairingBootstrappingMethod} uses + * {@link #PAIRING_BOOTSTRAPPING_METHOD_DISPLAY_PINCODE}, + * {@link #PAIRING_BOOTSTRAPPING_METHOD_DISPLAY_PASSPHRASE} or + * {@link #PAIRING_BOOTSTRAPPING_METHOD_OUT_OF_BAND}. + * Must be set to null for {@link #PAIRING_BOOTSTRAPPING_METHOD_OPPORTUNISTIC}, + * {@link #PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PINCODE} + * or {@link #PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PASSPHRASE}. + */ + @Nullable private String mPassword; + + private static boolean isValidPairingBootstrappingMethod(@WifiP2pPairingBootstrappingConfig + .PairingBootstrappingMethod int method) { + switch (method) { + case PAIRING_BOOTSTRAPPING_METHOD_OPPORTUNISTIC: + case PAIRING_BOOTSTRAPPING_METHOD_DISPLAY_PINCODE: + case PAIRING_BOOTSTRAPPING_METHOD_DISPLAY_PASSPHRASE: + case PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PINCODE: + case PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PASSPHRASE: + case PAIRING_BOOTSTRAPPING_METHOD_OUT_OF_BAND: + return true; + default: + return false; + } + } + + /** @hide */ + public int getPairingBootstrappingMethod() { + return mPairingBootstrappingMethod; + } + + /** @hide */ + public String getPairingBootstrappingPassword() { + return mPassword; + } + + /** @hide */ + public void setPairingBootstrappingPassword(@NonNull String password) { + mPassword = password; + } + + /** + * Constructor for a WifiP2pPairingBootstrappingConfig. + * @param method One of the {@code PAIRING_BOOTSTRAPPING_METHOD_*}. + * @param password Password or PIN for pairing setup. if {@code method} is + * {@link #PAIRING_BOOTSTRAPPING_METHOD_DISPLAY_PINCODE}, the password must be + * a string containing 4 or more digits (0-9). For example: "1234", "56789". if + * {@code method} is {@link #PAIRING_BOOTSTRAPPING_METHOD_DISPLAY_PASSPHRASE} + * or {@link #PAIRING_BOOTSTRAPPING_METHOD_OUT_OF_BAND}, the password must be a + * UTF-8 string of minimum of 1 character. + * The password must be set to null if the + * {@code method} is {@link #PAIRING_BOOTSTRAPPING_METHOD_OPPORTUNISTIC}, + * {@link #PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PINCODE} or + * {@link #PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PASSPHRASE}. + * + * @throws IllegalArgumentException if the input pairing bootstrapping method is not + * one of the {@code PAIRING_BOOTSTRAPPING_METHOD_*}. + * @throws IllegalArgumentException if a non-null password is set for pairing bootstrapping + * method {@link #PAIRING_BOOTSTRAPPING_METHOD_OPPORTUNISTIC}, + * {@link #PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PINCODE} or + * {@link #PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PASSPHRASE}. + */ + public WifiP2pPairingBootstrappingConfig( + @WifiP2pPairingBootstrappingConfig.PairingBootstrappingMethod int method, + @Nullable String password) { + if (!isValidPairingBootstrappingMethod(method)) { + throw new IllegalArgumentException("Invalid PairingBootstrappingMethod =" + method); + } + mPairingBootstrappingMethod = method; + if (!TextUtils.isEmpty(password) + && (method == PAIRING_BOOTSTRAPPING_METHOD_OPPORTUNISTIC + || method == PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PINCODE + || method == PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PASSPHRASE)) { + throw new IllegalArgumentException("Password is not required for =" + method); + } + mPassword = password; + } + + /** + * Generates a string of all the defined elements. + * + * @return a compiled string representing all elements + */ + public String toString() { + StringBuilder sbuf = new StringBuilder("WifiP2pPairingBootstrappingConfig:"); + sbuf.append("\n BootstrappingMethod: ").append(mPairingBootstrappingMethod); + return sbuf.toString(); + } + + /** Implement the Parcelable interface */ + public int describeContents() { + return 0; + } + + /** + * Copy Constructor + * + * @hide + */ + public WifiP2pPairingBootstrappingConfig(@NonNull WifiP2pPairingBootstrappingConfig source) { + if (source != null) { + mPairingBootstrappingMethod = source.mPairingBootstrappingMethod; + mPassword = source.mPassword; + } + } + + /** Implement the Parcelable interface */ + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mPairingBootstrappingMethod); + dest.writeString(mPassword); + } + + /** Implement the Parcelable interface */ + public static final @android.annotation.NonNull + Creator<WifiP2pPairingBootstrappingConfig> CREATOR = + new Creator<WifiP2pPairingBootstrappingConfig>() { + public WifiP2pPairingBootstrappingConfig createFromParcel(Parcel in) { + int pairingBootstrappingMethod = in.readInt(); + String password = in.readString(); + WifiP2pPairingBootstrappingConfig config = + new WifiP2pPairingBootstrappingConfig( + pairingBootstrappingMethod, password); + return config; + } + + public WifiP2pPairingBootstrappingConfig[] newArray(int size) { + return new WifiP2pPairingBootstrappingConfig[size]; + } + }; +} diff --git a/framework/java/android/net/wifi/p2p/WifiP2pProvDiscEvent.java b/framework/java/android/net/wifi/p2p/WifiP2pProvDiscEvent.java index 481075b9ac..11a3095201 100644 --- a/framework/java/android/net/wifi/p2p/WifiP2pProvDiscEvent.java +++ b/framework/java/android/net/wifi/p2p/WifiP2pProvDiscEvent.java @@ -38,21 +38,40 @@ public class WifiP2pProvDiscEvent { private static final String TAG = "WifiP2pProvDiscEvent"; - public static final int PBC_REQ = 1; - public static final int PBC_RSP = 2; - public static final int ENTER_PIN = 3; - public static final int SHOW_PIN = 4; - - /* One of PBC_REQ, PBC_RSP, ENTER_PIN or SHOW_PIN */ + public static final int WPS_PBC_REQ = 1; + public static final int WPS_PBC_RSP = 2; + public static final int WPS_ENTER_PIN = 3; + public static final int WPS_SHOW_PIN = 4; + public static final int PAIRING_BOOTSTRAPPING_OPPORTUNISTIC_REQ = 5; + public static final int PAIRING_BOOTSTRAPPING_OPPORTUNISTIC_RSP = 6; + public static final int PAIRING_BOOTSTRAPPING_ENTER_PIN = 7; + public static final int PAIRING_BOOTSTRAPPING_SHOW_PIN = 8; + public static final int PAIRING_BOOTSTRAPPING_ENTER_PASSPHRASE = 9; + public static final int PAIRING_BOOTSTRAPPING_SHOW_PASSPHRASE = 10; + + /* + * One of WPS_PBC_REQ, WPS_PBC_RSP, WPS_ENTER_PIN, WPS_SHOW_PIN, + * PAIRING_BOOTSTRAPPING_OPPORTUNISTIC_REQ, PAIRING_BOOTSTRAPPING_OPPORTUNISTIC_RSP, + * PAIRING_BOOTSTRAPPING_ENTER_PIN, PAIRING_BOOTSTRAPPING_SHOW_PIN, + * PAIRING_BOOTSTRAPPING_ENTER_PASSPHRASE or PAIRING_BOOTSTRAPPING_SHOW_PASSPHRASE. + */ @UnsupportedAppUsage public int event; @UnsupportedAppUsage public WifiP2pDevice device; - /* Valid when event = SHOW_PIN */ + /* + * Valid when event = WPS_SHOW_PIN + */ + @UnsupportedAppUsage + public String wpsPin; + + /* + * Valid when event = PAIRING_BOOTSTRAPPING_SHOW_PIN or PAIRING_BOOTSTRAPPING_SHOW_PASSPHRASE. + */ @UnsupportedAppUsage - public String pin; + public String pairingPinOrPassphrase; /** List of {@link OuiKeyedData} providing vendor-specific configuration data. */ private @NonNull List<OuiKeyedData> mVendorData = Collections.emptyList(); @@ -114,18 +133,24 @@ public class WifiP2pProvDiscEvent { throw new IllegalArgumentException("Malformed event " + string); } - if (tokens[0].endsWith("PBC-REQ")) event = PBC_REQ; - else if (tokens[0].endsWith("PBC-RESP")) event = PBC_RSP; - else if (tokens[0].endsWith("ENTER-PIN")) event = ENTER_PIN; - else if (tokens[0].endsWith("SHOW-PIN")) event = SHOW_PIN; - else throw new IllegalArgumentException("Malformed event " + string); + if (tokens[0].endsWith("PBC-REQ")) { + event = WPS_PBC_REQ; + } else if (tokens[0].endsWith("PBC-RESP")) { + event = WPS_PBC_RSP; + } else if (tokens[0].endsWith("ENTER-PIN")) { + event = WPS_ENTER_PIN; + } else if (tokens[0].endsWith("SHOW-PIN")) { + event = WPS_SHOW_PIN; + } else { + throw new IllegalArgumentException("Malformed event " + string); + } device = new WifiP2pDevice(); device.deviceAddress = tokens[1]; - if (event == SHOW_PIN) { - pin = tokens[2]; + if (event == WPS_SHOW_PIN) { + wpsPin = tokens[2]; } } @@ -133,7 +158,8 @@ public class WifiP2pProvDiscEvent { StringBuffer sbuf = new StringBuffer(); sbuf.append(device); sbuf.append("\n event: ").append(event); - sbuf.append("\n pin: ").append(pin); + sbuf.append("\n wpsPin: ").append(wpsPin); + sbuf.append("\n PairingPinOrPassphrase: ").append(pairingPinOrPassphrase); sbuf.append("\n vendorData: ").append(mVendorData); return sbuf.toString(); } diff --git a/framework/java/android/net/wifi/p2p/WifiP2pUsdBasedLocalServiceAdvertisementConfig.java b/framework/java/android/net/wifi/p2p/WifiP2pUsdBasedLocalServiceAdvertisementConfig.java new file mode 100644 index 0000000000..3845f45c70 --- /dev/null +++ b/framework/java/android/net/wifi/p2p/WifiP2pUsdBasedLocalServiceAdvertisementConfig.java @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2024 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 android.net.wifi.p2p; + +import android.annotation.FlaggedApi; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.RequiresApi; +import android.os.Build; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.wifi.flags.Flags; + +/** + * A class representing a Wi-Fi P2P USD based service advertisement configuration for advertising + * the services. + */ +@RequiresApi(Build.VERSION_CODES.BAKLAVA) +@FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) +public final class WifiP2pUsdBasedLocalServiceAdvertisementConfig implements Parcelable { + /** + * Default channel frequency for USD based service discovery. + */ + private static final int USD_DEFAULT_DISCOVERY_CHANNEL_MHZ = 2437; + + /** + * Frequency on which the service needs to be advertised. + */ + private int mFrequencyMhz; + + private WifiP2pUsdBasedLocalServiceAdvertisementConfig(int frequencyMhz) { + mFrequencyMhz = frequencyMhz; + } + + /** + * Get the frequency on which the service is advertised. + */ + @IntRange(from = 0) + public int getFrequencyMhz() { + return mFrequencyMhz; + } + + /** + * Generates a string of all the defined elements. + * + * @return a compiled string representing all elements + */ + public String toString() { + StringBuilder sbuf = new StringBuilder("WifiP2pUsdBasedLocalServiceAdvertisementConfig:"); + sbuf.append("\n Frequency: ").append(mFrequencyMhz); + return sbuf.toString(); + } + + /** Implement the Parcelable interface */ + public int describeContents() { + return 0; + } + + /** Implement the Parcelable interface */ + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mFrequencyMhz); + } + + /** Implement the Parcelable interface */ + private WifiP2pUsdBasedLocalServiceAdvertisementConfig(@NonNull Parcel in) { + this.mFrequencyMhz = in.readInt(); + } + + /** Implement the Parcelable interface */ + @NonNull + public static final Creator<WifiP2pUsdBasedLocalServiceAdvertisementConfig> CREATOR = + new Creator<WifiP2pUsdBasedLocalServiceAdvertisementConfig>() { + public WifiP2pUsdBasedLocalServiceAdvertisementConfig createFromParcel(Parcel in) { + return new WifiP2pUsdBasedLocalServiceAdvertisementConfig(in); + } + + public WifiP2pUsdBasedLocalServiceAdvertisementConfig[] newArray(int size) { + return new WifiP2pUsdBasedLocalServiceAdvertisementConfig[size]; + } + }; + + /** + * Builder for {@link WifiP2pUsdBasedLocalServiceAdvertisementConfig}. + */ + public static final class Builder { + private int mFrequencyMhz; + + /** + * Constructs a Builder with default values. + */ + public Builder() { + mFrequencyMhz = USD_DEFAULT_DISCOVERY_CHANNEL_MHZ; + } + + /** + * Specifies the frequency requested for advertising the service. + * + * @param frequencyMhz The requested frequency on which the service needs to be advertised. + * If not set, the default frequency is + * {@link #USD_DEFAULT_DISCOVERY_CHANNEL_MHZ} MHz. + * + * @return The builder to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. + * + */ + @NonNull + public Builder setFrequencyMhz(@IntRange(from = 1) int frequencyMhz) { + if (frequencyMhz <= 0) { + throw new IllegalArgumentException("Frequency must be greater than 0"); + } + mFrequencyMhz = frequencyMhz; + return this; + } + + /** + * Build {@link WifiP2pUsdBasedLocalServiceAdvertisementConfig} given the + * current requests made on the builder. + * @return {@link WifiP2pUsdBasedLocalServiceAdvertisementConfig} constructed based on + * builder method calls. + */ + @NonNull + public WifiP2pUsdBasedLocalServiceAdvertisementConfig build() { + return new WifiP2pUsdBasedLocalServiceAdvertisementConfig(mFrequencyMhz); + } + } +} diff --git a/framework/java/android/net/wifi/p2p/WifiP2pUsdBasedServiceDiscoveryConfig.java b/framework/java/android/net/wifi/p2p/WifiP2pUsdBasedServiceDiscoveryConfig.java new file mode 100644 index 0000000000..dfbe54c2ee --- /dev/null +++ b/framework/java/android/net/wifi/p2p/WifiP2pUsdBasedServiceDiscoveryConfig.java @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2024 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 android.net.wifi.p2p; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresApi; +import android.net.wifi.ScanResult; +import android.os.Build; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.wifi.flags.Flags; + +import java.util.Arrays; +import java.util.Objects; + +/** + * A class representing a Wi-Fi P2P USD based service discovery configuration for + * discovering the services. + */ +@RequiresApi(Build.VERSION_CODES.BAKLAVA) +@FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) +public final class WifiP2pUsdBasedServiceDiscoveryConfig implements Parcelable { + /** + * Default channel frequency for USD based service discovery. + */ + private static final int USD_DEFAULT_DISCOVERY_CHANNEL_MHZ = 2437; + + /** One of the WIFI_BAND */ + private @ScanResult.WifiBand int mBand; + + /** + * Frequencies on which the service needs to be scanned for. + * Used when band is set to ScanResult.UNSPECIFIED. + */ + private int[] mFrequenciesMhz; + + private WifiP2pUsdBasedServiceDiscoveryConfig(int band, @NonNull int[] frequencies) { + mBand = band; + mFrequenciesMhz = frequencies; + } + + /** + * Get the band to scan for services. See {@link Builder#setBand(int)} + */ + @ScanResult.WifiBand + public int getBand() { + return mBand; + } + + /** + * Get the frequencies to scan for services. See {@link Builder#setFrequenciesMhz(int[])} + */ + @Nullable + public int[] getFrequenciesMhz() { + return mFrequenciesMhz; + } + + /** + * Generates a string of all the defined elements. + * + * @return a compiled string representing all elements + */ + public String toString() { + StringBuilder sbuf = new StringBuilder("WifiP2pUsdBasedServiceDiscoveryConfig:"); + sbuf.append("\n Band: ").append(mBand); + sbuf.append("\n Frequencies: ").append((mFrequenciesMhz == null) + ? "<null>" : Arrays.toString(mFrequenciesMhz)); + return sbuf.toString(); + } + + /** Implement the Parcelable interface */ + public int describeContents() { + return 0; + } + + /** Implement the Parcelable interface */ + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mBand); + dest.writeIntArray(mFrequenciesMhz); + } + + private WifiP2pUsdBasedServiceDiscoveryConfig(@NonNull Parcel in) { + this.mBand = in.readInt(); + this.mFrequenciesMhz = in.createIntArray(); + } + + private static boolean isBandValid(@ScanResult.WifiBand int band) { + int bandAny = ScanResult.WIFI_BAND_24_GHZ | ScanResult.WIFI_BAND_5_GHZ + | ScanResult.WIFI_BAND_6_GHZ; + return ((band != 0) && ((band & ~bandAny) == 0)); + } + + /** Implement the Parcelable interface */ + @NonNull + public static final Creator<WifiP2pUsdBasedServiceDiscoveryConfig> CREATOR = + new Creator<WifiP2pUsdBasedServiceDiscoveryConfig>() { + public WifiP2pUsdBasedServiceDiscoveryConfig createFromParcel(Parcel in) { + return new WifiP2pUsdBasedServiceDiscoveryConfig(in); + } + + public WifiP2pUsdBasedServiceDiscoveryConfig[] newArray(int size) { + return new WifiP2pUsdBasedServiceDiscoveryConfig[size]; + } + }; + + /** + * Builder for {@link WifiP2pUsdBasedServiceDiscoveryConfig}. + */ + public static final class Builder { + /** Maximum allowed number of channel frequencies */ + private static final int MAXIMUM_CHANNEL_FREQUENCIES = 48; + private int mBand; + private int[] mFrequenciesMhz; + + /** + * Constructs a Builder with default values. + */ + public Builder() { + mBand = ScanResult.UNSPECIFIED; + mFrequenciesMhz = new int[] {USD_DEFAULT_DISCOVERY_CHANNEL_MHZ}; + } + + /** + * Specifies the band requested for service discovery. The band should + * be one of the following band constants defined in {@code ScanResult#WIFI_BAND_24_GHZ}, + * {@code ScanResult#WIFI_BAND_5_GHZ} or {@code ScanResult#WIFI_BAND_6_GHZ} + * + * <p> + * {@link #setBand(int)} and {@link #setFrequenciesMhz(int[])} are + * mutually exclusive. Setting operating band and frequency both is invalid. + * <p> + * Optional. {@code ScanResult#UNSPECIFIED} by default. + * + * @param band The requested band. + * @return Instance of {@link Builder} to enable chaining of the builder method. + * + * @throws IllegalArgumentException - if the band specified is not one among the list + * of bands mentioned above. + */ + public @NonNull Builder setBand(int band) { + if (!isBandValid(band)) { + throw new IllegalArgumentException("Invalid band: " + band); + } + mBand = band; + return this; + } + + /** + * Set the frequencies requested for service discovery. + * + * <p> + * {@link #setBand(int)} and {@link #setFrequenciesMhz(int[])} are + * mutually exclusive. Setting band and frequencies both is invalid. + * <p> + * Optional. 2437 by default. + * @param frequenciesMhz Frequencies in MHz to scan for services. This value cannot be an + * empty array of frequencies. + * @return Instance of {@link Builder} to enable chaining of the builder method. + */ + @NonNull + public Builder setFrequenciesMhz(@NonNull int[] frequenciesMhz) { + Objects.requireNonNull(frequenciesMhz, "Frequencies cannot be null"); + if (frequenciesMhz.length < 1 || frequenciesMhz.length > MAXIMUM_CHANNEL_FREQUENCIES) { + throw new IllegalArgumentException("Number of frequencies: " + + frequenciesMhz.length + + " must be between 1 and " + MAXIMUM_CHANNEL_FREQUENCIES); + } + mFrequenciesMhz = frequenciesMhz; + return this; + } + + /** + * Build {@link WifiP2pUsdBasedServiceDiscoveryConfig} given the current requests made + * on the builder. + * @return {@link WifiP2pUsdBasedServiceDiscoveryConfig} constructed based on builder + * method calls. + */ + @NonNull + public WifiP2pUsdBasedServiceDiscoveryConfig build() { + if (mBand != ScanResult.UNSPECIFIED && mFrequenciesMhz != null) { + throw new IllegalStateException( + "Frequencies and band are mutually exclusive."); + } + return new WifiP2pUsdBasedServiceDiscoveryConfig(mBand, mFrequenciesMhz); + } + } +} diff --git a/framework/java/android/net/wifi/p2p/nsd/WifiP2pServiceInfo.java b/framework/java/android/net/wifi/p2p/nsd/WifiP2pServiceInfo.java index 5d018e7548..40dc1803b9 100644 --- a/framework/java/android/net/wifi/p2p/nsd/WifiP2pServiceInfo.java +++ b/framework/java/android/net/wifi/p2p/nsd/WifiP2pServiceInfo.java @@ -16,13 +16,21 @@ package android.net.wifi.p2p.nsd; +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresApi; import android.compat.annotation.UnsupportedAppUsage; +import android.net.wifi.util.Environment; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; +import com.android.wifi.flags.Flags; + import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * A class for storing service information that is advertised @@ -89,6 +97,28 @@ public class WifiP2pServiceInfo implements Parcelable { private List<String> mQueryList; /** + * This field is used only when the service advertisement is using un-synchronized service + * discovery (USD) protocol. Refer Wi-Fi Alliance Wi-Fi Direct R2 specification section 3.7 - + * "Unsynchronized Service Discovery (USD)" for the details. + */ + private WifiP2pUsdBasedServiceConfig mUsdServiceConfig; + + /** + * Service advertisement session ID / Publish ID for USD based service advertisement. + * This is a nonzero value used to identify the instance of service advertisement. + * This value is filled in the service discovery response frame (USD publish frame), + * Service descriptor attribute (SDA) - instance ID field. + */ + /** + * Service advertisement session ID (Advertiser ID) for USD based service discovery response. + * The session ID is used to identify a local advertisement session. + * It is a nonzero ID in the range of 1 to 255 filled in the Service descriptor attribute (SDA) + * - instance ID field of the service discovery response frame (Publish frame). + * Zero by default indicates that the USD session for this service is not running. + */ + private int mUsdSessionId; + + /** * This is only used in subclass. * * @param queryList query string for wpa_supplicant @@ -102,6 +132,39 @@ public class WifiP2pServiceInfo implements Parcelable { mQueryList = queryList; } + /** + * This constructor is only used in Parcelable. + * + * @param queryList query string for wpa_supplicant + * @param usdConfig See {@link WifiP2pUsdBasedServiceConfig} + * @param usdSessionId The USD based service advertisement session ID. + */ + private WifiP2pServiceInfo(List<String> queryList, + @NonNull WifiP2pUsdBasedServiceConfig usdConfig, int usdSessionId) { + mQueryList = queryList; + mUsdServiceConfig = usdConfig; + mUsdSessionId = usdSessionId; + } + + /** + * Constructor for creating a service information for advertising the service using + * un-synchronized service discovery (USD) protocol. Refer Wi-Fi Alliance Wi-Fi Direct R2 + * specification section 3.7 - "Unsynchronized Service Discovery (USD)" for the details. + * + * @param usdConfig See {@link WifiP2pUsdBasedServiceConfig} + * + * @return service info containing USD based service configuration. + */ + @RequiresApi(Build.VERSION_CODES.BAKLAVA) + @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) + public WifiP2pServiceInfo(@NonNull WifiP2pUsdBasedServiceConfig usdConfig) { + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + Objects.requireNonNull(usdConfig, "usd based service config cannot be null"); + mUsdServiceConfig = usdConfig; + } + /** * Return the list of the query string for wpa_supplicant. * @@ -112,6 +175,53 @@ public class WifiP2pServiceInfo implements Parcelable { return mQueryList; } + /** + * Get the service information configured to advertise using un-synchronized service discovery + * (USD) protocol. + * See {@link #WifiP2pServiceInfo(WifiP2pUsdBasedServiceConfig)} + * + * @return A valid or not null {@link WifiP2pUsdBasedServiceConfig} if the service information + * is configured to advertise using un-synchronized service discovery (USD) protocol. + * Otherwise, it is null. + */ + @RequiresApi(Build.VERSION_CODES.BAKLAVA) + @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) + @Nullable + public WifiP2pUsdBasedServiceConfig getWifiP2pUsdBasedServiceConfig() { + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + return mUsdServiceConfig; + } + + /** + * Return the Service advertisement session ID for USD based service advertisement. + * + * @return session id + * @hide + */ + /** + * Return the Service advertisement session ID for USD based service advertisement. + * This ID is used to identify a service advertisement session. + * + * @return A nonzero ID in the range of 1 to 255 when the session is running. + * @hide + */ + public int getUsdSessionId() { + return mUsdSessionId; + } + + /** + * Set the service advertisement session ID for USD based service advertisement. + * Default value is zero. + * + * @param sessionId nonzero session ID is set when the USD session for this service is started. + * @hide + */ + public void setUsdSessionId(int sessionId) { + mUsdSessionId = sessionId; + } + /** * Converts byte array to hex string. * @@ -141,23 +251,29 @@ public class WifiP2pServiceInfo implements Parcelable { @Override public boolean equals(Object o) { - if (o == this) { - return true; - } - if (!(o instanceof WifiP2pServiceInfo)) { - return false; - } + if (o == this) { + return true; + } + if (!(o instanceof WifiP2pServiceInfo)) { + return false; + } - WifiP2pServiceInfo servInfo = (WifiP2pServiceInfo)o; - return mQueryList.equals(servInfo.mQueryList); + /* + * Don't compare USD based service advertisement session ID. + * The session ID may be changed on each service discovery advertisement. + */ + WifiP2pServiceInfo servInfo = (WifiP2pServiceInfo) o; + return Objects.equals(mQueryList, servInfo.mQueryList) + && Objects.equals(mUsdServiceConfig, servInfo.mUsdServiceConfig); } - @Override - public int hashCode() { - int result = 17; - result = 31 * result + (mQueryList == null ? 0 : mQueryList.hashCode()); - return result; - } + @Override + public int hashCode() { + int result = 17; + result = 31 * result + (mQueryList == null ? 0 : mQueryList.hashCode()); + result = 31 * result + (mUsdServiceConfig == null ? 0 : mUsdServiceConfig.hashCode()); + return result; + } /** Implement the Parcelable interface {@hide} */ public int describeContents() { @@ -167,21 +283,30 @@ public class WifiP2pServiceInfo implements Parcelable { /** Implement the Parcelable interface {@hide} */ public void writeToParcel(Parcel dest, int flags) { dest.writeStringList(mQueryList); + if (Environment.isSdkAtLeastB() && Flags.wifiDirectR2()) { + dest.writeParcelable(mUsdServiceConfig, flags); + dest.writeInt(mUsdSessionId); + } } /** Implement the Parcelable interface {@hide} */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final @android.annotation.NonNull Creator<WifiP2pServiceInfo> CREATOR = - new Creator<WifiP2pServiceInfo>() { - public WifiP2pServiceInfo createFromParcel(Parcel in) { - - List<String> data = new ArrayList<String>(); - in.readStringList(data); - return new WifiP2pServiceInfo(data); - } - - public WifiP2pServiceInfo[] newArray(int size) { - return new WifiP2pServiceInfo[size]; - } - }; + new Creator<WifiP2pServiceInfo>() { + public WifiP2pServiceInfo createFromParcel(Parcel in) { + List<String> data = new ArrayList<String>(); + in.readStringList(data); + WifiP2pUsdBasedServiceConfig config = null; + int usdSessionId = 0; + if (Environment.isSdkAtLeastB() && Flags.wifiDirectR2()) { + config = in.readParcelable( + WifiP2pUsdBasedServiceConfig.class.getClassLoader()); + usdSessionId = in.readInt(); + } + return new WifiP2pServiceInfo(data, config, usdSessionId); + } + public WifiP2pServiceInfo[] newArray(int size) { + return new WifiP2pServiceInfo[size]; + } + }; } diff --git a/framework/java/android/net/wifi/p2p/nsd/WifiP2pServiceRequest.java b/framework/java/android/net/wifi/p2p/nsd/WifiP2pServiceRequest.java index dea0477cf0..f76c17da46 100644 --- a/framework/java/android/net/wifi/p2p/nsd/WifiP2pServiceRequest.java +++ b/framework/java/android/net/wifi/p2p/nsd/WifiP2pServiceRequest.java @@ -16,13 +16,21 @@ package android.net.wifi.p2p.nsd; +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresApi; import android.compat.annotation.UnsupportedAppUsage; import android.net.wifi.p2p.WifiP2pManager; +import android.net.wifi.util.Environment; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; +import com.android.wifi.flags.Flags; + import java.util.Locale; +import java.util.Objects; /** * A class for creating a service discovery request for use with @@ -68,6 +76,24 @@ public class WifiP2pServiceRequest implements Parcelable { private String mQuery; /** + * This field is used only when the service discovery request is using un-synchronized service + * discovery (USD) protocol. Refer Wi-Fi Alliance Wi-Fi Direct R2 specification section 3.7 - + * "Unsynchronized Service Discovery (USD)" for the details. + */ + private WifiP2pUsdBasedServiceConfig mUsdServiceConfig; + + /** + * Service discovery request session ID (Seeker ID) for USD based service discovery. + * The session ID is used to match the USD based service discovery request/response frames. + * A nonzero ID in the range of 1 to 255 is filled in the Service descriptor attribute (SDA) - + * instance ID field of the service discovery request frame (Subscribe frame). The responding + * device copies this ID in the Service descriptor attribute (SDA) - requester instance ID + * field of the service discovery response frame (Publish frame). + * Zero by default indicates that the USD session for this service is not running. + */ + private int mUsdSessionId = 0; + + /** * This constructor is only used in newInstance(). * * @param protocolType service discovery protocol. @@ -88,19 +114,24 @@ public class WifiP2pServiceRequest implements Parcelable { } /** - * This constructor is only used in Parcelable. + * This constructor is only used in parcelable. * * @param serviceType service discovery type. * @param length the length of service discovery packet. * @param transId the transaction id * @param query The part of service specific query. + * @param usdConfig The USD based service config. + * @param usdSessionId The USD based service discovery request session ID. */ private WifiP2pServiceRequest(int serviceType, int length, - int transId, String query) { + int transId, String query, @NonNull WifiP2pUsdBasedServiceConfig usdConfig, + int usdSessionId) { mProtocolType = serviceType; mLength = length; mTransId = transId; mQuery = query; + mUsdServiceConfig = usdConfig; + mUsdSessionId = usdSessionId; } /** @@ -151,6 +182,48 @@ public class WifiP2pServiceRequest implements Parcelable { } /** + * Return the USD based service discovery request session ID. + * This ID is used to match the USD based service request/response frames. + * + * @return A nonzero ID in the range of 1 to 255 when the session is running. + * @hide + */ + public int getUsdSessionId() { + return mUsdSessionId; + } + + /** + * Set the USD based service discovery request session ID. + * Default value is zero. + * + * @param sessionId nonzero session ID is set when the USD session for this service is started. + * @hide + */ + public void setUsdSessionId(int sessionId) { + mUsdSessionId = sessionId; + } + + /** + /** + * Get the service information configured to discover a service using un-synchronized service + * discovery (USD) protocol. + * See {@link #WifiP2pServiceRequest(WifiP2pUsdBasedServiceConfig)}. + * + * @return A valid or not null {@link WifiP2pUsdBasedServiceConfig} if the service information + * is configured to discover a service using un-synchronized service discovery (USD) protocol. + * Otherwise, it is null. + */ + @RequiresApi(Build.VERSION_CODES.BAKLAVA) + @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) + @Nullable + public WifiP2pUsdBasedServiceConfig getWifiP2pUsdBasedServiceConfig() { + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + return mUsdServiceConfig; + } + + /** * Validate query. * * <p>If invalid, throw IllegalArgumentException. @@ -214,6 +287,25 @@ public class WifiP2pServiceRequest implements Parcelable { return new WifiP2pServiceRequest(protocolType, null); } + /** + * Constructor for creating a service discovery request for discovering the service using + * un-synchronized service discovery (USD) protocol. Refer Wi-Fi Alliance Wi-Fi Direct R2 + * specification section 3.7 - "Unsynchronized Service Discovery (USD)" for the details. + * + * @param usdConfig See {@link WifiP2pUsdBasedServiceConfig} + * + * @return service discovery request containing USD based service configuration. + */ + @RequiresApi(Build.VERSION_CODES.BAKLAVA) + @FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) + public WifiP2pServiceRequest(@NonNull WifiP2pUsdBasedServiceConfig usdConfig) { + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + Objects.requireNonNull(usdConfig, "usdConfig cannot be null"); + mUsdServiceConfig = usdConfig; + } + @Override public boolean equals(Object o) { if (o == this) { @@ -229,17 +321,10 @@ public class WifiP2pServiceRequest implements Parcelable { * Not compare transaction id. * Transaction id may be changed on each service discovery operation. */ - if ((req.mProtocolType != mProtocolType) || - (req.mLength != mLength)) { - return false; - } - - if (req.mQuery == null && mQuery == null) { - return true; - } else if (req.mQuery != null) { - return req.mQuery.equals(mQuery); - } - return false; + return mProtocolType == req.mProtocolType + && mLength == req.mLength + && Objects.equals(mQuery, req.mQuery) + && Objects.equals(mUsdServiceConfig, req.mUsdServiceConfig); } @Override @@ -248,6 +333,7 @@ public class WifiP2pServiceRequest implements Parcelable { result = 31 * result + mProtocolType; result = 31 * result + mLength; result = 31 * result + (mQuery == null ? 0 : mQuery.hashCode()); + result = 31 * result + (mUsdServiceConfig == null ? 0 : mUsdServiceConfig.hashCode()); return result; } @@ -262,22 +348,33 @@ public class WifiP2pServiceRequest implements Parcelable { dest.writeInt(mLength); dest.writeInt(mTransId); dest.writeString(mQuery); + if (Environment.isSdkAtLeastB() && Flags.wifiDirectR2()) { + dest.writeParcelable(mUsdServiceConfig, flags); + dest.writeInt(mUsdSessionId); + } } /** Implement the Parcelable interface {@hide} */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final @android.annotation.NonNull Creator<WifiP2pServiceRequest> CREATOR = - new Creator<WifiP2pServiceRequest>() { - public WifiP2pServiceRequest createFromParcel(Parcel in) { - int servType = in.readInt(); - int length = in.readInt(); - int transId = in.readInt(); - String query = in.readString(); - return new WifiP2pServiceRequest(servType, length, transId, query); - } - - public WifiP2pServiceRequest[] newArray(int size) { - return new WifiP2pServiceRequest[size]; - } - }; + new Creator<WifiP2pServiceRequest>() { + public WifiP2pServiceRequest createFromParcel(Parcel in) { + int servType = in.readInt(); + int length = in.readInt(); + int transId = in.readInt(); + String query = in.readString(); + WifiP2pUsdBasedServiceConfig config = null; + int usdSessionId = 0; + if (Environment.isSdkAtLeastB() && Flags.wifiDirectR2()) { + config = in.readParcelable( + WifiP2pUsdBasedServiceConfig.class.getClassLoader()); + usdSessionId = in.readInt(); + } + return new WifiP2pServiceRequest(servType, length, transId, query, config, + usdSessionId); + } + public WifiP2pServiceRequest[] newArray(int size) { + return new WifiP2pServiceRequest[size]; + } + }; } diff --git a/framework/java/android/net/wifi/p2p/nsd/WifiP2pServiceResponse.java b/framework/java/android/net/wifi/p2p/nsd/WifiP2pServiceResponse.java index 1b9c080993..16025d512e 100644 --- a/framework/java/android/net/wifi/p2p/nsd/WifiP2pServiceResponse.java +++ b/framework/java/android/net/wifi/p2p/nsd/WifiP2pServiceResponse.java @@ -16,16 +16,22 @@ package android.net.wifi.p2p.nsd; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.net.wifi.p2p.WifiP2pDevice; +import android.net.wifi.util.Environment; import android.os.Parcel; import android.os.Parcelable; +import com.android.wifi.flags.Flags; + import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Objects; /** * The class for a response of service discovery. @@ -65,6 +71,22 @@ public class WifiP2pServiceResponse implements Parcelable { */ protected byte[] mData; + /** + * This field is used only for USD based service discovery response. + */ + private WifiP2pUsdBasedServiceResponse mUsdBasedServiceResponse; + + /** + * Service discovery response requester session ID (Seeker ID) for USD based service discovery. + * The session ID is used to match the USD based service discovery request/response frames. + * A nonzero ID in the range of 1 to 255 is filled in the Service descriptor attribute (SDA) - + * instance ID field of the service discovery request frame (Subscribe frame). The responding + * device copies this ID in the Service descriptor attribute (SDA) - requester instance ID + * field of the service discovery response frame (Publish frame). + * + */ + private int mUsdSessionId; + /** * The status code of service discovery response. @@ -125,6 +147,45 @@ public class WifiP2pServiceResponse implements Parcelable { } /** + * Hidden constructor. This is only used in framework. + * + * @param device source device. + * @param usdResponseData USD based service response data. + * @param usdSessionId The USD based service discovery request/response session ID. + * @hide + */ + public WifiP2pServiceResponse(WifiP2pDevice device, + @NonNull WifiP2pUsdBasedServiceResponse usdResponseData, int usdSessionId) { + mServiceType = 0; + mStatus = 0; + mTransId = 0; + mDevice = device; + mData = null; + mUsdBasedServiceResponse = usdResponseData; + mUsdSessionId = usdSessionId; + } + + /** + * Return the USD based service discovery session ID. + * + * @return A nonzero ID in the range of 1 to 255. + * @hide + */ + public int getUsdSessionId() { + return mUsdSessionId; + } + + /** + * Set the USD based service discovery session ID. + * + * @param sessionId A nonzero ID in the range of 1 to 255. + * @hide + */ + public void setUsdSessionId(int sessionId) { + mUsdSessionId = sessionId; + } + + /** * Return the service type of service discovery response. * * @return service discovery type.<br> @@ -182,6 +243,20 @@ public class WifiP2pServiceResponse implements Parcelable { this.mDevice = dev; } + /** + * Get the service response data received through un-synchronized service + * discovery (USD) protocol. + * + * @return A valid or not null {@link WifiP2pUsdBasedServiceResponse} if the service response + * data is received through un-synchronized service discovery (USD) protocol. + * Otherwise, it is null. + * @hide + */ + @Nullable + public WifiP2pUsdBasedServiceResponse getWifiP2pUsdBasedServiceResponse() { + return mUsdBasedServiceResponse; + } + /** * Create the list of WifiP2pServiceResponse instance from supplicant event. @@ -289,6 +364,11 @@ public class WifiP2pServiceResponse implements Parcelable { sbuf.append(" status:").append(Status.toString(mStatus)); sbuf.append(" srcAddr:").append(mDevice.deviceAddress); sbuf.append(" data:").append(Arrays.toString(mData)); + if (Environment.isSdkAtLeastB() && Flags.wifiDirectR2()) { + sbuf.append(" USD based service response:") + .append((mUsdBasedServiceResponse == null) + ? "<null>" : mUsdBasedServiceResponse.toString()); + } return sbuf.toString(); } @@ -303,10 +383,11 @@ public class WifiP2pServiceResponse implements Parcelable { WifiP2pServiceResponse req = (WifiP2pServiceResponse)o; - return (req.mServiceType == mServiceType) && - (req.mStatus == mStatus) && - equals(req.mDevice.deviceAddress, mDevice.deviceAddress) && - Arrays.equals(req.mData, mData); + return mServiceType == req.mServiceType + && mStatus == req.mStatus + && Objects.equals(mDevice.deviceAddress, req.mDevice.deviceAddress) + && Arrays.equals(mData, req.mData) + && Objects.equals(mUsdBasedServiceResponse, req.mUsdBasedServiceResponse); } private boolean equals(Object a, Object b) { @@ -327,6 +408,8 @@ public class WifiP2pServiceResponse implements Parcelable { result = 31 * result + (mDevice.deviceAddress == null ? 0 : mDevice.deviceAddress.hashCode()); result = 31 * result + (mData == null ? 0 : Arrays.hashCode(mData)); + result = 31 * result + (mUsdBasedServiceResponse == null + ? 0 : mUsdBasedServiceResponse.hashCode()); return result; } @@ -347,35 +430,44 @@ public class WifiP2pServiceResponse implements Parcelable { dest.writeInt(mData.length); dest.writeByteArray(mData); } + if (Environment.isSdkAtLeastB() && Flags.wifiDirectR2()) { + dest.writeParcelable(mUsdBasedServiceResponse, flags); + dest.writeInt(mUsdSessionId); + } } /** Implement the Parcelable interface {@hide} */ public static final @android.annotation.NonNull Creator<WifiP2pServiceResponse> CREATOR = - new Creator<WifiP2pServiceResponse>() { - public WifiP2pServiceResponse createFromParcel(Parcel in) { - - int type = in.readInt(); - int status = in.readInt(); - int transId = in.readInt(); - WifiP2pDevice dev = (WifiP2pDevice)in.readParcelable(null); - int len = in.readInt(); - byte[] data = null; - if (len > 0) { - data = new byte[len]; - in.readByteArray(data); + new Creator<WifiP2pServiceResponse>() { + public WifiP2pServiceResponse createFromParcel(Parcel in) { + int type = in.readInt(); + int status = in.readInt(); + int transId = in.readInt(); + WifiP2pDevice dev = in.readParcelable(WifiP2pDevice.class.getClassLoader()); + int len = in.readInt(); + byte[] data = null; + if (len > 0) { + data = new byte[len]; + in.readByteArray(data); + } + WifiP2pUsdBasedServiceResponse usdServResponse = null; + int usdSessionId = 0; + if (Environment.isSdkAtLeastB() && Flags.wifiDirectR2()) { + usdServResponse = in.readParcelable( + WifiP2pUsdBasedServiceResponse.class.getClassLoader()); + usdSessionId = in.readInt(); + } + if (type == WifiP2pServiceInfo.SERVICE_TYPE_BONJOUR) { + return WifiP2pDnsSdServiceResponse.newInstance(status, transId, dev, data); + } else if (type == WifiP2pServiceInfo.SERVICE_TYPE_UPNP) { + return WifiP2pUpnpServiceResponse.newInstance(status, transId, dev, data); + } else if (usdServResponse != null) { + return new WifiP2pServiceResponse(dev, usdServResponse, usdSessionId); + } + return new WifiP2pServiceResponse(type, status, transId, dev, data); } - if (type == WifiP2pServiceInfo.SERVICE_TYPE_BONJOUR) { - return WifiP2pDnsSdServiceResponse.newInstance(status, - transId, dev, data); - } else if (type == WifiP2pServiceInfo.SERVICE_TYPE_UPNP) { - return WifiP2pUpnpServiceResponse.newInstance(status, - transId, dev, data); + public WifiP2pServiceResponse[] newArray(int size) { + return new WifiP2pServiceResponse[size]; } - return new WifiP2pServiceResponse(type, status, transId, dev, data); - } - - public WifiP2pServiceResponse[] newArray(int size) { - return new WifiP2pServiceResponse[size]; - } - }; + }; } diff --git a/framework/java/android/net/wifi/p2p/nsd/WifiP2pUsdBasedServiceConfig.java b/framework/java/android/net/wifi/p2p/nsd/WifiP2pUsdBasedServiceConfig.java new file mode 100644 index 0000000000..74834f3d44 --- /dev/null +++ b/framework/java/android/net/wifi/p2p/nsd/WifiP2pUsdBasedServiceConfig.java @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2024 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 android.net.wifi.p2p.nsd; + +import android.annotation.FlaggedApi; +import android.annotation.IntDef; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresApi; +import android.annotation.Size; +import android.os.Build; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; + +import com.android.wifi.flags.Flags; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; +import java.util.Objects; + +/** + * A class for creating a USD based service discovery configuration for use with + * {@link WifiP2pServiceInfo}.<br> or {@link WifiP2pServiceRequest}.<br> + * For the details of the configuration, refer Wi-Fi Alliance Wi-Fi Direct R2 specification + * - Appendix H - Unsynchronized Service Discovery (as defined in Wi-Fi Aware) and section + * 4.2.13 USD frame format. + */ +@RequiresApi(Build.VERSION_CODES.BAKLAVA) +@FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) +public final class WifiP2pUsdBasedServiceConfig implements Parcelable { + /** Maximum allowed length of service specific information */ + private static final int SERVICE_SPECIFIC_INFO_MAXIMUM_LENGTH = 1024; + + /** Bonjour service protocol type */ + public static final int SERVICE_PROTOCOL_TYPE_BONJOUR = 1; + + /** Generic service protocol type */ + public static final int SERVICE_PROTOCOL_TYPE_GENERIC = 2; + + /** + * Currently for Wi-Fi Direct R2, status codes are defined in Wi-Fi Direct R2 specification + * (Table 129). + * @hide + */ + @IntDef(flag = false, prefix = { "SERVICE_PROTOCOL_TYPE_" }, value = { + SERVICE_PROTOCOL_TYPE_BONJOUR, + SERVICE_PROTOCOL_TYPE_GENERIC, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ServiceProtocolType {} + + /** + * Service protocol type. + */ + private int mServiceProtocolType; + /** + * UTF-8 string defining the service. + */ + private String mServiceName; + /** + * Optional Service specific information content determined by the application. + */ + private byte[] mServiceSpecificInfo; + + /** Get the service protocol type of this USD service configuration. See also + * {@link Builder#setServiceProtocolType(int)}. + * + * @return A non-negative service layer protocol type. + */ + @IntRange(from = 0, to = 255) + public int getServiceProtocolType() { + return mServiceProtocolType; + } + + /** Get the service name of this USD service configuration. See also + * {@link Builder}. + * + * @return UTF-8 string defining the service. + */ + @NonNull + public String getServiceName() { + return mServiceName; + } + + /** Get the service specific info of this USD service configuration. See also + * {@link Builder#setServiceSpecificInfo(byte[])} . + * + * + * @return A byte-array of service specification information, or null if unset. + */ + @Nullable + public byte[] getServiceSpecificInfo() { + return mServiceSpecificInfo; + } + + /** + * Maximum allowed length of service specific information that can be set in the USD service + * configuration. + * See also {@link Builder#setServiceSpecificInfo(byte[])}. + */ + public static int getMaxAllowedServiceSpecificInfoLength() { + return SERVICE_SPECIFIC_INFO_MAXIMUM_LENGTH; + } + + /** + * Generates a string of all the defined elements. + * + * @return a compiled string representing all elements + */ + public String toString() { + StringBuilder sbuf = new StringBuilder("WifiP2pUsdBasedServiceConfig:"); + sbuf.append("\n Protocol type: ").append(mServiceProtocolType); + sbuf.append("\n Service name : ").append(mServiceName); + sbuf.append("\n Service specific info : ").append((mServiceSpecificInfo == null) + ? "<null>" : Arrays.toString(mServiceSpecificInfo)); + return sbuf.toString(); + } + + /** Implement the Parcelable interface */ + public int describeContents() { + return 0; + } + + /** Implement the Parcelable interface */ + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mServiceProtocolType); + dest.writeString(mServiceName); + dest.writeByteArray(mServiceSpecificInfo); + } + + /** Implement the Parcelable interface */ + @NonNull + public static final Creator<WifiP2pUsdBasedServiceConfig> CREATOR = + new Creator<WifiP2pUsdBasedServiceConfig>() { + public WifiP2pUsdBasedServiceConfig createFromParcel(Parcel in) { + WifiP2pUsdBasedServiceConfig config = new WifiP2pUsdBasedServiceConfig(); + config.mServiceProtocolType = in.readInt(); + config.mServiceName = in.readString(); + config.mServiceSpecificInfo = in.createByteArray(); + return config; + } + + public WifiP2pUsdBasedServiceConfig[] newArray(int size) { + return new WifiP2pUsdBasedServiceConfig[size]; + } + }; + + /** + * Builder used to build {@link WifiP2pUsdBasedServiceConfig} objects for + * USD based service discovery and advertisement. + */ + public static final class Builder { + /** Maximum allowed length of service name */ + private static final int SERVICE_NAME_MAXIMUM_LENGTH = 100; + private int mServiceProtocolType = SERVICE_PROTOCOL_TYPE_GENERIC; + private @NonNull String mServiceName; + byte[] mServiceSpecificInfo; + + /** + * Constructor for {@link Builder}. + * + * @param serviceName The service name defining the service. The maximum + * allowed length of the service name is 100 characters. + */ + public Builder(@Size(min = 1) @NonNull String serviceName) { + Objects.requireNonNull(serviceName, "Service name cannot be null"); + if (TextUtils.isEmpty(serviceName)) { + throw new IllegalArgumentException("Service name cannot be empty!"); + } + if (serviceName.length() > SERVICE_NAME_MAXIMUM_LENGTH) { + throw new IllegalArgumentException("Service name length: " + serviceName.length() + + " must be less than " + SERVICE_NAME_MAXIMUM_LENGTH); + } + mServiceName = serviceName; + } + + + /** + * Specify the service discovery protocol type. + * + * <p> + * Optional. {@code SERVICE_PROTOCOL_TYPE_GENERIC} by default. + * + * @param serviceProtocolType One of the {@code SERVICE_PROTOCOL_TYPE_*} or a non-negative + * number set by the service layer. + * @return The builder to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. + */ + @NonNull + public Builder setServiceProtocolType( + @IntRange(from = 0, to = 255) int serviceProtocolType) { + if (serviceProtocolType < 0 || serviceProtocolType > 255) { + throw new IllegalArgumentException( + "serviceProtocolType must be between 0-255 (inclusive)"); + } + mServiceProtocolType = serviceProtocolType; + return this; + } + + /** + * Specify service specific information content determined by the application. + * <p> + * Optional. Empty by default. + * + * @param serviceSpecificInfo A byte-array of service-specific information available to the + * application to send additional information. Users must call + * {@link #getMaxAllowedServiceSpecificInfoLength()} method to + * know maximum allowed legth. + * + * @return The builder to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. + */ + @NonNull + public Builder setServiceSpecificInfo( + @Size(min = 1) @Nullable byte[] serviceSpecificInfo) { + if (serviceSpecificInfo != null + && serviceSpecificInfo.length > getMaxAllowedServiceSpecificInfoLength()) { + throw new IllegalArgumentException("Service specific info length: " + + serviceSpecificInfo.length + + " must be less than " + getMaxAllowedServiceSpecificInfoLength()); + } + mServiceSpecificInfo = serviceSpecificInfo; + return this; + } + + /** + * Build {@link WifiP2pUsdBasedServiceConfig} given the current requests made on the + * builder. + * @return {@link WifiP2pUsdBasedServiceConfig} constructed based on builder method calls. + */ + @NonNull + public WifiP2pUsdBasedServiceConfig build() { + if (TextUtils.isEmpty(mServiceName)) { + throw new IllegalStateException( + "Service name must be non-empty"); + } + WifiP2pUsdBasedServiceConfig config = new WifiP2pUsdBasedServiceConfig(); + config.mServiceName = mServiceName; + config.mServiceProtocolType = mServiceProtocolType; + config.mServiceSpecificInfo = mServiceSpecificInfo; + return config; + } + } +} diff --git a/framework/java/android/net/wifi/p2p/nsd/WifiP2pUsdBasedServiceResponse.java b/framework/java/android/net/wifi/p2p/nsd/WifiP2pUsdBasedServiceResponse.java new file mode 100644 index 0000000000..279a6f4efe --- /dev/null +++ b/framework/java/android/net/wifi/p2p/nsd/WifiP2pUsdBasedServiceResponse.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2024 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 android.net.wifi.p2p.nsd; + +import android.annotation.FlaggedApi; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresApi; +import android.os.Build; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.wifi.flags.Flags; + +import java.util.Arrays; + +/** + * A class for a response for USD based service discovery. + * For the details of the configuration, refer Wi-Fi Alliance Wi-Fi Direct R2 specification + * - Appendix H - Unsynchronized Service Discovery (as defined in Wi-Fi Aware) and section + * 4.2.13 USD frame format. + */ +@RequiresApi(Build.VERSION_CODES.BAKLAVA) +@FlaggedApi(Flags.FLAG_WIFI_DIRECT_R2) +public final class WifiP2pUsdBasedServiceResponse implements Parcelable { + /** + * Service discovery protocol tye. It's defined in table 129 in Wi-Fi Direct R2 specification. + */ + private int mServiceProtocolType = -1; + + /** + * Optional Service specific information content send in the response frame. + */ + private byte[] mServiceSpecificInfo; + + /** + * Hidden constructor. This is only used in framework. + * + * @param serviceProtocolType The service protocol type. + * @param serviceSpecificInfo The service specific information. + * @hide + * + */ + public WifiP2pUsdBasedServiceResponse(int serviceProtocolType, + @Nullable byte[] serviceSpecificInfo) { + mServiceProtocolType = serviceProtocolType; + mServiceSpecificInfo = serviceSpecificInfo; + } + + /** + * Get the service protocol type provided by the peer device in the USD service response. + * See also {@link WifiP2pUsdBasedServiceConfig.Builder#setServiceProtocolType(int)} + * + * @return A non-negative service layer protocol type. + */ + @IntRange(from = 0, to = 255) + public int getServiceProtocolType() { + return mServiceProtocolType; + } + + /** Get the service specific information provided by the peer device in the USD service + * response. + * See also {@link WifiP2pUsdBasedServiceConfig.Builder#setServiceSpecificInfo(byte[])} + * + * @return A byte-array of service specification information, or null if unset. + */ + @Nullable + public byte[] getServiceSpecificInfo() { + return mServiceSpecificInfo; + } + + /** + * Generates a string of all the defined elements. + * + * @return a compiled string representing all elements + */ + public String toString() { + StringBuilder sbuf = new StringBuilder("WifiP2pUsdBasedServiceResponse:"); + sbuf.append("\n Protocol type: ").append(mServiceProtocolType); + sbuf.append("\n Service specific info : ").append((mServiceSpecificInfo == null) + ? "<null>" : Arrays.toString(mServiceSpecificInfo)); + return sbuf.toString(); + } + + /** Implement the Parcelable interface */ + public int describeContents() { + return 0; + } + + /** Implement the Parcelable interface */ + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mServiceProtocolType); + dest.writeByteArray(mServiceSpecificInfo); + } + + /** Implement the Parcelable interface */ + @NonNull + public static final Creator<WifiP2pUsdBasedServiceResponse> CREATOR = + new Creator<WifiP2pUsdBasedServiceResponse>() { + public WifiP2pUsdBasedServiceResponse createFromParcel(Parcel in) { + int serviceProtocolType = in.readInt(); + byte[] ssi = in.createByteArray(); + return new WifiP2pUsdBasedServiceResponse(serviceProtocolType, ssi); + } + + public WifiP2pUsdBasedServiceResponse[] newArray(int size) { + return new WifiP2pUsdBasedServiceResponse[size]; + } + }; +} diff --git a/framework/java/android/net/wifi/rtt/PasnConfig.java b/framework/java/android/net/wifi/rtt/PasnConfig.java new file mode 100644 index 0000000000..9688f5eee3 --- /dev/null +++ b/framework/java/android/net/wifi/rtt/PasnConfig.java @@ -0,0 +1,432 @@ +/* + * Copyright (C) 2024 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 android.net.wifi.rtt; + +import android.annotation.FlaggedApi; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.wifi.ScanResult; +import android.net.wifi.WifiSsid; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.modules.utils.build.SdkLevel; +import com.android.wifi.flags.Flags; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * Pre-association security negotiation (PASN) configuration. + * <p> + * PASN configuration in IEEE 802.11az focuses on securing the ranging process before a device + * fully associates with a Wi-Fi network. IEEE 802.11az supports various based AKMs as in + * {@code AKM_*} for PASN and cipher as in {@code CIPHER_*}. Password is also another input to + * some base AKMs. + * <p> + * Once PASN is initiated, the AP and the client device exchange messages to authenticate each + * other and establish security keys. This process ensures that only authorized devices can + * participate in ranging. + * <p> + * After successful PASN authentication, ranging operations are performed using the established + * secure channel. This protects the ranging measurements from eavesdropping and tampering. + * <p> + * The keys derived during the PASN process are used to protect the LTFs exchanged during ranging. + * This ensures that the LTFs are encrypted and authenticated, preventing unauthorized access + * and manipulation. + */ +@FlaggedApi(Flags.FLAG_SECURE_RANGING) +public final class PasnConfig implements Parcelable { + + /** + * Various base Authentication and Key Management (AKM) protocol supported by the PASN. + * + * @hide + */ + @IntDef(prefix = {"AKM_"}, flag = true, value = { + AKM_NONE, + AKM_PASN, + AKM_SAE, + AKM_FT_EAP_SHA256, + AKM_FT_PSK_SHA256, + AKM_FT_EAP_SHA384, + AKM_FT_PSK_SHA384, + AKM_FILS_EAP_SHA256, + AKM_FILS_EAP_SHA384}) + @Retention(RetentionPolicy.SOURCE) + public @interface AkmType { + } + + /** + * No authentication and key management. + */ + public static final int AKM_NONE = 0; + /** + * Pre-association security negotiation (PASN). + */ + public static final int AKM_PASN = 1 << 0; + /** + * Simultaneous authentication of equals (SAE). + */ + public static final int AKM_SAE = 1 << 1; + /** + * Fast BSS Transition (FT) with Extensible Authentication Protocol (EAP) and SHA-256. + */ + public static final int AKM_FT_EAP_SHA256 = 1 << 2; + /** + * Fast BSS Transition (FT) with Pre-Shared Key (PSK) and SHA-256. + */ + public static final int AKM_FT_PSK_SHA256 = 1 << 3; + /** + * Fast BSS Transition (FT) with Extensible Authentication Protocol (EAP) and SHA-384. + */ + public static final int AKM_FT_EAP_SHA384 = 1 << 4; + /** + * Fast BSS Transition (FT) with Pre-Shared Key (PSK) and SHA-384. + */ + public static final int AKM_FT_PSK_SHA384 = 1 << 5; + /** + * Fast Initial Link Setup (FILS) with Extensible Authentication Protocol (EAP) and SHA-256. + */ + public static final int AKM_FILS_EAP_SHA256 = 1 << 6; + /** + * Fast Initial Link Setup (FILS) with Extensible Authentication Protocol (EAP) and SHA-384. + */ + public static final int AKM_FILS_EAP_SHA384 = 1 << 7; + + /** + * @hide + */ + private static final Map<String, Integer> sStringToAkm = new HashMap<>(); + + static { + sStringToAkm.put("None", AKM_NONE); + sStringToAkm.put("PASN-", AKM_PASN); + // Transition mode. e.g. "[RSN-SAE+SAE_EXT_KEY-CCMP]" + sStringToAkm.put("SAE+", AKM_SAE); + // SAE mode only. e.g. "[RSN-PSK+SAE-CCMP]" + sStringToAkm.put("SAE-", AKM_SAE); + sStringToAkm.put("EAP-FILS-SHA256-", AKM_FILS_EAP_SHA256); + sStringToAkm.put("EAP-FILS-SHA384-", AKM_FILS_EAP_SHA384); + sStringToAkm.put("FT/EAP-", AKM_FT_EAP_SHA256); + sStringToAkm.put("FT/PSK-", AKM_FT_PSK_SHA256); + sStringToAkm.put("EAP-FT-SHA384-", AKM_FT_EAP_SHA384); + sStringToAkm.put("FT/PSK-SHA384-", AKM_FT_PSK_SHA384); + } + + /** + * Pairwise cipher used for encryption. + * + * @hide + */ + @IntDef(prefix = {"CIPHER_"}, flag = true, value = { + CIPHER_NONE, + CIPHER_CCMP_128, + CIPHER_CCMP_256, + CIPHER_GCMP_128, + CIPHER_GCMP_256}) + @Retention(RetentionPolicy.SOURCE) + public @interface Cipher { + } + + /** + * No encryption. + */ + public static final int CIPHER_NONE = 0; + /** + * Counter Mode with Cipher Block Chaining Message Authentication Code Protocol (CCMP) with + * 128-bit key. + */ + public static final int CIPHER_CCMP_128 = 1 << 0; + /** + * Counter Mode with Cipher Block Chaining Message Authentication Code Protocol (CCMP) with + * 256-bit key. + */ + public static final int CIPHER_CCMP_256 = 1 << 1; + /** + * Galois/Counter Mode Protocol (GCMP) with 128-bit key. + */ + public static final int CIPHER_GCMP_128 = 1 << 2; + /** + * Galois/Counter Mode Protocol (GCMP) with 256-bit key. + */ + public static final int CIPHER_GCMP_256 = 1 << 3; + private static final Map<String, Integer> sStringToCipher = new HashMap<>(); + + static { + sStringToCipher.put("None", CIPHER_NONE); + sStringToCipher.put("-CCMP]", CIPHER_CCMP_128); + sStringToCipher.put("-CCMP-256]", CIPHER_CCMP_256); + sStringToCipher.put("-GCMP-128]", CIPHER_GCMP_128); + sStringToCipher.put("-GCMP-256]", CIPHER_GCMP_256); + } + + @AkmType + private final int mBaseAkms; + @Cipher + private final int mCiphers; + private final String mPassword; + private final WifiSsid mWifiSsid; + private final byte[] mPasnComebackCookie; + + /** + * Return base AKMs (Authentication and Key Management). + */ + public @AkmType int getBaseAkms() { + return mBaseAkms; + } + + /** + * Return pairwise ciphers. + */ + public @Cipher int getCiphers() { + return mCiphers; + } + + /** + * Get password used by base AKM. If null, password is retrieved from the saved network + * profile for the PASN authentication. See {@link #getWifiSsid()} on retrieving saved + * network profile. + */ + @Nullable + public String getPassword() { + return mPassword; + } + + /** + * Get Wifi SSID which is used to retrieve saved network profile if {@link #getPassword()} + * is null. If Wifi SSID and password are not set and there is no saved profile corresponding to + * the responder, unauthenticated PASN will be used if {@link RangingRequest#getSecurityMode()} + * allows. See {@code SECURITY_MODE_*} for more details. + */ + @Nullable + public WifiSsid getWifiSsid() { + return mWifiSsid; + } + + /** + * Get PASN comeback cookie. See {@link Builder#setPasnComebackCookie(byte[])}. + **/ + @Nullable + public byte[] getPasnComebackCookie() { + return mPasnComebackCookie; + } + + + private PasnConfig(@NonNull Parcel in) { + mBaseAkms = in.readInt(); + mCiphers = in.readInt(); + mPassword = in.readString(); + mWifiSsid = (SdkLevel.isAtLeastT()) ? in.readParcelable(WifiSsid.class.getClassLoader(), + WifiSsid.class) : in.readParcelable(WifiSsid.class.getClassLoader()); + mPasnComebackCookie = in.createByteArray(); + } + + public static final @NonNull Creator<PasnConfig> CREATOR = new Creator<PasnConfig>() { + @Override + public PasnConfig createFromParcel(Parcel in) { + return new PasnConfig(in); + } + + @Override + public PasnConfig[] newArray(int size) { + return new PasnConfig[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@androidx.annotation.NonNull Parcel dest, int flags) { + dest.writeInt(mBaseAkms); + dest.writeInt(mCiphers); + dest.writeString(mPassword); + dest.writeByteArray(mPasnComebackCookie); + } + + /** + * Convert capability string from {@link ScanResult} to a set of + * {@code AKM_*} supported by the PASN. + * + * @hide + */ + public @AkmType static int getBaseAkmsFromCapabilities(String capabilities) { + @AkmType int akms = AKM_NONE; + if (capabilities == null) return akms; + for (String akm : sStringToAkm.keySet()) { + if (capabilities.contains(akm)) { + akms |= sStringToAkm.get(akm); + } + } + return akms; + } + + /** + * Convert capability string from {@link ScanResult} to a set of + * {@code CIPHER_*}. + * + * @hide + */ + public @Cipher static int getCiphersFromCapabilities(String capabilities) { + @Cipher int ciphers = CIPHER_NONE; + if (capabilities == null) return ciphers; + for (String cipher : sStringToCipher.keySet()) { + if (capabilities.contains(cipher)) { + ciphers |= sStringToCipher.get(cipher); + } + } + return ciphers; + } + + private PasnConfig(Builder builder) { + mBaseAkms = builder.mBaseAkms; + mCiphers = builder.mCiphers; + mPassword = builder.mPassword; + mWifiSsid = builder.mWifiSsid; + mPasnComebackCookie = builder.mPasnComebackCookie; + } + + /** + * @hide + */ + public static boolean isAkmRequiresPassword(int akms) { + return (akms & AKM_SAE) != 0; + } + + /** + * Builder for {@link PasnConfig} + */ + @FlaggedApi(Flags.FLAG_SECURE_RANGING) + public static final class Builder { + private final int mBaseAkms; + private final int mCiphers; + private String mPassword = null; + private WifiSsid mWifiSsid = null; + byte[] mPasnComebackCookie = null; + + /** + * Builder + * + * @param baseAkms The AKMs that PASN is configured to use. PASN will use the most secure + * AKM in the configuration. + * @param ciphers The CIPHERs that PASN is configured to use. PASN will use the most + * secure CIPHER in the configuration which is applicable to the base AKM + */ + public Builder(@AkmType int baseAkms, @Cipher int ciphers) { + mBaseAkms = baseAkms; + mCiphers = ciphers; + } + + /** + * Sets the password if needed by the base AKM of the PASN. If not set, password is + * retrieved from the saved profile identified by the SSID. See + * {@link #setWifiSsid(WifiSsid)}. + * + * Note: If password and SSID is not set, secure ranging will use unauthenticated PASN. + * + * @param password password string + * @return a reference to this Builder + */ + @NonNull + public Builder setPassword(@NonNull String password) { + Objects.requireNonNull(password, "Password must not be null"); + this.mPassword = password; + return this; + } + + /** + * Sets the Wi-Fi Service Set Identifier (SSID). This is used to get the saved profile to + * retrieve password if password is not set using {@link #setPassword(String)}. + * + * Note: If password and SSID is not set, secure ranging will use unauthenticated PASN. + * + * @param wifiSsid Wi-Fi Service Set Identifier (SSID) + * @return a reference to this Builder + */ + @NonNull + public Builder setWifiSsid(@NonNull WifiSsid wifiSsid) { + Objects.requireNonNull(wifiSsid, "SSID must not be null"); + this.mWifiSsid = wifiSsid; + return this; + } + + /** + * Set PASN comeback cookie. PASN authentication allows the station to provide comeback + * cookie which was indicated in the {@link RangingResult} by the AP with a deferral time. + * <p> + * When an AP receives a large volume of initial PASN Authentication frames, it can use + * the comeback after field in the PASN Parameters element to indicate a deferral time + * and optionally provide a comeback cookie which is an opaque sequence of octets. Upon + * receiving this response, the ranging initiator (STA) must wait for the specified time + * before retrying secure authentication, presenting the received cookie to the AP. See + * {@link RangingResult#getPasnComebackCookie()} and + * {@link RangingResult#getPasnComebackAfterMillis()}. + * + * @param pasnComebackCookie an opaque sequence of octets + * @return a reference to this Builder + */ + @NonNull + public Builder setPasnComebackCookie(@NonNull byte[] pasnComebackCookie) { + Objects.requireNonNull(pasnComebackCookie, "PASN comeback cookie must not be null"); + if (pasnComebackCookie.length > 255 || pasnComebackCookie.length == 0) { + throw new IllegalArgumentException("Cookie with invalid length " + + pasnComebackCookie.length); + } + mPasnComebackCookie = pasnComebackCookie; + return this; + } + + /** + * Builds a {@link PasnConfig} object. + */ + @NonNull + public PasnConfig build() { + return new PasnConfig(this); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof PasnConfig that)) return false; + return mBaseAkms == that.mBaseAkms && mCiphers == that.mCiphers && Objects.equals( + mPassword, that.mPassword) && Objects.equals(mWifiSsid, that.mWifiSsid) + && Arrays.equals(mPasnComebackCookie, that.mPasnComebackCookie); + } + + @Override + public int hashCode() { + int result = Objects.hash(mBaseAkms, mCiphers, mPassword, mWifiSsid); + result = 31 * result + Arrays.hashCode(mPasnComebackCookie); + return result; + } + + @Override + public String toString() { + return "PasnConfig{" + "mBaseAkms=" + mBaseAkms + ", mCiphers=" + mCiphers + ", mPassword='" + + mPassword + '\'' + ", mWifiSsid=" + mWifiSsid + ", mPasnComebackCookie=" + + Arrays.toString(mPasnComebackCookie) + '}'; + } +} diff --git a/framework/java/android/net/wifi/rtt/RangingRequest.java b/framework/java/android/net/wifi/rtt/RangingRequest.java index 8360dc466a..bca71db208 100644 --- a/framework/java/android/net/wifi/rtt/RangingRequest.java +++ b/framework/java/android/net/wifi/rtt/RangingRequest.java @@ -17,8 +17,10 @@ package android.net.wifi.rtt; import android.annotation.FlaggedApi; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.net.MacAddress; import android.net.wifi.OuiKeyedData; @@ -33,14 +35,18 @@ import android.os.Build; import android.os.Handler; import android.os.Parcel; import android.os.Parcelable; +import android.util.Log; import androidx.annotation.RequiresApi; import com.android.modules.utils.build.SdkLevel; import com.android.wifi.flags.Flags; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collections; +import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.StringJoiner; @@ -56,12 +62,51 @@ import java.util.StringJoiner; * {@link RangingRequest.Builder#addAccessPoints(List)}). */ public final class RangingRequest implements Parcelable { + private static final String TAG = "RangingRequest"; private static final int MAX_PEERS = 10; private static final int DEFAULT_RTT_BURST_SIZE = 8; private static final int MIN_RTT_BURST_SIZE = 2; private static final int MAX_RTT_BURST_SIZE = 31; /** + * In this mode, the ranging is performed with all available responders in open mode. If a + * responder does not allow open mode ranging, the responder will be skipped from the + * ranging request. + *<p> + * Note: If {@link ScanResult#isRangingFrameProtectionRequired()} is {@code true}, then open + * mode ranging is not supported by the AP. + */ + @FlaggedApi(Flags.FLAG_SECURE_RANGING) + public static final int SECURITY_MODE_OPEN = 0; + + /** + * In this mode, secure ranging is enabled automatically for compatible responders, + * simplifying the user experience and requiring no configuration. If the secure ranging is not + * possible for any of the responders, open mode ranging is used instead as in + * {@link #SECURITY_MODE_OPEN}. This mode is backward compatible with existing applications. + * + * Note: This is the default mode + */ + @FlaggedApi(Flags.FLAG_SECURE_RANGING) + public static final int SECURITY_MODE_OPPORTUNISTIC = 1; + + /** + * To ensure maximum security, this mode only ranges with responders using PASN with base AKM + * (Authenticated). This necessitates an authenticated PASN handshake with a shared key + * between the initiator and responder. Consequently, all responders in the ranging request + * must support secure authentication. If not supported, the responder will be skipped from the + * ranging request. + */ + @FlaggedApi(Flags.FLAG_SECURE_RANGING) + public static final int SECURITY_MODE_SECURE_AUTH = 2; + + /** @hide */ + @IntDef(prefix = {"SECURITY_MODE_"}, value = {SECURITY_MODE_OPEN, SECURITY_MODE_OPPORTUNISTIC, + SECURITY_MODE_SECURE_AUTH}) + @Retention(RetentionPolicy.SOURCE) + public @interface SecurityMode {} + + /** * Returns the maximum number of peers to range which can be specified in a single {@code * RangingRequest}. The limit applies no matter how the peers are added to the request, e.g. * through {@link RangingRequest.Builder#addAccessPoint(ScanResult)} or @@ -106,6 +151,9 @@ public final class RangingRequest implements Parcelable { /** @hide */ public final int mRttBurstSize; + /** @hide */ + public final int mSecurityMode; + /** * List of {@link OuiKeyedData} providing vendor-specific configuration data. */ @@ -113,9 +161,10 @@ public final class RangingRequest implements Parcelable { /** @hide */ private RangingRequest(List<ResponderConfig> rttPeers, int rttBurstSize, - @NonNull List<OuiKeyedData> vendorData) { + @SecurityMode int securityMode, @NonNull List<OuiKeyedData> vendorData) { mRttPeers = rttPeers; mRttBurstSize = rttBurstSize; + mSecurityMode = securityMode; mVendorData = new ArrayList<>(vendorData); } @@ -142,6 +191,16 @@ public final class RangingRequest implements Parcelable { } /** + * Returns security mode for the ranging request. See {@code SECURITY_MODE_*} for more details. + * + * @return security mode for the ranging request + */ + @FlaggedApi(Flags.FLAG_SECURE_RANGING) + public @SecurityMode int getSecurityMode() { + return mSecurityMode; + } + + /** * Return the vendor-provided configuration data, if it exists. See also {@link * Builder#setVendorData(List)} * @@ -168,6 +227,7 @@ public final class RangingRequest implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeList(mRttPeers); dest.writeInt(mRttBurstSize); + dest.writeInt(mSecurityMode); dest.writeList(mVendorData); } @@ -179,7 +239,7 @@ public final class RangingRequest implements Parcelable { @Override public RangingRequest createFromParcel(Parcel in) { - return new RangingRequest(in.readArrayList(null), in.readInt(), + return new RangingRequest(in.readArrayList(null), in.readInt(), in.readInt(), ParcelUtil.readOuiKeyedDataList(in)); } }; @@ -192,6 +252,7 @@ public final class RangingRequest implements Parcelable { sj.add(rc.toString()); } sj.add("mRttBurstSize=" + mRttBurstSize); + sj.add("mSecurityMode=" + mSecurityMode); sj.add("mVendorData=" + mVendorData); return sj.toString(); } @@ -222,6 +283,7 @@ public final class RangingRequest implements Parcelable { private List<ResponderConfig> mRttPeers = new ArrayList<>(); private int mRttBurstSize = DEFAULT_RTT_BURST_SIZE; private @NonNull List<OuiKeyedData> mVendorData = Collections.emptyList(); + private @SecurityMode int mSecurityMode = SECURITY_MODE_OPPORTUNISTIC; /** * Set the RTT Burst size for the ranging request. @@ -524,11 +586,64 @@ public final class RangingRequest implements Parcelable { } /** + * Sets the overall security mode for ranging, determining if secure ranging is attempted + * with each responder and if fallback to unauthenticated secure ranging is permitted. The + * mode also permits retry with no security when secure ranging fails. If not set, default + * mode will be {@link #SECURITY_MODE_OPPORTUNISTIC}. + * <p> + * See {@code SECURITY_MODE_*} for different modes of operation. + * + * @param securityMode security mode for ranging + * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. + */ + @FlaggedApi(Flags.FLAG_SECURE_RANGING) + @NonNull + public Builder setSecurityMode(@SecurityMode int securityMode) { + mSecurityMode = securityMode; + return this; + } + + + /** + * Filter the peer list for the security modes SECURITY_MODE_SECURE_AUTH and + * SECURITY_MODE_OPEN. + */ + @SuppressLint("NewApi") + private void filterRttPeersBasedOnSecurityMode() { + Iterator<ResponderConfig> peers = mRttPeers.iterator(); + while (peers.hasNext()) { + ResponderConfig peer = peers.next(); + SecureRangingConfig config = peer.getSecureRangingConfig(); + // For SECURITY_MODE_SECURE_AUTH, remove any non-authenticated peer. + if (mSecurityMode == SECURITY_MODE_SECURE_AUTH) { + PasnConfig pasn = (config != null) ? config.getPasnConfig() : null; + if (pasn == null || pasn.getBaseAkms() == PasnConfig.AKM_PASN) { + peers.remove(); + Log.i(TAG, "SECURITY_MODE_SECURE_AUTH is set, removing non-secure peer: " + + peer.getMacAddress()); + } + } else if (mSecurityMode == SECURITY_MODE_OPEN) { + // For SECURITY_MODE_OPEN, remove any frame protection enabled peer. + // At AIDL level, secure config will not be passed to HAL. + if (config.isRangingFrameProtectionEnabled()) { + peers.remove(); + Log.i(TAG, "SECURITY_MODE_OPEN is set, removing secure peer: " + + peer.getMacAddress()); + } + } + } + } + + + /** * Build {@link RangingRequest} given the current configurations made on the * builder. */ public RangingRequest build() { - return new RangingRequest(mRttPeers, mRttBurstSize, mVendorData); + if (mSecurityMode != SECURITY_MODE_OPPORTUNISTIC) { + filterRttPeersBasedOnSecurityMode(); + } + return new RangingRequest(mRttPeers, mRttBurstSize, mSecurityMode, mVendorData); } } @@ -547,11 +662,12 @@ public final class RangingRequest implements Parcelable { return mRttPeers.size() == lhs.mRttPeers.size() && mRttPeers.containsAll(lhs.mRttPeers) && mRttBurstSize == lhs.mRttBurstSize + && mSecurityMode == lhs.mSecurityMode && Objects.equals(mVendorData, lhs.mVendorData); } @Override public int hashCode() { - return Objects.hash(mRttPeers, mRttBurstSize, mVendorData); + return Objects.hash(mRttPeers, mRttBurstSize, mSecurityMode, mVendorData); } } diff --git a/framework/java/android/net/wifi/rtt/RangingResult.java b/framework/java/android/net/wifi/rtt/RangingResult.java index 6739291929..604c879daa 100644 --- a/framework/java/android/net/wifi/rtt/RangingResult.java +++ b/framework/java/android/net/wifi/rtt/RangingResult.java @@ -19,6 +19,7 @@ package android.net.wifi.rtt; import android.annotation.ElapsedRealtimeLong; import android.annotation.FlaggedApi; import android.annotation.IntDef; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; @@ -32,6 +33,7 @@ import android.net.wifi.aware.PeerHandle; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; +import android.util.Log; import androidx.annotation.RequiresApi; @@ -40,6 +42,7 @@ import com.android.wifi.flags.Flags; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -114,6 +117,12 @@ public final class RangingResult implements Parcelable { private final int mNumTxSpatialStreams; private final int mNumRxSpatialStreams; private List<OuiKeyedData> mVendorData; + private final boolean mIsRangingAuthenticated; + private final boolean mIsRangingFrameProtected; + private final boolean mIsSecureHeLtfEnabled; + private final int mSecureHeLtfProtocolVersion; + private final byte[] mPasnComebackCookie; + private final long mPasnComebackAfterMillis; /** * Builder class used to construct {@link RangingResult} objects. @@ -143,6 +152,53 @@ public final class RangingResult implements Parcelable { private int mNumTxSpatialStreams = UNSPECIFIED; private int mNumRxSpatialStreams = UNSPECIFIED; private List<OuiKeyedData> mVendorData = Collections.emptyList(); + private boolean mIsRangingAuthenticated; + private boolean mIsRangingFrameProtected; + private boolean mIsSecureHeLtfEnabled; + private int mSecureHeLtfProtocolVersion; + private byte[] mPasnComebackCookie = null; + private long mPasnComebackAfterMillis = UNSPECIFIED; + + /** + * Constructs a Builder with default values (see {@link Builder}). + */ + public Builder() {} + + /** + * Constructs a Builder initialized from an existing {@link RangingResult} instance. + * + * @hide + */ + public Builder(@NonNull RangingResult other) { + if (other == null) { + Log.e(TAG, "Cannot provide a null RangingResult"); + return; + } + + mStatus = other.mStatus; + mMac = other.mMac; + mPeerHandle = other.mPeerHandle; + mDistanceMm = other.mDistanceMm; + mDistanceStdDevMm = other.mDistanceStdDevMm; + mRssi = other.mRssi; + mNumAttemptedMeasurements = other.mNumAttemptedMeasurements; + mNumSuccessfulMeasurements = other.mNumSuccessfulMeasurements; + if (other.mLci != null) mLci = other.mLci.clone(); + if (other.mLcr != null) mLcr = other.mLcr.clone(); + mResponderLocation = new ResponderLocation(mLci, mLcr); + mTimestamp = other.mTimestamp; + mIs80211mcMeasurement = other.mIs80211mcMeasurement; + mFrequencyMHz = other.mFrequencyMHz; + mPacketBw = other.mPacketBw; + mIs80211azNtbMeasurement = other.mIs80211azNtbMeasurement; + mNtbMinMeasurementTime = other.mNtbMinMeasurementTime; + mNtbMaxMeasurementTime = other.mNtbMaxMeasurementTime; + mI2rTxLtfRepetitions = other.mI2rTxLtfRepetitions; + mR2iTxLtfRepetitions = other.mR2iTxLtfRepetitions; + mNumTxSpatialStreams = other.mNumTxSpatialStreams; + mNumRxSpatialStreams = other.mNumRxSpatialStreams; + mVendorData = new ArrayList<>(other.mVendorData); + } /** * Sets the Range result status. @@ -496,6 +552,96 @@ public final class RangingResult implements Parcelable { } /** + * Set whether mutual authentication is done for the ranging. Authentication of ranging + * enables frame protection also. See {@link #setRangingFrameProtected(boolean)}. + * + * @param isRangingAuthenticated true if ranging is mutually authenticated, otherwise false. + * @return The builder to facilitate chaining. + */ + @NonNull + @FlaggedApi(Flags.FLAG_SECURE_RANGING) + public Builder setRangingAuthenticated(boolean isRangingAuthenticated) { + mIsRangingAuthenticated = isRangingAuthenticated; + return this; + } + + /** + * Set whether ranging frames are protected. Frame protection provides both encryption and + * integrity protection to the ranging frames. + * + * @param isRangingFrameProtected true if ranging frames are protected, otherwise false. + * @return The builder to facilitate chaining. + */ + @NonNull + @FlaggedApi(Flags.FLAG_SECURE_RANGING) + public Builder setRangingFrameProtected(boolean isRangingFrameProtected) { + mIsRangingFrameProtected = isRangingFrameProtected; + return this; + } + + /** + * Set whether secure HE-LTF is used for this ranging. + * + * @param isSecureHeLtfEnabled true if secure HE-LTF is enabled, otherwise false. + * @return The builder to facilitate chaining. + */ + @NonNull + @FlaggedApi(Flags.FLAG_SECURE_RANGING) + public Builder setSecureHeLtfEnabled(boolean isSecureHeLtfEnabled) { + mIsSecureHeLtfEnabled = isSecureHeLtfEnabled; + return this; + } + + /** + * Set secure HE-LTF protocol version used for this ranging. + * + * The secure HE-LTF negotiation supports negotiation of the secure HE-LTF protocol version + * which allows a responder and an initiator to negotiate the highest mutually supported + * secure HE-LTF protocol version. + * + * Refer IEEE 802.11az-2022 spec, section 9.4.2.298 Ranging Parameters element. + * + * @param secureHeLtfProtocolVersion Secure HE-LTF protocol version. + * @return The builder to facilitate chaining. + */ + @NonNull + @FlaggedApi(Flags.FLAG_SECURE_RANGING) + public Builder setSecureHeLtfProtocolVersion( + @IntRange(from = 0, to = 7) int secureHeLtfProtocolVersion) { + mSecureHeLtfProtocolVersion = secureHeLtfProtocolVersion; + return this; + } + + /** + * Set comeback cookie. See {@link #getPasnComebackCookie()}. If not set, default value + * is null. + * + * @param pasnComebackCookie an opaque sequence of octets + * @return The builder to facilitate chaining. + */ + @NonNull + @FlaggedApi(Flags.FLAG_SECURE_RANGING) + public Builder setPasnComebackCookie(@NonNull byte[] pasnComebackCookie) { + mPasnComebackCookie = pasnComebackCookie; + return this; + } + + /** + * Set comeback after time. See {@link #getPasnComebackAfterMillis()}. If not set default + * value is {@link RangingResult#UNSPECIFIED}. + * + * @param comebackAfterMillis the ranging initiator (STA) must wait for the specified + * time before retrying secure ranging + * @return The builder to facilitate chaining. + */ + @NonNull + @FlaggedApi(Flags.FLAG_SECURE_RANGING) + public Builder setPasnComebackAfterMillis(long comebackAfterMillis) { + mPasnComebackAfterMillis = comebackAfterMillis; + return this; + } + + /** * Build {@link RangingResult} * @return an instance of {@link RangingResult} */ @@ -539,6 +685,12 @@ public final class RangingResult implements Parcelable { mNumRxSpatialStreams = builder.mNumRxSpatialStreams; mNumTxSpatialStreams = builder.mNumTxSpatialStreams; mVendorData = builder.mVendorData; + mIsRangingAuthenticated = builder.mIsRangingAuthenticated; + mIsRangingFrameProtected = builder.mIsRangingFrameProtected; + mIsSecureHeLtfEnabled = builder.mIsSecureHeLtfEnabled; + mSecureHeLtfProtocolVersion = builder.mSecureHeLtfProtocolVersion; + mPasnComebackCookie = builder.mPasnComebackCookie; + mPasnComebackAfterMillis = builder.mPasnComebackAfterMillis; } /** @@ -904,6 +1056,76 @@ public final class RangingResult implements Parcelable { return mVendorData; } + /** + * @return whether the ranging is authenticated or not. + * + * Refer IEEE 802.11az-2022 spec, section 12 Security. + */ + @FlaggedApi(Flags.FLAG_SECURE_RANGING) + public boolean isRangingAuthenticated() { + return mIsRangingAuthenticated; + } + + /** + * @return whether the ranging frames are protected or not. + * + * Refer IEEE 802.11az-2022 spec, section 12 Security. + */ + @FlaggedApi(Flags.FLAG_SECURE_RANGING) + public boolean isRangingFrameProtected() { + return mIsRangingFrameProtected; + } + + /** + * @return whether the secure HE-LTF is enabled or not. + * + * Refer IEEE 802.11az-2022 spec, section 9.4.2.298 Ranging Parameters element. + */ + @FlaggedApi(Flags.FLAG_SECURE_RANGING) + public boolean isSecureHeLtfEnabled() { + return mIsSecureHeLtfEnabled; + } + + /** + * Get Secure HE-LTF protocol version used. + * + * The secure HE-LTF negotiation supports negotiation of the secure HE-LTF protocol version + * which allows a responder and an initiator to negotiate the highest mutually supported + * secure HE-LTF protocol version. + * + * Refer IEEE 802.11az-2022 spec, section 9.4.2.298 Ranging Parameters element. + */ + @FlaggedApi(Flags.FLAG_SECURE_RANGING) + @IntRange(from = 0, to = 7) + public int getSecureHeLtfProtocolVersion() { + return mSecureHeLtfProtocolVersion; + } + + /** + * Get PASN comeback cookie. PASN authentication allows an AP to indicate the deferral time + * and optionally a Cookie. See {@link #getPasnComebackAfterMillis()} + * <p> + * When an AP receives a large volume of initial PASN Authentication frames, it can use + * the comeback after field in the PASN Parameters element to indicate a deferral time + * and optionally provide a comeback cookie which is an opaque sequence of octets. Upon + * receiving this response, the ranging initiator (STA) must wait for the specified time + * before retrying secure authentication, presenting the received cookie to the AP. + **/ + @FlaggedApi(Flags.FLAG_SECURE_RANGING) + @Nullable + public byte[] getPasnComebackCookie() { + return mPasnComebackCookie; + } + + /** + * Get Comeback after time in milliseconds. See {@link #getPasnComebackCookie()}. A value 0 + * indicates the ranging request operation can be tried immediately with the cookie. + */ + @FlaggedApi(Flags.FLAG_SECURE_RANGING) + public long getPasnComebackAfterMillis() { + return mPasnComebackAfterMillis; + } + @Override public int describeContents() { return 0; @@ -1017,6 +1239,11 @@ public final class RangingResult implements Parcelable { .append(", numTxSpatialStreams=").append(mNumTxSpatialStreams) .append(", numRxSpatialStreams=").append(mNumRxSpatialStreams) .append(", vendorData=").append(mVendorData) + .append(", isRangingAuthenticated").append(mIsRangingAuthenticated) + .append(", isRangingFrameProtected").append(mIsRangingFrameProtected) + .append(", isSecureHeLtfEnabled").append(mIsSecureHeLtfEnabled) + .append(", pasnComebackCookie").append(Arrays.toString(mPasnComebackCookie)) + .append(", pasnComebackAfterMillis").append(mPasnComebackAfterMillis) .append("]").toString(); } @@ -1050,7 +1277,13 @@ public final class RangingResult implements Parcelable { && mR2iTxLtfRepetitions == lhs.mR2iTxLtfRepetitions && mNumTxSpatialStreams == lhs.mNumTxSpatialStreams && mNumRxSpatialStreams == lhs.mNumRxSpatialStreams - && Objects.equals(mVendorData, lhs.mVendorData); + && Objects.equals(mVendorData, lhs.mVendorData) + && mIsRangingAuthenticated == lhs.mIsRangingAuthenticated + && mIsRangingFrameProtected == lhs.mIsRangingFrameProtected + && mIsSecureHeLtfEnabled == lhs.isSecureHeLtfEnabled() + && mPasnComebackAfterMillis == lhs.mPasnComebackAfterMillis + && Arrays.equals(mPasnComebackCookie, lhs.mPasnComebackCookie); + } @Override @@ -1060,6 +1293,8 @@ public final class RangingResult implements Parcelable { Arrays.hashCode(mLcr), mResponderLocation, mTimestamp, mIs80211mcMeasurement, mFrequencyMHz, mPacketBw, mIs80211azNtbMeasurement, mNtbMinMeasurementTime, mNtbMaxMeasurementTime, mI2rTxLtfRepetitions, mR2iTxLtfRepetitions, - mNumTxSpatialStreams, mR2iTxLtfRepetitions, mVendorData); + mNumTxSpatialStreams, mR2iTxLtfRepetitions, mVendorData, mIsRangingAuthenticated, + mIsRangingFrameProtected, mIsSecureHeLtfEnabled, mPasnComebackAfterMillis, + Arrays.hashCode(mPasnComebackCookie)); } } diff --git a/framework/java/android/net/wifi/rtt/ResponderConfig.java b/framework/java/android/net/wifi/rtt/ResponderConfig.java index 5db9290b32..5b4f2da08a 100644 --- a/framework/java/android/net/wifi/rtt/ResponderConfig.java +++ b/framework/java/android/net/wifi/rtt/ResponderConfig.java @@ -264,6 +264,7 @@ public final class ResponderConfig implements Parcelable { private long mNtbMinMeasurementTime = DEFAULT_NTB_MIN_TIME_BETWEEN_MEASUREMENTS_MICROS; private long mNtbMaxMeasurementTime = DEFAULT_NTB_MAX_TIME_BETWEEN_MEASUREMENTS_MICROS; + private final SecureRangingConfig mSecureRangingConfig; /** * Constructs Responder configuration from the builder @@ -287,6 +288,7 @@ public final class ResponderConfig implements Parcelable { this.preamble = builder.mPreamble; this.mNtbMinMeasurementTime = builder.mNtbMinMeasurementTime; this.mNtbMaxMeasurementTime = builder.mNtbMaxMeasurementTime; + this.mSecureRangingConfig = builder.mSecureRangingConfig; } /** @@ -332,6 +334,7 @@ public final class ResponderConfig implements Parcelable { this.supports80211azNtb = false; this.mNtbMinMeasurementTime = DEFAULT_NTB_MIN_TIME_BETWEEN_MEASUREMENTS_MICROS; this.mNtbMaxMeasurementTime = DEFAULT_NTB_MAX_TIME_BETWEEN_MEASUREMENTS_MICROS; + this.mSecureRangingConfig = null; } /** @@ -372,6 +375,7 @@ public final class ResponderConfig implements Parcelable { this.supports80211azNtb = false; this.mNtbMinMeasurementTime = DEFAULT_NTB_MIN_TIME_BETWEEN_MEASUREMENTS_MICROS; this.mNtbMaxMeasurementTime = DEFAULT_NTB_MAX_TIME_BETWEEN_MEASUREMENTS_MICROS; + this.mSecureRangingConfig = null; } /** @@ -416,6 +420,7 @@ public final class ResponderConfig implements Parcelable { this.supports80211azNtb = false; this.mNtbMinMeasurementTime = DEFAULT_NTB_MIN_TIME_BETWEEN_MEASUREMENTS_MICROS; this.mNtbMaxMeasurementTime = DEFAULT_NTB_MAX_TIME_BETWEEN_MEASUREMENTS_MICROS; + this.mSecureRangingConfig = null; } /** @@ -434,6 +439,10 @@ public final class ResponderConfig implements Parcelable { int centerFreq1 = scanResult.centerFreq1; int preamble; + // The IEEE 802.11mc is only compatible with HE and EHT when using the 6 GHz band. + // However, the IEEE 802.11az supports HE and EHT across all Wi-Fi bands (2.4GHz, 5 GHz, + // and 6 GHz). + boolean isHeOrEhtAllowed = supports80211azNtbRanging || ScanResult.is6GHz(frequency); if (scanResult.informationElements != null && scanResult.informationElements.length != 0) { boolean htCapabilitiesPresent = false; boolean vhtCapabilitiesPresent = false; @@ -452,9 +461,9 @@ public final class ResponderConfig implements Parcelable { } } - if (ehtCapabilitiesPresent && ScanResult.is6GHz(frequency)) { + if (ehtCapabilitiesPresent && isHeOrEhtAllowed) { preamble = ScanResult.PREAMBLE_EHT; - } else if (heCapabilitiesPresent && ScanResult.is6GHz(frequency)) { + } else if (heCapabilitiesPresent && isHeOrEhtAllowed) { preamble = ScanResult.PREAMBLE_HE; } else if (vhtCapabilitiesPresent) { preamble = ScanResult.PREAMBLE_VHT; @@ -465,9 +474,10 @@ public final class ResponderConfig implements Parcelable { } } else { Log.e(TAG, "Scan Results do not contain IEs - using backup method to select preamble"); - if (channelWidth == ScanResult.CHANNEL_WIDTH_320MHZ) { + if (channelWidth == ScanResult.CHANNEL_WIDTH_320MHZ && isHeOrEhtAllowed) { preamble = ScanResult.PREAMBLE_EHT; - } else if (channelWidth == ScanResult.CHANNEL_WIDTH_80MHZ + } else if (channelWidth == ScanResult.CHANNEL_WIDTH_320MHZ + || channelWidth == ScanResult.CHANNEL_WIDTH_80MHZ || channelWidth == ScanResult.CHANNEL_WIDTH_160MHZ) { preamble = ScanResult.PREAMBLE_VHT; } else { @@ -475,7 +485,7 @@ public final class ResponderConfig implements Parcelable { } } - return new ResponderConfig.Builder() + Builder builder = new Builder() .setMacAddress(macAddress) .setResponderType(responderType) .set80211mcSupported(supports80211mc) @@ -484,10 +494,33 @@ public final class ResponderConfig implements Parcelable { .setFrequencyMhz(frequency) .setCenterFreq0Mhz(centerFreq0) .setCenterFreq1Mhz(centerFreq1) - .setPreamble(preamble) + .setPreamble(preamble); + + if (isSecureRangingResponder(scanResult)) { + builder.setSecureRangingConfig(getSecureRangingConfig(scanResult)); + } + + return builder.build(); + } + + private static boolean isSecureRangingResponder(ScanResult scanResult) { + return ((scanResult.capabilities != null) && (scanResult.capabilities.contains("PASN"))); + } + + private static SecureRangingConfig getSecureRangingConfig(ScanResult scanResult) { + PasnConfig.Builder pasnConfigBuilder = new PasnConfig.Builder( + PasnConfig.getBaseAkmsFromCapabilities(scanResult.capabilities), + PasnConfig.getCiphersFromCapabilities(scanResult.capabilities)); + if (scanResult.getWifiSsid() != null) { + pasnConfigBuilder.setWifiSsid(scanResult.getWifiSsid()); + } + return new SecureRangingConfig.Builder(pasnConfigBuilder.build()) + .setSecureHeLtfEnabled(scanResult.isSecureHeLtfSupported()) + .setRangingFrameProtectionEnabled(scanResult.isRangingFrameProtectionRequired()) .build(); } + /** * Creates a Responder configuration from a MAC address corresponding to a Wi-Fi Aware * Responder. The Responder parameters are set to defaults. @@ -690,6 +723,15 @@ public final class ResponderConfig implements Parcelable { } /** + * Get secure ranging configuration. + * @return Secure ranging configuration. Returns null for non-secure ranging configuration. + */ + @Nullable + @FlaggedApi(Flags.FLAG_SECURE_RANGING) + public SecureRangingConfig getSecureRangingConfig() { + return mSecureRangingConfig; + } + /** * Builder class used to construct {@link ResponderConfig} objects. */ public static final class Builder { @@ -705,6 +747,8 @@ public final class ResponderConfig implements Parcelable { private @PreambleType int mPreamble = PREAMBLE_LEGACY; private long mNtbMinMeasurementTime = DEFAULT_NTB_MIN_TIME_BETWEEN_MEASUREMENTS_MICROS; private long mNtbMaxMeasurementTime = DEFAULT_NTB_MAX_TIME_BETWEEN_MEASUREMENTS_MICROS; + private SecureRangingConfig mSecureRangingConfig = null; + /** * Sets the Responder MAC Address. @@ -909,6 +953,34 @@ public final class ResponderConfig implements Parcelable { } /** + * Set secure ranging configuration. See {@link SecureRangingConfig} for more details. + * <p> + * Note: Secure ranging will get enabled only if the device and responder support. For + * device support see {@link WifiRttManager#getRttCharacteristics()}. Following capabilities + * needs to be enabled, + * <ul> + * <li>{@link WifiRttManager#CHARACTERISTICS_KEY_BOOLEAN_NTB_INITIATOR} + * <li>{@link WifiRttManager#CHARACTERISTICS_KEY_BOOLEAN_SECURE_HE_LTF_SUPPORTED} and/or + * <li>{@link WifiRttManager#CHARACTERISTICS_KEY_BOOLEAN_RANGING_FRAME_PROTECTION_SUPPORTED} + * </ul> + * For the responder support (from scan result), + * <ul> + * <li> {@link ScanResult#capabilities} string contains PASN and optionally a base AKM + * <li> {@link ScanResult#isSecureHeLtfSupported()} + * <li> {@link ScanResult#isRangingFrameProtectionRequired()} + * </ul> + * @param secureRangingConfig Secure ranging configuration + * @return the builder to facilitate chaining {@code builder.setXXX(..).setXXX(..)}. + */ + @NonNull + @FlaggedApi(Flags.FLAG_SECURE_RANGING) + public Builder setSecureRangingConfig(@NonNull SecureRangingConfig secureRangingConfig) { + Objects.requireNonNull(secureRangingConfig, "secureRangingConfig cannot be null"); + mSecureRangingConfig = secureRangingConfig; + return this; + } + + /** * Build {@link ResponderConfig} given the current configurations made on the builder. * @return an instance of {@link ResponderConfig} */ @@ -960,6 +1032,7 @@ public final class ResponderConfig implements Parcelable { dest.writeInt(preamble); dest.writeLong(mNtbMinMeasurementTime); dest.writeLong(mNtbMaxMeasurementTime); + dest.writeParcelable(mSecureRangingConfig, flags); } public static final @android.annotation.NonNull Creator<ResponderConfig> CREATOR = new Creator<ResponderConfig>() { @@ -981,7 +1054,7 @@ public final class ResponderConfig implements Parcelable { peerHandle = new PeerHandle(in.readInt()); } - return new ResponderConfig.Builder() + ResponderConfig.Builder builder = new Builder() .setMacAddress(macAddress) .setPeerHandle(peerHandle) .setResponderType(in.readInt()) @@ -993,8 +1066,15 @@ public final class ResponderConfig implements Parcelable { .setCenterFreq1Mhz(in.readInt()) .setPreamble(in.readInt()) .setNtbMinTimeBetweenMeasurementsMicros(in.readLong()) - .setNtbMaxTimeBetweenMeasurementsMicros(in.readLong()) - .build(); + .setNtbMaxTimeBetweenMeasurementsMicros(in.readLong()); + SecureRangingConfig secureRangingConfig = in.readParcelable( + SecureRangingConfig.class.getClassLoader()); + + if (secureRangingConfig != null) { + builder.setSecureRangingConfig(secureRangingConfig); + } + return builder.build(); + } }; @@ -1017,19 +1097,20 @@ public final class ResponderConfig implements Parcelable { && centerFreq1 == lhs.centerFreq1 && preamble == lhs.preamble && supports80211azNtb == lhs.supports80211azNtb && mNtbMinMeasurementTime == lhs.mNtbMinMeasurementTime - && mNtbMaxMeasurementTime == lhs.mNtbMaxMeasurementTime; + && mNtbMaxMeasurementTime == lhs.mNtbMaxMeasurementTime + && Objects.equals(mSecureRangingConfig, lhs.mSecureRangingConfig); } @Override public int hashCode() { return Objects.hash(macAddress, peerHandle, responderType, supports80211mc, channelWidth, frequency, centerFreq0, centerFreq1, preamble, supports80211azNtb, - mNtbMinMeasurementTime, mNtbMaxMeasurementTime); + mNtbMinMeasurementTime, mNtbMaxMeasurementTime, mSecureRangingConfig); } @Override public String toString() { - return new StringBuffer("ResponderConfig: macAddress=").append(macAddress) + StringBuffer sb = new StringBuffer("ResponderConfig: macAddress=").append(macAddress) .append(", peerHandle=").append(peerHandle == null ? "<null>" : peerHandle.peerId) .append(", responderType=").append(responderType) .append(", supports80211mc=").append(supports80211mc) @@ -1039,9 +1120,15 @@ public final class ResponderConfig implements Parcelable { .append(", centerFreq1=").append(centerFreq1) .append(", preamble=").append(preamble) .append(", supports80211azNtb=").append(supports80211azNtb) - .append(", mNtbMinMeasurementTime ").append(mNtbMinMeasurementTime) - .append(", mNtbMaxMeasurementTime ").append(mNtbMaxMeasurementTime) - .toString(); + .append(", mNtbMinMeasurementTime=").append(mNtbMinMeasurementTime) + .append(", mNtbMaxMeasurementTime=").append(mNtbMaxMeasurementTime); + + if (mSecureRangingConfig != null) { + sb.append(", mSecureRangingConfig=").append(mSecureRangingConfig); + } + + return sb.toString(); + } /** @@ -1052,7 +1139,7 @@ public final class ResponderConfig implements Parcelable { * * @hide */ - static int translateFromScanResultToLocalChannelWidth( + public static int translateFromScanResultToLocalChannelWidth( @WifiAnnotations.ChannelWidth int scanResultChannelWidth) { switch (scanResultChannelWidth) { case ScanResult.CHANNEL_WIDTH_20MHZ: @@ -1081,7 +1168,8 @@ public final class ResponderConfig implements Parcelable { * * @hide */ - static int translateFromLocalToScanResultChannelWidth(@ChannelWidth int localChannelWidth) { + public static int translateFromLocalToScanResultChannelWidth( + @ChannelWidth int localChannelWidth) { switch (localChannelWidth) { case CHANNEL_WIDTH_20MHZ: return ScanResult.CHANNEL_WIDTH_20MHZ; @@ -1109,7 +1197,7 @@ public final class ResponderConfig implements Parcelable { * * @hide */ - static int translateFromScanResultToLocalPreamble( + public static int translateFromScanResultToLocalPreamble( @WifiAnnotations.PreambleType int scanResultPreamble) { switch (scanResultPreamble) { case ScanResult.PREAMBLE_LEGACY: @@ -1136,7 +1224,7 @@ public final class ResponderConfig implements Parcelable { * * @hide */ - static int translateFromLocalToScanResultPreamble(@PreambleType int localPreamble) { + public static int translateFromLocalToScanResultPreamble(@PreambleType int localPreamble) { switch (localPreamble) { case PREAMBLE_LEGACY: return ScanResult.PREAMBLE_LEGACY; diff --git a/framework/java/android/net/wifi/rtt/SecureRangingConfig.java b/framework/java/android/net/wifi/rtt/SecureRangingConfig.java new file mode 100644 index 0000000000..68a47d3578 --- /dev/null +++ b/framework/java/android/net/wifi/rtt/SecureRangingConfig.java @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2024 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 android.net.wifi.rtt; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.modules.utils.build.SdkLevel; +import com.android.wifi.flags.Flags; + +import java.util.Objects; + +/** + * Secure ranging configuration. + * Refer IEEE Std 802.11az-2022, section 12. Security. + */ +@FlaggedApi(Flags.FLAG_SECURE_RANGING) +public final class SecureRangingConfig implements Parcelable { + private final boolean mEnableSecureHeLtf; + private final boolean mEnableRangingFrameProtection; + private final PasnConfig mPasnConfig; + + + private SecureRangingConfig(boolean enableSecureHeLtf, boolean enableRangingFrameProtection, + @NonNull PasnConfig pasnConfig) { + Objects.requireNonNull(pasnConfig, "pasnConfig cannot be null"); + mEnableSecureHeLtf = enableSecureHeLtf; + mEnableRangingFrameProtection = enableRangingFrameProtection; + mPasnConfig = pasnConfig; + } + + private SecureRangingConfig(@NonNull Parcel in) { + mEnableSecureHeLtf = in.readByte() != 0; + mEnableRangingFrameProtection = in.readByte() != 0; + mPasnConfig = (SdkLevel.isAtLeastT()) ? in.readParcelable(PasnConfig.class.getClassLoader(), + PasnConfig.class) : in.readParcelable(PasnConfig.class.getClassLoader()); + } + + @FlaggedApi(Flags.FLAG_SECURE_RANGING) + public static final @NonNull Creator<SecureRangingConfig> CREATOR = + new Creator<SecureRangingConfig>() { + @Override + public SecureRangingConfig createFromParcel(Parcel in) { + return new SecureRangingConfig(in); + } + + @Override + public SecureRangingConfig[] newArray(int size) { + return new SecureRangingConfig[size]; + } + }; + + private SecureRangingConfig(Builder builder) { + mEnableSecureHeLtf = builder.mEnableSecureHeLtf; + mEnableRangingFrameProtection = builder.mEnableRangingFrameProtection; + mPasnConfig = builder.mPasnConfig; + } + + /** + * Returns whether secure HE-LTF is enabled or not. + */ + @FlaggedApi(Flags.FLAG_SECURE_RANGING) + public boolean isSecureHeLtfEnabled() { + return mEnableSecureHeLtf; + } + + /** + * Returns whether ranging frame protection is enabled or not. + */ + @FlaggedApi(Flags.FLAG_SECURE_RANGING) + public boolean isRangingFrameProtectionEnabled() { + return mEnableRangingFrameProtection; + } + + /** + * Returns Pre-association security negotiation (PASN) configuration used for secure + * ranging. + * + * @return {@link PasnConfig} object. + */ + @FlaggedApi(Flags.FLAG_SECURE_RANGING) + @NonNull + public PasnConfig getPasnConfig() { + return mPasnConfig; + } + + @FlaggedApi(Flags.FLAG_SECURE_RANGING) + @Override + public int describeContents() { + return 0; + } + + @FlaggedApi(Flags.FLAG_SECURE_RANGING) + @Override + public void writeToParcel(@androidx.annotation.NonNull Parcel dest, int flags) { + dest.writeByte((byte) (mEnableSecureHeLtf ? 1 : 0)); + dest.writeByte((byte) (mEnableRangingFrameProtection ? 1 : 0)); + dest.writeParcelable(mPasnConfig, flags); + } + + @FlaggedApi(Flags.FLAG_SECURE_RANGING) + @Override + public String toString() { + return "SecureRangingConfig{" + "mEnableSecureHeLtf=" + mEnableSecureHeLtf + + ", mEnableRangingProtection=" + mEnableRangingFrameProtection + ", mPasnConfig=" + + mPasnConfig + '}'; + } + + @FlaggedApi(Flags.FLAG_SECURE_RANGING) + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof SecureRangingConfig that)) return false; + return mEnableSecureHeLtf == that.mEnableSecureHeLtf + && mEnableRangingFrameProtection == that.mEnableRangingFrameProtection + && Objects.equals(mPasnConfig, that.mPasnConfig); + } + + @FlaggedApi(Flags.FLAG_SECURE_RANGING) + @Override + public int hashCode() { + return Objects.hash(mEnableSecureHeLtf, mEnableRangingFrameProtection, mPasnConfig); + } + + /** + * Builder for {@link SecureRangingConfig} + */ + @FlaggedApi(Flags.FLAG_SECURE_RANGING) + public static final class Builder { + private boolean mEnableSecureHeLtf = true; + private boolean mEnableRangingFrameProtection = true; + private final PasnConfig mPasnConfig; + + /** + * Builder constructor. + * + * @param pasnConfig PASN configuration + */ + @FlaggedApi(Flags.FLAG_SECURE_RANGING) + public Builder(@NonNull PasnConfig pasnConfig) { + Objects.requireNonNull(pasnConfig, "pasnConfig must not be null"); + mPasnConfig = pasnConfig; + } + + /** + * Enable or disable secure HE-LTF and returns a reference to this Builder enabling + * method chaining. If not set, secure HE-LTF is enabled. + * + * @param enableSecureHeLtf the {@code enableSecureHeLtf} to set + * @return a reference to this Builder + */ + @FlaggedApi(Flags.FLAG_SECURE_RANGING) + @NonNull + public Builder setSecureHeLtfEnabled(boolean enableSecureHeLtf) { + this.mEnableSecureHeLtf = enableSecureHeLtf; + return this; + } + + /** + * Enable or disable ranging frame protection and returns a reference to this Builder + * enabling method chaining. If not set, ranging frame protection is enabled. + * + * @param enableRangingFrameProtection the {@code enableRangingFrameProtection} to set + * @return a reference to this Builder + */ + @FlaggedApi(Flags.FLAG_SECURE_RANGING) + @NonNull + public Builder setRangingFrameProtectionEnabled(boolean enableRangingFrameProtection) { + this.mEnableRangingFrameProtection = enableRangingFrameProtection; + return this; + } + + /** + * Returns a {@code SecureRangingConfig} built from the parameters previously set. + * + * @return a {@code SecureRangingConfig} built with parameters of this + * {@code SecureRangingConfig.Builder} + */ + @FlaggedApi(Flags.FLAG_SECURE_RANGING) + @NonNull + public SecureRangingConfig build() { + return new SecureRangingConfig(this); + } + } +} diff --git a/framework/java/android/net/wifi/rtt/WifiRttManager.java b/framework/java/android/net/wifi/rtt/WifiRttManager.java index f86eaa4382..515e5b0816 100644 --- a/framework/java/android/net/wifi/rtt/WifiRttManager.java +++ b/framework/java/android/net/wifi/rtt/WifiRttManager.java @@ -116,6 +116,31 @@ public class WifiRttManager { @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) public static final String CHARACTERISTICS_KEY_BOOLEAN_NTB_INITIATOR = "key_ntb_initiator"; + /** + * Bundle key to access if device supports secure HE-LTF (High Efficiency Long Training Field). + * Secure HE-LTF is a critical security enhancement in the IEEE 802.11az standard that aims to + * protect ranging measurements from spoofing and manipulation. + */ + @FlaggedApi(Flags.FLAG_SECURE_RANGING) + public static final String CHARACTERISTICS_KEY_BOOLEAN_SECURE_HE_LTF_SUPPORTED = + "key_secure_he_ltf_supported"; + + /** + * Bundle key to access if device supports ranging frame protection. IEEE 802.11az introduces + * Protected Management Frames for FTM (Fine Timing Measurement), adding a layer of encryption + * and integrity protection to these frames. + */ + @FlaggedApi(Flags.FLAG_SECURE_RANGING) + public static final String CHARACTERISTICS_KEY_BOOLEAN_RANGING_FRAME_PROTECTION_SUPPORTED = + "key_rnm_mfp_supported"; + + /** + * Bundle key to access the maximum supported secure HE-LTF protocol version. + */ + @FlaggedApi(Flags.FLAG_SECURE_RANGING) + public static final String CHARACTERISTICS_KEY_INT_MAX_SUPPORTED_SECURE_HE_LTF_PROTO_VERSION = + "key_max_supported_secure_he_ltf_proto_ver"; + /** @hide */ @StringDef(prefix = { "CHARACTERISTICS_KEY_"}, value = { CHARACTERISTICS_KEY_BOOLEAN_ONE_SIDED_RTT, @@ -123,6 +148,9 @@ public class WifiRttManager { CHARACTERISTICS_KEY_BOOLEAN_LCR, CHARACTERISTICS_KEY_BOOLEAN_STA_RESPONDER, CHARACTERISTICS_KEY_BOOLEAN_NTB_INITIATOR, + CHARACTERISTICS_KEY_BOOLEAN_SECURE_HE_LTF_SUPPORTED, + CHARACTERISTICS_KEY_BOOLEAN_RANGING_FRAME_PROTECTION_SUPPORTED, + CHARACTERISTICS_KEY_INT_MAX_SUPPORTED_SECURE_HE_LTF_PROTO_VERSION }) @Retention(RetentionPolicy.SOURCE) public @interface RttCharacteristicsKey {} diff --git a/framework/java/android/net/wifi/usd/Characteristics.java b/framework/java/android/net/wifi/usd/Characteristics.java new file mode 100644 index 0000000000..3467eeeaab --- /dev/null +++ b/framework/java/android/net/wifi/usd/Characteristics.java @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2024 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 android.net.wifi.usd; + +import android.annotation.FlaggedApi; +import android.annotation.SystemApi; +import android.net.wifi.flags.Flags; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +import androidx.annotation.NonNull; + +import java.util.List; +import java.util.concurrent.Executor; +import java.util.function.Consumer; + +/** + * The characteristics of the USD implementation. + * + * @hide + */ +@SystemApi +@FlaggedApi(Flags.FLAG_USD) +public final class Characteristics implements Parcelable { + private final Bundle mCharacteristics; + /** @hide */ + public static final String KEY_MAX_SERVICE_NAME_LENGTH = "key_max_service_name_length"; + /** @hide */ + public static final String KEY_MAX_SERVICE_SPECIFIC_INFO_LENGTH = + "key_max_service_specific_info_length"; + /** @hide */ + public static final String KEY_MAX_MATCH_FILTER_LENGTH = "key_max_match_filter_length"; + /** @hide */ + public static final String KEY_MAX_NUM_PUBLISH_SESSIONS = "key_max_num_publish_session"; + /** @hide */ + public static final String KEY_MAX_NUM_SUBSCRIBE_SESSIONS = "key_max_num_subscribe_session"; + + + /** @hide : should not be created by apps */ + public Characteristics(Bundle characteristics) { + mCharacteristics = characteristics; + } + + private Characteristics(@NonNull Parcel in) { + mCharacteristics = in.readBundle(getClass().getClassLoader()); + } + + @NonNull + public static final Creator<Characteristics> CREATOR = new Creator<Characteristics>() { + @Override + public Characteristics createFromParcel(Parcel in) { + return new Characteristics(in); + } + + @Override + public Characteristics[] newArray(int size) { + return new Characteristics[size]; + } + }; + + /** + * Returns the maximum string length that can be used to specify a USD service name. + * + * @return A positive integer, maximum string length of USD service name. + */ + public int getMaxServiceNameLength() { + return mCharacteristics.getInt(KEY_MAX_SERVICE_NAME_LENGTH); + } + + /** + * Returns the maximum length of byte array that can be used to specify a service specific + * information field: the arbitrary load used in discovery or the message length of USD + * message exchange. Restricts the parameters of the + * {@link PublishConfig.Builder#setServiceSpecificInfo(byte[])}, + * {@link SubscribeConfig.Builder#setServiceSpecificInfo(byte[])}, + * {@link PublishSession#sendMessage(int, byte[], Executor, Consumer)} and + * {@link SubscribeSession#sendMessage(int, byte[], Executor, Consumer)} + * variants. + * + * @return A positive integer, maximum length of byte array for USD messaging. + */ + public int getMaxServiceSpecificInfoLength() { + return mCharacteristics.getInt(KEY_MAX_SERVICE_SPECIFIC_INFO_LENGTH); + } + + /** + * Returns the maximum length of byte array that can be used to specify a USD match filter. + * Restricts the parameters of the + * {@link PublishConfig.Builder#setTxMatchFilter(List)}, + * {@link PublishConfig.Builder#setRxMatchFilter(List)}, + * {@link SubscribeConfig.Builder#setTxMatchFilter(List)} and + * {@link SubscribeConfig.Builder#setRxMatchFilter(List)} + * + * @return A positive integer, maximum length of byte array for USD discovery match filter. + */ + public int getMaxMatchFilterLength() { + return mCharacteristics.getInt(KEY_MAX_MATCH_FILTER_LENGTH); + } + + /** + * Returns the maximum number of publish sessions supported by USD + * + * @return A positive integer + */ + public int getMaxNumberOfPublishSessions() { + return mCharacteristics.getInt(KEY_MAX_NUM_PUBLISH_SESSIONS); + } + + /** + * Returns the maximum number of subscribe sessions supported by USD + * + * @return A positive integer + */ + public int getMaxNumberOfSubscribeSessions() { + return mCharacteristics.getInt(KEY_MAX_NUM_SUBSCRIBE_SESSIONS); + } + + @Override + public int describeContents() { + return 0; + } + + /** + * Flatten this object in to a Parcel. + * + * @param dest The Parcel in which the object should be written. + * @param flags Additional flags about how the object should be written. + * May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}. + */ + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeBundle(mCharacteristics); + } +} diff --git a/framework/java/android/net/wifi/usd/Config.java b/framework/java/android/net/wifi/usd/Config.java new file mode 100644 index 0000000000..be358fa6b0 --- /dev/null +++ b/framework/java/android/net/wifi/usd/Config.java @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2024 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 android.net.wifi.usd; + +import android.annotation.FlaggedApi; +import android.annotation.IntDef; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.net.wifi.aware.TlvBufferUtils; +import android.net.wifi.flags.Flags; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +/** + * USD configuration for publish and subscribe operation. This is the base class and not intended + * to be created directly. + * + * @hide + */ +@SystemApi +@FlaggedApi(Flags.FLAG_USD) +public abstract class Config { + /** @hide */ + public static final int MAX_NUM_OF_OPERATING_FREQUENCIES = 32; + + /** + * Transmission type. + * + * @hide + */ + @IntDef({TRANSMISSION_TYPE_UNICAST, TRANSMISSION_TYPE_MULTICAST}) + @Retention(RetentionPolicy.SOURCE) + public @interface TransmissionType { + } + + /** + * A unicast transmission sends data from one device to a single, specific destination device. + */ + public static final int TRANSMISSION_TYPE_UNICAST = 0; + + /** + * A multicast transmission sends data from one device to a group of devices on the network + * simultaneously. + */ + public static final int TRANSMISSION_TYPE_MULTICAST = 1; + + /** + * Subscribe type. + * + * @hide + */ + @IntDef({SUBSCRIBE_TYPE_PASSIVE, SUBSCRIBE_TYPE_ACTIVE}) + @Retention(RetentionPolicy.SOURCE) + public @interface SubscribeType { + } + + /** + * Defines a passive subscribe session - a subscribe session where subscribe packets are not + * transmitted over-the-air and the device listens and matches to received publish packets. + */ + public static final int SUBSCRIBE_TYPE_PASSIVE = 0; + + /** + * Defines an active subscribe session - a subscribe session where subscribe packets are + * transmitted over-the-air. + */ + public static final int SUBSCRIBE_TYPE_ACTIVE = 1; + + /** + * Service Protocol Type. + * + * @hide + */ + @IntDef({SERVICE_PROTO_TYPE_GENERIC, SERVICE_PROTO_TYPE_CSA_MATTER}) + @Retention(RetentionPolicy.SOURCE) + public @interface ServiceProtoType { + } + + /** + * Generic type. + */ + public static final int SERVICE_PROTO_TYPE_GENERIC = 0; + + /** + * CSA (Connectivity Standards Alliance) Matter. + * Note: CSA Matter is an open-source standard for smart home technology that allows devices to + * work with any Matter-certified ecosystem. + */ + public static final int SERVICE_PROTO_TYPE_CSA_MATTER = 1; + + private final byte[] mServiceName; + private final int mTtlSeconds; + @ServiceProtoType + private final int mServiceProtoType; + private final byte[] mTxMatchFilterTlv; + private final byte[] mRxMatchFilterTlv; + private final byte[] mServiceSpecificInfo; + private final int[] mOperatingFrequencies; + + /** + * @hide + */ + public Config(@NonNull byte[] serviceName, int ttlSeconds, int serviceProtoType, + @Nullable byte[] txMatchFilterTlv, @Nullable byte[] rxMatchFilterTlv, + @Nullable byte[] serviceSpecificInfo, @Nullable int[] operatingFrequencies) { + mServiceName = serviceName; + mTtlSeconds = ttlSeconds; + mServiceProtoType = serviceProtoType; + mTxMatchFilterTlv = txMatchFilterTlv; + mRxMatchFilterTlv = rxMatchFilterTlv; + mServiceSpecificInfo = serviceSpecificInfo; + mOperatingFrequencies = operatingFrequencies; + } + + /** + * Gets the service name of the USD session. + * <p> + * The Service Name is a UTF-8 encoded string from 1 to 255 bytes in length. + * The only acceptable single-byte UTF-8 symbols for a Service Name are alphanumeric + * values (A-Z, a-z, 0-9), the hyphen ('-'), the period ('.') and the underscore ('_'). All + * valid multi-byte UTF-8 characters are acceptable in a Service Name. + * + * @return service name + */ + @NonNull + public byte[] getServiceName() { + return mServiceName; + } + + /** + * Gets the time interval (in seconds) a USD session will be alive. When the TTL is reached the + * session will be terminated with an event. + * + * @return ttl value in seconds + */ + @IntRange(from = 0) + public int getTtlSeconds() { + return mTtlSeconds; + } + + /** + * Get the Service protocol type for the USD session. + * + * @return service protocol type as defined in {@code SERVICE_PROTOCOL_TYPE_*} + */ + @ServiceProtoType + public int getServiceProtoType() { + return mServiceProtoType; + } + + /** + * Gets the Tx filter which is an ordered sequence of (length, value) pairs to be included in + * the USD discovery frame. + * + * @return tx match filter or empty list + */ + @NonNull + public List<byte[]> getTxMatchFilter() { + return new TlvBufferUtils.TlvIterable(0, 1, mTxMatchFilterTlv).toList(); + } + + /** + * @return tx match filter in TLV format + * @hide + */ + @Nullable + public byte[] getTxMatchFilterTlv() { + return mTxMatchFilterTlv; + } + + /** + * Gets the Rx match filter, which is an ordered sequence of (length, value) pairs that specify + * further the response conditions beyond the service name used to filter subscribe messages. + * + * @return rx match filter or empty list + */ + @NonNull + public List<byte[]> getRxMatchFilter() { + return new TlvBufferUtils.TlvIterable(0, 1, mRxMatchFilterTlv).toList(); + } + + /** + * @return receive match filter in TLV format. + * @hide + */ + @Nullable + public byte[] getRxMatchFilterTlv() { + return mRxMatchFilterTlv; + } + + /** + * Get the service specific information set for the USD session. + * + * @return byte array or null + */ + @Nullable + public byte[] getServiceSpecificInfo() { + return mServiceSpecificInfo; + } + + /** + * Get the frequencies where the USD session operates if overridden by {@code + * setOperatingFrequenciesMhz(int[])}. If null, the application has not set the operating + * frequencies using {@link PublishConfig.Builder#setOperatingFrequenciesMhz(int[])} for the + * publisher or {@link SubscribeConfig.Builder#setOperatingFrequenciesMhz(int[])} for the + * subscriber. + * + * <p>If the operating frequencies are not set the default behavior for the publisher and + * subscriber is, + * <ul> + * <li>The publisher defaults to channel 6 (in the 2.4 GHz band) and a list of allowed channels + * in the 2.4 GHz and 5 GHz bands for multichannel publishing. Publisher may prioritize the + * channel with Access Points having best RSSI. + * <li>The subscriber defaults to either channel 6 (in the 2.4 Ghz band) or Station channel or + * pick a channel from + * {@link SubscribeConfig.Builder#setRecommendedOperatingFrequenciesMhz(int[])} in given order + * of preference. + * </ul> + * + * @return an array of frequencies or null + */ + @Nullable + public int[] getOperatingFrequenciesMhz() { + return mOperatingFrequencies; + } + + @Override + public String toString() { + return "Config{" + "mServiceName=" + Arrays.toString(mServiceName) + ", mTtlSeconds=" + + mTtlSeconds + ", mServiceProtoType=" + mServiceProtoType + ", mTxMatchFilterTlv=" + + Arrays.toString(mTxMatchFilterTlv) + ", mRxMatchFilterTlv=" + Arrays.toString( + mRxMatchFilterTlv) + ", mServiceSpecificInfo=" + Arrays.toString( + mServiceSpecificInfo) + ", mOperatingFrequencies=" + Arrays.toString( + mOperatingFrequencies) + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Config config)) return false; + return mTtlSeconds == config.mTtlSeconds && mServiceProtoType == config.mServiceProtoType + && Arrays.equals(mServiceName, config.mServiceName) + && Arrays.equals(mTxMatchFilterTlv, config.mTxMatchFilterTlv) + && Arrays.equals(mRxMatchFilterTlv, config.mRxMatchFilterTlv) + && Arrays.equals(mServiceSpecificInfo, config.mServiceSpecificInfo) + && Arrays.equals(mOperatingFrequencies, config.mOperatingFrequencies); + } + + @Override + public int hashCode() { + int result = Objects.hash(mTtlSeconds, mServiceProtoType); + result = 31 * result + Arrays.hashCode(mServiceName); + result = 31 * result + Arrays.hashCode(mTxMatchFilterTlv); + result = 31 * result + Arrays.hashCode(mRxMatchFilterTlv); + result = 31 * result + Arrays.hashCode(mServiceSpecificInfo); + result = 31 * result + Arrays.hashCode(mOperatingFrequencies); + return result; + } +} diff --git a/framework/java/android/net/wifi/usd/DiscoveryResult.java b/framework/java/android/net/wifi/usd/DiscoveryResult.java new file mode 100644 index 0000000000..48ecbbea3b --- /dev/null +++ b/framework/java/android/net/wifi/usd/DiscoveryResult.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2024 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 android.net.wifi.usd; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.net.wifi.flags.Flags; + +/** + * A class providing information about a USD discovery session with a specific peer. + * + * @hide + */ +@SystemApi +@FlaggedApi(Flags.FLAG_USD) +public class DiscoveryResult { + private final int mPeerId; + private final byte[] mServiceSpecificInfo; + @Config.ServiceProtoType + private final int mServiceProtoType; + private final boolean mIsFsdEnabled; + + private DiscoveryResult(Builder builder) { + mPeerId = builder.mPeerId; + mServiceSpecificInfo = builder.mServiceSpecificInfo; + mServiceProtoType = builder.mServiceProtoType; + mIsFsdEnabled = builder.mIsFsdEnabled; + } + + /** + * Get the peer id. + */ + public int getPeerId() { + return mPeerId; + } + + /** + * Get the service specific info from the peer. If null, service discovery is without service + * specific info. + */ + @Nullable + public byte[] getServiceSpecificInfo() { + return mServiceSpecificInfo; + } + + /** + * Get service specific protocol type {@code (SERVICE_PROTO_TYPE_*)}. + */ + @Config.ServiceProtoType + public int getServiceProtoType() { + return mServiceProtoType; + } + + /** + * Return whether Further Service Discovery (FSD) is enabled or not. + */ + public boolean isFsdEnabled() { + return mIsFsdEnabled; + } + + /** + * {@code DiscoveryResult} builder static inner class. + */ + @FlaggedApi(Flags.FLAG_USD) + public static final class Builder { + private final int mPeerId; + private byte[] mServiceSpecificInfo; + private int mServiceProtoType; + private boolean mIsFsdEnabled; + + /** + * Builder constructor. + * + * @param peerId an id of the peer + */ + public Builder(int peerId) { + mPeerId = peerId; + } + + + /** + * Sets the service specific information and returns a reference to this Builder enabling + * method chaining. + * + * @param serviceSpecificInfo the {@code serviceSpecificInfo} to set + * @return a reference to this Builder + */ + @NonNull + public Builder setServiceSpecificInfo(@NonNull byte[] serviceSpecificInfo) { + this.mServiceSpecificInfo = serviceSpecificInfo; + return this; + } + + /** + * Sets the service protocol type and returns a reference to this Builder enabling method + * chaining. + * + * @param serviceProtoType the {@code serviceProtoType} to set + * @return a reference to this Builder + */ + @NonNull + public Builder setServiceProtoType(@Config.ServiceProtoType int serviceProtoType) { + this.mServiceProtoType = serviceProtoType; + return this; + } + + /** + * Sets whether Further Service Discovery (FSD) is enabled or not and returns a reference + * to this Builder enabling method chaining. + * + * @param isFsdEnabled the {@code isFsdEnabled} to set + * @return a reference to this Builder + */ + @NonNull + public Builder setFsdEnabled(boolean isFsdEnabled) { + this.mIsFsdEnabled = isFsdEnabled; + return this; + } + + /** + * Returns a {@code DiscoveryResult} built from the parameters previously set. + * + * @return a {@code DiscoveryResult} built with parameters of this {@code DiscoveryResult + * .Builder} + */ + @NonNull + public DiscoveryResult build() { + return new DiscoveryResult(this); + } + } +} diff --git a/framework/java/android/net/wifi/usd/IPublishSessionCallback.aidl b/framework/java/android/net/wifi/usd/IPublishSessionCallback.aidl new file mode 100644 index 0000000000..5037085644 --- /dev/null +++ b/framework/java/android/net/wifi/usd/IPublishSessionCallback.aidl @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2024 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 android.net.wifi.usd; + +/** + * Interface for USD publish session callback. + * + * @hide + */ +oneway interface IPublishSessionCallback { + void onPublishFailed(int reasonCode); + void onPublishStarted(int sessionId); + void onPublishReplied(int peerId, in byte[] ssi, int protoType, boolean isFsdEnabled); + void onPublishSessionTerminated(int reasonCode); + void onMessageReceived(int peerId, in byte[] message); +} diff --git a/framework/java/android/net/wifi/usd/ISubscribeSessionCallback.aidl b/framework/java/android/net/wifi/usd/ISubscribeSessionCallback.aidl new file mode 100644 index 0000000000..581d50dfeb --- /dev/null +++ b/framework/java/android/net/wifi/usd/ISubscribeSessionCallback.aidl @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2024 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 android.net.wifi.usd; + +/** + * Interface for USD subscribe session callback. + * + * @hide + */ +oneway interface ISubscribeSessionCallback { + void onSubscribeFailed(int reasonCode); + void onSubscribeStarted(int sessionId); + void onSubscribeDiscovered(int peerId, in byte[] ssi, int protoType, boolean isFsdEnabled); + void onSubscribeSessionTerminated(int reasonCode); + void onMessageReceived(int peerId, in byte[] message); +} diff --git a/framework/java/android/net/wifi/usd/IUsdManager.aidl b/framework/java/android/net/wifi/usd/IUsdManager.aidl new file mode 100644 index 0000000000..9e5812ccab --- /dev/null +++ b/framework/java/android/net/wifi/usd/IUsdManager.aidl @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2024 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 android.net.wifi.usd; + +import android.net.wifi.IBooleanListener; +import android.net.wifi.usd.Characteristics; +import android.net.wifi.usd.IPublishSessionCallback; +import android.net.wifi.usd.ISubscribeSessionCallback; +import android.net.wifi.usd.PublishConfig; +import android.net.wifi.usd.SubscribeConfig; + +/** + * Interface that UsdService implements + * + * {@hide} + */ +interface IUsdManager { + Characteristics getCharacteristics(); + void sendMessage(int sessionId, int peerId, in byte[] message, in IBooleanListener listener); + void cancelSubscribe(int sessionId); + void cancelPublish(int sessionId); + void updatePublish(int sessionId, in byte[] ssi); + void publish(in PublishConfig publishConfig, IPublishSessionCallback callback); + void subscribe(in SubscribeConfig subscribeConfig, ISubscribeSessionCallback callback); + void registerSubscriberStatusListener(IBooleanListener listener); + void unregisterSubscriberStatusListener(IBooleanListener listener); + void registerPublisherStatusListener(IBooleanListener listener); + void unregisterPublisherStatusListener(IBooleanListener listener); +} diff --git a/framework/java/android/net/wifi/usd/PublishConfig.java b/framework/java/android/net/wifi/usd/PublishConfig.java new file mode 100644 index 0000000000..1428d1214d --- /dev/null +++ b/framework/java/android/net/wifi/usd/PublishConfig.java @@ -0,0 +1,385 @@ +/* + * Copyright (C) 2024 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 android.net.wifi.usd; + +import android.annotation.FlaggedApi; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.net.wifi.WifiNetworkSpecifier; +import android.net.wifi.aware.TlvBufferUtils; +import android.net.wifi.aware.WifiAwareUtils; +import android.net.wifi.flags.Flags; +import android.os.Parcel; +import android.os.Parcelable; + +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Objects; + +/** + * Defines the configuration of USD publish session + * + * @hide + */ +@SystemApi +@FlaggedApi(Flags.FLAG_USD) +public final class PublishConfig extends Config implements Parcelable { + + @TransmissionType + private final int mSolicitedTransmissionType; + private final int mAnnouncementPeriodMillis; + private final boolean mEnableEvents; + + private PublishConfig(Parcel in) { + super(in.createByteArray(), in.readInt(), in.readInt(), in.createByteArray(), + in.createByteArray(), in.createByteArray(), in.createIntArray()); + mSolicitedTransmissionType = in.readInt(); + mAnnouncementPeriodMillis = in.readInt(); + mEnableEvents = in.readBoolean(); + } + + @NonNull + public static final Creator<PublishConfig> CREATOR = new Creator<PublishConfig>() { + + @Override + public PublishConfig createFromParcel(Parcel in) { + return new PublishConfig(in); + } + + @Override + public PublishConfig[] newArray(int size) { + return new PublishConfig[size]; + } + }; + + private PublishConfig(Builder builder) { + super(builder.mServiceName, builder.mTtlSeconds, builder.mServiceProtoType, + builder.mTxMatchFilterTlv, builder.mRxMatchFilterTlv, builder.mServiceSpecificInfo, + builder.mOperatingFrequencies); + mSolicitedTransmissionType = builder.mSolicitedTransmissionType; + mAnnouncementPeriodMillis = builder.mAnnouncementPeriodMillis; + mEnableEvents = builder.mEnableEvents; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeByteArray(getServiceName()); + dest.writeInt(getTtlSeconds()); + dest.writeInt(getServiceProtoType()); + dest.writeByteArray(getTxMatchFilterTlv()); + dest.writeByteArray(getRxMatchFilterTlv()); + dest.writeByteArray(getServiceSpecificInfo()); + dest.writeIntArray(getOperatingFrequenciesMhz()); + dest.writeInt(mSolicitedTransmissionType); + dest.writeInt(mAnnouncementPeriodMillis); + dest.writeBoolean(mEnableEvents); + } + + /** + * @return whether a solicited transmission is an unicast or a multicast transmission + */ + @TransmissionType + public int getSolicitedTransmissionType() { + return mSolicitedTransmissionType; + } + + /** + * @return announcement period in milliseconds, which is the Recommended periodicity of + * unsolicited transmissions + */ + @IntRange(from = 0) + public int getAnnouncementPeriodMillis() { + return mAnnouncementPeriodMillis; + } + + /** + * @return whether publish events are enabled or not. See + * {@link Builder#setEventsEnabled(boolean)}. + */ + public boolean isEventsEnabled() { + return mEnableEvents; + } + + @Override + public String toString() { + return super.toString() + " PublishConfig{" + "mSolicitedTransmissionType=" + + mSolicitedTransmissionType + ", mAnnouncementPeriodMillis=" + + mAnnouncementPeriodMillis + ", mEnableEvents=" + mEnableEvents + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof PublishConfig that)) return false; + if (!super.equals(o)) return false; + return mSolicitedTransmissionType == that.mSolicitedTransmissionType + && mAnnouncementPeriodMillis == that.mAnnouncementPeriodMillis + && mEnableEvents == that.mEnableEvents; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), mSolicitedTransmissionType, mAnnouncementPeriodMillis, + mEnableEvents); + } + + /** + * {@code PublishConfig} builder static inner class. + */ + public static final class Builder { + @TransmissionType + private int mSolicitedTransmissionType = TRANSMISSION_TYPE_UNICAST; + private int mAnnouncementPeriodMillis = 100; + private boolean mEnableEvents = false; + private final byte[] mServiceName; + private int mTtlSeconds = 3000; + @ServiceProtoType + private int mServiceProtoType = SERVICE_PROTO_TYPE_GENERIC; + private byte[] mTxMatchFilterTlv = null; + private byte[] mRxMatchFilterTlv = null; + private byte[] mServiceSpecificInfo = null; + private int[] mOperatingFrequencies = null; + + /** + * Builder for {@link PublishConfig} + * + * @param serviceName Specify the service name of the USD session. The Service Name is a + * UTF-8 encoded string from 1 to + * {@link Characteristics#getMaxServiceNameLength()} bytes in length. + * The only acceptable single-byte UTF-8 symbols for a Service Name are + * alphanumeric values (A-Z, a-z, 0-9), the hyphen ('-'), the period + * ('.') and the underscore ('_'). All valid multi-byte UTF-8 + * characters are acceptable in a Service Name. + */ + public Builder(@NonNull String serviceName) { + Objects.requireNonNull(serviceName, "serviceName must not be null"); + mServiceName = serviceName.getBytes(StandardCharsets.UTF_8); + WifiAwareUtils.validateServiceName(mServiceName); + } + + /** + * Sets the time to live for the USD session and returns a reference to this Builder + * enabling method chaining. Default value is 3000 seconds. + * + * @param ttlSeconds Time to live in seconds. Value 0 indicating the session does not + * terminate on its own. + * @return a reference to this Builder + */ + @NonNull + public Builder setTtlSeconds(@IntRange(from = 0) int ttlSeconds) { + if (ttlSeconds < 0) { + throw new IllegalArgumentException("Invalid ttlSec - must be non-negative"); + } + mTtlSeconds = ttlSeconds; + return this; + } + + /** + * Sets the {@code serviceProtoType} and returns a reference to this Builder enabling method + * chaining. Supported service protocol is defined as {@code SERVICE_PROTO_TYPE_*}. Default + * value is {@link #SERVICE_PROTO_TYPE_GENERIC}. + * + * @param serviceProtoType the {@code serviceProtoType} to set + * @return a reference to this Builder + */ + @NonNull + public Builder setServiceProtoType(@ServiceProtoType int serviceProtoType) { + if (serviceProtoType < SERVICE_PROTO_TYPE_GENERIC + || serviceProtoType > SERVICE_PROTO_TYPE_CSA_MATTER) { + throw new IllegalArgumentException("Invalid serviceProtoType - " + + serviceProtoType); + } + mServiceProtoType = serviceProtoType; + return this; + } + + /** + * Sets the {@code txMatchFilter} and returns a reference to this Builder enabling method + * chaining. The {@code txMatchFilter} is the ordered sequence of (length, value) pairs to + * be included in the subscribe frame. If not set, empty by default. + * + * <p>See Wi-Fi Aware Specification Version 4.0, section: Appendix H (Informative) Matching + * filter examples. + * + * @param txMatchFilter the {@code txMatchFilter} to set + * @return a reference to this Builder + */ + @NonNull + public Builder setTxMatchFilter(@NonNull List<byte[]> txMatchFilter) { + Objects.requireNonNull(txMatchFilter, "txMatchFilter must not be null"); + this.mTxMatchFilterTlv = new TlvBufferUtils.TlvConstructor(0, 1).allocateAndPut( + txMatchFilter).getArray(); + if (!TlvBufferUtils.isValid(mTxMatchFilterTlv, 0, 1)) { + throw new IllegalArgumentException( + "Invalid txMatchFilter configuration - LV fields do not match up to " + + "length"); + } + return this; + } + + /** + * Sets the {@code rxMatchFilter} and returns a reference to this Builder enabling method + * chaining. The {@code rxMatchFilter} is the ordered sequence of (length, value) pairs + * that specify further the matching conditions beyond the service name used to filter + * the USD discovery messages. When a subscriber receives a publish message, it matches the + * matching filter field in the publish message against its own matching_filter_rx. If not + * set, empty by default. + * + * <p>See Wi-Fi Aware Specification Version 4.0, section: Appendix H (Informative) Matching + * filter examples. + * + * @param rxMatchFilter the {@code rxMatchFilter} to set + * @return a reference to this Builder + */ + @NonNull + public Builder setRxMatchFilter(@NonNull List<byte[]> rxMatchFilter) { + Objects.requireNonNull(rxMatchFilter, "rxMatchFilter must not be null"); + this.mRxMatchFilterTlv = new TlvBufferUtils.TlvConstructor(0, 1).allocateAndPut( + rxMatchFilter).getArray(); + if (!TlvBufferUtils.isValid(mRxMatchFilterTlv, 0, 1)) { + throw new IllegalArgumentException( + "Invalid rxMatchFilter configuration - LV fields do not match up to " + + "length"); + } + return this; + } + + /** + * Sets the solicited transmission type and returns a reference to this Builder enabling + * method chaining. The type determines whether the transmission is unicast or multicast. + * Default is unicast. + * + * @param solicitedTransmissionType the transmission type {@code TRANSMISSION_TYPE_*} to + * set + * @return a reference to this Builder + */ + @NonNull + public Builder setSolicitedTransmissionType( + @TransmissionType int solicitedTransmissionType) { + if (solicitedTransmissionType < TRANSMISSION_TYPE_UNICAST + || solicitedTransmissionType > TRANSMISSION_TYPE_MULTICAST) { + throw new IllegalArgumentException("Invalid solicitedTransmissionType - " + + solicitedTransmissionType); + } + this.mSolicitedTransmissionType = solicitedTransmissionType; + return this; + } + + /** + * Sets announcement period and returns a reference to this Builder enabling method + * chaining. Announcement period is the recommended periodicity of unsolicited + * transmissions. Default value is 100 ms. + * + * @param announcementPeriodMillis announcement period in milliseconds to set + * @return a reference to this Builder + */ + @NonNull + public Builder setAnnouncementPeriodMillis( + @IntRange(from = 0) int announcementPeriodMillis) { + if (announcementPeriodMillis < 0) { + throw new IllegalArgumentException( + "Invalid announcementPeriodMillis - must be non-negative"); + } + this.mAnnouncementPeriodMillis = announcementPeriodMillis; + return this; + } + + /** + * Enable or disable publish related events and returns a reference to this Builder + * enabling method chaining. If enabled, publish replied events are generated on each + * solicited transmission. By default, publish replied events are disabled. + * See {@link PublishSessionCallback#onPublishReplied(DiscoveryResult)}. + * + * @param enableEvents the publish related events are enabled + * @return a reference to this Builder + */ + @NonNull + public Builder setEventsEnabled(boolean enableEvents) { + this.mEnableEvents = enableEvents; + return this; + } + + /** + * Specify service specific information for the publish session. This is a free-form byte + * array available to the application to send additional information as part of the + * discovery operation - it will not be used to determine whether a publish/subscribe + * match occurs. Default value is null; + * + * Note: Maximum length is limited by + * {@link Characteristics#getMaxServiceSpecificInfoLength()} + * + * @param serviceSpecificInfo A byte-array for the service-specific + * information field. + * @return a reference to this Builder + */ + @NonNull + public Builder setServiceSpecificInfo(@NonNull byte[] serviceSpecificInfo) { + Objects.requireNonNull(serviceSpecificInfo, "serviceSpecificInfo must not be null"); + mServiceSpecificInfo = serviceSpecificInfo.clone(); + return this; + } + + /** + * Sets the operating frequencies used for publish operation. This overrides the default + * channel selection for publish. All frequencies have to be 20 Mhz channel in 2.4 Ghz or + * 5 Ghz band per regulation in the geographical location. In {@code operatingFrequencies}, + * <ul> + * <li>The first frequency is the channel used for single channel publish. + * <li>Any additional frequencies enable multiple channel publish. + * </ul> + * + * <p>If not set or an empty array is provided, the system defaults to 2437 MHz (channel 6 + * in the 2.4 GHz band) for single channel publish and a list of allowed channels in the 2.4 + * GHz and 5 GHz bands for multichannel publishing. + * + * <p>Note: the dwell time for the single and multi publish channels are defined in the + * Wifi Aware Specification Version 4, section 4.5.1 Publisher behavior in USD. + * + * @param operatingFrequencies frequencies used for publish operation + * @return a reference to this Builder + * @throws IllegalArgumentException if frequencies are invalid or the number frequencies + * are more than the number of 20 Mhz channels in 2.4 Ghz and 5 Ghz as per regulatory. + */ + @NonNull + public Builder setOperatingFrequenciesMhz(@NonNull int[] operatingFrequencies) { + Objects.requireNonNull(operatingFrequencies, "operatingFrequencies must not be null"); + if ((operatingFrequencies.length > MAX_NUM_OF_OPERATING_FREQUENCIES) + || !WifiNetworkSpecifier.validateChannelFrequencyInMhz(operatingFrequencies)) { + throw new IllegalArgumentException("Invalid operatingFrequencies"); + } + mOperatingFrequencies = operatingFrequencies.clone(); + return this; + } + + /** + * Returns a {@code PublishConfig} built from the parameters previously set. + * + * @return a {@code PublishConfig} built with parameters of this {@code PublishConfig + * .Builder} + */ + @NonNull + public PublishConfig build() { + return new PublishConfig(this); + } + } +} diff --git a/framework/java/android/net/wifi/usd/PublishSession.java b/framework/java/android/net/wifi/usd/PublishSession.java new file mode 100644 index 0000000000..cc2ef59e5d --- /dev/null +++ b/framework/java/android/net/wifi/usd/PublishSession.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2024 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 android.net.wifi.usd; + +import static android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION; + +import android.annotation.CallbackExecutor; +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.RequiresApi; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; +import android.net.wifi.flags.Flags; +import android.net.wifi.util.Environment; +import android.os.Build; +import android.util.Log; + +import java.lang.ref.WeakReference; +import java.util.Objects; +import java.util.concurrent.Executor; +import java.util.function.Consumer; + + +/** + * A class to represent the USD publish session + * + * @hide + */ +@RequiresApi(Build.VERSION_CODES.BAKLAVA) +@SystemApi +@FlaggedApi(Flags.FLAG_USD) +public class PublishSession { + private static final String TAG = PublishSession.class.getName(); + private final WeakReference<UsdManager> mUsdManager; + private final int mSessionId; + + /** @hide */ + public PublishSession(@NonNull UsdManager usdManager, int sessionId) { + mUsdManager = new WeakReference<>(usdManager); + mSessionId = sessionId; + } + + + /** @hide */ + public int getSessionId() { + return mSessionId; + } + + /** + * Cancel the Publish Session + * + */ + @RequiresPermission(MANAGE_WIFI_NETWORK_SELECTION) + public void cancel() { + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + UsdManager usdManager = mUsdManager.get(); + if (usdManager == null) { + Log.w(TAG, "cancel is called after the UsdManager has been garbage collected"); + return; + } + usdManager.cancelPublish(mSessionId); + } + + /** + * Update the publish session with service specific info. The new value will override any + * service specific information previously passed to the publish or updatePublish methods for + * this session. To clear service specific info, set an empty byte array. + * + * @param serviceSpecificInfo service specific info + */ + @RequiresPermission(MANAGE_WIFI_NETWORK_SELECTION) + public void updatePublish(@NonNull byte[] serviceSpecificInfo) { + Objects.requireNonNull(serviceSpecificInfo, "serviceSpecificInfo must not be null"); + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + UsdManager usdManager = mUsdManager.get(); + if (usdManager == null) { + Log.w(TAG, "updatePublish is called after the UsdManager has been garbage collected"); + return; + } + usdManager.updatePublish(mSessionId, serviceSpecificInfo); + } + + /** + * Send a message to the peer. Message length is limited by + * {@link Characteristics#getMaxServiceSpecificInfoLength()}. + * + * @param peerId peer id obtained from {@link DiscoveryResult#getPeerId()} + * @param message byte array + * @param executor executor + * @param resultCallback result callback + */ + @RequiresPermission(MANAGE_WIFI_NETWORK_SELECTION) + public void sendMessage(int peerId, @NonNull byte[] message, + @NonNull @CallbackExecutor Executor executor, + @NonNull Consumer<Boolean> resultCallback) { + Objects.requireNonNull(executor, "executor must not be null"); + Objects.requireNonNull(resultCallback, "resultCallback must not be null"); + Objects.requireNonNull(message, "message must not be null"); + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + UsdManager usdManager = mUsdManager.get(); + if (usdManager == null) { + Log.w(TAG, "sendMessage is called after the UsdManager has been garbage collected"); + executor.execute(() -> resultCallback.accept(false)); + return; + } + usdManager.sendMessage(mSessionId, peerId, message, executor, resultCallback); + } +} diff --git a/framework/java/android/net/wifi/usd/PublishSessionCallback.java b/framework/java/android/net/wifi/usd/PublishSessionCallback.java new file mode 100644 index 0000000000..e5d957e4ab --- /dev/null +++ b/framework/java/android/net/wifi/usd/PublishSessionCallback.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2024 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 android.net.wifi.usd; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.RequiresApi; +import android.annotation.SystemApi; +import android.net.wifi.flags.Flags; +import android.os.Build; + + +/** + * USD publish session callbacks. Should be extended by applications wanting notifications. + * + * @hide + */ +@RequiresApi(Build.VERSION_CODES.BAKLAVA) +@SystemApi +@FlaggedApi(Flags.FLAG_USD) +public class PublishSessionCallback extends SessionCallback { + + /** + * Called when a publish session cannot be created. + * + * @param reason reason code as defined in {@code FAILURE_XXX} + */ + public void onPublishFailed(@FailureCode int reason) { + } + + /** + * Called when a publish operation is started successfully. + * + * @param session publish session + */ + public void onPublishStarted(@NonNull PublishSession session) { + } + + /** + * Called for each solicited publish transmission if + * {@link PublishConfig.Builder#setEventsEnabled(boolean)} is enabled. + */ + public void onPublishReplied(@NonNull DiscoveryResult discoveryResult) { + } +} diff --git a/framework/java/android/net/wifi/usd/SessionCallback.java b/framework/java/android/net/wifi/usd/SessionCallback.java new file mode 100644 index 0000000000..d226cd49e5 --- /dev/null +++ b/framework/java/android/net/wifi/usd/SessionCallback.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2024 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 android.net.wifi.usd; + +import android.annotation.FlaggedApi; +import android.annotation.IntDef; +import android.annotation.Nullable; +import android.annotation.RequiresApi; +import android.annotation.SystemApi; +import android.net.wifi.flags.Flags; +import android.os.Build; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + + +/** + * Base class for USD session events callbacks. Should be extended by applications wanting + * notifications. The callbacks are set when a publish or subscribe session is created. + * + * @hide + */ +@RequiresApi(Build.VERSION_CODES.BAKLAVA) +@SystemApi +@FlaggedApi(Flags.FLAG_USD) +public abstract class SessionCallback { + /** + * Failure code + * + * @hide + */ + @IntDef({FAILURE_UNKNOWN, FAILURE_TIMEOUT, FAILURE_NOT_AVAILABLE, FAILURE_MAX_SESSIONS_REACHED}) + @Retention(RetentionPolicy.SOURCE) + public @interface FailureCode { + } + + /** + * Failure is unknown. + */ + public static final int FAILURE_UNKNOWN = 0; + + /** + * Failure due to timeout in the requested operation. + */ + public static final int FAILURE_TIMEOUT = 1; + + /** + * Failure due to the requested operation is not available currently. + */ + public static final int FAILURE_NOT_AVAILABLE = 2; + /** + * Failure due to the maximum session reached. Maximum number of publish and subscribe sessions + * are limited by {@link Characteristics#getMaxNumberOfPublishSessions()} and + * {@link Characteristics#getMaxNumberOfSubscribeSessions()} respectively. + */ + public static final int FAILURE_MAX_SESSIONS_REACHED = 3; + + + /** + * Termination reason code + * + * @hide + */ + @IntDef({TERMINATION_REASON_UNKNOWN, TERMINATION_REASON_NOT_AVAILABLE, + TERMINATION_REASON_USER_INITIATED}) + @Retention(RetentionPolicy.SOURCE) + public @interface TerminationReasonCode { + } + + /** + * Termination due to unknown reason. + */ + public static final int TERMINATION_REASON_UNKNOWN = 0; + + /** + * Termination due the USD session is not available. + */ + public static final int TERMINATION_REASON_NOT_AVAILABLE = 1; + + /** + * Termination due to user initiated {@link PublishSession#cancel()} or + * {@link SubscribeSession#cancel()} + */ + public static final int TERMINATION_REASON_USER_INITIATED = 2; + + /** + * Called when a publish or subscribe session terminates. Termination may be due to + * <ul> + * <li> user request e.g. {@link SubscribeSession#cancel()} or {@link PublishSession#cancel()} + * <li> application expiration e.g. {@link PublishConfig.Builder#setTtlSeconds(int)} or + * {@link SubscribeConfig.Builder#setTtlSeconds(int)}. + * </ul> + * @param reason reason code as in {@code TERMINATION_REASON_*} + */ + public void onSessionTerminated(@TerminationReasonCode int reason) { + } + + /** + * Called when a message is received from another USD peer. + * + * @param peerId an identifier of the remote peer + * @param message a byte array containing the message. + */ + public void onMessageReceived(int peerId, @Nullable byte[] message) { + } +} diff --git a/framework/java/android/net/wifi/usd/SubscribeConfig.java b/framework/java/android/net/wifi/usd/SubscribeConfig.java new file mode 100644 index 0000000000..2aa935025c --- /dev/null +++ b/framework/java/android/net/wifi/usd/SubscribeConfig.java @@ -0,0 +1,408 @@ +/* + * Copyright (C) 2024 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 android.net.wifi.usd; + +import android.annotation.FlaggedApi; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.net.wifi.WifiNetworkSpecifier; +import android.net.wifi.aware.TlvBufferUtils; +import android.net.wifi.aware.WifiAwareUtils; +import android.net.wifi.flags.Flags; +import android.os.Parcel; +import android.os.Parcelable; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + + +/** + * Defines the configuration of USD subscribe session. + * + * @hide + */ +@SystemApi +@FlaggedApi(Flags.FLAG_USD) +public final class SubscribeConfig extends Config implements Parcelable { + + private SubscribeConfig(Builder builder) { + super(builder.mServiceName, builder.mTtlSeconds, builder.mServiceProtoType, + builder.mTxMatchFilterTlv, builder.mRxMatchFilterTlv, builder.mServiceSpecificInfo, + builder.mOperatingFrequencies); + mSubscribeType = builder.mSubscribeType; + mQueryPeriodMillis = builder.mQueryPeriodMillis; + mRecommendedFrequencies = builder.mRecommendedFrequencies; + } + + @SubscribeType + private final int mSubscribeType; + private final int mQueryPeriodMillis; + private final int[] mRecommendedFrequencies; + + private SubscribeConfig(Parcel in) { + super(in.createByteArray(), in.readInt(), in.readInt(), in.createByteArray(), + in.createByteArray(), in.createByteArray(), in.createIntArray()); + mSubscribeType = in.readInt(); + mQueryPeriodMillis = in.readInt(); + mRecommendedFrequencies = in.createIntArray(); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeByteArray(getServiceName()); + dest.writeInt(getTtlSeconds()); + dest.writeInt(getServiceProtoType()); + dest.writeByteArray(getTxMatchFilterTlv()); + dest.writeByteArray(getRxMatchFilterTlv()); + dest.writeByteArray(getServiceSpecificInfo()); + dest.writeIntArray(getOperatingFrequenciesMhz()); + dest.writeInt(mSubscribeType); + dest.writeInt(mQueryPeriodMillis); + dest.writeIntArray(mRecommendedFrequencies); + } + + @Override + public int describeContents() { + return 0; + } + + @NonNull + public static final Creator<SubscribeConfig> CREATOR = new Creator<SubscribeConfig>() { + + @Override + public SubscribeConfig createFromParcel(Parcel in) { + return new SubscribeConfig(in); + } + + @Override + public SubscribeConfig[] newArray(int size) { + return new SubscribeConfig[size]; + } + }; + + /** + * Gets the type of subscribe session. See {@code SUBSCRIBE_TYPE_XXX} for different types of + * subscribe. + * + * @return subscribe type + */ + @SubscribeType + public int getSubscribeType() { + return mSubscribeType; + } + + /** + * Gets the recommended periodicity of query transmissions for the subscribe session. + * + * @return Query period in milliseconds + */ + @IntRange(from = 0) + public int getQueryPeriodMillis() { + return mQueryPeriodMillis; + } + + /** + * Gets the recommended frequency list to be used for subscribe operation. See + * {@link Builder#setRecommendedOperatingFrequenciesMhz(int[])}. + * + * @return frequency list or null if not set + */ + @Nullable + public int[] getRecommendedOperatingFrequenciesMhz() { + return mRecommendedFrequencies; + } + + @Override + public String toString() { + return super.toString() + " SubscribeConfig{" + "mSubscribeType=" + mSubscribeType + + ", mQueryPeriodMillis=" + mQueryPeriodMillis + ", mRecommendedFrequencies=" + + Arrays.toString(mRecommendedFrequencies) + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof SubscribeConfig that)) return false; + if (!super.equals(o)) return false; + return mSubscribeType == that.mSubscribeType + && mQueryPeriodMillis == that.mQueryPeriodMillis + && Arrays.equals(mRecommendedFrequencies, that.mRecommendedFrequencies); + } + + @Override + public int hashCode() { + int result = Objects.hash(super.hashCode(), mSubscribeType, mQueryPeriodMillis); + result = 31 * result + Arrays.hashCode(mRecommendedFrequencies); + return result; + } + + /** + * {@code SubscribeConfig} builder static inner class. + */ + @FlaggedApi(Flags.FLAG_USD) + public static final class Builder { + @SubscribeType private int mSubscribeType = SUBSCRIBE_TYPE_ACTIVE; + private int mQueryPeriodMillis = 100; + private int[] mRecommendedFrequencies = null; + private final byte[] mServiceName; + private int mTtlSeconds = 3000; + @ServiceProtoType private int mServiceProtoType = SERVICE_PROTO_TYPE_GENERIC; + private byte[] mTxMatchFilterTlv = null; + private byte[] mRxMatchFilterTlv = null; + private byte[] mServiceSpecificInfo = null; + private int[] mOperatingFrequencies = null; + + /** + * Builder for {@link SubscribeConfig} + * + * @param serviceName Specify the service name of the USD session. The Service Name is a + * UTF-8 encoded string from 1 to + * {@link Characteristics#getMaxServiceNameLength()} bytes in length. + * The only acceptable single-byte UTF-8 symbols for a Service Name are + * alphanumeric values (A-Z, a-z, 0-9), the hyphen ('-'), the period + * ('.') and the underscore ('_'). Allvalid multi-byte UTF-8 + * characters are acceptable in a Service Name. + */ + public Builder(@NonNull String serviceName) { + Objects.requireNonNull(serviceName, "serviceName must not be null"); + mServiceName = serviceName.getBytes(StandardCharsets.UTF_8); + WifiAwareUtils.validateServiceName(mServiceName); + } + + /** + * Sets the time to live for the USD session and returns a reference to this Builder + * enabling method chaining. Default value is 3000 seconds. + * + * @param ttlSeconds Time to live in seconds. Value 0 indicating the session does not + * terminate on its own. + * @return a reference to this Builder + */ + @NonNull + public Builder setTtlSeconds(@IntRange(from = 0) int ttlSeconds) { + if (ttlSeconds < 0) { + throw new IllegalArgumentException("Invalid ttlSec - must be non-negative"); + } + mTtlSeconds = ttlSeconds; + return this; + } + + /** + * Sets the {@code serviceProtoType} and returns a reference to this Builder enabling method + * chaining. Supported service protocol is defined as {@code SERVICE_PROTO_TYPE_*}. Default + * value is {@link #SERVICE_PROTO_TYPE_GENERIC}. + * + * @param serviceProtoType the {@code serviceProtoType} to set + * @return a reference to this Builder + */ + @NonNull + public Builder setServiceProtoType(@ServiceProtoType int serviceProtoType) { + if (serviceProtoType < SERVICE_PROTO_TYPE_GENERIC + || serviceProtoType > SERVICE_PROTO_TYPE_CSA_MATTER) { + throw new IllegalArgumentException("Invalid serviceProtoType - " + + serviceProtoType); + } + mServiceProtoType = serviceProtoType; + return this; + } + + /** + * Sets the {@code txMatchFilter} and returns a reference to this Builder enabling method + * chaining. The {@code txMatchFilter} is the ordered sequence of (length, value) pairs to + * be included in the subscribe frame. If not set, empty by default. + * + * <p>See Wi-Fi Aware Specification Version 4.0, section: Appendix H (Informative) Matching + * filter examples. + * + * @param txMatchFilter the {@code txMatchFilter} to set + * @return a reference to this Builder + */ + @NonNull + public Builder setTxMatchFilter(@NonNull List<byte[]> txMatchFilter) { + Objects.requireNonNull(txMatchFilter, "txMatchFilter must not be null"); + mTxMatchFilterTlv = new TlvBufferUtils.TlvConstructor(0, 1).allocateAndPut( + txMatchFilter).getArray(); + if (!TlvBufferUtils.isValid(mTxMatchFilterTlv, 0, 1)) { + throw new IllegalArgumentException( + "Invalid txMatchFilter configuration - LV fields do not match up to " + + "length"); + } + return this; + } + + /** + * Sets the {@code rxMatchFilter} and returns a reference to this Builder enabling method + * chaining. The {@code rxMatchFilter} is the ordered sequence of (length, value) pairs + * that specify further the matching conditions beyond the service name used to filter + * the USD discovery messages. When a subscriber receives a publish message, it matches the + * matching filter field in the publish message against its own matching_filter_rx. If not + * set, empty by default. + * + * <p>See Wi-Fi Aware Specification Version 4.0, section: Appendix H (Informative) Matching + * filter examples. + * + * @param rxMatchFilter the {@code rxMatchFilter} to set + * @return a reference to this Builder + */ + @NonNull + public Builder setRxMatchFilter(@NonNull List<byte[]> rxMatchFilter) { + Objects.requireNonNull(rxMatchFilter, "rxMatchFilter must not be null"); + mRxMatchFilterTlv = new TlvBufferUtils.TlvConstructor(0, 1).allocateAndPut( + rxMatchFilter).getArray(); + if (!TlvBufferUtils.isValid(mRxMatchFilterTlv, 0, 1)) { + throw new IllegalArgumentException( + "Invalid rxMatchFilter configuration - LV fields do not match up to " + + "length"); + } + return this; + } + + /** + * Sets the susbcribe type and returns a reference to this Builder enabling method chaining. + * + * @param subscribeType the {@code subscribeType} to set + * @return a reference to this Builder + */ + @NonNull + public Builder setSubscribeType(@SubscribeType int subscribeType) { + if (subscribeType < SUBSCRIBE_TYPE_PASSIVE || subscribeType > SUBSCRIBE_TYPE_ACTIVE) { + throw new IllegalArgumentException("Invalid subscribeType - " + subscribeType); + } + mSubscribeType = subscribeType; + return this; + } + + /** + * Sets the query period and returns a reference to this Builder enabling method chaining. + * Default value is 100 ms. + * + * @param queryPeriodMillis the {@code queryPeriodMillis} to set + * @return a reference to this Builder + */ + @NonNull + public Builder setQueryPeriodMillis(@IntRange(from = 0) int queryPeriodMillis) { + if (queryPeriodMillis < 0) { + throw new IllegalArgumentException( + "Invalid queryPeriodMillis - must be non-negative"); + } + mQueryPeriodMillis = queryPeriodMillis; + return this; + } + + /** + * Sets the recommended frequencies to use in case the framework couldn't pick a default + * channel for the subscriber operation. This will be a no-op if + * {@link #setOperatingFrequenciesMhz(int[])} is used. + * + * <p>Here is the default subscriber channel selection preference order with {@code + * recommendedFreqList} + * <ol> + * <li>Channel 6 in 2.4 Ghz if there is no multichannel concurrency. + * <li>Station channel if the station connected on non-DFS/Indoor channel. + * <li>Pick a channel from {@code recommendedFreqList} if regulatory permits. + * <li>Pick any available channel + * </ol> + * + * <p>Note: If multiple channels are available for the subscriber, the channel having AP + * with the best RSSI will be picked. + * + * @param recommendedFrequencies the {@code recommendedFreqList} to set + * @return a reference to this Builder + * @throws IllegalArgumentException if frequencies are invalid or the number frequencies + * are more than the number of 20 Mhz channels in 2.4 Ghz and 5 Ghz as per regulatory. + */ + @NonNull + public Builder setRecommendedOperatingFrequenciesMhz( + @NonNull int[] recommendedFrequencies) { + Objects.requireNonNull(recommendedFrequencies, + "recommendedFrequencies must not be null"); + if ((recommendedFrequencies.length > MAX_NUM_OF_OPERATING_FREQUENCIES) + || !WifiNetworkSpecifier.validateChannelFrequencyInMhz( + recommendedFrequencies)) { + throw new IllegalArgumentException("Invalid recommendedFrequencies"); + } + this.mRecommendedFrequencies = recommendedFrequencies.clone(); + return this; + } + + /** + * Specify service specific information for the publish session. This is a free-form byte + * array available to the application to send additional information as part of the + * discovery operation - it will not be used to determine whether a publish/subscribe + * match occurs. Default value is null; + * + * Note: Maximum length is limited by + * {@link Characteristics#getMaxServiceSpecificInfoLength()} + * + * @param serviceSpecificInfo A byte-array for the service-specific + * information field. + * @return a reference to this Builder + */ + @NonNull + public Builder setServiceSpecificInfo(@NonNull byte[] serviceSpecificInfo) { + Objects.requireNonNull(serviceSpecificInfo, "serviceSpecificInfo must not be null"); + mServiceSpecificInfo = serviceSpecificInfo.clone(); + return this; + } + + /** + * Sets the frequencies used for subscribe operation. The subscriber picks one of the + * frequencies from this list. This overrides the default channel selection as described + * below. + * + * <p>If null, here is the default subscriber channel selection preference order, + * <ol> + * <li>Channel 6 in 2.4 Ghz if there is no multichannel concurrency. + * <li>Station channel if the station connected on non-DFS/Indoor channel. + * <li>Pick a channel from {@link #setRecommendedOperatingFrequenciesMhz(int[])} if + * regulatory permits. + * <li>Pick any available channel. + * </ol> + * <p>Note: the dwell time for subscriber operation is calculated internally based on + * existing concurrency operation (e.g. Station + USD). + * + * @param operatingFrequencies frequencies used for subscribe operation + * @return a reference to this Builder + * @throws IllegalArgumentException if frequencies are invalid or the number frequencies + * are more than the number of 20 Mhz channels in 2.4 Ghz and 5 Ghz as per regulatory. + */ + @NonNull + public Builder setOperatingFrequenciesMhz(@NonNull int[] operatingFrequencies) { + Objects.requireNonNull(operatingFrequencies, "operatingFrequencies must not be null"); + if ((operatingFrequencies.length > MAX_NUM_OF_OPERATING_FREQUENCIES) + || !WifiNetworkSpecifier.validateChannelFrequencyInMhz(operatingFrequencies)) { + throw new IllegalArgumentException("Invalid operatingFrequencies"); + } + mOperatingFrequencies = operatingFrequencies.clone(); + return this; + } + + /** + * Returns a {@code SubscribeConfig} built from the parameters previously set. + * + * @return a {@code SubscribeConfig} built with parameters of this {@code SubscribeConfig + * .Builder} + */ + @NonNull + public SubscribeConfig build() { + return new SubscribeConfig(this); + } + } +} diff --git a/framework/java/android/net/wifi/usd/SubscribeSession.java b/framework/java/android/net/wifi/usd/SubscribeSession.java new file mode 100644 index 0000000000..f5d8d534e5 --- /dev/null +++ b/framework/java/android/net/wifi/usd/SubscribeSession.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2024 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 android.net.wifi.usd; + +import static android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION; + +import android.annotation.CallbackExecutor; +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.RequiresApi; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; +import android.net.wifi.flags.Flags; +import android.net.wifi.util.Environment; +import android.os.Build; +import android.util.Log; + +import java.lang.ref.WeakReference; +import java.util.Objects; +import java.util.concurrent.Executor; +import java.util.function.Consumer; + +/** + * A class to represent the USD Subscribe session. + * + * @hide + */ +@RequiresApi(Build.VERSION_CODES.BAKLAVA) +@SystemApi +@FlaggedApi(Flags.FLAG_USD) +public class SubscribeSession { + private static final String TAG = SubscribeSession.class.getName(); + private final WeakReference<UsdManager> mUsdManager; + private final int mSessionId; + + /** @hide */ + public SubscribeSession(@NonNull UsdManager usdManager, int sessionId) { + mUsdManager = new WeakReference<>(usdManager); + mSessionId = sessionId; + } + + /** @hide */ + public int getSessionId() { + return mSessionId; + } + + /** + * Cancel the Subscribe Session + * + */ + @RequiresPermission(MANAGE_WIFI_NETWORK_SELECTION) + public void cancel() { + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + UsdManager usdManager = mUsdManager.get(); + if (usdManager == null) { + Log.w(TAG, "cancelSubscribe is called after the UsdManager has been garbage collected"); + return; + } + usdManager.cancelSubscribe(mSessionId); + } + + /** + * Send a message to the peer. Message length is limited by + * {@link Characteristics#getMaxServiceSpecificInfoLength()}. + * + * @param peerId peer id obtained from {@link DiscoveryResult#getPeerId()} + * @param message byte array + * @param executor executor + * @param resultCallback result callback + */ + @RequiresPermission(MANAGE_WIFI_NETWORK_SELECTION) + public void sendMessage(int peerId, @NonNull byte[] message, + @NonNull @CallbackExecutor Executor executor, + @NonNull Consumer<Boolean> resultCallback) { + Objects.requireNonNull(executor, "executor must not be null"); + Objects.requireNonNull(resultCallback, "resultCallback must not be null"); + Objects.requireNonNull(message, "message must not be null"); + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + UsdManager usdManager = mUsdManager.get(); + if (usdManager == null) { + Log.w(TAG, "sendMessage is called after the UsdManager has been garbage collected"); + executor.execute(() -> resultCallback.accept(false)); + return; + } + usdManager.sendMessage(mSessionId, peerId, message, executor, resultCallback); + } +} diff --git a/framework/java/android/net/wifi/usd/SubscribeSessionCallback.java b/framework/java/android/net/wifi/usd/SubscribeSessionCallback.java new file mode 100644 index 0000000000..659085de43 --- /dev/null +++ b/framework/java/android/net/wifi/usd/SubscribeSessionCallback.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2024 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 android.net.wifi.usd; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.RequiresApi; +import android.annotation.SystemApi; +import android.net.wifi.flags.Flags; +import android.os.Build; + + +/** + * USD subscribe session callbacks. Should be extended by applications wanting notifications. + * + * @hide + */ +@RequiresApi(Build.VERSION_CODES.BAKLAVA) +@SystemApi +@FlaggedApi(Flags.FLAG_USD) +public class SubscribeSessionCallback extends SessionCallback { + + /** + * Called when a subscribe session cannot be created. + * + * @param reason reason code as defined in {@code FAILURE_XXX} + */ + public void onSubscribeFailed(@FailureCode int reason) { + } + + /** + * Called when a subscribe operation is started successfully. + * + * @param session subscribe session + */ + public void onSubscribeStarted(@NonNull SubscribeSession session) { + } + + /** + * Called when a subscribe operation results in a service discovery. + * + * @param discoveryResult A structure containing information of the discovery session and + * discovered peer + */ + public void onServiceDiscovered(@NonNull DiscoveryResult discoveryResult) { + } +} diff --git a/framework/java/android/net/wifi/usd/UsdManager.java b/framework/java/android/net/wifi/usd/UsdManager.java new file mode 100644 index 0000000000..1e77dfdd6e --- /dev/null +++ b/framework/java/android/net/wifi/usd/UsdManager.java @@ -0,0 +1,458 @@ +/* + * Copyright (C) 2024 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 android.net.wifi.usd; + +import static android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION; + +import android.annotation.CallbackExecutor; +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresApi; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; +import android.annotation.SystemService; +import android.content.Context; +import android.net.wifi.IBooleanListener; +import android.net.wifi.flags.Flags; +import android.net.wifi.util.Environment; +import android.os.Binder; +import android.os.Build; +import android.os.RemoteException; +import android.util.Log; +import android.util.SparseArray; + +import java.util.Objects; +import java.util.concurrent.Executor; +import java.util.function.Consumer; + +/** + * This class provides the APIs for managing Unsynchronized Service Discovery (USD). USD is a + * mechanism that allows devices to discover services offered by other devices without requiring + * prior time and channel synchronization. This feature is especially useful for quickly finding + * services on new devices entering the range. + * + * <p>A publisher device makes its services discoverable, and a subscriber device actively + * or passively searches for those services. Publishers in USD operate continuously, switching + * between single and multiple channel states to advertise their services. When a subscriber + * device receives a relevant service advertisement, it sends a follow-up message to the + * publisher, temporarily pausing the publisher on its current channel to facilitate further + * communication. + * + * <p>Once the discovery of device and service is complete, the subscriber and publisher perform + * further service discovery in which they exchange follow-up messages. The follow-up messages + * carry the service specific information useful for device and service configuration. + * + * <p>Note: This implementation adhere with Wi-Fi Aware Specification Version 4.0. + * @hide + */ +@RequiresApi(Build.VERSION_CODES.BAKLAVA) +@SystemService(Context.WIFI_USD_SERVICE) +@SystemApi +@FlaggedApi(Flags.FLAG_USD) +public class UsdManager { + private final Context mContext; + private final IUsdManager mService; + private static final String TAG = UsdManager.class.getName(); + private static final SparseArray<IBooleanListener> sPublisherAvailabilityListenerMap = + new SparseArray<>(); + private static final SparseArray<IBooleanListener> sSubscriberAvailabilityListenerMap = + new SparseArray<>(); + + /** @hide */ + public UsdManager(@NonNull Context context, @NonNull IUsdManager service) { + mContext = context; + mService = service; + } + + /** @hide */ + public void sendMessage(int sessionId, int peerId, @NonNull byte[] message, + @NonNull Executor executor, @NonNull Consumer<Boolean> resultCallback) { + try { + mService.sendMessage(sessionId, peerId, message, new IBooleanListener.Stub() { + @Override + public void onResult(boolean value) throws RemoteException { + Binder.clearCallingIdentity(); + executor.execute(() -> resultCallback.accept(value)); + } + }); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** @hide */ + public void cancelSubscribe(int sessionId) { + try { + mService.cancelSubscribe(sessionId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** @hide */ + public void cancelPublish(int sessionId) { + try { + mService.cancelPublish(sessionId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** @hide */ + public void updatePublish(int sessionId, byte[] serviceSpecificInfo) { + try { + mService.updatePublish(sessionId, serviceSpecificInfo); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Gets the characteristics of USD: a set of parameters which specify limitations on + * configurations, e.g. maximum service name length. + * + * @return An object specifying the configuration limitation of USD. Return {@code null} if + * USD feature is not supported. + */ + @RequiresPermission(MANAGE_WIFI_NETWORK_SELECTION) + public @Nullable Characteristics getCharacteristics() { + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + try { + return mService.getCharacteristics(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + private static class PublishSessionCallbackProxy extends IPublishSessionCallback.Stub { + private final Executor mExecutor; + private final PublishSessionCallback mPublishSessionCallback; + private final UsdManager mUsdManager; + + private PublishSessionCallbackProxy(UsdManager usdManager, Executor executor, + PublishSessionCallback publishSessionCallback) { + mUsdManager = usdManager; + mExecutor = executor; + mPublishSessionCallback = publishSessionCallback; + } + + @Override + public void onPublishFailed(int reasonCode) throws RemoteException { + Log.d(TAG, "onPublishFailed (reasonCode = " + reasonCode + " )"); + Binder.clearCallingIdentity(); + mExecutor.execute(() -> mPublishSessionCallback.onPublishFailed(reasonCode)); + } + + @Override + public void onPublishStarted(int sessionId) throws RemoteException { + Log.d(TAG, "onPublishStarted ( sessionId = " + sessionId + " )"); + Binder.clearCallingIdentity(); + mExecutor.execute(() -> mPublishSessionCallback.onPublishStarted( + new PublishSession(mUsdManager, sessionId))); + } + + @Override + public void onPublishReplied(int peerId, byte[] ssi, int protoType, boolean isFsdEnabled) + throws RemoteException { + Log.d(TAG, "onPublishReplied ( peerId = " + peerId + ", protoType = " + protoType + + ", isFsdEnabled = " + isFsdEnabled + " )"); + Binder.clearCallingIdentity(); + DiscoveryResult discoveryResult = new DiscoveryResult.Builder(peerId) + .setServiceSpecificInfo(ssi) + .setServiceProtoType(protoType) + .setFsdEnabled(isFsdEnabled) + .build(); + mExecutor.execute(() -> mPublishSessionCallback.onPublishReplied(discoveryResult)); + } + + @Override + public void onPublishSessionTerminated(int reasonCode) throws RemoteException { + Log.d(TAG, "onPublishSessionTerminated ( reasonCode = " + reasonCode + " )"); + Binder.clearCallingIdentity(); + mExecutor.execute(() -> mPublishSessionCallback.onSessionTerminated(reasonCode)); + } + + @Override + public void onMessageReceived(int peerId, byte[] message) throws RemoteException { + Log.d(TAG, "onMessageReceived ( peerId = " + peerId + " )"); + Binder.clearCallingIdentity(); + mExecutor.execute(() -> mPublishSessionCallback.onMessageReceived(peerId, message)); + } + } + + + /** + * Issue a request to the USD service to create a new publish session using the specified + * {@link PublishConfig} configuration. The result of the publish operation are routed to the + * callbacks of {@link PublishSessionCallback}. + * + * <p>Note: Maximum number of publish sessions are limited by + * {@link Characteristics#getMaxNumberOfPublishSessions()}. + * + * @param publishConfig The {@link PublishConfig} specifying the configuration of the + * requested publish session. + * @param executor The Executor on whose thread to execute the callbacks of the + * {@link PublishSessionCallback} + * @param publishSessionCallback A {@link PublishSessionCallback} object to be used for session + * event callback + */ + @RequiresPermission(MANAGE_WIFI_NETWORK_SELECTION) + public void publish(@NonNull PublishConfig publishConfig, + @NonNull @CallbackExecutor Executor executor, + @NonNull PublishSessionCallback publishSessionCallback) { + Objects.requireNonNull(publishConfig, "publishConfig must not be null"); + Objects.requireNonNull(executor, "executor must not be null"); + Objects.requireNonNull(publishSessionCallback, "publishSessionCallback must not be null"); + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + try { + PublishSessionCallbackProxy publishSessionCallbackProxy = + new PublishSessionCallbackProxy(this, executor, publishSessionCallback); + mService.publish(publishConfig, publishSessionCallbackProxy); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + private static class SubscribeSessionCallbackProxy extends ISubscribeSessionCallback.Stub { + private final UsdManager mUsdManager; + private final Executor mExecutor; + private final SubscribeSessionCallback mSubscribeSessionCallback; + + private SubscribeSessionCallbackProxy(UsdManager usdManager, Executor executor, + SubscribeSessionCallback subscribeSessionCallback) { + mUsdManager = usdManager; + mExecutor = executor; + mSubscribeSessionCallback = subscribeSessionCallback; + } + + @Override + public void onSubscribeFailed(int reasonCode) throws RemoteException { + Log.d(TAG, "onSubscribeFailed (reasonCode = " + reasonCode + " )"); + Binder.clearCallingIdentity(); + mExecutor.execute(() -> mSubscribeSessionCallback.onSubscribeFailed(reasonCode)); + } + + @Override + public void onSubscribeStarted(int sessionId) throws RemoteException { + Log.d(TAG, "onSubscribeStarted ( sessionId = " + sessionId + " )"); + Binder.clearCallingIdentity(); + mExecutor.execute(() -> mSubscribeSessionCallback.onSubscribeStarted( + new SubscribeSession(mUsdManager, sessionId))); + } + + @Override + public void onSubscribeDiscovered(int peerId, byte[] ssi, int protoType, + boolean isFsdEnabled) + throws RemoteException { + Log.d(TAG, "onSubscribeDiscovered ( peerId = " + peerId + ", protoType = " + protoType + + ", isFsdEnabled = " + isFsdEnabled + " )"); + Binder.clearCallingIdentity(); + DiscoveryResult discoveryResult = new DiscoveryResult.Builder(peerId) + .setServiceSpecificInfo(ssi) + .setServiceProtoType(protoType) + .setFsdEnabled(isFsdEnabled) + .build(); + mExecutor.execute(() -> mSubscribeSessionCallback.onServiceDiscovered(discoveryResult)); + } + + @Override + public void onSubscribeSessionTerminated(int reasonCode) throws RemoteException { + Log.d(TAG, "onSubscribeSessionTerminated ( reasonCode = " + reasonCode + " )"); + Binder.clearCallingIdentity(); + mExecutor.execute(() -> mSubscribeSessionCallback.onSessionTerminated(reasonCode)); + } + + @Override + public void onMessageReceived(int peerId, byte[] message) throws RemoteException { + Log.d(TAG, "onMessageReceived ( peerId = " + peerId + " )"); + Binder.clearCallingIdentity(); + mExecutor.execute(() -> mSubscribeSessionCallback.onMessageReceived(peerId, message)); + } + } + + + /** + * Issue a request to the USD service to create a new subscribe session using the specified + * {@link SubscribeConfig} configuration. The result of the subscribe operation are + * routed to + * the callbacks of {@link SubscribeSessionCallback}. + * + * <p>Note: Maximum number of subscribe sessions are limited by + * {@link Characteristics#getMaxNumberOfSubscribeSessions()}. + * + * @param subscribeConfig The {@link SubscribeConfig} specifying the + * configuration of the requested subscribe session. + * @param executor The Executor on whose thread to execute the callbacks of + * the {@link SubscribeSessionCallback} + * @param subscribeSessionCallback A {@link SubscribeSessionCallback} object to be used for + * session event callback + */ + @RequiresPermission(MANAGE_WIFI_NETWORK_SELECTION) + public void subscribe(@NonNull SubscribeConfig subscribeConfig, + @NonNull @CallbackExecutor Executor executor, + @NonNull SubscribeSessionCallback subscribeSessionCallback) { + Objects.requireNonNull(subscribeConfig, "subscribeConfig must not be null"); + Objects.requireNonNull(executor, "executor must not be null"); + Objects.requireNonNull(subscribeSessionCallback, + "subscribeSessionCallback must not be null"); + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + try { + SubscribeSessionCallbackProxy subscribeSessionCallbackProxy = + new SubscribeSessionCallbackProxy(this, executor, subscribeSessionCallback); + mService.subscribe(subscribeConfig, subscribeSessionCallbackProxy); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Register for subscriber availability. Concurrent operations such as Station, SoftAP, Wi-Fi + * Aware, Wi-Fi Direct ..etc. impact the current availability of subscriber functionality. + * Current availability status will be returned immediately after registering. + * + * @param executor The executor on which callback will be invoked. + * @param callback An asynchronous callback that will return {@code Boolean} indicating + * whether subscriber is available or not. {@code true} if subscriber is + * available, otherwise unavailable. + */ + @RequiresPermission(MANAGE_WIFI_NETWORK_SELECTION) + public void registerSubscriberStatusListener(@NonNull Executor executor, + @NonNull Consumer<Boolean> callback) { + Objects.requireNonNull(executor, "executor must not be null"); + Objects.requireNonNull(callback, "callback must not be null"); + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + final int callbackHash = System.identityHashCode(callback); + synchronized (sSubscriberAvailabilityListenerMap) { + try { + IBooleanListener listener = new IBooleanListener.Stub() { + @Override + public void onResult(boolean value) throws RemoteException { + Binder.clearCallingIdentity(); + executor.execute(() -> callback.accept(value)); + } + }; + sSubscriberAvailabilityListenerMap.put(callbackHash, listener); + mService.registerSubscriberStatusListener(listener); + } catch (RemoteException e) { + sSubscriberAvailabilityListenerMap.remove(callbackHash); + throw e.rethrowFromSystemServer(); + } + } + } + + /** + * Unregister the callback previously registered for subscriber availability. + * + * @param callback A registered callback. + */ + @RequiresPermission(MANAGE_WIFI_NETWORK_SELECTION) + public void unregisterSubscriberStatusListener(@NonNull Consumer<Boolean> callback) { + Objects.requireNonNull(callback, "callback must not be null"); + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + final int callbackHash = System.identityHashCode(callback); + synchronized (sSubscriberAvailabilityListenerMap) { + if (!sSubscriberAvailabilityListenerMap.contains(callbackHash)) { + Log.w(TAG, "Unknown callback"); + return; + } + try { + mService.unregisterSubscriberStatusListener( + sSubscriberAvailabilityListenerMap.get(callbackHash)); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } finally { + sSubscriberAvailabilityListenerMap.remove(callbackHash); + } + } + } + + /** + * Register for publisher availability. Concurrent operations such as Station, SoftAP, Wi-Fi + * Aware, Wi-Fi Direct ..etc. impact the current availability of publisher functionality. + * Current availability status will be returned immediately after registering. + * + * @param executor The executor on which callback will be invoked. + * @param callback An asynchronous callback that will return {@code Boolean} indicating + * whether publisher is available or not. {@code true} if publisher is + * available, otherwise unavailable. + */ + @RequiresPermission(MANAGE_WIFI_NETWORK_SELECTION) + public void registerPublisherStatusListener(@NonNull Executor executor, + @NonNull Consumer<Boolean> callback) { + Objects.requireNonNull(executor, "executor must not be null"); + Objects.requireNonNull(callback, "callback must not be null"); + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + final int callbackHash = System.identityHashCode(callback); + synchronized (sPublisherAvailabilityListenerMap) { + try { + IBooleanListener listener = new IBooleanListener.Stub() { + @Override + public void onResult(boolean value) throws RemoteException { + Binder.clearCallingIdentity(); + executor.execute(() -> callback.accept(value)); + } + }; + sPublisherAvailabilityListenerMap.put(callbackHash, listener); + mService.registerPublisherStatusListener(listener); + } catch (RemoteException e) { + sPublisherAvailabilityListenerMap.remove(callbackHash); + throw e.rethrowFromSystemServer(); + } + } + } + + /** + * Unregister the callback previously registered for publisher availability. + * + * @param callback A registered callback. + */ + @RequiresPermission(MANAGE_WIFI_NETWORK_SELECTION) + public void unregisterPublisherStatusListener(@NonNull Consumer<Boolean> callback) { + Objects.requireNonNull(callback, "callback must not be null"); + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException(); + } + final int callbackHash = System.identityHashCode(callback); + synchronized (sPublisherAvailabilityListenerMap) { + if (!sPublisherAvailabilityListenerMap.contains(callbackHash)) { + Log.w(TAG, "Unknown callback"); + return; + } + try { + mService.unregisterPublisherStatusListener( + sPublisherAvailabilityListenerMap.get(callbackHash)); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } finally { + sPublisherAvailabilityListenerMap.remove(callbackHash); + } + } + } +} diff --git a/framework/java/android/net/wifi/util/Environment.java b/framework/java/android/net/wifi/util/Environment.java index f2e8266ed9..f98265845a 100644 --- a/framework/java/android/net/wifi/util/Environment.java +++ b/framework/java/android/net/wifi/util/Environment.java @@ -90,4 +90,13 @@ public class Environment { } return vndkApiLevel > apiLevel; } + + /** + * Check if the device has a SDK >= 36 + * @return True if the SDK >= 36 + */ + public static boolean isSdkAtLeastB() { + return Build.VERSION.CODENAME.equals("Baklava") + || Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA; + } } diff --git a/framework/java/android/net/wifi/util/ScanResultUtil.java b/framework/java/android/net/wifi/util/ScanResultUtil.java index ca8d62b3d3..5ee576ce1a 100644 --- a/framework/java/android/net/wifi/util/ScanResultUtil.java +++ b/framework/java/android/net/wifi/util/ScanResultUtil.java @@ -26,6 +26,8 @@ import android.net.wifi.SecurityParams; import android.net.wifi.WifiConfiguration; import android.util.Log; +import androidx.annotation.Keep; + import com.android.internal.annotations.VisibleForTesting; import java.io.PrintWriter; @@ -314,6 +316,7 @@ public class ScanResultUtil { /** * Creates a network configuration object using the provided |scanResult|. */ + @Keep public static @Nullable WifiConfiguration createNetworkFromScanResult( @NonNull ScanResult scanResult) { WifiConfiguration config = new WifiConfiguration(); diff --git a/framework/java/android/net/wifi/util/WifiResourceCache.java b/framework/java/android/net/wifi/util/WifiResourceCache.java index 1d987d3fb4..88d57f040e 100644 --- a/framework/java/android/net/wifi/util/WifiResourceCache.java +++ b/framework/java/android/net/wifi/util/WifiResourceCache.java @@ -67,11 +67,13 @@ public class WifiResourceCache { String resourceName = mContext.getResources().getResourceEntryName(resourceId); if (mResourceNameMap.containsKey(resourceName)) { int tempId = mResourceNameMap.get(resourceName); - boolean value = mBooleanResourceMap.get(tempId); - mBooleanResourceMap.put(resourceId, value); - mBooleanResourceMap.remove(tempId); - mResourceNameMap.put(resourceName, resourceId); - return value; + if (mBooleanResourceMap.containsKey(tempId)) { + boolean value = mBooleanResourceMap.get(tempId); + mBooleanResourceMap.put(resourceId, value); + mBooleanResourceMap.remove(tempId); + mResourceNameMap.put(resourceName, resourceId); + return value; + } } mResourceNameMap.put(resourceName, resourceId); return mBooleanResourceMap.computeIfAbsent(resourceId, @@ -91,11 +93,13 @@ public class WifiResourceCache { String resourceName = mContext.getResources().getResourceEntryName(resourceId); if (mResourceNameMap.containsKey(resourceName)) { int tempId = mResourceNameMap.get(resourceName); - int value = mIntegerResourceMap.get(tempId); - mIntegerResourceMap.put(resourceId, value); - mIntegerResourceMap.remove(tempId); - mResourceNameMap.put(resourceName, resourceId); - return value; + if (mIntegerResourceMap.containsKey(tempId)) { + int value = mIntegerResourceMap.get(tempId); + mIntegerResourceMap.put(resourceId, value); + mIntegerResourceMap.remove(tempId); + mResourceNameMap.put(resourceName, resourceId); + return value; + } } mResourceNameMap.put(resourceName, resourceId); return mIntegerResourceMap.computeIfAbsent(resourceId, @@ -115,11 +119,13 @@ public class WifiResourceCache { String resourceName = mContext.getResources().getResourceEntryName(resourceId); if (mResourceNameMap.containsKey(resourceName)) { int tempId = mResourceNameMap.get(resourceName); - String value = mStringResourceMap.get(tempId); - mStringResourceMap.put(resourceId, value); - mStringResourceMap.remove(tempId); - mResourceNameMap.put(resourceName, resourceId); - return value; + if (mStringResourceMap.containsKey(tempId)) { + String value = mStringResourceMap.get(tempId); + mStringResourceMap.put(resourceId, value); + mStringResourceMap.remove(tempId); + mResourceNameMap.put(resourceName, resourceId); + return value; + } } mResourceNameMap.put(resourceName, resourceId); return mStringResourceMap.computeIfAbsent(resourceId, @@ -139,11 +145,13 @@ public class WifiResourceCache { String resourceName = mContext.getResources().getResourceEntryName(resourceId); if (mResourceNameMap.containsKey(resourceName)) { int tempId = mResourceNameMap.get(resourceName); - String[] value = mStringArrayResourceMap.get(tempId); - mStringArrayResourceMap.put(resourceId, value); - mStringArrayResourceMap.remove(tempId); - mResourceNameMap.put(resourceName, resourceId); - return value; + if (mStringArrayResourceMap.containsKey(tempId)) { + String[] value = mStringArrayResourceMap.get(tempId); + mStringArrayResourceMap.put(resourceId, value); + mStringArrayResourceMap.remove(tempId); + mResourceNameMap.put(resourceName, resourceId); + return value; + } } mResourceNameMap.put(resourceName, resourceId); return mStringArrayResourceMap.computeIfAbsent(resourceId, @@ -163,11 +171,13 @@ public class WifiResourceCache { String resourceName = mContext.getResources().getResourceEntryName(resourceId); if (mResourceNameMap.containsKey(resourceName)) { int tempId = mResourceNameMap.get(resourceName); - int[] value = mIntArrayResourceMap.get(tempId); - mIntArrayResourceMap.put(resourceId, value); - mIntArrayResourceMap.remove(tempId); - mResourceNameMap.put(resourceName, resourceId); - return value; + if (mIntArrayResourceMap.containsKey(tempId)) { + int[] value = mIntArrayResourceMap.get(tempId); + mIntArrayResourceMap.put(resourceId, value); + mIntArrayResourceMap.remove(tempId); + mResourceNameMap.put(resourceName, resourceId); + return value; + } } mResourceNameMap.put(resourceName, resourceId); return mIntArrayResourceMap.computeIfAbsent(resourceId, @@ -319,4 +329,12 @@ public class WifiResourceCache { mResourceNameMap.clear(); } } + + /** + * Handle the locale change to apply the translation + */ + public void handleLocaleChange() { + mStringResourceMap.clear(); + mStringArrayResourceMap.clear(); + } } diff --git a/framework/tests/src/android/net/wifi/BlockingOptionTest.java b/framework/tests/src/android/net/wifi/BlockingOptionTest.java new file mode 100644 index 0000000000..99f0e54f51 --- /dev/null +++ b/framework/tests/src/android/net/wifi/BlockingOptionTest.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2024 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 android.net.wifi; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +import android.os.Parcel; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; + +@SmallTest +public class BlockingOptionTest { + + @Test + public void testBuilderWithValidInput() { + BlockingOption option = new BlockingOption.Builder(100) + .setBlockingBssidOnly(true) + .build(); + assertEquals(100, option.getBlockingTimeSeconds()); + assertTrue(option.isBlockingBssidOnly()); + } + + @Test + public void testBuilderWithInValidInput() { + assertThrows(IllegalArgumentException.class, () -> { + new BlockingOption.Builder(0) + .setBlockingBssidOnly(true) + .build(); + }); + assertThrows(IllegalArgumentException.class, () -> { + new BlockingOption.Builder(1000000) + .setBlockingBssidOnly(true) + .build(); + }); + } + + @Test + public void testParcel() { + BlockingOption option = new BlockingOption.Builder(100) + .setBlockingBssidOnly(true) + .build(); + Parcel parcelW = Parcel.obtain(); + option.writeToParcel(parcelW, 0); + byte[] bytes = parcelW.marshall(); + parcelW.recycle(); + + Parcel parcelR = Parcel.obtain(); + parcelR.unmarshall(bytes, 0, bytes.length); + parcelR.setDataPosition(0); + BlockingOption parcelOption = BlockingOption.CREATOR.createFromParcel(parcelR); + assertEquals(option, parcelOption); + } +} diff --git a/framework/tests/src/android/net/wifi/SoftApConfigurationTest.java b/framework/tests/src/android/net/wifi/SoftApConfigurationTest.java index b817177e91..7e4f5ea310 100644 --- a/framework/tests/src/android/net/wifi/SoftApConfigurationTest.java +++ b/framework/tests/src/android/net/wifi/SoftApConfigurationTest.java @@ -22,6 +22,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThrows; @@ -32,6 +33,7 @@ import static org.junit.Assume.assumeTrue; import static org.mockito.Mockito.mock; import android.net.MacAddress; +import android.net.wifi.util.Environment; import android.os.Parcel; import android.os.PersistableBundle; import android.util.SparseIntArray; @@ -131,6 +133,9 @@ public class SoftApConfigurationTest { .isEqualTo(SoftApConfiguration.RANDOMIZATION_PERSISTENT); } } + if (Environment.isSdkAtLeastB()) { + assertFalse(original.isClientIsolationEnabled()); + } SoftApConfiguration unparceled = parcelUnparcel(original); assertThat(unparceled).isNotSameInstanceAs(original); @@ -229,7 +234,9 @@ public class SoftApConfigurationTest { originalBuilder.setBridgedModeOpportunisticShutdownTimeoutMillis(300_000); originalBuilder.setVendorElements(TEST_TWO_VENDOR_ELEMENTS); } - + if (Environment.isSdkAtLeastB()) { + originalBuilder.setClientIsolationEnabled(true); + } SoftApConfiguration original = originalBuilder.build(); assertThat(original.getPassphrase()).isEqualTo("secretsecret"); assertThat(original.getSecurityType()).isEqualTo( @@ -261,6 +268,9 @@ public class SoftApConfigurationTest { assertThat(original.getVendorElements()) .isEqualTo(TEST_TWO_VENDOR_ELEMENTS); } + if (Environment.isSdkAtLeastB()) { + assertTrue(original.isClientIsolationEnabled()); + } SoftApConfiguration unparceled = parcelUnparcel(original); assertThat(unparceled).isNotSameInstanceAs(original); diff --git a/framework/tests/src/android/net/wifi/SoftApInfoTest.java b/framework/tests/src/android/net/wifi/SoftApInfoTest.java index b2f9e35844..4a084923fb 100644 --- a/framework/tests/src/android/net/wifi/SoftApInfoTest.java +++ b/framework/tests/src/android/net/wifi/SoftApInfoTest.java @@ -41,6 +41,7 @@ public class SoftApInfoTest { private static final int TEST_BANDWIDTH = SoftApInfo.CHANNEL_WIDTH_20MHZ; private static final int TEST_WIFI_STANDARD = ScanResult.WIFI_STANDARD_LEGACY; private static final MacAddress TEST_AP_MAC = MacAddress.fromString("aa:bb:cc:dd:ee:ff"); + private static final MacAddress TEST_MLD_MAC = MacAddress.fromString("11:22:33:44:55:66"); private static final long TEST_SHUTDOWN_TIMEOUT_MILLIS = 100_000; private static final List<OuiKeyedData> TEST_VENDOR_DATA = OuiKeyedDataUtil.createTestOuiKeyedDataList(5); @@ -56,6 +57,7 @@ public class SoftApInfoTest { info.setBssid(TEST_AP_MAC); info.setWifiStandard(TEST_WIFI_STANDARD); info.setApInstanceIdentifier(TEST_AP_INSTANCE); + info.setMldAddress(TEST_MLD_MAC); if (SdkLevel.isAtLeastV()) { info.setVendorData(TEST_VENDOR_DATA); } @@ -77,6 +79,7 @@ public class SoftApInfoTest { info.setBssid(TEST_AP_MAC); info.setWifiStandard(TEST_WIFI_STANDARD); info.setApInstanceIdentifier(TEST_AP_INSTANCE); + info.setMldAddress(TEST_MLD_MAC); if (SdkLevel.isAtLeastV()) { info.setVendorData(TEST_VENDOR_DATA); } @@ -112,6 +115,7 @@ public class SoftApInfoTest { if (SdkLevel.isAtLeastV()) { assertNotNull(info.getVendorData()); } + assertEquals(info.getMldAddress(), null); } /** @@ -126,6 +130,7 @@ public class SoftApInfoTest { info.setWifiStandard(TEST_WIFI_STANDARD); info.setApInstanceIdentifier(TEST_AP_INSTANCE); info.setAutoShutdownTimeoutMillis(TEST_SHUTDOWN_TIMEOUT_MILLIS); + info.setMldAddress(TEST_MLD_MAC); assertEquals(info.getFrequency(), TEST_FREQUENCY); assertEquals(info.getBandwidth(), TEST_BANDWIDTH); if (SdkLevel.isAtLeastS()) { @@ -138,6 +143,7 @@ public class SoftApInfoTest { info.setVendorData(TEST_VENDOR_DATA); assertTrue(TEST_VENDOR_DATA.equals(info.getVendorData())); } + assertEquals(info.getMldAddress(), TEST_MLD_MAC); } } diff --git a/framework/tests/src/android/net/wifi/WifiClientTest.java b/framework/tests/src/android/net/wifi/WifiClientTest.java index 704656320a..2fa0d3c04c 100644 --- a/framework/tests/src/android/net/wifi/WifiClientTest.java +++ b/framework/tests/src/android/net/wifi/WifiClientTest.java @@ -34,17 +34,19 @@ import org.junit.Test; @SmallTest public class WifiClientTest { private static final String INTERFACE_NAME = "wlan0"; + private static final String INTERFACE_NAME_1 = "wlan1"; private static final String MAC_ADDRESS_STRING = "00:0a:95:9d:68:16"; private static final MacAddress MAC_ADDRESS = MacAddress.fromString(MAC_ADDRESS_STRING); + private static final int DISCONNECT_REASON = DeauthenticationReasonCode.REASON_DEAUTH_LEAVING; /** * Verify parcel write/read with WifiClient. */ @Test public void testWifiClientParcelWriteRead() throws Exception { - WifiClient writeWifiClient = new WifiClient(MAC_ADDRESS, INTERFACE_NAME); + WifiClient writeWifiClient = new WifiClient(MAC_ADDRESS, INTERFACE_NAME, DISCONNECT_REASON); - assertParcelSane(writeWifiClient, 2); + assertParcelSane(writeWifiClient, 3); } /** @@ -52,19 +54,20 @@ public class WifiClientTest { */ @Test public void testWifiClientEquals() throws Exception { - WifiClient writeWifiClient = new WifiClient(MAC_ADDRESS, INTERFACE_NAME); - WifiClient writeWifiClientEquals = new WifiClient(MAC_ADDRESS, INTERFACE_NAME); + WifiClient writeWifiClient = new WifiClient(MAC_ADDRESS, INTERFACE_NAME, DISCONNECT_REASON); + WifiClient writeWifiClientEquals = new WifiClient(MAC_ADDRESS, INTERFACE_NAME, + DISCONNECT_REASON); assertEquals(writeWifiClient, writeWifiClientEquals); assertEquals(writeWifiClient.hashCode(), writeWifiClientEquals.hashCode()); - assertFieldCountEquals(2, WifiClient.class); + assertFieldCountEquals(3, WifiClient.class); } /** - * Verify not-equals with WifiClient. + * Verify not-equals for 2 WifiClients with different mac address. */ @Test - public void testWifiClientNotEquals() throws Exception { + public void testWifiClientEqualsFailsWhenMacAddressIsDifferent() throws Exception { final MacAddress macAddressNotEquals = MacAddress.fromString("00:00:00:00:00:00"); WifiClient writeWifiClient = new WifiClient(MAC_ADDRESS, INTERFACE_NAME); WifiClient writeWifiClientNotEquals = new WifiClient(macAddressNotEquals, INTERFACE_NAME); @@ -72,4 +75,51 @@ public class WifiClientTest { assertNotEquals(writeWifiClient, writeWifiClientNotEquals); assertNotEquals(writeWifiClient.hashCode(), writeWifiClientNotEquals.hashCode()); } + + /** + * Verify not-equals for 2 WifiClients with different interface name. + */ + @Test + public void testWifiClientEqualsFailsWhenInstanceIsDifferent() throws Exception { + WifiClient writeWifiClient = new WifiClient(MAC_ADDRESS, INTERFACE_NAME); + WifiClient writeWifiClientNotEquals = new WifiClient(MAC_ADDRESS, INTERFACE_NAME_1); + + assertNotEquals(writeWifiClient, writeWifiClientNotEquals); + assertNotEquals(writeWifiClient.hashCode(), writeWifiClientNotEquals.hashCode()); + } + + /** + * Verify not-equals for 2 WifiClients with different disconnect reason. + */ + @Test + public void testWifiClientEqualsFailsWhenDisconnectReasonIsDifferent() throws Exception { + WifiClient writeWifiClient = new WifiClient(MAC_ADDRESS, INTERFACE_NAME, DISCONNECT_REASON); + WifiClient writeWifiClientNotEquals = new WifiClient(MAC_ADDRESS, INTERFACE_NAME, + DeauthenticationReasonCode.REASON_AKMP_NOT_VALID); + + assertNotEquals(writeWifiClient, writeWifiClientNotEquals); + assertNotEquals(writeWifiClient.hashCode(), writeWifiClientNotEquals.hashCode()); + } + + /** + * Verify that getDisconnectReason() returns REASON_UNKNOWN as the default value. + */ + @Test + public void testWifiClientGetDefaultDisconnectReason() throws Exception { + WifiClient wifiClient = new WifiClient(MAC_ADDRESS, INTERFACE_NAME); + assertEquals(wifiClient.getDisconnectReason(), DeauthenticationReasonCode.REASON_UNKNOWN); + } + + /** + * Verify that all getter methods in WifiClient (getMacAddress(), + * getApInstanceIdentifier(), getDisconnectReason()) return the + * expected values when a WifiClient object is created with specific data. + */ + @Test + public void testWifiClientGetMethods() throws Exception { + WifiClient wifiClient = new WifiClient(MAC_ADDRESS, INTERFACE_NAME, DISCONNECT_REASON); + assertEquals(wifiClient.getMacAddress(), MAC_ADDRESS); + assertEquals(wifiClient.getApInstanceIdentifier(), INTERFACE_NAME); + assertEquals(wifiClient.getDisconnectReason(), DISCONNECT_REASON); + } } diff --git a/framework/tests/src/android/net/wifi/WifiManagerTest.java b/framework/tests/src/android/net/wifi/WifiManagerTest.java index 54c16f36ba..2c297a9822 100644 --- a/framework/tests/src/android/net/wifi/WifiManagerTest.java +++ b/framework/tests/src/android/net/wifi/WifiManagerTest.java @@ -53,10 +53,7 @@ import static android.net.wifi.WifiManager.WIFI_FEATURE_DPP_AKM; import static android.net.wifi.WifiManager.WIFI_FEATURE_DPP_ENROLLEE_RESPONDER; import static android.net.wifi.WifiManager.WIFI_FEATURE_DUAL_BAND_SIMULTANEOUS; import static android.net.wifi.WifiManager.WIFI_FEATURE_OWE; -import static android.net.wifi.WifiManager.WIFI_FEATURE_P2P; -import static android.net.wifi.WifiManager.WIFI_FEATURE_PASSPOINT; import static android.net.wifi.WifiManager.WIFI_FEATURE_PASSPOINT_TERMS_AND_CONDITIONS; -import static android.net.wifi.WifiManager.WIFI_FEATURE_SCANNER; import static android.net.wifi.WifiManager.WIFI_FEATURE_T2LM_NEGOTIATION; import static android.net.wifi.WifiManager.WIFI_FEATURE_TRUST_ON_FIRST_USE; import static android.net.wifi.WifiManager.WIFI_FEATURE_WEP; @@ -121,8 +118,11 @@ import android.net.wifi.WifiManager.TrafficStateCallback; import android.net.wifi.WifiManager.WifiConnectedNetworkScorer; import android.net.wifi.WifiUsabilityStatsEntry.ContentionTimeStats; import android.net.wifi.WifiUsabilityStatsEntry.LinkStats; +import android.net.wifi.WifiUsabilityStatsEntry.PacketStats; +import android.net.wifi.WifiUsabilityStatsEntry.PeerInfo; import android.net.wifi.WifiUsabilityStatsEntry.RadioStats; import android.net.wifi.WifiUsabilityStatsEntry.RateStats; +import android.net.wifi.WifiUsabilityStatsEntry.ScanResultWithSameFreq; import android.net.wifi.twt.TwtRequest; import android.net.wifi.twt.TwtSessionCallback; import android.os.Build; @@ -134,6 +134,7 @@ import android.os.connectivity.WifiActivityEnergyInfo; import android.os.test.TestLooper; import android.util.ArraySet; import android.util.SparseArray; +import android.util.SparseIntArray; import androidx.test.filters.SmallTest; @@ -141,6 +142,8 @@ import com.android.modules.utils.HandlerExecutor; import com.android.modules.utils.build.SdkLevel; import com.android.wifi.x.com.android.modules.utils.ParceledListSlice; +import com.google.common.collect.ImmutableList; + import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; @@ -190,6 +193,8 @@ public class WifiManagerTest { private static final String TEST_SSID = "\"Test WiFi Networks\""; private static final byte[] TEST_OUI = new byte[]{0x01, 0x02, 0x03}; private static final int TEST_LINK_LAYER_STATS_POLLING_INTERVAL_MS = 1000; + private static final int TEST_DISCONNECT_REASON = + DeauthenticationReasonCode.REASON_AUTHORIZED_ACCESS_LIMIT_REACHED; private static final TetheringManager.TetheringRequest TEST_TETHERING_REQUEST = new TetheringManager.TetheringRequest.Builder(TetheringManager.TETHERING_WIFI).build(); @@ -221,6 +226,7 @@ public class WifiManagerTest { private WifiNetworkSuggestion mWifiNetworkSuggestion; private ScanResultsCallback mScanResultsCallback; private CoexCallback mCoexCallback; + private WifiManager.WifiStateChangedListener mWifiStateChangedListener; private SubsystemRestartTrackingCallback mRestartCallback; private int mRestartCallbackMethodRun = 0; // 1: restarting, 2: restarted private WifiActivityEnergyInfo mWifiActivityEnergyInfo; @@ -310,7 +316,8 @@ public class WifiManagerTest { mApplicationInfo.targetSdkVersion = Build.VERSION_CODES.Q; when(mContext.getApplicationInfo()).thenReturn(mApplicationInfo); when(mContext.getOpPackageName()).thenReturn(TEST_PACKAGE_NAME); - mWifiManager = new WifiManager(mContext, mWifiService, mLooper.getLooper()); + when(mContext.getMainLooper()).thenReturn(mLooper.getLooper()); + mWifiManager = new WifiManager(mContext, mWifiService); verify(mWifiService).getVerboseLoggingLevel(); mWifiNetworkSuggestion = new WifiNetworkSuggestion(); mScanResultsCallback = new ScanResultsCallback() { @@ -319,6 +326,7 @@ public class WifiManagerTest { mRunnable.run(); } }; + mWifiStateChangedListener = () -> mRunnable.run(); if (SdkLevel.isAtLeastS()) { mCoexCallback = new CoexCallback() { @Override @@ -597,7 +605,8 @@ public class WifiManagerTest { SoftApConfiguration softApConfig = generatorTestSoftApConfig(); TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString(), - nullable(String.class), eq(null), any())).thenReturn(REQUEST_REGISTERED); + nullable(String.class), eq(null), any(), anyBoolean())) + .thenReturn(REQUEST_REGISTERED); mWifiManager.startLocalOnlyHotspot(callback, mHandler); callback.onStarted(mWifiManager.new LocalOnlyHotspotReservation(softApConfig)); @@ -619,7 +628,8 @@ public class WifiManagerTest { SoftApConfiguration softApConfig = generatorTestSoftApConfig(); TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString(), - nullable(String.class), eq(null), any())).thenReturn(REQUEST_REGISTERED); + nullable(String.class), eq(null), any(), anyBoolean())) + .thenReturn(REQUEST_REGISTERED); mWifiManager.startLocalOnlyHotspot(callback, mHandler); callback.onStarted(mWifiManager.new LocalOnlyHotspotReservation(softApConfig)); @@ -797,7 +807,7 @@ public class WifiManagerTest { mWifiManager.startLocalOnlyHotspot(callback, mHandler); verify(mWifiService).startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), - anyString(), nullable(String.class), eq(null), any()); + anyString(), nullable(String.class), eq(null), any(), eq(false)); } /** @@ -809,7 +819,7 @@ public class WifiManagerTest { TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); doThrow(new SecurityException()).when(mWifiService).startLocalOnlyHotspot( any(ILocalOnlyHotspotCallback.class), anyString(), nullable(String.class), - eq(null), any()); + eq(null), any(), anyBoolean()); mWifiManager.startLocalOnlyHotspot(callback, mHandler); } @@ -822,7 +832,7 @@ public class WifiManagerTest { TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); doThrow(new IllegalStateException()).when(mWifiService).startLocalOnlyHotspot( any(ILocalOnlyHotspotCallback.class), anyString(), nullable(String.class), - eq(null), any()); + eq(null), any(), anyBoolean()); mWifiManager.startLocalOnlyHotspot(callback, mHandler); } @@ -833,7 +843,8 @@ public class WifiManagerTest { public void testCorrectLooperIsUsedForHandler() throws Exception { TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString(), - nullable(String.class), eq(null), any())).thenReturn(ERROR_INCOMPATIBLE_MODE); + nullable(String.class), eq(null), any(), anyBoolean())) + .thenReturn(ERROR_INCOMPATIBLE_MODE); mWifiManager.startLocalOnlyHotspot(callback, mHandler); mLooper.dispatchAll(); assertEquals(ERROR_INCOMPATIBLE_MODE, callback.mFailureReason); @@ -852,7 +863,8 @@ public class WifiManagerTest { when(mContext.getMainExecutor()).thenReturn(altLooper.getNewExecutor()); TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString(), - nullable(String.class), eq(null), any())).thenReturn(ERROR_INCOMPATIBLE_MODE); + nullable(String.class), eq(null), any(), anyBoolean())) + .thenReturn(ERROR_INCOMPATIBLE_MODE); mWifiManager.startLocalOnlyHotspot(callback, null); altLooper.dispatchAll(); assertEquals(ERROR_INCOMPATIBLE_MODE, callback.mFailureReason); @@ -873,7 +885,8 @@ public class WifiManagerTest { ArgumentCaptor<ILocalOnlyHotspotCallback> internalCallback = ArgumentCaptor.forClass(ILocalOnlyHotspotCallback.class); when(mWifiService.startLocalOnlyHotspot(internalCallback.capture(), anyString(), - nullable(String.class), eq(null), any())).thenReturn(REQUEST_REGISTERED); + nullable(String.class), eq(null), any(), anyBoolean())) + .thenReturn(REQUEST_REGISTERED); mWifiManager.startLocalOnlyHotspot(callback, callbackHandler); callbackLooper.dispatchAll(); mLooper.dispatchAll(); @@ -905,7 +918,8 @@ public class WifiManagerTest { ArgumentCaptor<ILocalOnlyHotspotCallback> internalCallback = ArgumentCaptor.forClass(ILocalOnlyHotspotCallback.class); when(mWifiService.startLocalOnlyHotspot(internalCallback.capture(), anyString(), - nullable(String.class), eq(null), any())).thenReturn(REQUEST_REGISTERED); + nullable(String.class), eq(null), any(), anyBoolean())) + .thenReturn(REQUEST_REGISTERED); mWifiManager.startLocalOnlyHotspot(callback, callbackHandler); callbackLooper.dispatchAll(); mLooper.dispatchAll(); @@ -932,7 +946,8 @@ public class WifiManagerTest { ArgumentCaptor<ILocalOnlyHotspotCallback> internalCallback = ArgumentCaptor.forClass(ILocalOnlyHotspotCallback.class); when(mWifiService.startLocalOnlyHotspot(internalCallback.capture(), anyString(), - nullable(String.class), eq(null), any())).thenReturn(REQUEST_REGISTERED); + nullable(String.class), eq(null), any(), anyBoolean())) + .thenReturn(REQUEST_REGISTERED); mWifiManager.startLocalOnlyHotspot(callback, callbackHandler); callbackLooper.dispatchAll(); mLooper.dispatchAll(); @@ -957,7 +972,8 @@ public class WifiManagerTest { ArgumentCaptor<ILocalOnlyHotspotCallback> internalCallback = ArgumentCaptor.forClass(ILocalOnlyHotspotCallback.class); when(mWifiService.startLocalOnlyHotspot(internalCallback.capture(), anyString(), - nullable(String.class), eq(null), any())).thenReturn(REQUEST_REGISTERED); + nullable(String.class), eq(null), any(), anyBoolean())) + .thenReturn(REQUEST_REGISTERED); mWifiManager.startLocalOnlyHotspot(callback, callbackHandler); callbackLooper.dispatchAll(); mLooper.dispatchAll(); @@ -980,7 +996,8 @@ public class WifiManagerTest { ArgumentCaptor<ILocalOnlyHotspotCallback> internalCallback = ArgumentCaptor.forClass(ILocalOnlyHotspotCallback.class); when(mWifiService.startLocalOnlyHotspot(internalCallback.capture(), anyString(), - nullable(String.class), eq(null), any())).thenReturn(REQUEST_REGISTERED); + nullable(String.class), eq(null), any(), anyBoolean())) + .thenReturn(REQUEST_REGISTERED); mWifiManager.startLocalOnlyHotspot(callback, callbackHandler); callbackLooper.dispatchAll(); mLooper.dispatchAll(); @@ -999,7 +1016,8 @@ public class WifiManagerTest { public void testLocalOnlyHotspotCallbackFullOnIncompatibleMode() throws Exception { TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString(), - nullable(String.class), eq(null), any())).thenReturn(ERROR_INCOMPATIBLE_MODE); + nullable(String.class), eq(null), any(), anyBoolean())) + .thenReturn(ERROR_INCOMPATIBLE_MODE); mWifiManager.startLocalOnlyHotspot(callback, mHandler); mLooper.dispatchAll(); assertEquals(ERROR_INCOMPATIBLE_MODE, callback.mFailureReason); @@ -1015,7 +1033,8 @@ public class WifiManagerTest { public void testLocalOnlyHotspotCallbackFullOnTetheringDisallowed() throws Exception { TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString(), - nullable(String.class), eq(null), any())).thenReturn(ERROR_TETHERING_DISALLOWED); + nullable(String.class), eq(null), any(), anyBoolean())) + .thenReturn(ERROR_TETHERING_DISALLOWED); mWifiManager.startLocalOnlyHotspot(callback, mHandler); mLooper.dispatchAll(); assertEquals(ERROR_TETHERING_DISALLOWED, callback.mFailureReason); @@ -1033,7 +1052,7 @@ public class WifiManagerTest { TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); doThrow(new SecurityException()).when(mWifiService).startLocalOnlyHotspot( any(ILocalOnlyHotspotCallback.class), anyString(), nullable(String.class), - eq(null), any()); + eq(null), any(), anyBoolean()); try { mWifiManager.startLocalOnlyHotspot(callback, mHandler); } catch (SecurityException e) { @@ -1054,7 +1073,8 @@ public class WifiManagerTest { public void testLocalOnlyHotspotCallbackFullOnNoChannelError() throws Exception { TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString(), - nullable(String.class), eq(null), any())).thenReturn(REQUEST_REGISTERED); + nullable(String.class), eq(null), any(), anyBoolean())) + .thenReturn(REQUEST_REGISTERED); mWifiManager.startLocalOnlyHotspot(callback, mHandler); mLooper.dispatchAll(); //assertEquals(ERROR_NO_CHANNEL, callback.mFailureReason); @@ -1070,7 +1090,8 @@ public class WifiManagerTest { public void testCancelLocalOnlyHotspotRequestCallsStopOnWifiService() throws Exception { TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString(), - nullable(String.class), eq(null), any())).thenReturn(REQUEST_REGISTERED); + nullable(String.class), eq(null), any(), anyBoolean())) + .thenReturn(REQUEST_REGISTERED); mWifiManager.startLocalOnlyHotspot(callback, mHandler); mWifiManager.cancelLocalOnlyHotspotRequest(); verify(mWifiService).stopLocalOnlyHotspot(); @@ -1092,7 +1113,8 @@ public class WifiManagerTest { public void testCallbackAfterLocalOnlyHotspotWasCancelled() throws Exception { TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString(), - nullable(String.class), eq(null), any())).thenReturn(REQUEST_REGISTERED); + nullable(String.class), eq(null), any(), anyBoolean())) + .thenReturn(REQUEST_REGISTERED); mWifiManager.startLocalOnlyHotspot(callback, mHandler); mWifiManager.cancelLocalOnlyHotspotRequest(); verify(mWifiService).stopLocalOnlyHotspot(); @@ -1111,7 +1133,8 @@ public class WifiManagerTest { public void testCancelAfterLocalOnlyHotspotCallbackTriggered() throws Exception { TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); when(mWifiService.startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), anyString(), - nullable(String.class), eq(null), any())).thenReturn(ERROR_INCOMPATIBLE_MODE); + nullable(String.class), eq(null), any(), anyBoolean())) + .thenReturn(ERROR_INCOMPATIBLE_MODE); mWifiManager.startLocalOnlyHotspot(callback, mHandler); mLooper.dispatchAll(); assertEquals(ERROR_INCOMPATIBLE_MODE, callback.mFailureReason); @@ -1130,7 +1153,7 @@ public class WifiManagerTest { TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); mWifiManager.startLocalOnlyHotspot(customConfig, mExecutor, callback); verify(mWifiService).startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), - anyString(), nullable(String.class), eq(customConfig), any()); + anyString(), nullable(String.class), eq(customConfig), any(), eq(true)); } /** @@ -1813,6 +1836,25 @@ public class WifiManagerTest { } /* + * Verify client-provided callback is being called through callback proxy. + */ + @Test + public void softApCallbackProxyCallsOnClientsDisconnected() throws Exception { + WifiClient testWifiClient = new WifiClient(MacAddress.fromString("22:33:44:55:66:77"), + TEST_AP_INSTANCES[0], TEST_DISCONNECT_REASON); + ArgumentCaptor<ISoftApCallback.Stub> callbackCaptor = + ArgumentCaptor.forClass(ISoftApCallback.Stub.class); + mWifiManager.registerSoftApCallback(new HandlerExecutor(mHandler), mSoftApCallback); + verify(mWifiService).registerSoftApCallback(callbackCaptor.capture()); + + callbackCaptor.getValue().onClientsDisconnected(mTestApInfo1, + ImmutableList.of(testWifiClient)); + mLooper.dispatchAll(); + verify(mSoftApCallback).onClientsDisconnected(mTestApInfo1, + ImmutableList.of(testWifiClient)); + } + + /* * Verify client-provided callback is being called through callback proxy on multiple events */ @Test @@ -2508,27 +2550,40 @@ public class WifiManagerTest { contentionTimeStats[1] = new ContentionTimeStats(5, 6, 7, 8); contentionTimeStats[2] = new ContentionTimeStats(9, 10, 11, 12); contentionTimeStats[3] = new ContentionTimeStats(13, 14, 15, 16); + PacketStats[] packetStats = new PacketStats[4]; + packetStats[0] = new PacketStats(1, 2, 3, 4); + packetStats[1] = new PacketStats(5, 6, 7, 8); + packetStats[2] = new PacketStats(9, 10, 11, 12); + packetStats[3] = new PacketStats(13, 14, 15, 16); RateStats[] rateStats = new RateStats[2]; rateStats[0] = new RateStats(1, 3, 5, 7, 9, 11, 13, 15, 17); rateStats[1] = new RateStats(2, 4, 6, 8, 10, 12, 14, 16, 18); RadioStats[] radioStats = new RadioStats[2]; radioStats[0] = new RadioStats(0, 10, 11, 12, 13, 14, 15, 16, 17, 18); - radioStats[1] = new RadioStats(1, 20, 21, 22, 23, 24, 25, 26, 27, 28); + radioStats[1] = new RadioStats(1, 20, 21, 22, 23, 24, 25, 26, 27, 28, new int[] {1, 2, 3}); + PeerInfo[] peerInfo = new PeerInfo[1]; + peerInfo[0] = new PeerInfo(1, 50, rateStats); + ScanResultWithSameFreq[] scanResultsWithSameFreq2G = new ScanResultWithSameFreq[1]; + scanResultsWithSameFreq2G[0] = new ScanResultWithSameFreq(100, -50, 2412); + ScanResultWithSameFreq[] scanResultsWithSameFreq5G = new ScanResultWithSameFreq[1]; + scanResultsWithSameFreq5G[0] = new ScanResultWithSameFreq(100, -50, 5500); SparseArray<LinkStats> linkStats = new SparseArray<>(); linkStats.put(0, - new LinkStats(0, WifiUsabilityStatsEntry.LINK_STATE_NOT_IN_USE, 0, -50, 300, - 200, - 188, 2, 2, 100, 300, 100, 100, 200, - contentionTimeStats, rateStats)); + new LinkStats(0, WifiUsabilityStatsEntry.LINK_STATE_NOT_IN_USE, 0, -50, 2412, + -50, 0, 0, 0, 300, 200, 188, 2, 2, 100, 300, 100, 100, 200, + contentionTimeStats, rateStats, packetStats, peerInfo, + scanResultsWithSameFreq2G)); linkStats.put(1, - new LinkStats(0, WifiUsabilityStatsEntry.LINK_STATE_IN_USE, 0, -40, 860, 600, - 388, 2, 2, 200, 400, 100, 100, 200, - contentionTimeStats, rateStats)); + new LinkStats(0, WifiUsabilityStatsEntry.LINK_STATE_IN_USE, 0, -40, 5500, + -40, 1, 0, 0, 860, 600, 388, 2, 2, 200, 400, 100, 100, 200, + contentionTimeStats, rateStats, packetStats, peerInfo, + scanResultsWithSameFreq5G)); callbackCaptor.getValue().onWifiUsabilityStats(1, true, new WifiUsabilityStatsEntry(System.currentTimeMillis(), -50, 100, 10, 0, 5, 5, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 1, 100, 10, 100, 27, contentionTimeStats, rateStats, radioStats, 101, true, true, true, - 0, 10, 10, true, linkStats)); + 0, 10, 10, true, linkStats, 1, 0, 10, 20, 1, 2, 1, 1, 1, 1, false, 0, + false, 100, 100, 1, 3, 1)); verify(mOnWifiUsabilityStatsListener).onWifiUsabilityStats(anyInt(), anyBoolean(), any(WifiUsabilityStatsEntry.class)); } @@ -2553,11 +2608,11 @@ public class WifiManagerTest { */ @Test public void testIsEnhancedOpenSupported() throws Exception { - when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(WIFI_FEATURE_OWE)); + when(mWifiService.isFeatureSupported(eq(WIFI_FEATURE_OWE))) + .thenReturn(true); assertTrue(mWifiManager.isEnhancedOpenSupported()); - when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(~WIFI_FEATURE_OWE)); + when(mWifiService.isFeatureSupported(eq(WIFI_FEATURE_OWE))) + .thenReturn(false); assertFalse(mWifiManager.isEnhancedOpenSupported()); } @@ -2566,11 +2621,11 @@ public class WifiManagerTest { */ @Test public void testIsWpa3SaeSupported() throws Exception { - when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(WIFI_FEATURE_WPA3_SAE)); + when(mWifiService.isFeatureSupported(eq(WIFI_FEATURE_WPA3_SAE))) + .thenReturn(true); assertTrue(mWifiManager.isWpa3SaeSupported()); - when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(~WIFI_FEATURE_WPA3_SAE)); + when(mWifiService.isFeatureSupported(eq(WIFI_FEATURE_WPA3_SAE))) + .thenReturn(false); assertFalse(mWifiManager.isWpa3SaeSupported()); } @@ -2579,11 +2634,11 @@ public class WifiManagerTest { */ @Test public void testIsWpa3SuiteBSupported() throws Exception { - when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(WIFI_FEATURE_WPA3_SUITE_B)); + when(mWifiService.isFeatureSupported(eq(WIFI_FEATURE_WPA3_SUITE_B))) + .thenReturn(true); assertTrue(mWifiManager.isWpa3SuiteBSupported()); - when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(~WIFI_FEATURE_WPA3_SUITE_B)); + when(mWifiService.isFeatureSupported(eq(WIFI_FEATURE_WPA3_SUITE_B))) + .thenReturn(false); assertFalse(mWifiManager.isWpa3SuiteBSupported()); } @@ -2592,11 +2647,11 @@ public class WifiManagerTest { */ @Test public void testIsEasyConnectSupported() throws Exception { - when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(WIFI_FEATURE_DPP)); + when(mWifiService.isFeatureSupported(eq(WIFI_FEATURE_DPP))) + .thenReturn(true); assertTrue(mWifiManager.isEasyConnectSupported()); - when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(~WIFI_FEATURE_DPP)); + when(mWifiService.isFeatureSupported(eq(WIFI_FEATURE_DPP))) + .thenReturn(false); assertFalse(mWifiManager.isEasyConnectSupported()); } @@ -2605,11 +2660,11 @@ public class WifiManagerTest { */ @Test public void testIsEasyConnectDppAkmSupported() throws Exception { - when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(WIFI_FEATURE_DPP_AKM)); + when(mWifiService.isFeatureSupported(eq(WIFI_FEATURE_DPP_AKM))) + .thenReturn(true); assertTrue(mWifiManager.isEasyConnectDppAkmSupported()); - when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(~WIFI_FEATURE_DPP_AKM)); + when(mWifiService.isFeatureSupported(eq(WIFI_FEATURE_DPP_AKM))) + .thenReturn(false); assertFalse(mWifiManager.isEasyConnectDppAkmSupported()); } @@ -2619,12 +2674,11 @@ public class WifiManagerTest { @Test public void testIsEasyConnectEnrolleeResponderModeSupported() throws Exception { assumeTrue(SdkLevel.isAtLeastS()); - - when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(WIFI_FEATURE_DPP_ENROLLEE_RESPONDER)); + when(mWifiService.isFeatureSupported(eq(WIFI_FEATURE_DPP_ENROLLEE_RESPONDER))) + .thenReturn(true); assertTrue(mWifiManager.isEasyConnectEnrolleeResponderModeSupported()); - when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(~WIFI_FEATURE_DPP_ENROLLEE_RESPONDER)); + when(mWifiService.isFeatureSupported(eq(WIFI_FEATURE_DPP_ENROLLEE_RESPONDER))) + .thenReturn(false); assertFalse(mWifiManager.isEasyConnectEnrolleeResponderModeSupported()); } @@ -2633,11 +2687,11 @@ public class WifiManagerTest { */ @Test public void testIsStaApConcurrencyOpenSupported() throws Exception { - when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(WIFI_FEATURE_AP_STA)); + when(mWifiService.isFeatureSupported(eq(WIFI_FEATURE_AP_STA))) + .thenReturn(true); assertTrue(mWifiManager.isStaApConcurrencySupported()); - when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(~WIFI_FEATURE_AP_STA)); + when(mWifiService.isFeatureSupported(eq(WIFI_FEATURE_AP_STA))) + .thenReturn(false); assertFalse(mWifiManager.isStaApConcurrencySupported()); } @@ -2646,23 +2700,27 @@ public class WifiManagerTest { */ @Test public void testIsStaConcurrencySupported() throws Exception { - when(mWifiService.getSupportedFeatures()).thenReturn(0L); + when(mWifiService.isFeatureSupported(anyInt())).thenReturn(false); assertFalse(mWifiManager.isStaConcurrencyForLocalOnlyConnectionsSupported()); assertFalse(mWifiManager.isMakeBeforeBreakWifiSwitchingSupported()); assertFalse(mWifiManager.isStaConcurrencyForRestrictedConnectionsSupported()); assertFalse(mWifiManager.isStaConcurrencyForMultiInternetSupported()); - when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(WIFI_FEATURE_ADDITIONAL_STA_LOCAL_ONLY)); + when(mWifiService.isFeatureSupported(eq(WIFI_FEATURE_ADDITIONAL_STA_LOCAL_ONLY))) + .thenReturn(true); assertTrue(mWifiManager.isStaConcurrencyForLocalOnlyConnectionsSupported()); assertFalse(mWifiManager.isMakeBeforeBreakWifiSwitchingSupported()); assertFalse(mWifiManager.isStaConcurrencyForRestrictedConnectionsSupported()); assertFalse(mWifiManager.isStaConcurrencyForMultiInternetSupported()); - when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(WIFI_FEATURE_ADDITIONAL_STA_MBB - | WIFI_FEATURE_ADDITIONAL_STA_RESTRICTED - | WIFI_FEATURE_ADDITIONAL_STA_MULTI_INTERNET)); + when(mWifiService.isFeatureSupported(eq(WIFI_FEATURE_ADDITIONAL_STA_LOCAL_ONLY))) + .thenReturn(false); + when(mWifiService.isFeatureSupported(eq(WIFI_FEATURE_ADDITIONAL_STA_MBB))) + .thenReturn(true); + when(mWifiService.isFeatureSupported(eq(WIFI_FEATURE_ADDITIONAL_STA_RESTRICTED))) + .thenReturn(true); + when(mWifiService.isFeatureSupported(eq(WIFI_FEATURE_ADDITIONAL_STA_MULTI_INTERNET))) + .thenReturn(true); assertFalse(mWifiManager.isStaConcurrencyForLocalOnlyConnectionsSupported()); assertTrue(mWifiManager.isMakeBeforeBreakWifiSwitchingSupported()); assertTrue(mWifiManager.isStaConcurrencyForRestrictedConnectionsSupported()); @@ -2839,30 +2897,6 @@ public class WifiManagerTest { } /** - * Test behavior of {@link WifiManager#getSupportedFeatures()} - */ - @Test - public void testGetSupportedFeatures() throws Exception { - long supportedFeatures = - WIFI_FEATURE_SCANNER - | WIFI_FEATURE_PASSPOINT - | WIFI_FEATURE_P2P; - when(mWifiService.getSupportedFeatures()) - .thenReturn(Long.valueOf(supportedFeatures)); - - assertTrue(mWifiManager.isWifiScannerSupported()); - assertTrue(mWifiManager.isPasspointSupported()); - assertTrue(mWifiManager.isP2pSupported()); - assertFalse(mWifiManager.isPortableHotspotSupported()); - assertFalse(mWifiManager.isDeviceToDeviceRttSupported()); - assertFalse(mWifiManager.isDeviceToApRttSupported()); - assertFalse(mWifiManager.isPreferredNetworkOffloadSupported()); - assertFalse(mWifiManager.isTdlsSupported()); - assertFalse(mWifiManager.isOffChannelTdlsSupported()); - assertFalse(mWifiManager.isEnhancedPowerReportingSupported()); - } - - /** * Tests that passing a null Executor to {@link WifiManager#getWifiActivityEnergyInfoAsync} * throws an exception. */ @@ -3245,11 +3279,9 @@ public class WifiManagerTest { */ @Test public void testIsWapiSupported() throws Exception { - when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(WifiManager.WIFI_FEATURE_WAPI)); + when(mWifiService.isFeatureSupported(eq(WifiManager.WIFI_FEATURE_WAPI))).thenReturn(true); assertTrue(mWifiManager.isWapiSupported()); - when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(~WifiManager.WIFI_FEATURE_WAPI)); + when(mWifiService.isFeatureSupported(eq(WifiManager.WIFI_FEATURE_WAPI))).thenReturn(false); assertFalse(mWifiManager.isWapiSupported()); } @@ -3575,11 +3607,11 @@ public class WifiManagerTest { */ @Test public void testIsPasspointTermsAndConditionsSupported() throws Exception { - when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(WIFI_FEATURE_PASSPOINT_TERMS_AND_CONDITIONS)); + when(mWifiService.isFeatureSupported(eq(WIFI_FEATURE_PASSPOINT_TERMS_AND_CONDITIONS))) + .thenReturn(true); assertTrue(mWifiManager.isPasspointTermsAndConditionsSupported()); - when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(~WIFI_FEATURE_PASSPOINT_TERMS_AND_CONDITIONS)); + when(mWifiService.isFeatureSupported(eq(WIFI_FEATURE_PASSPOINT_TERMS_AND_CONDITIONS))) + .thenReturn(false); assertFalse(mWifiManager.isPasspointTermsAndConditionsSupported()); } @@ -3643,11 +3675,11 @@ public class WifiManagerTest { */ @Test public void testIsDecoratedIdentitySupported() throws Exception { - when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(WIFI_FEATURE_DECORATED_IDENTITY)); + when(mWifiService.isFeatureSupported(eq(WIFI_FEATURE_DECORATED_IDENTITY))) + .thenReturn(true); assertTrue(mWifiManager.isDecoratedIdentitySupported()); - when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(~WIFI_FEATURE_DECORATED_IDENTITY)); + when(mWifiService.isFeatureSupported(eq(WIFI_FEATURE_DECORATED_IDENTITY))) + .thenReturn(false); assertFalse(mWifiManager.isDecoratedIdentitySupported()); } @@ -3656,11 +3688,11 @@ public class WifiManagerTest { */ @Test public void testIsTrustOnFirstUseSupported() throws Exception { - when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(WIFI_FEATURE_TRUST_ON_FIRST_USE)); + when(mWifiService.isFeatureSupported(eq(WIFI_FEATURE_TRUST_ON_FIRST_USE))) + .thenReturn(true); assertTrue(mWifiManager.isTrustOnFirstUseSupported()); - when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(~WIFI_FEATURE_TRUST_ON_FIRST_USE)); + when(mWifiService.isFeatureSupported(eq(WIFI_FEATURE_TRUST_ON_FIRST_USE))) + .thenReturn(false); assertFalse(mWifiManager.isTrustOnFirstUseSupported()); } @@ -3932,11 +3964,11 @@ public class WifiManagerTest { */ @Test public void testIsStaConcurrencyForMultiInternetSupported() throws Exception { - when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(WIFI_FEATURE_ADDITIONAL_STA_MULTI_INTERNET)); + when(mWifiService.isFeatureSupported(eq(WIFI_FEATURE_ADDITIONAL_STA_MULTI_INTERNET))) + .thenReturn(true); assertTrue(mWifiManager.isStaConcurrencyForMultiInternetSupported()); - when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(~WIFI_FEATURE_ADDITIONAL_STA_MULTI_INTERNET)); + when(mWifiService.isFeatureSupported(eq(WIFI_FEATURE_ADDITIONAL_STA_MULTI_INTERNET))) + .thenReturn(false); assertFalse(mWifiManager.isStaConcurrencyForMultiInternetSupported()); } @@ -3966,11 +3998,11 @@ public class WifiManagerTest { */ @Test public void testIsDualBandSimultaneousSupported() throws Exception { - when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(WIFI_FEATURE_DUAL_BAND_SIMULTANEOUS)); + when(mWifiService.isFeatureSupported(eq(WIFI_FEATURE_DUAL_BAND_SIMULTANEOUS))) + .thenReturn(true); assertTrue(mWifiManager.isDualBandSimultaneousSupported()); - when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(~WIFI_FEATURE_DUAL_BAND_SIMULTANEOUS)); + when(mWifiService.isFeatureSupported(eq(WIFI_FEATURE_DUAL_BAND_SIMULTANEOUS))) + .thenReturn(false); assertFalse(mWifiManager.isDualBandSimultaneousSupported()); } /* @@ -3978,9 +4010,11 @@ public class WifiManagerTest { */ @Test public void testIsTidToLinkMappingSupported() throws Exception { - when(mWifiService.getSupportedFeatures()).thenReturn(WIFI_FEATURE_T2LM_NEGOTIATION); + when(mWifiService.isFeatureSupported(eq(WIFI_FEATURE_T2LM_NEGOTIATION))) + .thenReturn(true); assertTrue(mWifiManager.isTidToLinkMappingNegotiationSupported()); - when(mWifiService.getSupportedFeatures()).thenReturn(~WIFI_FEATURE_T2LM_NEGOTIATION); + when(mWifiService.isFeatureSupported(eq(WIFI_FEATURE_T2LM_NEGOTIATION))) + .thenReturn(false); assertFalse(mWifiManager.isTidToLinkMappingNegotiationSupported()); } @@ -4158,11 +4192,11 @@ public class WifiManagerTest { */ @Test public void testIsWepSupported() throws Exception { - when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(WIFI_FEATURE_WEP)); + when(mWifiService.isFeatureSupported(eq(WIFI_FEATURE_WEP))) + .thenReturn(true); assertTrue(mWifiManager.isWepSupported()); - when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(~WIFI_FEATURE_WEP)); + when(mWifiService.isFeatureSupported(eq(WIFI_FEATURE_WEP))) + .thenReturn(false); assertFalse(mWifiManager.isWepSupported()); } @@ -4171,11 +4205,11 @@ public class WifiManagerTest { */ @Test public void testIsWpaPersonalSupported() throws Exception { - when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(WIFI_FEATURE_WPA_PERSONAL)); + when(mWifiService.isFeatureSupported(eq(WIFI_FEATURE_WPA_PERSONAL))) + .thenReturn(true); assertTrue(mWifiManager.isWpaPersonalSupported()); - when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(~WIFI_FEATURE_WPA_PERSONAL)); + when(mWifiService.isFeatureSupported(eq(WIFI_FEATURE_WPA_PERSONAL))) + .thenReturn(false); assertFalse(mWifiManager.isWpaPersonalSupported()); } @@ -4334,11 +4368,11 @@ public class WifiManagerTest { */ @Test public void testIsD2dSupportedWhenInfraStaDisabled() throws Exception { - when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(WIFI_FEATURE_D2D_WHEN_INFRA_STA_DISABLED)); + when(mWifiService.isFeatureSupported(eq(WIFI_FEATURE_D2D_WHEN_INFRA_STA_DISABLED))) + .thenReturn(true); assertTrue(mWifiManager.isD2dSupportedWhenInfraStaDisabled()); - when(mWifiService.getSupportedFeatures()) - .thenReturn(new Long(~WIFI_FEATURE_D2D_WHEN_INFRA_STA_DISABLED)); + when(mWifiService.isFeatureSupported(eq(WIFI_FEATURE_D2D_WHEN_INFRA_STA_DISABLED))) + .thenReturn(false); assertFalse(mWifiManager.isD2dSupportedWhenInfraStaDisabled()); } @@ -4390,4 +4424,150 @@ public class WifiManagerTest { mWifiManager.isPreferredNetworkOffloadSupported(); verify(mWifiService).isPnoSupported(); } + + @Test + public void testSetAutojoinDisallowedSecurityTypesToWifiServiceImpl() throws Exception { + assumeTrue(SdkLevel.isAtLeastT()); + int[] restrictions = { + WifiInfo.SECURITY_TYPE_OPEN, + WifiInfo.SECURITY_TYPE_WEP, + WifiInfo.SECURITY_TYPE_OWE }; + int restrictionBitmap = (0x1 << WifiInfo.SECURITY_TYPE_OPEN) + | (0x1 << WifiInfo.SECURITY_TYPE_WEP) + | (0x1 << WifiInfo.SECURITY_TYPE_OWE); + ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); + mWifiManager.setAutojoinDisallowedSecurityTypes(restrictions); + verify(mWifiService).setAutojoinDisallowedSecurityTypes(eq(restrictionBitmap), + bundleCaptor.capture()); + assertEquals(mContext.getAttributionSource(), + bundleCaptor.getValue().getParcelable(EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE)); + + // Null argument + assertThrows(NullPointerException.class, + () -> mWifiManager.setAutojoinDisallowedSecurityTypes(null)); + } + + @Test + public void testGetAutojoinDisallowedSecurityTypesToWifiServiceImpl() throws Exception { + assumeTrue(SdkLevel.isAtLeastT()); + final int[] restrictionToSet = { + WifiInfo.SECURITY_TYPE_OPEN, + WifiInfo.SECURITY_TYPE_WEP, + WifiInfo.SECURITY_TYPE_OWE }; + + final int restrictionBitmap = (0x1 << WifiInfo.SECURITY_TYPE_OPEN) + | (0x1 << WifiInfo.SECURITY_TYPE_WEP) + | (0x1 << WifiInfo.SECURITY_TYPE_OWE); + + SynchronousExecutor executor = mock(SynchronousExecutor.class); + Consumer<int[]> mockResultsCallback = mock(Consumer.class); + + // null executor + assertThrows(NullPointerException.class, + () -> mWifiManager.getAutojoinDisallowedSecurityTypes(null, mockResultsCallback)); + // null resultsCallback + assertThrows(NullPointerException.class, + () -> mWifiManager.getAutojoinDisallowedSecurityTypes(executor, null)); + + ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); + ArgumentCaptor<IIntegerListener.Stub> cbCaptor = ArgumentCaptor.forClass( + IIntegerListener.Stub.class); + + ArgumentCaptor<int[]> resultCaptor = ArgumentCaptor.forClass(int[].class); + + mWifiManager.getAutojoinDisallowedSecurityTypes(new SynchronousExecutor(), + mockResultsCallback); + verify(mWifiService).getAutojoinDisallowedSecurityTypes(cbCaptor.capture(), + bundleCaptor.capture()); + assertEquals(mContext.getAttributionSource(), + bundleCaptor.getValue().getParcelable(EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE)); + + cbCaptor.getValue().onResult(restrictionBitmap); + + verify(mockResultsCallback).accept(resultCaptor.capture()); + assertArrayEquals(restrictionToSet, resultCaptor.getValue()); + } + + @Test + public void testStartLocalOnlyHotspotWithConfiguration() throws Exception { + // setChannels supported from S. + assumeTrue(SdkLevel.isAtLeastS()); + SparseIntArray testChannel = new SparseIntArray(1); + testChannel.put(SoftApConfiguration.BAND_5GHZ, 0); + SoftApConfiguration customConfig = new SoftApConfiguration.Builder() + .setChannels(testChannel) + .build(); + TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(); + mWifiManager.startLocalOnlyHotspotWithConfiguration(customConfig, mExecutor, callback); + SoftApConfiguration userConfig = + new SoftApConfiguration.Builder(customConfig) + .setUserConfiguration(true).build(); + verify(mWifiService).startLocalOnlyHotspot(any(ILocalOnlyHotspotCallback.class), + anyString(), nullable(String.class), eq(userConfig), any(), eq(false)); + assertThrows(NullPointerException.class, + () -> mWifiManager.startLocalOnlyHotspotWithConfiguration( + null, mExecutor, callback)); + assertThrows(NullPointerException.class, + () -> mWifiManager.startLocalOnlyHotspotWithConfiguration( + customConfig, null, callback)); + assertThrows(NullPointerException.class, + () -> mWifiManager.startLocalOnlyHotspotWithConfiguration( + customConfig, mExecutor, null)); + } + + /** + * Verify an IllegalArgumentException is thrown if listener is not provided. + */ + @Test(expected = NullPointerException.class) + public void testAddWifiStateChangedListenerWithNullListener() throws Exception { + mWifiManager.addWifiStateChangedListener(mExecutor, null); + } + + /** + * Verify an IllegalArgumentException is thrown if executor is not provided. + */ + @Test(expected = NullPointerException.class) + public void testAddWifiStateChangedListenerWithNullExecutor() throws Exception { + mWifiManager.addWifiStateChangedListener(null, mWifiStateChangedListener); + } + + /** + * Verify client provided listener is being called to the right listener. + */ + @Test + public void testAddWifiStateChangedListenerAndReceiveEvent() throws Exception { + ArgumentCaptor<IWifiStateChangedListener.Stub> listenerCaptor = + ArgumentCaptor.forClass(IWifiStateChangedListener.Stub.class); + mWifiManager.addWifiStateChangedListener(new SynchronousExecutor(), + mWifiStateChangedListener); + verify(mWifiService).addWifiStateChangedListener(listenerCaptor.capture()); + listenerCaptor.getValue().onWifiStateChanged(); + verify(mRunnable).run(); + } + + /** + * Verify client removeWifiStateChangedListener. + */ + @Test + public void testRemoveUnknownWifiStateChangedListener() throws Exception { + mWifiManager.removeWifiStateChangedListener(mWifiStateChangedListener); + verify(mWifiService, never()).removeWifiStateChangedListener(any()); + } + + /** + * Verify client removeWifiStateChangedListener with null listener will cause an exception. + */ + @Test(expected = NullPointerException.class) + public void testRemoveWifiStateChangedListenerWithNullListener() throws Exception { + mWifiManager.removeWifiStateChangedListener(null); + } + + @Test + public void testDisallowCurrentSuggestedNetwork() throws RemoteException { + assertThrows(NullPointerException.class, + () -> mWifiManager.disallowCurrentSuggestedNetwork(null)); + BlockingOption option = new BlockingOption.Builder(100).build(); + mWifiManager.disallowCurrentSuggestedNetwork(option); + verify(mWifiService).disallowCurrentSuggestedNetwork(eq(option), eq(TEST_PACKAGE_NAME)); + } } diff --git a/framework/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java b/framework/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java index bc22b808c5..a6718c1f11 100644 --- a/framework/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java +++ b/framework/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java @@ -195,7 +195,7 @@ public class WifiNetworkAgentSpecifierTest { ssidPattern, bssidPattern, ScanResult.WIFI_BAND_5_GHZ, - wificonfigurationNetworkSpecifier, new int[0]); + wificonfigurationNetworkSpecifier, new int[0], false); assertTrue(wifiNetworkSpecifier.canBeSatisfiedBy(wifiNetworkAgentSpecifier)); assertTrue(wifiNetworkAgentSpecifier.canBeSatisfiedBy(wifiNetworkSpecifier)); @@ -224,7 +224,7 @@ public class WifiNetworkAgentSpecifierTest { ssidPattern, bssidPattern, ScanResult.WIFI_BAND_5_GHZ, - wificonfigurationNetworkSpecifier, new int[0]); + wificonfigurationNetworkSpecifier, new int[0], false); assertTrue(wifiNetworkSpecifier.canBeSatisfiedBy(wifiNetworkAgentSpecifier)); assertTrue(wifiNetworkAgentSpecifier.canBeSatisfiedBy(wifiNetworkSpecifier)); @@ -253,7 +253,7 @@ public class WifiNetworkAgentSpecifierTest { ssidPattern, bssidPattern, ScanResult.WIFI_BAND_5_GHZ, - wificonfigurationNetworkSpecifier, new int[0]); + wificonfigurationNetworkSpecifier, new int[0], false); assertTrue(wifiNetworkSpecifier.canBeSatisfiedBy(wifiNetworkAgentSpecifier)); assertTrue(wifiNetworkAgentSpecifier.canBeSatisfiedBy(wifiNetworkSpecifier)); @@ -285,7 +285,7 @@ public class WifiNetworkAgentSpecifierTest { ssidPattern, bssidPattern, ScanResult.WIFI_BAND_24_GHZ, - wificonfigurationNetworkSpecifier, new int[0]); + wificonfigurationNetworkSpecifier, new int[0], false); assertFalse(wifiNetworkSpecifier.canBeSatisfiedBy(wifiNetworkAgentSpecifier)); assertFalse(wifiNetworkAgentSpecifier.canBeSatisfiedBy(wifiNetworkSpecifier)); @@ -316,7 +316,7 @@ public class WifiNetworkAgentSpecifierTest { ssidPattern, bssidPattern, ScanResult.WIFI_BAND_5_GHZ, - wificonfigurationNetworkSpecifier, new int[0]); + wificonfigurationNetworkSpecifier, new int[0], false); assertTrue(wifiNetworkSpecifier.canBeSatisfiedBy(wifiNetworkAgentSpecifier)); assertTrue(wifiNetworkAgentSpecifier.canBeSatisfiedBy(wifiNetworkSpecifier)); @@ -325,7 +325,7 @@ public class WifiNetworkAgentSpecifierTest { ssidPattern, bssidPattern, ScanResult.WIFI_BAND_24_GHZ, - wificonfigurationNetworkSpecifier, new int[0]); + wificonfigurationNetworkSpecifier, new int[0], false); assertFalse(wifiNetworkSpecifier.canBeSatisfiedBy(wifiNetworkAgentSpecifier)); assertFalse(wifiNetworkAgentSpecifier.canBeSatisfiedBy(wifiNetworkSpecifier)); @@ -334,7 +334,7 @@ public class WifiNetworkAgentSpecifierTest { ssidPattern, bssidPattern, ScanResult.UNSPECIFIED, - wificonfigurationNetworkSpecifier, new int[0]); + wificonfigurationNetworkSpecifier, new int[0], false); assertTrue(wifiNetworkSpecifier.canBeSatisfiedBy(wifiNetworkAgentSpecifier)); assertTrue(wifiNetworkAgentSpecifier.canBeSatisfiedBy(wifiNetworkSpecifier)); } @@ -373,17 +373,17 @@ public class WifiNetworkAgentSpecifierTest { ssidPattern, bssidPattern, ScanResult.WIFI_BAND_5_GHZ, - wificonfigurationNetworkSpecifier, new int[0]); + wificonfigurationNetworkSpecifier, new int[0], false); WifiNetworkSpecifier wifi5GLowNetworkSpecifier = new WifiNetworkSpecifier( ssidPattern, bssidPattern, ScanResult.WIFI_BAND_5_GHZ_LOW, - wificonfigurationNetworkSpecifier, new int[0]); + wificonfigurationNetworkSpecifier, new int[0], false); WifiNetworkSpecifier wifi5GHighNetworkSpecifier = new WifiNetworkSpecifier( ssidPattern, bssidPattern, ScanResult.WIFI_BAND_5_GHZ_HIGH, - wificonfigurationNetworkSpecifier, new int[0]); + wificonfigurationNetworkSpecifier, new int[0], false); // mBand = WIFI_BAND_5_GHZ_LOW // Same band matches. @@ -435,7 +435,7 @@ public class WifiNetworkAgentSpecifierTest { ssidPattern, bssidPattern, ScanResult.WIFI_BAND_5_GHZ, - wificonfigurationNetworkSpecifier, new int[0]); + wificonfigurationNetworkSpecifier, new int[0], false); assertFalse(wifiNetworkSpecifier.canBeSatisfiedBy(wifiNetworkAgentSpecifier)); assertFalse(wifiNetworkAgentSpecifier.canBeSatisfiedBy(wifiNetworkSpecifier)); @@ -454,7 +454,7 @@ public class WifiNetworkAgentSpecifierTest { ssidPattern, bssidPattern, ScanResult.WIFI_BAND_24_GHZ, - wificonfigurationNetworkSpecifier, new int[0]); + wificonfigurationNetworkSpecifier, new int[0], false); assertFalse(wifiNetworkSpecifier.canBeSatisfiedBy(wifiNetworkAgentSpecifier)); assertFalse(wifiNetworkAgentSpecifier.canBeSatisfiedBy(wifiNetworkSpecifier)); @@ -464,7 +464,7 @@ public class WifiNetworkAgentSpecifierTest { ssidPattern, bssidPattern, ScanResult.UNSPECIFIED, - wificonfigurationNetworkSpecifier, new int[0]); + wificonfigurationNetworkSpecifier, new int[0], false); assertFalse(wifiNetworkSpecifier.canBeSatisfiedBy(wifiNetworkAgentSpecifier)); assertFalse(wifiNetworkAgentSpecifier.canBeSatisfiedBy(wifiNetworkSpecifier)); } @@ -496,7 +496,7 @@ public class WifiNetworkAgentSpecifierTest { ssidPattern, bssidPattern, ScanResult.WIFI_BAND_5_GHZ, - wificonfigurationNetworkSpecifier, new int[0]); + wificonfigurationNetworkSpecifier, new int[0], false); assertFalse(wifiNetworkSpecifier.canBeSatisfiedBy(wifiNetworkAgentSpecifier)); assertFalse(wifiNetworkAgentSpecifier.canBeSatisfiedBy(wifiNetworkSpecifier)); @@ -529,7 +529,7 @@ public class WifiNetworkAgentSpecifierTest { ssidPattern, bssidPattern, ScanResult.WIFI_BAND_24_GHZ, - wificonfigurationNetworkSpecifier, new int[0]); + wificonfigurationNetworkSpecifier, new int[0], false); assertFalse(wifiNetworkSpecifier.canBeSatisfiedBy(wifiNetworkAgentSpecifier)); assertFalse(wifiNetworkAgentSpecifier.canBeSatisfiedBy(wifiNetworkSpecifier)); @@ -557,7 +557,7 @@ public class WifiNetworkAgentSpecifierTest { ssidPattern, bssidPattern, ScanResult.UNSPECIFIED, - wificonfigurationNetworkSpecifier, new int[0]); + wificonfigurationNetworkSpecifier, new int[0], false); assertFalse(wifiNetworkSpecifier.canBeSatisfiedBy(wifiNetworkAgentSpecifier)); assertFalse(wifiNetworkAgentSpecifier.canBeSatisfiedBy(wifiNetworkSpecifier)); diff --git a/framework/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java b/framework/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java index 12dfd6e9a5..cc6d008cb2 100644 --- a/framework/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java +++ b/framework/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java @@ -657,7 +657,7 @@ public class WifiNetworkSpecifierTest { Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), MacAddress.fromString(TEST_BSSID_OUI_MASK)), WIFI_BAND_5_GHZ, - wifiConfiguration, new int[0]); + wifiConfiguration, new int[0], false); Parcel parcelW = Parcel.obtain(); specifier.writeToParcel(parcelW, 0); @@ -689,7 +689,7 @@ public class WifiNetworkSpecifierTest { Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), MacAddress.fromString(TEST_BSSID_OUI_MASK)), ScanResult.UNSPECIFIED, /* band */ - wifiConfiguration, new int[0]); + wifiConfiguration, new int[0], false); assertFalse(specifier.canBeSatisfiedBy(null)); assertFalse(specifier.canBeSatisfiedBy(new MatchAllNetworkSpecifier())); @@ -712,14 +712,14 @@ public class WifiNetworkSpecifierTest { Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), MacAddress.fromString(TEST_BSSID_OUI_MASK)), WIFI_BAND_5_GHZ, - wifiConfiguration, new int[0]); + wifiConfiguration, new int[0], false); WifiNetworkSpecifier specifier2 = new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL), Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), MacAddress.fromString(TEST_BSSID_OUI_MASK)), WIFI_BAND_5_GHZ, - wifiConfiguration, new int[0]); + wifiConfiguration, new int[0], false); assertTrue(specifier2.canBeSatisfiedBy(specifier1)); } @@ -741,7 +741,7 @@ public class WifiNetworkSpecifierTest { Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), MacAddress.fromString(TEST_BSSID_OUI_MASK)), ScanResult.WIFI_BAND_24_GHZ, - wifiConfiguration1, new int[0]); + wifiConfiguration1, new int[0], false); WifiConfiguration wifiConfiguration2 = new WifiConfiguration(); wifiConfiguration2.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); @@ -750,7 +750,7 @@ public class WifiNetworkSpecifierTest { Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), MacAddress.fromString(TEST_BSSID_OUI_MASK)), ScanResult.WIFI_BAND_24_GHZ, - wifiConfiguration2, new int[0]); + wifiConfiguration2, new int[0], false); assertFalse(specifier2.canBeSatisfiedBy(specifier1)); } @@ -772,14 +772,14 @@ public class WifiNetworkSpecifierTest { Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), MacAddress.fromString(TEST_BSSID_OUI_MASK)), WIFI_BAND_5_GHZ, - wifiConfiguration, new int[0]); + wifiConfiguration, new int[0], false); WifiNetworkSpecifier specifier2 = new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL), Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), MacAddress.fromString(TEST_BSSID_OUI_MASK)), WIFI_BAND_5_GHZ, - wifiConfiguration, new int[0]); + wifiConfiguration, new int[0], false); assertFalse(specifier2.canBeSatisfiedBy(specifier1)); } @@ -801,14 +801,14 @@ public class WifiNetworkSpecifierTest { Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), MacAddress.fromString(TEST_BSSID_OUI_MASK)), ScanResult.WIFI_BAND_24_GHZ, - wifiConfiguration, new int[0]); + wifiConfiguration, new int[0], false); WifiNetworkSpecifier specifier2 = new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL), Pair.create(WifiManager.ALL_ZEROS_MAC_ADDRESS, WifiManager.ALL_ZEROS_MAC_ADDRESS), ScanResult.WIFI_BAND_24_GHZ, - wifiConfiguration, new int[0]); + wifiConfiguration, new int[0], false); assertFalse(specifier2.canBeSatisfiedBy(specifier1)); } @@ -827,14 +827,14 @@ public class WifiNetworkSpecifierTest { Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), MacAddress.fromString(TEST_BSSID_OUI_MASK)), ScanResult.WIFI_BAND_24_GHZ, - wifiConfiguration, new int[0]); + wifiConfiguration, new int[0], false); WifiNetworkSpecifier specifier2 = new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL), Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), MacAddress.fromString(TEST_BSSID_OUI_MASK)), ScanResult.WIFI_BAND_24_GHZ, - wifiConfiguration, new int[0]); + wifiConfiguration, new int[0], false); // Same band matches. assertTrue(specifier2.canBeSatisfiedBy(specifier1)); @@ -845,7 +845,7 @@ public class WifiNetworkSpecifierTest { Pair.create(WifiManager.ALL_ZEROS_MAC_ADDRESS, WifiManager.ALL_ZEROS_MAC_ADDRESS), WIFI_BAND_5_GHZ, - wifiConfiguration, new int[0]); + wifiConfiguration, new int[0], false); // Different band does not match. assertFalse(specifier2.canBeSatisfiedBy(specifier1)); @@ -856,7 +856,7 @@ public class WifiNetworkSpecifierTest { Pair.create(WifiManager.ALL_ZEROS_MAC_ADDRESS, WifiManager.ALL_ZEROS_MAC_ADDRESS), ScanResult.UNSPECIFIED, - wifiConfiguration, new int[0]); + wifiConfiguration, new int[0], false); // An UNSPECIFIED band does not match a specified band, because a WifiNetworkSpecifier // satisfies another only if they are equal. @@ -878,7 +878,7 @@ public class WifiNetworkSpecifierTest { Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), MacAddress.fromString(TEST_BSSID_OUI_MASK)), WIFI_BAND_5_GHZ, - wifiConfiguration, new int[0]); + wifiConfiguration, new int[0], false); final NetworkSpecifier redacted = specifier.redact(); if (SdkLevel.isAtLeastS()) { @@ -902,4 +902,14 @@ public class WifiNetworkSpecifierTest { builder.setBand(WIFI_BAND_5_GHZ); assertThrows(IllegalStateException.class, builder::build); } + + @Test + public void testSetPreferSecondarySta() { + WifiNetworkSpecifier.Builder builder = new WifiNetworkSpecifier.Builder() + .setSsidPattern(new PatternMatcher(TEST_SSID, PATTERN_PREFIX)); + // By default this should be false + assertEquals(false, builder.build().isPreferSecondarySta()); + // It should be true if set to true + assertEquals(true, builder.setPreferSecondarySta(true).build().isPreferSecondarySta()); + } } diff --git a/framework/tests/src/android/net/wifi/WifiScannerTest.java b/framework/tests/src/android/net/wifi/WifiScannerTest.java index 25a0cb6df3..237dc5dbf8 100644 --- a/framework/tests/src/android/net/wifi/WifiScannerTest.java +++ b/framework/tests/src/android/net/wifi/WifiScannerTest.java @@ -105,7 +105,7 @@ public class WifiScannerTest { MockitoAnnotations.initMocks(this); mLooper = new TestLooper(); mHandler = spy(new Handler(mLooper.getLooper())); - mWifiScanner = new WifiScanner(mContext, mService, mLooper.getLooper()); + mWifiScanner = new WifiScanner(mContext, mService); mLooper.dispatchAll(); when(mParcelableScanData.getResults()).thenReturn(mScanData); when(mContext.getOpPackageName()).thenReturn(TEST_PACKAGE_NAME); @@ -597,12 +597,12 @@ public class WifiScannerTest { @Test public void testWifiScannerConcurrentServiceStart() { WifiScanner wifiScanner = new WifiScanner( - mContext, mService, WifiFrameworkInitializer.getInstanceLooper()); + mContext, mService); Thread thread1 = new Thread(() -> { try { WifiScanner wifiScanner1 = new WifiScanner( - mContext, mService, WifiFrameworkInitializer.getInstanceLooper()); + mContext, mService); } catch (NullPointerException e) { fail("WifiScanner can't be initialized! " + e); } diff --git a/framework/tests/src/android/net/wifi/WifiUsabilityStatsEntryTest.java b/framework/tests/src/android/net/wifi/WifiUsabilityStatsEntryTest.java index 972c31099d..a70dc446c4 100644 --- a/framework/tests/src/android/net/wifi/WifiUsabilityStatsEntryTest.java +++ b/framework/tests/src/android/net/wifi/WifiUsabilityStatsEntryTest.java @@ -23,8 +23,11 @@ import static org.junit.Assert.fail; import static org.mockito.Mockito.validateMockitoUsage; import android.net.wifi.WifiUsabilityStatsEntry.ContentionTimeStats; +import android.net.wifi.WifiUsabilityStatsEntry.PacketStats; +import android.net.wifi.WifiUsabilityStatsEntry.PeerInfo; import android.net.wifi.WifiUsabilityStatsEntry.RadioStats; import android.net.wifi.WifiUsabilityStatsEntry.RateStats; +import android.net.wifi.WifiUsabilityStatsEntry.ScanResultWithSameFreq; import android.os.Parcel; import android.util.SparseArray; @@ -80,37 +83,48 @@ public class WifiUsabilityStatsEntryTest { contentionTimeStats[1] = new ContentionTimeStats(5, 6, 7, 8); contentionTimeStats[2] = new ContentionTimeStats(9, 10, 11, 12); contentionTimeStats[3] = new ContentionTimeStats(13, 14, 15, 16); + PacketStats[] packetStats = new PacketStats[4]; + packetStats[0] = new PacketStats(1, 2, 3, 4); + packetStats[1] = new PacketStats(5, 6, 7, 8); + packetStats[2] = new PacketStats(9, 10, 11, 12); + packetStats[3] = new PacketStats(13, 14, 15, 16); RateStats[] rateStats = new RateStats[2]; rateStats[0] = new RateStats(1, 3, 4, 7, 9, 11, 13, 15, 17); rateStats[1] = new RateStats(2, 2, 3, 8, 10, 12, 14, 16, 18); RadioStats[] radioStats = new RadioStats[2]; radioStats[0] = new RadioStats(0, 10, 11, 12, 13, 14, 15, 16, 17, 18); - radioStats[1] = new RadioStats(1, 20, 21, 22, 23, 24, 25, 26, 27, 28); + radioStats[1] = new RadioStats(1, 20, 21, 22, 23, 24, 25, 26, 27, 28, new int[] {1, 2, 3}); + PeerInfo[] peerInfo = new PeerInfo[1]; + peerInfo[0] = new PeerInfo(1, 50, rateStats); + ScanResultWithSameFreq[] scanResultsWithSameFreq2G = new ScanResultWithSameFreq[1]; + scanResultsWithSameFreq2G[0] = new ScanResultWithSameFreq(100, -50, 2412); + ScanResultWithSameFreq[] scanResultsWithSameFreq5G = new ScanResultWithSameFreq[1]; + scanResultsWithSameFreq5G[0] = new ScanResultWithSameFreq(100, -50, 5500); SparseArray<WifiUsabilityStatsEntry.LinkStats> linkStats = new SparseArray<>(); linkStats.put(0, new WifiUsabilityStatsEntry.LinkStats(0, - WifiUsabilityStatsEntry.LINK_STATE_UNKNOWN, 0, -50, 300, 200, 188, 2, 2, - 100, - 300, 100, 100, 200, - contentionTimeStats, rateStats)); + WifiUsabilityStatsEntry.LINK_STATE_UNKNOWN, 0, -50, 2412, -50, 0, 0, 0, + 300, 200, 188, 2, 2, 100, 300, 100, 100, 200, + contentionTimeStats, rateStats, packetStats, peerInfo, scanResultsWithSameFreq2G)); linkStats.put(1, new WifiUsabilityStatsEntry.LinkStats(1, - WifiUsabilityStatsEntry.LINK_STATE_UNKNOWN, 0, -40, 860, 600, 388, 2, 2, - 200, - 400, 100, 150, 300, - contentionTimeStats, rateStats)); + WifiUsabilityStatsEntry.LINK_STATE_UNKNOWN, 0, -40, 5500, -40, 1, 0, 0, + 860, 600, 388, 2, 2, 200, 400, 100, 150, 300, + contentionTimeStats, rateStats, packetStats, peerInfo, scanResultsWithSameFreq5G)); WifiUsabilityStatsEntry usabilityStatsEntry = new WifiUsabilityStatsEntry( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 32, contentionTimeStats, rateStats, radioStats, 100, true, - true, true, 23, 24, 25, true, linkStats); + true, true, 23, 24, 25, true, linkStats, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + false, 36, false, 37, 38, 39, 40, 41); assertEquals(32, usabilityStatsEntry.getTimeSliceDutyCycleInPercent()); WifiUsabilityStatsEntry usabilityStatsEntryWithInvalidDutyCycleValue = new WifiUsabilityStatsEntry( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, -1, contentionTimeStats, rateStats, radioStats, 101, true, true, - true, 23, 24, 25, true, linkStats); + true, 23, 24, 25, true, linkStats, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + true, 36, true, 37, 38, 39, 40, 41); try { usabilityStatsEntryWithInvalidDutyCycleValue.getTimeSliceDutyCycleInPercent(); fail(); @@ -136,27 +150,39 @@ public class WifiUsabilityStatsEntryTest { contentionTimeStats[1] = new ContentionTimeStats(5, 6, 7, 8); contentionTimeStats[2] = new ContentionTimeStats(9, 10, 11, 12); contentionTimeStats[3] = new ContentionTimeStats(13, 14, 15, 16); + PacketStats[] packetStats = new PacketStats[4]; + packetStats[0] = new PacketStats(1, 2, 3, 4); + packetStats[1] = new PacketStats(5, 6, 7, 8); + packetStats[2] = new PacketStats(9, 10, 11, 12); + packetStats[3] = new PacketStats(13, 14, 15, 16); RateStats[] rateStats = new RateStats[2]; rateStats[0] = new RateStats(1, 3, 4, 7, 9, 11, 13, 15, 17); rateStats[1] = new RateStats(2, 2, 3, 8, 10, 12, 14, 16, 18); RadioStats[] radioStats = new RadioStats[2]; radioStats[0] = new RadioStats(0, 10, 11, 12, 13, 14, 15, 16, 17, 18); - radioStats[1] = new RadioStats(1, 20, 21, 22, 23, 24, 25, 26, 27, 28); + radioStats[1] = new RadioStats(1, 20, 21, 22, 23, 24, 25, 26, 27, 28, new int[] {1, 2, 3}); + PeerInfo[] peerInfo = new PeerInfo[1]; + peerInfo[0] = new PeerInfo(1, 50, rateStats); + ScanResultWithSameFreq[] scanResultsWithSameFreq2G = new ScanResultWithSameFreq[1]; + scanResultsWithSameFreq2G[0] = new ScanResultWithSameFreq(100, -50, 2412); + ScanResultWithSameFreq[] scanResultsWithSameFreq5G = new ScanResultWithSameFreq[1]; + scanResultsWithSameFreq5G[0] = new ScanResultWithSameFreq(100, -50, 5500); SparseArray<WifiUsabilityStatsEntry.LinkStats> linkStats = new SparseArray<>(); linkStats.put(0, new WifiUsabilityStatsEntry.LinkStats(3, - WifiUsabilityStatsEntry.LINK_STATE_IN_USE, 0, -50, 300, 200, 188, 2, 2, 100, - 300, 100, 100, 200, - contentionTimeStats, rateStats)); + WifiUsabilityStatsEntry.LINK_STATE_IN_USE, 0, -50, 2412, -50, 0, 0, 0, 300, + 200, 188, 2, 2, 100, 300, 100, 100, 200, + contentionTimeStats, rateStats, packetStats, peerInfo, scanResultsWithSameFreq2G)); linkStats.put(1, new WifiUsabilityStatsEntry.LinkStats(8, - WifiUsabilityStatsEntry.LINK_STATE_IN_USE, 0, -40, 860, 600, 388, 2, 2, 200, - 400, 100, 150, 300, - contentionTimeStats, rateStats)); + WifiUsabilityStatsEntry.LINK_STATE_IN_USE, 0, -40, 5500, -40, 1, 0, 0, 860, + 600, 388, 2, 2, 200, 400, 100, 150, 300, + contentionTimeStats, rateStats, packetStats, peerInfo, scanResultsWithSameFreq5G)); return new WifiUsabilityStatsEntry( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 50, contentionTimeStats, rateStats, radioStats, 102, true, - true, true, 23, 24, 25, true, linkStats + true, true, 23, 24, 25, true, linkStats, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + true, 36, false, 37, 38, 39, 40, 41 ); } @@ -336,6 +362,13 @@ public class WifiUsabilityStatsEntryTest { for (int link : links) { assertEquals(expected.getRssi(link), actual.getRssi(link)); assertEquals(expected.getRadioId(link), actual.getRadioId(link)); + assertEquals(expected.getFrequencyMhz(link), actual.getFrequencyMhz(link)); + assertEquals(expected.getRssiMgmt(link), actual.getRssiMgmt(link)); + assertEquals(expected.getChannelWidth(link), actual.getChannelWidth(link)); + assertEquals(expected.getCenterFreqFirstSegment(link), + actual.getCenterFreqFirstSegment(link)); + assertEquals(expected.getCenterFreqSecondSegment(link), + actual.getCenterFreqSecondSegment(link)); assertEquals(expected.getTxLinkSpeedMbps(link), actual.getTxLinkSpeedMbps(link)); assertEquals(expected.getRxLinkSpeedMbps(link), @@ -467,6 +500,118 @@ public class WifiUsabilityStatsEntryTest { actual.getContentionTimeStats(link, WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_VO) .getContentionNumSamples()); + assertEquals( + expected.getPacketStats(link, + WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_BE) + .getTxSuccess(), + actual.getPacketStats(link, + WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_BE) + .getTxSuccess()); + assertEquals( + expected.getPacketStats(link, + WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_BE) + .getTxRetries(), + actual.getPacketStats(link, + WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_BE) + .getTxRetries()); + assertEquals( + expected.getPacketStats(link, + WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_BE) + .getTxBad(), + actual.getPacketStats(link, + WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_BE) + .getTxBad()); + assertEquals( + expected.getPacketStats(link, + WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_BE) + .getRxSuccess(), + actual.getPacketStats(link, + WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_BE) + .getRxSuccess()); + assertEquals( + expected.getPacketStats(link, + WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_BK) + .getTxSuccess(), + actual.getPacketStats(link, + WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_BK) + .getTxSuccess()); + assertEquals( + expected.getPacketStats(link, + WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_BK) + .getTxRetries(), + actual.getPacketStats(link, + WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_BK) + .getTxRetries()); + assertEquals( + expected.getPacketStats(link, + WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_BK) + .getTxBad(), + actual.getPacketStats(link, + WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_BK) + .getTxBad()); + assertEquals( + expected.getPacketStats(link, + WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_BK) + .getRxSuccess(), + actual.getPacketStats(link, + WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_BK) + .getRxSuccess()); + assertEquals( + expected.getPacketStats(link, + WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_VI) + .getTxSuccess(), + actual.getPacketStats(link, + WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_VI) + .getTxSuccess()); + assertEquals( + expected.getPacketStats(link, + WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_VI) + .getTxRetries(), + actual.getPacketStats(link, + WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_VI) + .getTxRetries()); + assertEquals( + expected.getPacketStats(link, + WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_VI) + .getTxBad(), + actual.getPacketStats(link, + WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_VI) + .getTxBad()); + assertEquals( + expected.getPacketStats(link, + WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_VI) + .getRxSuccess(), + actual.getPacketStats(link, + WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_VI) + .getRxSuccess()); + assertEquals( + expected.getPacketStats(link, + WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_VO) + .getTxSuccess(), + actual.getPacketStats(link, + WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_VO) + .getTxSuccess()); + assertEquals( + expected.getPacketStats(link, + WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_VO) + .getTxRetries(), + actual.getPacketStats(link, + WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_VO) + .getTxRetries()); + assertEquals( + expected.getPacketStats(link, + WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_VO) + .getTxBad(), + actual.getPacketStats(link, + WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_VO) + .getTxBad()); + assertEquals( + expected.getPacketStats(link, + WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_VO) + .getRxSuccess(), + actual.getPacketStats(link, + WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_VO) + .getRxSuccess()); for (int j = 0; j < expected.getRateStats(link).size(); j++) { RateStats expectedStats = expected.getRateStats(link).get(j); @@ -483,9 +628,35 @@ public class WifiUsabilityStatsEntryTest { assertEquals(expectedStats.getMpduLost(), actualStats.getMpduLost()); assertEquals(expectedStats.getRetries(), actualStats.getRetries()); } - + for (int j = 0; j < expected.getPeerInfo(link).size(); j++) { + PeerInfo expectedStats = expected.getPeerInfo(link).get(j); + PeerInfo actualStats = actual.getPeerInfo(link).get(j); + assertEquals(expectedStats.getStaCount(), actualStats.getStaCount()); + assertEquals(expectedStats.getChanUtil(), actualStats.getChanUtil()); + } + for (int j = 0; j < expected.getScanResultsWithSameFreq(link).length; j++) { + assertEquals(expected.getScanResultsWithSameFreq(link)[j] + .getScanResultTimestampMicros(), + actual.getScanResultsWithSameFreq(link)[j] + .getScanResultTimestampMicros()); + assertEquals(expected.getScanResultsWithSameFreq(link)[j].getRssi(), + actual.getScanResultsWithSameFreq(link)[j].getRssi()); + assertEquals(expected.getScanResultsWithSameFreq(link)[j].getFrequency(), + actual.getScanResultsWithSameFreq(link)[j].getFrequency()); + } } } + assertEquals(expected.getWifiLinkCount(), actual.getWifiLinkCount()); + assertEquals(expected.isNetworkCapabilitiesDownstreamSufficient(), + actual.isNetworkCapabilitiesDownstreamSufficient()); + assertEquals(expected.isNetworkCapabilitiesUpstreamSufficient(), + actual.isNetworkCapabilitiesUpstreamSufficient()); + assertEquals(expected.isThroughputPredictorDownstreamSufficient(), + actual.isThroughputPredictorDownstreamSufficient()); + assertEquals(expected.isThroughputPredictorUpstreamSufficient(), + actual.isThroughputPredictorUpstreamSufficient()); + assertEquals(expected.isBluetoothConnected(), actual.isBluetoothConnected()); + assertEquals(expected.getStatusDataStall(), actual.getStatusDataStall()); } /** @@ -496,14 +667,14 @@ public class WifiUsabilityStatsEntryTest { SparseArray<WifiUsabilityStatsEntry.LinkStats> linkStats = new SparseArray<>(); linkStats.put(0, new WifiUsabilityStatsEntry.LinkStats(0, - WifiUsabilityStatsEntry.LINK_STATE_IN_USE, 0, -50, 300, 200, 188, 2, 2, 100, - 300, 100, 100, 200, - null, null)); + WifiUsabilityStatsEntry.LINK_STATE_IN_USE, 0, -50, 2412, -50, 0, 0, 0, 300, + 200, 188, 2, 2, 100, 300, 100, 100, 200, + null, null, null, null, null)); WifiUsabilityStatsEntry usabilityStatsEntry = new WifiUsabilityStatsEntry( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, - 32, null, null, null, 100, true, - true, true, 23, 24, 25, true, linkStats); + 32, null, null, null, 100, true, true, true, 23, 24, 25, true, linkStats, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, true, 36, true, 37, 38, 39, 40, 41); assertThrows("linkId is invalid - " + MloLink.INVALID_MLO_LINK_ID, NoSuchElementException.class, diff --git a/framework/tests/src/android/net/wifi/p2p/WifiP2pConfigTest.java b/framework/tests/src/android/net/wifi/p2p/WifiP2pConfigTest.java index 15eba6fd8c..afdcd66142 100644 --- a/framework/tests/src/android/net/wifi/p2p/WifiP2pConfigTest.java +++ b/framework/tests/src/android/net/wifi/p2p/WifiP2pConfigTest.java @@ -18,15 +18,20 @@ package android.net.wifi.p2p; import static android.net.wifi.p2p.WifiP2pConfig.GROUP_CLIENT_IP_PROVISIONING_MODE_IPV4_DHCP; import static android.net.wifi.p2p.WifiP2pConfig.GROUP_CLIENT_IP_PROVISIONING_MODE_IPV6_LINK_LOCAL; +import static android.net.wifi.p2p.WifiP2pConfig.P2P_VERSION_2; +import static android.net.wifi.p2p.WifiP2pConfig.PCC_MODE_CONNECTION_TYPE_LEGACY_OR_R2; +import static android.net.wifi.p2p.WifiP2pConfig.PCC_MODE_CONNECTION_TYPE_R2_ONLY; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.junit.Assume.assumeTrue; import android.net.MacAddress; import android.net.wifi.OuiKeyedDataUtil; +import android.net.wifi.util.Environment; import android.os.Parcel; import androidx.test.filters.SmallTest; @@ -42,6 +47,8 @@ import org.junit.Test; public class WifiP2pConfigTest { private static final String DEVICE_ADDRESS = "aa:bb:cc:dd:ee:ff"; + private static final String TEST_NETWORK_NAME = "DIRECT-xy-Android"; + private static final String TEST_PASSPHRASE = "password"; /** * Check network name setter */ @@ -108,12 +115,18 @@ public class WifiP2pConfigTest { @Test public void testBuilderInvalidPassphrase() throws Exception { WifiP2pConfig.Builder b = new WifiP2pConfig.Builder(); - // sunny case try { - b.setPassphrase("abcd1234"); + b.setPassphrase(TEST_PASSPHRASE); } catch (IllegalArgumentException e) { - fail("Unexpected IllegalArgumentException"); + throw new AssertionError("the test failed", e); + } + + // sunny case - password length of less than 128bytes + try { + b.setPassphrase("abed"); + } catch (IllegalArgumentException e) { + throw new AssertionError("the test failed", e); } // null string. @@ -122,11 +135,46 @@ public class WifiP2pConfigTest { fail("should throw IllegalArgumentException"); } catch (IllegalArgumentException e) { // expected exception. + } catch (NullPointerException e) { + // expected exception. + } + + // empty string. + try { + b.setPassphrase(""); + fail("should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected exception. + } + + // Password length of more than 128bytes . + try { + b.setPassphrase("j7YxZqK2gD5fT8rN9bW6hL0vQ3pO1mK4jU7iY9zX8cV5bN2hG1fS6dJ3kH0g" + + "L9wQ8rP7oM6nN5lK4mJ3iO2uY1tX0zW9vU8hG7fS6eD5cR4baa7YxZqK2gD5fT8rN9" + + "bW6hL0vQ2sweder"); + fail("should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected exception. + } + + WifiP2pConfig.Builder c = new WifiP2pConfig.Builder(); + + // sunny case + try { + c.setDeviceAddress(MacAddress.fromString(DEVICE_ADDRESS)) + .setNetworkName(TEST_NETWORK_NAME) + .setPassphrase(TEST_PASSPHRASE) + .build(); + } catch (IllegalArgumentException e) { + throw new AssertionError("the test failed", e); } // less than 8 characters. try { - b.setPassphrase("12abcde"); + c.setDeviceAddress(MacAddress.fromString(DEVICE_ADDRESS)) + .setNetworkName(TEST_NETWORK_NAME) + .setPassphrase("12abide") + .build(); fail("should throw IllegalArgumentException"); } catch (IllegalArgumentException e) { // expected exception. @@ -134,14 +182,86 @@ public class WifiP2pConfigTest { // more than 63 characters. try { - b.setPassphrase( - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890+/"); + c.setDeviceAddress(MacAddress.fromString(DEVICE_ADDRESS)) + .setNetworkName(TEST_NETWORK_NAME) + .setPassphrase("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQ" + + "RSTUVWXYZ1234567890+/") + .build(); fail("should throw IllegalArgumentException"); } catch (IllegalArgumentException e) { // expected exception. } } + /** + * Check Pcc Mode passphrase setter + */ + @Test + public void testPccModeBuilderSetterInvalidPassphrase() throws Exception { + assumeTrue(Environment.isSdkAtLeastB()); + + WifiP2pConfig.Builder c = new WifiP2pConfig.Builder(); + + // sunny case + try { + c.setDeviceAddress(MacAddress.fromString(DEVICE_ADDRESS)) + .setNetworkName(TEST_NETWORK_NAME) + .setPassphrase(TEST_PASSPHRASE) + .setPccModeConnectionType(PCC_MODE_CONNECTION_TYPE_LEGACY_OR_R2) + .build(); + } catch (IllegalArgumentException e) { + throw new AssertionError("the test failed", e); + } + + // more than 63 characters in PCC Mode is not allowed. + try { + c.setDeviceAddress(MacAddress.fromString(DEVICE_ADDRESS)) + .setNetworkName(TEST_NETWORK_NAME) + .setPassphrase("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQ" + + "RSTUVWXYZ1234567890+/") + .setPccModeConnectionType(PCC_MODE_CONNECTION_TYPE_LEGACY_OR_R2) + .build(); + fail("should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected exception. + } + + // less than 8 characters in PCC Mode is not allowed. + try { + c.setDeviceAddress(MacAddress.fromString(DEVICE_ADDRESS)) + .setNetworkName(TEST_NETWORK_NAME) + .setPassphrase("12abcde") + .setPccModeConnectionType(PCC_MODE_CONNECTION_TYPE_LEGACY_OR_R2) + .build(); + fail("should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected exception. + } + + // less than 8 characters is allowed in R2 only mode. + try { + c.setDeviceAddress(MacAddress.fromString(DEVICE_ADDRESS)) + .setNetworkName(TEST_NETWORK_NAME) + .setPassphrase("12") + .setPccModeConnectionType(PCC_MODE_CONNECTION_TYPE_R2_ONLY) + .build(); + } catch (IllegalArgumentException e) { + throw new AssertionError("the test failed", e); + } + + // more than 8 characters is allowed in R2 only mode. + try { + c.setDeviceAddress(MacAddress.fromString(DEVICE_ADDRESS)) + .setNetworkName(TEST_NETWORK_NAME) + .setPassphrase("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQ" + + "RSTUVWXYZ1234567890+/") + .setPccModeConnectionType(PCC_MODE_CONNECTION_TYPE_R2_ONLY) + .build(); + } catch (IllegalArgumentException e) { + throw new AssertionError("the test failed", e); + } + } + /** Verify that a default config can be built. */ @Test public void testBuildDefaultConfig() { @@ -300,4 +420,71 @@ public class WifiP2pConfigTest { config.invalidate(); assertEquals("", config.deviceAddress); } + + /** Verify that a config with the PCC Mode connection type field can be built. */ + @Test + public void testBuildConfigWithPccModeConnectionType() throws Exception { + assumeTrue(Environment.isSdkAtLeastB()); + WifiP2pConfig c = new WifiP2pConfig.Builder() + .setDeviceAddress(MacAddress.fromString(DEVICE_ADDRESS)) + .setPccModeConnectionType(PCC_MODE_CONNECTION_TYPE_LEGACY_OR_R2) + .build(); + assertEquals(c.deviceAddress, DEVICE_ADDRESS); + assertEquals(PCC_MODE_CONNECTION_TYPE_LEGACY_OR_R2, c.getPccModeConnectionType()); + } + + /** Verify that a config with the group owner version field can be built. */ + @Test + public void testBuildConfigWithGroupOwnerVersion() throws Exception { + assumeTrue(Environment.isSdkAtLeastB()); + WifiP2pConfig c = new WifiP2pConfig.Builder() + .setDeviceAddress(MacAddress.fromString(DEVICE_ADDRESS)) + .build(); + c.setGroupOwnerVersion(P2P_VERSION_2); + assertEquals(c.deviceAddress, DEVICE_ADDRESS); + assertEquals(PCC_MODE_CONNECTION_TYPE_LEGACY_OR_R2, c.getGroupOwnerVersion()); + } + + /** Verify that a config pairing bootstrapping configuration can be built. */ + @Test + public void testBuildConfigWithPairingBootstrappingConfig() throws Exception { + assumeTrue(Environment.isSdkAtLeastB()); + WifiP2pPairingBootstrappingConfig expectedPairingBootstrappingConfig = + new WifiP2pPairingBootstrappingConfig(WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_DISPLAY_PINCODE, "1234"); + WifiP2pConfig c = new WifiP2pConfig.Builder() + .setDeviceAddress(MacAddress.fromString(DEVICE_ADDRESS)) + .setPairingBootstrappingConfig(expectedPairingBootstrappingConfig) + .build(); + assertEquals(c.deviceAddress, DEVICE_ADDRESS); + WifiP2pPairingBootstrappingConfig pairingBootstrappingConfig = + c.getPairingBootstrappingConfig(); + assertNotNull(pairingBootstrappingConfig); + assertEquals(expectedPairingBootstrappingConfig, pairingBootstrappingConfig); + assertEquals(c.getGroupClientIpProvisioningMode(), + GROUP_CLIENT_IP_PROVISIONING_MODE_IPV6_LINK_LOCAL); + } + + /** + * Verify that a config with the request to authorize a connection request from a peer device + * can be built. + */ + @Test + public void testBuildConfigWithAuthorizeConnectionFromPeer() throws Exception { + assumeTrue(Environment.isSdkAtLeastB()); + WifiP2pPairingBootstrappingConfig expectedPairingBootstrappingConfig = + new WifiP2pPairingBootstrappingConfig(WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_OUT_OF_BAND, "1234"); + WifiP2pConfig c = new WifiP2pConfig.Builder() + .setDeviceAddress(MacAddress.fromString(DEVICE_ADDRESS)) + .setPairingBootstrappingConfig(expectedPairingBootstrappingConfig) + .setGroupOperatingFrequency(2437) + .setAuthorizeConnectionFromPeerEnabled(true) + .build(); + WifiP2pPairingBootstrappingConfig pairingBootstrappingConfig = + c.getPairingBootstrappingConfig(); + assertNotNull(pairingBootstrappingConfig); + assertEquals(expectedPairingBootstrappingConfig, pairingBootstrappingConfig); + assertTrue(c.isAuthorizeConnectionFromPeerEnabled()); + } } diff --git a/framework/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java b/framework/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java index 611792d7d7..72ee957b83 100644 --- a/framework/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java +++ b/framework/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java @@ -23,6 +23,7 @@ import static org.junit.Assume.assumeTrue; import android.net.MacAddress; import android.net.wifi.OuiKeyedDataUtil; import android.net.wifi.ScanResult; +import android.net.wifi.util.Environment; import android.os.Parcel; import androidx.test.filters.SmallTest; @@ -161,6 +162,10 @@ public class WifiP2pDeviceTest { if (SdkLevel.isAtLeastV()) { device.setVendorData(OuiKeyedDataUtil.createTestOuiKeyedDataList(5)); } + if (Environment.isSdkAtLeastB()) { + device.setPairingBootStrappingMethods(WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_OPPORTUNISTIC); + } Parcel parcel = Parcel.obtain(); device.writeToParcel(parcel, 0); @@ -178,6 +183,9 @@ public class WifiP2pDeviceTest { if (SdkLevel.isAtLeastV()) { assertEquals(device.getVendorData(), unparceledDevice.getVendorData()); } + if (Environment.isSdkAtLeastB()) { + assertTrue(device.isOpportunisticBootstrappingMethodSupported()); + } } /** @@ -207,4 +215,25 @@ public class WifiP2pDeviceTest { device.setIpAddress(ipAddress); assertEquals("192.168.49.1", device.getIpAddress().getHostAddress()); } + + /** + * Test the setter/getter for pairing bootstrapping methods. + */ + @Test + public void testSetPairingBootStrappingMethods() { + assumeTrue(Environment.isSdkAtLeastB()); + WifiP2pDevice device = new WifiP2pDevice(); + device.setPairingBootStrappingMethods(WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_OPPORTUNISTIC + | WifiP2pPairingBootstrappingConfig.PAIRING_BOOTSTRAPPING_METHOD_DISPLAY_PINCODE + | WifiP2pPairingBootstrappingConfig.PAIRING_BOOTSTRAPPING_METHOD_DISPLAY_PASSPHRASE + | WifiP2pPairingBootstrappingConfig.PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PINCODE + | WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PASSPHRASE); + assertTrue(device.isOpportunisticBootstrappingMethodSupported()); + assertTrue(device.isPinCodeDisplayBootstrappingMethodSupported()); + assertTrue(device.isPassphraseDisplayBootstrappingMethodSupported()); + assertTrue(device.isPinCodeKeypadBootstrappingMethodSupported()); + assertTrue(device.isPassphraseKeypadBootstrappingMethodSupported()); + } } diff --git a/framework/tests/src/android/net/wifi/p2p/WifiP2pDirInfoTest.java b/framework/tests/src/android/net/wifi/p2p/WifiP2pDirInfoTest.java new file mode 100644 index 0000000000..f032fbeaba --- /dev/null +++ b/framework/tests/src/android/net/wifi/p2p/WifiP2pDirInfoTest.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2024 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 android.net.wifi.p2p; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assume.assumeTrue; + +import android.net.MacAddress; +import android.net.wifi.util.Environment; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; +/** + * Unit tests for {@link WifiP2pDirInfo} + */ +@SmallTest +public final class WifiP2pDirInfoTest { + private static final String TEST_MAC_ADDRESS_STRING = "00:11:22:33:44:55"; + private static final byte[] TEST_NONCE = {10, 20, 30, 40, 50, 60, 70, 80}; + private static final byte[] TEST_DIR_TAG = {11, 22, 33, 44, 55, 66, 77, 88}; + @Test + public void testWifiP2pDirInfo() { + assumeTrue(Environment.isSdkAtLeastB()); + WifiP2pDirInfo dirInfo = new WifiP2pDirInfo( + MacAddress.fromString(TEST_MAC_ADDRESS_STRING), TEST_NONCE, TEST_DIR_TAG); + assertNotNull(dirInfo); + assertEquals(MacAddress.fromString(TEST_MAC_ADDRESS_STRING), dirInfo.getMacAddress()); + assertArrayEquals(TEST_NONCE, dirInfo.getNonce()); + assertArrayEquals(TEST_DIR_TAG, dirInfo.getDirTag()); + } +} diff --git a/framework/tests/src/android/net/wifi/p2p/WifiP2pGroupTest.java b/framework/tests/src/android/net/wifi/p2p/WifiP2pGroupTest.java index e84a76bc28..4cb9785fa1 100644 --- a/framework/tests/src/android/net/wifi/p2p/WifiP2pGroupTest.java +++ b/framework/tests/src/android/net/wifi/p2p/WifiP2pGroupTest.java @@ -20,16 +20,19 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; import android.net.InetAddresses; import android.net.MacAddress; import android.net.wifi.OuiKeyedData; import android.net.wifi.OuiKeyedDataUtil; +import android.net.wifi.util.Environment; import android.os.Parcel; import androidx.test.filters.SmallTest; import com.android.modules.utils.build.SdkLevel; +import com.android.wifi.flags.Flags; import org.junit.Test; @@ -50,6 +53,8 @@ public class WifiP2pGroupTest { private static final int FREQUENCY = 5300; private static final String CLIENT_1_DEV_ADDRESS = "aa:bb:cc:dd:ee:01"; private static final String CLIENT_2_DEV_ADDRESS = "aa:bb:cc:dd:ee:02"; + private static final byte[] GROUP_OWNER_INTERFACE_ADDRESS = + { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }; private static final WifiP2pDevice CLIENT_1 = new WifiP2pDevice(CLIENT_1_DEV_ADDRESS); private static final WifiP2pDevice CLIENT_2 = new WifiP2pDevice(CLIENT_2_DEV_ADDRESS); private static final MacAddress CLIENT_1_INTERFACE_MAC_ADDRESS = @@ -83,6 +88,9 @@ public class WifiP2pGroupTest { if (SdkLevel.isAtLeastV()) { group.setVendorData(VENDOR_DATA); } + if (Environment.isSdkAtLeastB() && Flags.wifiDirectR2()) { + group.setSecurityType(WifiP2pGroup.SECURITY_TYPE_WPA3_COMPATIBILITY); + } assertEquals(INTERFACE, group.getInterface()); assertEquals(NETWORK_ID, group.getNetworkId()); @@ -94,6 +102,10 @@ public class WifiP2pGroupTest { if (SdkLevel.isAtLeastV()) { assertTrue(VENDOR_DATA.equals(group.getVendorData())); } + if (Environment.isSdkAtLeastB() && Flags.wifiDirectR2()) { + assertEquals(WifiP2pGroup.SECURITY_TYPE_WPA3_COMPATIBILITY, + group.getSecurityType()); + } assertFalse(group.isClientListEmpty()); assertTrue(group.contains(CLIENT_1)); @@ -135,4 +147,16 @@ public class WifiP2pGroupTest { assertEquals(group.toString(), fromParcel.toString()); } + + /** Verify {@link WifiP2pGroup#getGroupOwnerBssid()} */ + @Test + public void testGetGroupOwnerBssid() throws Exception { + assumeTrue(Environment.isSdkAtLeastB()); + WifiP2pGroup group = new WifiP2pGroup(); + group.setIsGroupOwner(true); + group.interfaceAddress = GROUP_OWNER_INTERFACE_ADDRESS; + group.setSecurityType(WifiP2pGroup.SECURITY_TYPE_WPA3_SAE); + assertEquals(MacAddress.fromBytes(GROUP_OWNER_INTERFACE_ADDRESS), + group.getGroupOwnerBssid()); + } } diff --git a/framework/tests/src/android/net/wifi/p2p/WifiP2pManagerTest.java b/framework/tests/src/android/net/wifi/p2p/WifiP2pManagerTest.java index 21c42d8760..f46456e276 100644 --- a/framework/tests/src/android/net/wifi/p2p/WifiP2pManagerTest.java +++ b/framework/tests/src/android/net/wifi/p2p/WifiP2pManagerTest.java @@ -35,6 +35,7 @@ import android.content.AttributionSource; import android.content.Context; import android.net.wifi.ScanResult; import android.net.wifi.SynchronousExecutor; +import android.net.wifi.util.Environment; import android.os.Bundle; import android.os.test.TestLooper; import android.view.Display; @@ -281,4 +282,32 @@ public class WifiP2pManagerTest { mDut.unregisterWifiP2pListener(listener); verify(mP2pServiceMock).unregisterWifiP2pListener(any(IWifiP2pListener.Stub.class)); } + + /** + * Test {@link WifiP2pManager#isWiFiDirectR2Supported()} works as + * expected. + */ + @Test + public void testIsWiFiDirectR2Supported() throws Exception { + assumeTrue(Environment.isSdkAtLeastB()); + when(mP2pServiceMock.getSupportedFeatures()).thenReturn(0L); + assertFalse(mDut.isWiFiDirectR2Supported()); + when(mP2pServiceMock.getSupportedFeatures()).thenReturn( + WifiP2pManager.FEATURE_WIFI_DIRECT_R2); + assertTrue(mDut.isWiFiDirectR2Supported()); + } + + /** + * Test {@link WifiP2pManager#isPccModeSupported()} works as + * expected. + */ + @Test + public void testIsPccModeSupported() throws Exception { + assumeTrue(Environment.isSdkAtLeastB()); + when(mP2pServiceMock.getSupportedFeatures()).thenReturn(0L); + assertFalse(mDut.isPccModeSupported()); + when(mP2pServiceMock.getSupportedFeatures()).thenReturn( + WifiP2pManager.FEATURE_PCC_MODE_ALLOW_LEGACY_AND_R2_CONNECTION); + assertTrue(mDut.isPccModeSupported()); + } } diff --git a/framework/tests/src/android/net/wifi/p2p/WifiP2pProvDiscEventTest.java b/framework/tests/src/android/net/wifi/p2p/WifiP2pProvDiscEventTest.java index e3b10a7a52..6a1705fb44 100644 --- a/framework/tests/src/android/net/wifi/p2p/WifiP2pProvDiscEventTest.java +++ b/framework/tests/src/android/net/wifi/p2p/WifiP2pProvDiscEventTest.java @@ -43,7 +43,7 @@ public class WifiP2pProvDiscEventTest { public void testPbcReqEvent() throws Exception { WifiP2pProvDiscEvent event = new WifiP2pProvDiscEvent(EVENT_PBC_REQ_STRING + " " + DEVICE_ADDRESS); - assertEquals(WifiP2pProvDiscEvent.PBC_REQ, event.event); + assertEquals(WifiP2pProvDiscEvent.WPS_PBC_REQ, event.event); assertEquals(DEVICE_ADDRESS, event.device.deviceAddress); } @@ -55,7 +55,7 @@ public class WifiP2pProvDiscEventTest { public void testPbcRespEvent() throws Exception { WifiP2pProvDiscEvent event = new WifiP2pProvDiscEvent(EVENT_PBC_RSP_STRING + " " + DEVICE_ADDRESS); - assertEquals(WifiP2pProvDiscEvent.PBC_RSP, event.event); + assertEquals(WifiP2pProvDiscEvent.WPS_PBC_RSP, event.event); assertEquals(DEVICE_ADDRESS, event.device.deviceAddress); } @@ -66,7 +66,7 @@ public class WifiP2pProvDiscEventTest { public void testEnterPinEvent() throws Exception { WifiP2pProvDiscEvent event = new WifiP2pProvDiscEvent(EVENT_ENTER_PIN_STRING + " " + DEVICE_ADDRESS); - assertEquals(WifiP2pProvDiscEvent.ENTER_PIN, event.event); + assertEquals(WifiP2pProvDiscEvent.WPS_ENTER_PIN, event.event); assertEquals(DEVICE_ADDRESS, event.device.deviceAddress); } @@ -78,9 +78,9 @@ public class WifiP2pProvDiscEventTest { WifiP2pProvDiscEvent event = new WifiP2pProvDiscEvent( EVENT_SHOW_PIN_STRING + " " + DEVICE_ADDRESS + " " + TEST_PIN); - assertEquals(WifiP2pProvDiscEvent.SHOW_PIN, event.event); + assertEquals(WifiP2pProvDiscEvent.WPS_SHOW_PIN, event.event); assertEquals(DEVICE_ADDRESS, event.device.deviceAddress); - assertEquals(TEST_PIN, event.pin); + assertEquals(TEST_PIN, event.wpsPin); } /** diff --git a/framework/tests/src/android/net/wifi/p2p/WifiP2pUsdBasedLocalServiceAdvertisementConfigTest.java b/framework/tests/src/android/net/wifi/p2p/WifiP2pUsdBasedLocalServiceAdvertisementConfigTest.java new file mode 100644 index 0000000000..6a465f32fc --- /dev/null +++ b/framework/tests/src/android/net/wifi/p2p/WifiP2pUsdBasedLocalServiceAdvertisementConfigTest.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2024 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 android.net.wifi.p2p; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assume.assumeTrue; + +import android.net.wifi.util.Environment; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; +/** + * Unit tests for {@link WifiP2pUsdBasedLocalServiceAdvertisementConfigTest} + */ +@SmallTest +public final class WifiP2pUsdBasedLocalServiceAdvertisementConfigTest { + private static final int TEST_USD_DISCOVERY_CHANNEL_FREQUENCY_MHZ = 2412; + @Test + public void testWifiP2pUsdBasedLocalServiceAdvertisementConfig() { + assumeTrue(Environment.isSdkAtLeastB()); + WifiP2pUsdBasedLocalServiceAdvertisementConfig localServiceAdvertisementConfig = + new WifiP2pUsdBasedLocalServiceAdvertisementConfig.Builder() + .setFrequencyMhz(TEST_USD_DISCOVERY_CHANNEL_FREQUENCY_MHZ).build(); + assertNotNull(localServiceAdvertisementConfig); + assertEquals(TEST_USD_DISCOVERY_CHANNEL_FREQUENCY_MHZ, + localServiceAdvertisementConfig.getFrequencyMhz()); + } +} diff --git a/framework/tests/src/android/net/wifi/p2p/WifiP2pUsdBasedServiceDiscoveryConfigTest.java b/framework/tests/src/android/net/wifi/p2p/WifiP2pUsdBasedServiceDiscoveryConfigTest.java new file mode 100644 index 0000000000..2f82851022 --- /dev/null +++ b/framework/tests/src/android/net/wifi/p2p/WifiP2pUsdBasedServiceDiscoveryConfigTest.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2024 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 android.net.wifi.p2p; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assume.assumeTrue; + +import android.net.wifi.ScanResult; +import android.net.wifi.util.Environment; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; +/** + * Unit tests for {@link WifiP2pUsdBasedServiceDiscoveryConfig} + */ +@SmallTest +public final class WifiP2pUsdBasedServiceDiscoveryConfigTest { + private static final int[] TEST_USD_DISCOVERY_CHANNEL_FREQUENCIES_MHZ = {2412, 2437, 2462}; + @Test + public void testWifiP2pUsdBasedServiceDiscoveryConfig() { + assumeTrue(Environment.isSdkAtLeastB()); + WifiP2pUsdBasedServiceDiscoveryConfig serviceDiscoveryConfig = + new WifiP2pUsdBasedServiceDiscoveryConfig.Builder() + .setFrequenciesMhz(TEST_USD_DISCOVERY_CHANNEL_FREQUENCIES_MHZ).build(); + assertNotNull(serviceDiscoveryConfig); + assertArrayEquals(TEST_USD_DISCOVERY_CHANNEL_FREQUENCIES_MHZ, + serviceDiscoveryConfig.getFrequenciesMhz()); + assertEquals(ScanResult.UNSPECIFIED, serviceDiscoveryConfig.getBand()); + } +} diff --git a/framework/tests/src/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceRequestTest.java b/framework/tests/src/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceRequestTest.java index 7d46a5f3a7..b2feabad9c 100644 --- a/framework/tests/src/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceRequestTest.java +++ b/framework/tests/src/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceRequestTest.java @@ -46,7 +46,7 @@ public class WifiP2pDnsSdServiceRequestTest { // failure case due to null service type try { - request = WifiP2pDnsSdServiceRequest.newInstance(null); + request = WifiP2pDnsSdServiceRequest.newInstance((String) null); fail("should throw IllegalArgumentException"); } catch (IllegalArgumentException ex) { // expected exception. diff --git a/framework/tests/src/android/net/wifi/p2p/nsd/WifiP2pUpnpServiceRequestTest.java b/framework/tests/src/android/net/wifi/p2p/nsd/WifiP2pUpnpServiceRequestTest.java index 79930dc833..60d906ec95 100644 --- a/framework/tests/src/android/net/wifi/p2p/nsd/WifiP2pUpnpServiceRequestTest.java +++ b/framework/tests/src/android/net/wifi/p2p/nsd/WifiP2pUpnpServiceRequestTest.java @@ -40,7 +40,7 @@ public class WifiP2pUpnpServiceRequestTest { // failure case due to null target string try { - request = WifiP2pUpnpServiceRequest.newInstance(null); + request = WifiP2pUpnpServiceRequest.newInstance((String) null); fail("should throw IllegalArgumentException"); } catch (IllegalArgumentException ex) { // expected exception. diff --git a/framework/tests/src/android/net/wifi/p2p/nsd/WifiP2pUsdBasedServiceInfoTest.java b/framework/tests/src/android/net/wifi/p2p/nsd/WifiP2pUsdBasedServiceInfoTest.java new file mode 100644 index 0000000000..1bf4518a71 --- /dev/null +++ b/framework/tests/src/android/net/wifi/p2p/nsd/WifiP2pUsdBasedServiceInfoTest.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2024 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 android.net.wifi.p2p.nsd; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assume.assumeTrue; + +import android.net.wifi.util.Environment; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; +/** + * Unit tests for creating {@link WifiP2pServiceInfo} with + * {@link WifiP2pUsdBasedServiceConfig}. + */ +@SmallTest +public final class WifiP2pUsdBasedServiceInfoTest { + private static final String TEST_USD_SERVICE_NAME = "test_service_name"; + private static final int TEST_USD_PROTOCOL_TYPE = 4; + private static final byte[] TEST_USD_SERVICE_SPECIFIC_INFO = {10, 20, 30, 40, 50, 60}; + @Test + public void testWifiP2pUsdBasedServiceInfo() { + assumeTrue(Environment.isSdkAtLeastB()); + WifiP2pUsdBasedServiceConfig expectedUsdConfig = new WifiP2pUsdBasedServiceConfig.Builder( + TEST_USD_SERVICE_NAME) + .setServiceProtocolType(TEST_USD_PROTOCOL_TYPE) + .setServiceSpecificInfo(TEST_USD_SERVICE_SPECIFIC_INFO).build(); + WifiP2pServiceInfo serviceInfo = new WifiP2pServiceInfo(expectedUsdConfig); + assertNotNull(serviceInfo); + WifiP2pUsdBasedServiceConfig usdConfig = + serviceInfo.getWifiP2pUsdBasedServiceConfig(); + assertNotNull(usdConfig); + assertEquals(TEST_USD_SERVICE_NAME, usdConfig.getServiceName()); + assertEquals(TEST_USD_PROTOCOL_TYPE, usdConfig.getServiceProtocolType()); + assertArrayEquals(TEST_USD_SERVICE_SPECIFIC_INFO, usdConfig.getServiceSpecificInfo()); + } +} diff --git a/framework/tests/src/android/net/wifi/p2p/nsd/WifiP2pUsdBasedServiceRequestTest.java b/framework/tests/src/android/net/wifi/p2p/nsd/WifiP2pUsdBasedServiceRequestTest.java new file mode 100644 index 0000000000..a53df44498 --- /dev/null +++ b/framework/tests/src/android/net/wifi/p2p/nsd/WifiP2pUsdBasedServiceRequestTest.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2024 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 android.net.wifi.p2p.nsd; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assume.assumeTrue; + +import android.net.wifi.util.Environment; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; +/** + * Unit tests for creating {@link WifiP2pServiceRequest} with + * {@link WifiP2pUsdBasedServiceConfig}. + */ +@SmallTest +public final class WifiP2pUsdBasedServiceRequestTest { + private static final String TEST_USD_SERVICE_NAME = "test_service_name"; + private static final int TEST_USD_PROTOCOL_TYPE = 4; + private static final byte[] TEST_USD_SERVICE_SPECIFIC_INFO = {10, 20, 30, 40, 50, 60}; + @Test + public void testWifiP2pUsdBasedServiceRequest() { + assumeTrue(Environment.isSdkAtLeastB()); + WifiP2pUsdBasedServiceConfig expectedUsdConfig = new WifiP2pUsdBasedServiceConfig.Builder( + TEST_USD_SERVICE_NAME) + .setServiceProtocolType(TEST_USD_PROTOCOL_TYPE) + .setServiceSpecificInfo(TEST_USD_SERVICE_SPECIFIC_INFO).build(); + WifiP2pServiceRequest serviceRequest = new WifiP2pServiceRequest(expectedUsdConfig); + assertNotNull(serviceRequest); + WifiP2pUsdBasedServiceConfig usdConfig = + serviceRequest.getWifiP2pUsdBasedServiceConfig(); + assertNotNull(usdConfig); + assertEquals(TEST_USD_SERVICE_NAME, usdConfig.getServiceName()); + assertEquals(TEST_USD_PROTOCOL_TYPE, usdConfig.getServiceProtocolType()); + assertArrayEquals(TEST_USD_SERVICE_SPECIFIC_INFO, usdConfig.getServiceSpecificInfo()); + } +} diff --git a/framework/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java b/framework/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java index 5514224ce6..f59fbc9243 100644 --- a/framework/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java +++ b/framework/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java @@ -634,6 +634,10 @@ public class WifiRttManagerTest { heCap.id = ScanResult.InformationElement.EID_EXTENSION_PRESENT; heCap.idExt = ScanResult.InformationElement.EID_EXT_HE_CAPABILITIES; + ScanResult.InformationElement ehtCap = new ScanResult.InformationElement(); + ehtCap.id = ScanResult.InformationElement.EID_EXTENSION_PRESENT; + ehtCap.idExt = ScanResult.InformationElement.EID_EXT_EHT_CAPABILITIES; + // no IE ScanResult scan = new ScanResult(); scan.BSSID = "00:01:02:03:04:05"; @@ -689,6 +693,57 @@ public class WifiRttManagerTest { config = ResponderConfig.fromScanResult(scan); assertEquals(ResponderConfig.PREAMBLE_HE, config.preamble); + + ScanResult.InformationElement[] ie = new ScanResult.InformationElement[3]; + ie[0] = vhtCap; + ie[1] = heCap; + ie[2] = ehtCap; + + ScanResult.Builder builder = new ScanResult.Builder() + .setBssid("00:01:02:03:04:05") + .setChannelWidth(ResponderConfig.CHANNEL_WIDTH_80MHZ); + + // Validate 11az & 11mc ranging in 5 Ghz and EHT + scan = builder.setFrequency(5200).setIs80211azNtbRTTResponder(true) + .setIs80211McRTTResponder(true).build(); + scan.informationElements = ie; + config = ResponderConfig.fromScanResult(scan); + assertEquals(ResponderConfig.PREAMBLE_EHT, config.preamble); + + // Validate 11az & 11mc ranging in 6 Ghz and EHT + scan = builder.setFrequency(5935).setIs80211azNtbRTTResponder(true) + .setIs80211McRTTResponder(true).build(); + scan.informationElements = ie; + config = ResponderConfig.fromScanResult(scan); + assertEquals(ResponderConfig.PREAMBLE_EHT, config.preamble); + + // Validate 11mc ranging in 5 Ghz with EHT + scan = builder.setFrequency(5200).setIs80211azNtbRTTResponder(false) + .setIs80211McRTTResponder(true).build(); + scan.informationElements = ie; + config = ResponderConfig.fromScanResult(scan); + assertEquals(ResponderConfig.PREAMBLE_VHT, config.preamble); + + // Validate one-sided ranging in 5 Ghz with EHT; Same result as 11mc. + scan = builder.setFrequency(5200).setIs80211azNtbRTTResponder(false) + .setIs80211McRTTResponder(false).build(); + scan.informationElements = ie; + config = ResponderConfig.fromScanResult(scan); + assertEquals(ResponderConfig.PREAMBLE_VHT, config.preamble); + + // Validate 11mc ranging in 6 Ghz with EHT + scan = builder.setFrequency(5935).setIs80211azNtbRTTResponder(false) + .setIs80211McRTTResponder(true).build(); + scan.informationElements = ie; + config = ResponderConfig.fromScanResult(scan); + assertEquals(ResponderConfig.PREAMBLE_EHT, config.preamble); + + // Validate one-sided ranging in 6 Ghz with EHT; Same result as 11mc. + scan = builder.setFrequency(5935).setIs80211azNtbRTTResponder(false) + .setIs80211McRTTResponder(false).build(); + scan.informationElements = ie; + config = ResponderConfig.fromScanResult(scan); + assertEquals(ResponderConfig.PREAMBLE_EHT, config.preamble); } @Test diff --git a/framework/tests/src/android/net/wifi/usd/SubscribeConfigTest.java b/framework/tests/src/android/net/wifi/usd/SubscribeConfigTest.java new file mode 100644 index 0000000000..fa76b12551 --- /dev/null +++ b/framework/tests/src/android/net/wifi/usd/SubscribeConfigTest.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2025 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 android.net.wifi.usd; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import android.os.Parcel; + +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + + +/** + * Unit test harness for SubscribeConfig class. + */ +@SmallTest +public class SubscribeConfigTest { + private static final String USD_SERVICE_NAME = "USD_UNIT_TEST"; + private static final byte[] TEST_SSI = new byte[]{1, 2, 3, 4}; + private static final int TEST_TTL_SECONDS = 3000; + private static final int TEST_QUERY_PERIOD_MILLIS = 200; + private static final int[] TEST_FREQUENCIES = new int[]{2412, 2437, 2462}; + private List<byte[]> mFilter; + + @Before + public void setUp() throws Exception { + mFilter = new ArrayList<>(); + mFilter.add(new byte[]{10, 11}); + mFilter.add(new byte[]{12, 13, 14}); + } + + /** + * Tests set and get for SubscribeConfig. + */ + @Test + public void testSubscribeConfig() { + SubscribeConfig subscribeConfig = new SubscribeConfig.Builder( + USD_SERVICE_NAME).setQueryPeriodMillis(TEST_QUERY_PERIOD_MILLIS).setSubscribeType( + SubscribeConfig.SUBSCRIBE_TYPE_ACTIVE).setServiceProtoType( + SubscribeConfig.SERVICE_PROTO_TYPE_GENERIC).setTtlSeconds( + TEST_TTL_SECONDS).setRecommendedOperatingFrequenciesMhz( + TEST_FREQUENCIES).setServiceSpecificInfo(TEST_SSI).setTxMatchFilter( + mFilter).setRxMatchFilter(mFilter).setOperatingFrequenciesMhz( + TEST_FREQUENCIES).build(); + assertArrayEquals(USD_SERVICE_NAME.getBytes(), subscribeConfig.getServiceName()); + assertEquals(200, subscribeConfig.getQueryPeriodMillis()); + assertEquals(SubscribeConfig.SUBSCRIBE_TYPE_ACTIVE, subscribeConfig.getSubscribeType()); + assertEquals(SubscribeConfig.SERVICE_PROTO_TYPE_GENERIC, + subscribeConfig.getServiceProtoType()); + assertEquals(TEST_TTL_SECONDS, subscribeConfig.getTtlSeconds()); + assertArrayEquals(TEST_FREQUENCIES, + subscribeConfig.getRecommendedOperatingFrequenciesMhz()); + assertArrayEquals(TEST_SSI, subscribeConfig.getServiceSpecificInfo()); + assertEquals(mFilter.size(), subscribeConfig.getRxMatchFilter().size()); + assertEquals(mFilter.size(), subscribeConfig.getTxMatchFilter().size()); + for (int i = 0; i < mFilter.size(); i++) { + assertArrayEquals(mFilter.get(i), subscribeConfig.getRxMatchFilter().get(i)); + assertArrayEquals(mFilter.get(i), subscribeConfig.getTxMatchFilter().get(i)); + } + assertArrayEquals(TEST_FREQUENCIES, subscribeConfig.getOperatingFrequenciesMhz()); + } + + /** + * Tests SubscribeConfig with invalid arguments. + */ + @Test + public void testSubscribeConfigWithInvalidArgs() { + assertThrows(NullPointerException.class, () -> new SubscribeConfig.Builder(null)); + assertThrows(IllegalArgumentException.class, () -> new SubscribeConfig.Builder("")); + assertThrows(IllegalArgumentException.class, + () -> new SubscribeConfig.Builder("a".repeat(258))); + SubscribeConfig.Builder builder = new SubscribeConfig.Builder(USD_SERVICE_NAME); + assertThrows(IllegalArgumentException.class, () -> builder.setQueryPeriodMillis(-1)); + assertThrows(IllegalArgumentException.class, () -> builder.setSubscribeType(4)); + assertThrows(IllegalArgumentException.class, () -> builder.setServiceProtoType(4)); + assertThrows(IllegalArgumentException.class, () -> builder.setTtlSeconds(-1)); + assertThrows(NullPointerException.class, + () -> builder.setRecommendedOperatingFrequenciesMhz(null)); + assertThrows(IllegalArgumentException.class, + () -> builder.setRecommendedOperatingFrequenciesMhz(new int[]{1, 2, 3})); + assertThrows(IllegalArgumentException.class, + () -> builder.setRecommendedOperatingFrequenciesMhz( + new int[Config.MAX_NUM_OF_OPERATING_FREQUENCIES + 1])); + assertThrows(IllegalArgumentException.class, () -> builder.setQueryPeriodMillis(-1)); + assertThrows(NullPointerException.class, () -> builder.setServiceSpecificInfo(null)); + assertThrows(NullPointerException.class, () -> builder.setRxMatchFilter(null)); + assertThrows(NullPointerException.class, () -> builder.setTxMatchFilter(null)); + assertThrows(NullPointerException.class, () -> builder.setOperatingFrequenciesMhz(null)); + assertThrows(IllegalArgumentException.class, + () -> builder.setOperatingFrequenciesMhz(new int[]{1, 2, 3})); + assertThrows(IllegalArgumentException.class, () -> builder.setOperatingFrequenciesMhz( + new int[Config.MAX_NUM_OF_OPERATING_FREQUENCIES + 1])); + } + + /** + * Tests SubscribeConfig object is correctly serialized and deserialized when using parcel. + */ + @Test + public void testSubscribeConfigParcel() { + // Create SubscribeConfig + SubscribeConfig subscribeConfig = new SubscribeConfig.Builder( + USD_SERVICE_NAME).setQueryPeriodMillis(TEST_QUERY_PERIOD_MILLIS).setSubscribeType( + SubscribeConfig.SUBSCRIBE_TYPE_ACTIVE).setServiceProtoType( + SubscribeConfig.SERVICE_PROTO_TYPE_GENERIC).setTtlSeconds( + TEST_TTL_SECONDS).setRecommendedOperatingFrequenciesMhz( + TEST_FREQUENCIES).setServiceSpecificInfo(TEST_SSI).setTxMatchFilter( + mFilter).setRxMatchFilter(mFilter).setOperatingFrequenciesMhz( + TEST_FREQUENCIES).build(); + // Serialize SubscribeConfig to parcel + Parcel parcel = Parcel.obtain(); + subscribeConfig.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + // Deserialize SubscribeConfig from parcel + SubscribeConfig deserializedSubscribeConfig = SubscribeConfig.CREATOR.createFromParcel( + parcel); + // Validate deserialized SubscribeConfig is equal to original SubscribeConfig + assertEquals(subscribeConfig, deserializedSubscribeConfig); + assertEquals(subscribeConfig.hashCode(), deserializedSubscribeConfig.hashCode()); + // Release the parcel + parcel.recycle(); + } +} diff --git a/framework/tests/src/android/net/wifi/util/WifiResourceCacheTest.java b/framework/tests/src/android/net/wifi/util/WifiResourceCacheTest.java index f1a7578211..c05b49f778 100644 --- a/framework/tests/src/android/net/wifi/util/WifiResourceCacheTest.java +++ b/framework/tests/src/android/net/wifi/util/WifiResourceCacheTest.java @@ -155,11 +155,11 @@ public class WifiResourceCacheTest { public void testOverrideStringArrayResource() { mWifiResourceCache.restoreStringArrayValue(String.valueOf(TEST_ID)); when(mResources.getStringArray(TEST_ID)).thenReturn(STRINGS_1); - assertEquals(STRINGS_1, mWifiResourceCache.getStringArray(TEST_ID)); + assertArrayEquals(STRINGS_1, mWifiResourceCache.getStringArray(TEST_ID)); mWifiResourceCache.overrideStringArrayValue(String.valueOf(TEST_ID), STRINGS_2); - assertEquals(STRINGS_2, mWifiResourceCache.getStringArray(TEST_ID)); + assertArrayEquals(STRINGS_2, mWifiResourceCache.getStringArray(TEST_ID)); mWifiResourceCache.restoreStringArrayValue(String.valueOf(TEST_ID)); - assertEquals(STRINGS_1, mWifiResourceCache.getStringArray(TEST_ID)); + assertArrayEquals(STRINGS_1, mWifiResourceCache.getStringArray(TEST_ID)); verify(mResources, times(2)).getStringArray(TEST_ID); } @@ -180,4 +180,17 @@ public class WifiResourceCacheTest { verify(mResources, times(2)).getBoolean(TEST_ID); verify(mResources, times(2)).getInteger(TEST_ID + 1); } + + @Test + public void testHandleLocaleChange() { + when(mResources.getStringArray(TEST_ID)).thenReturn(STRINGS_1); + when(mResources.getString(TEST_ID + 1)).thenReturn(STRING_1); + assertArrayEquals(STRINGS_1, mWifiResourceCache.getStringArray(TEST_ID)); + assertEquals(STRING_1, mWifiResourceCache.getString(TEST_ID + 1)); + when(mResources.getStringArray(TEST_ID)).thenReturn(STRINGS_2); + when(mResources.getString(TEST_ID + 1)).thenReturn(STRING_2); + mWifiResourceCache.handleLocaleChange(); + assertArrayEquals(STRINGS_2, mWifiResourceCache.getStringArray(TEST_ID)); + assertEquals(STRING_2, mWifiResourceCache.getString(TEST_ID + 1)); + } } diff --git a/jni/Android.bp b/jni/Android.bp new file mode 100644 index 0000000000..d67c6aaf3f --- /dev/null +++ b/jni/Android.bp @@ -0,0 +1,17 @@ +cc_library_shared { + name: "libservice-wifi-jni", + min_sdk_version: "30", + cflags: [ + "-Wall", + "-Werror", + ], + srcs: [ + "com_android_server_ServiceManagerWrapper.cpp", + ], + shared_libs: [ + "libbinder_ndk", + ], + apex_available: [ + "com.android.wifi", + ], +} diff --git a/jni/com_android_server_ServiceManagerWrapper.cpp b/jni/com_android_server_ServiceManagerWrapper.cpp new file mode 100644 index 0000000000..b13346b092 --- /dev/null +++ b/jni/com_android_server_ServiceManagerWrapper.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2024 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. + */ + +#include <android/binder_auto_utils.h> +#include <android/binder_ibinder_jni.h> +#include <android/binder_manager.h> +#include <jni.h> + +namespace android { + +// nativeWaitForService +extern "C" JNIEXPORT jobject JNICALL + Java_com_android_server_wifi_mainline_1supplicant_ServiceManagerWrapper_nativeWaitForService__Ljava_lang_String_2( + JNIEnv* env, jobject /* clazz */, jstring serviceNameJni) { + // AServiceManager_isDeclared and AServiceManager_waitForService were added in Android 31. + // Because this method will only be called on 35+, we can suppress the availability warning. + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wunguarded-availability" + const char* serviceName = env->GetStringUTFChars(serviceNameJni, nullptr); + return AIBinder_toJavaBinder(env, AServiceManager_waitForService(serviceName)); + #pragma clang diagnostic pop +} + +}; // namespace android diff --git a/service/Android.bp b/service/Android.bp index c111ede747..b3f5ca1a50 100644 --- a/service/Android.bp +++ b/service/Android.bp @@ -66,6 +66,7 @@ java_library { "framework-location.stubs.module_lib", "framework-statsd.stubs.module_lib", "framework-tethering.stubs.module_lib", + "framework-uwb.stubs.module_lib", "unsupportedappusage", "app-compat-annotations", "auto_value_annotations", @@ -77,9 +78,9 @@ java_library { static_libs: [ // Types-only package shared across the HALs - "android.hardware.wifi.common-V1-java", + "android.hardware.wifi.common-V2-java", // AIDL vendor hal implementation - "android.hardware.wifi-V2-java", + "android.hardware.wifi-V3-java", // HIDL vendor hal implementation "android.hardware.wifi-V1.0-java", "android.hardware.wifi-V1.1-java", @@ -89,7 +90,7 @@ java_library { "android.hardware.wifi-V1.5-java", "android.hardware.wifi-V1.6-java", // AIDL hostapd implementation - "android.hardware.wifi.hostapd-V2-java", + "android.hardware.wifi.hostapd-V3-java", // HIDL hostapd implementation "android.hardware.wifi.hostapd-V1.0-java", "android.hardware.wifi.hostapd-V1.1-java", @@ -104,6 +105,7 @@ java_library { "android.hardware.wifi.supplicant-V1.3-java", "android.hardware.wifi.supplicant-V1.4-java", "android.hidl.manager-V1.2-java", + "android.system.wifi.mainline_supplicant-java", "bouncycastle-unbundled", "ksoap2", // Note: libprotobuf-java-lite uses a few core platform APIs which @@ -121,7 +123,9 @@ java_library { "service-entitlement", "wifi-lite-protos", "wifi-nano-protos", + "com.android.net.flags-aconfig-java-export", "android.net.wifi.flags-aconfig-java", + "android.security.flags-aconfig-java-export", "net-utils-service-wifi", ], apex_available: ["com.android.wifi"], diff --git a/service/ServiceWifiResources/res/layout/wifi_p2p_dialog2_display_pin.xml b/service/ServiceWifiResources/res/layout/wifi_p2p_dialog2_display_pin.xml new file mode 100644 index 0000000000..020f987d8d --- /dev/null +++ b/service/ServiceWifiResources/res/layout/wifi_p2p_dialog2_display_pin.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2024 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. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + style="@style/wifi_p2p_dialog2_display_pin_section"> + <TextView + android:text="@string/wifi_p2p_dialog2_display_pin_label" + style="@style/wifi_p2p_dialog2_display_pin_label"/> + + <TextView android:id="@+id/wifi_p2p_dialog2_display_pin" + style="@style/wifi_p2p_dialog2_display_pin" /> +</LinearLayout> diff --git a/service/ServiceWifiResources/res/layout/wifi_p2p_dialog2_enter_pin.xml b/service/ServiceWifiResources/res/layout/wifi_p2p_dialog2_enter_pin.xml new file mode 100644 index 0000000000..32da7b78a1 --- /dev/null +++ b/service/ServiceWifiResources/res/layout/wifi_p2p_dialog2_enter_pin.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2024 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. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + style="@style/wifi_p2p_dialog2_enter_pin_section"> + <TextView + android:text="@string/wifi_p2p_dialog2_enter_pin_label" + style="@style/wifi_p2p_dialog2_enter_pin_label"/> + + <EditText android:id="@+id/wifi_p2p_dialog2_enter_pin" + style="@style/wifi_p2p_dialog2_enter_pin" /> +</LinearLayout> diff --git a/service/ServiceWifiResources/res/values-af/strings.xml b/service/ServiceWifiResources/res/values-af/strings.xml index 3473553ae0..0d2f519a23 100644 --- a/service/ServiceWifiResources/res/values-af/strings.xml +++ b/service/ServiceWifiResources/res/values-af/strings.xml @@ -48,8 +48,8 @@ <string name="wifi_watchdog_network_disabled" msgid="5769226742956006362">"Kon nie aan Wi-Fikoppel nie"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="1725243835135539125">" het \'n swak internetverbinding."</string> <string name="wifi_connect_alert_title" msgid="2368200646665663612">"Laat verbinding toe?"</string> - <string name="wifi_connect_alert_message" msgid="7226456300982080746">"Program %1$s wil aan Wi-Fi-netwerk %2$s koppel"</string> - <string name="wifi_connect_default_application" msgid="8917703737222707062">"\'n Program"</string> + <string name="wifi_connect_alert_message" msgid="7226456300982080746">"App %1$s wil aan wi-fi-netwerk %2$s koppel"</string> + <string name="wifi_connect_default_application" msgid="8917703737222707062">"\'n App"</string> <string name="accept" msgid="8346431649376483879">"Aanvaar"</string> <string name="decline" msgid="4172251727603762084">"Weier"</string> <string name="ok" msgid="847575529546290102">"OK"</string> @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Aan:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Voer die vereiste PIN in:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Toestelverbinding"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Toestelverbinding"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Uitnodiging gestuur na <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Voer hierdie PIN op <xliff:g id="DEVICE_NAME">%1$s</xliff:g> in om te koppel."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"OK"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Toestelverbinding"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Toestelverbinding"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Toestelverbinding"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> wil aan jou toestel koppel."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Voer die PIN in wat op <xliff:g id="DEVICE_NAME">%1$s</xliff:g> wys om te koppel."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> wil met die volgende PIN aan jou toestel koppel."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} wil aan jou toestel koppel. Aanvaar oor {countdown} sekonde.}other{{device} wil aan jou toestel koppel. Aanvaar oor {countdown} sekondes.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Voer die PIN wat op {device} wys binne {countdown} sekonde in om te koppel.}other{Voer die PIN wat op {device} wys binne {countdown} sekondes in om te koppel.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} wil met die volgende PIN aan jou toestel koppel. Aanvaar oor {countdown} sekonde.}other{{device} wil met die volgende PIN aan jou toestel koppel. Aanvaar oor {countdown} sekondes.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Koppel"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Kanselleer"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Jou toestel sal tydelik van wi-fi af ontkoppel word terwyl dit aan <xliff:g id="DEVICE_NAME">%1$s</xliff:g> gekoppel is"</string> <string name="dlg_ok" msgid="254496739491689405">"OK"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Kan nie aan <xliff:g id="SSID">%1$s</xliff:g> koppel nie"</string> diff --git a/service/ServiceWifiResources/res/values-am/strings.xml b/service/ServiceWifiResources/res/values-am/strings.xml index 4adbe8c0fb..89130795a8 100644 --- a/service/ServiceWifiResources/res/values-am/strings.xml +++ b/service/ServiceWifiResources/res/values-am/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"ለ፦"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"የሚፈለገውን ፒን ተይብ፦"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"ፒን፦"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"የመሣሪያ ግንኙነት"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"የመሣሪያ ግንኙነት"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"ግብዣ ለ<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ተልኳል።"</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"ለማገናኘት ይህን ፒን <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ላይ ያስገቡ።"</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"እሺ"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"የመሣሪያ ግንኙነት"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"የመሣሪያ ግንኙነት"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"የመሣሪያ ግንኙነት"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ከመሣሪያዎ ጋር መገናኘት ይፈልጋል።"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"ለማገናኘት <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ላይ የሚታየውን ፒን ያስገቡ።"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> በሚከተለው ፒን ከመሣሪያዎ ጋር መገናኘት ይፈልጋል።"</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} ከመሣሪያዎ ጋር መገናኘት ይፈልጋል። በ{countdown} ሰከንድ ውስጥ ይቀበሉ።}one{{device} ከመሣሪያዎ ጋር መገናኘት ይፈልጋል። በ{countdown} ሰከንድ ውስጥ ይቀበሉ።}other{{device} ከመሣሪያዎ ጋር መገናኘት ይፈልጋል። በ{countdown} ሰከንዶች ውስጥ ይቀበሉ።}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{ለማገናኘት {device} ላይ የሚታየውን ፒን በ{countdown} ሰከንድ ውስጥ ያስገቡ።}one{ለማገናኘት {device} ላይ የሚታየውን ፒን በ{countdown} ሰከንድ ውስጥ ያስገቡ።}other{ለማገናኘት {device} ላይ የሚታየውን ፒን በ{countdown} ሰከንዶች ውስጥ ያስገቡ።}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} በሚከተለው ፒን ከመሣሪያዎ ጋር መገናኘት ይፈልጋል። በ{countdown} ሰከንድ ውስጥ ይቀበሉ።}one{{device} በሚከተለው ፒን ከመሣሪያዎ ጋር መገናኘት ይፈልጋል። በ{countdown} ሰከንድ ውስጥ ይቀበሉ።}other{{device} በሚከተለው ፒን ከመሣሪያዎ ጋር መገናኘት ይፈልጋል። በ{countdown} ሰከንዶች ውስጥ ይቀበሉ።}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"አገናኝ"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"ይቅር"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"ፒን"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"ፒን"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"መሣሪያዎ ከ<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ጋር በሚገናኝበት ጊዜ ለጊዜው ከWi-Fi ጋር ግንኙነቱ ይቋረጣል"</string> <string name="dlg_ok" msgid="254496739491689405">"እሺ"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"ከ<xliff:g id="SSID">%1$s</xliff:g> ጋር መገናኘት አልተቻለም"</string> diff --git a/service/ServiceWifiResources/res/values-ar/strings.xml b/service/ServiceWifiResources/res/values-ar/strings.xml index 619d94501f..8d3ffa6fd7 100644 --- a/service/ServiceWifiResources/res/values-ar/strings.xml +++ b/service/ServiceWifiResources/res/values-ar/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"إلى:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"اكتب رمز PIN المطلوب:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"رقم التعريف الشخصي:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"اتصال الجهاز"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"اتصال الجهاز"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"تم إرسال الدعوة إلى جهاز \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\"."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"أدخِل رقم التعريف الشخصي هذا على جهاز \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\" لتوصيله بجهازك."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"حسنًا"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"اتصال الجهاز"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"اتصال الجهاز"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"اتصال الجهاز"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"يريد جهاز \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\" الاتصال بجهازك."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"أدخِل رقم التعريف الشخصي المعروض على جهاز \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\" لتوصيله بجهازك."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"يريد جهاز \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\" الاتصال بجهازك باستخدام رقم التعريف الشخصي الآتي."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{يريد جهاز \"{device}\" الاتصال بجهازك. يمكنك القبول خلال ثانية واحدة ({countdown}).}zero{يريد جهاز \"{device}\" الاتصال بجهازك. يمكنك القبول خلال {countdown} ثانية.}two{يريد جهاز \"{device}\" الاتصال بجهازك. يمكنك القبول خلال ثانيتين ({countdown}).}few{يريد جهاز \"{device}\" الاتصال بجهازك. يمكنك القبول خلال {countdown} ثوانٍ.}many{يريد جهاز \"{device}\" الاتصال بجهازك. يمكنك القبول خلال {countdown} ثانية.}other{يريد جهاز \"{device}\" الاتصال بجهازك. يمكنك القبول خلال {countdown} ثانية.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{أدخِل رقم التعريف الشخصي المعروض على جهاز \"{device}\" خلال ثانية واحدة ({countdown}) لتوصيله بجهازك.}zero{أدخِل رقم التعريف الشخصي المعروض على جهاز \"{device}\" خلال {countdown} ثانية لتوصيله بجهازك.}two{أدخِل رقم التعريف الشخصي المعروض على جهاز \"{device}\" خلال ثانيتين ({countdown}) لتوصيله بجهازك.}few{أدخِل رقم التعريف الشخصي المعروض على جهاز \"{device}\" خلال {countdown} ثوانٍ لتوصيله بجهازك.}many{أدخِل رقم التعريف الشخصي المعروض على جهاز \"{device}\" خلال {countdown} ثانية لتوصيله بجهازك.}other{أدخِل رقم التعريف الشخصي المعروض على جهاز \"{device}\" خلال {countdown} ثانية لتوصيله بجهازك.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{يريد \"{device}\" الاتصال بجهازك باستخدام رقم التعريف الشخصي الآتي. يمكنك القبول خلال ثانية واحدة ({countdown}).}zero{يريد جهاز \"{device}\" الاتصال بجهازك باستخدام رقم التعريف الشخصي الآتي. يمكنك القبول خلال {countdown} ثانية.}two{يريد جهاز \"{device}\" الاتصال بجهازك باستخدام رقم التعريف الشخصي الآتي. يمكنك القبول خلال ثانيتين ({countdown}).}few{يريد جهاز \"{device}\" الاتصال بجهازك باستخدام رقم التعريف الشخصي الآتي. يمكنك القبول خلال {countdown} ثوانٍ.}many{يريد جهاز \"{device}\" الاتصال بجهازك باستخدام رقم التعريف الشخصي الآتي. يمكنك القبول خلال {countdown} ثانية.}other{يريد جهاز \"{device}\" الاتصال بجهازك باستخدام رقم التعريف الشخصي الآتي. يمكنك القبول خلال {countdown} ثانية.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"اتصال"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"إلغاء"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"رقم التعريف الشخصي"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"رقم التعريف الشخصي"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"سيتم قطع اتصال الجهاز بشبكة Wi-Fi مؤقتًا أثناء اتصاله بـ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="dlg_ok" msgid="254496739491689405">"حسنًا"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"يتعذّر الاتصال بـ <xliff:g id="SSID">%1$s</xliff:g>."</string> diff --git a/service/ServiceWifiResources/res/values-as/strings.xml b/service/ServiceWifiResources/res/values-as/strings.xml index 4b2f966940..d1a79b9d11 100644 --- a/service/ServiceWifiResources/res/values-as/strings.xml +++ b/service/ServiceWifiResources/res/values-as/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"প্ৰতি:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"প্ৰয়োজনীয় পিন নম্বৰটো লিখক:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"পিন:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"ডিভাইচৰ সংযোগ"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"ডিভাইচৰ সংযোগ"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>লৈ আমন্ত্ৰণ পঠিওৱা হৈছে।"</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"সংযোগ কৰিবলৈ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>ত এই পিনটো দিয়ক।"</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"ঠিক আছে"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"ডিভাইচৰ সংযোগ"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"ডিভাইচৰ সংযোগ"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"ডিভাইচৰ সংযোগ"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>এ আপোনাৰ ডিভাইচৰ সৈতে সংযোগ কৰিব বিচাৰিছে।"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"সংযোগ কৰিবলৈ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>ত দেখুওৱা পিনটো দিয়ক।"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>এ তলত দিয়া পিনটোৰ জৰিয়তে আপোনাৰ ডিভাইচৰ সৈতে সংযোগ কৰিব বিচাৰিছে।"</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device}এ আপোনাৰ ডিভাইচৰ সৈতে সংযোগ কৰিব বিচাৰিছে। {countdown} ছেকেণ্ডত গ্ৰহণ কৰক।}one{{device}এ আপোনাৰ ডিভাইচৰ সৈতে সংযোগ কৰিব বিচাৰিছে। {countdown} ছেকেণ্ডত গ্ৰহণ কৰক।}other{{device}এ আপোনাৰ ডিভাইচৰ সৈতে সংযোগ কৰিব বিচাৰিছে। {countdown} ছেকেণ্ডত গ্ৰহণ কৰক।}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{সংযোগ কৰিবলৈ {countdown} ছেকেণ্ডৰ ভিতৰত {device}ত দেখুওৱা পিনটো দিয়ক।}one{সংযোগ কৰিবলৈ {countdown} ছেকেণ্ডৰ ভিতৰত {device}ত দেখুওৱা পিনটো দিয়ক।}other{সংযোগ কৰিবলৈ {countdown} ছেকেণ্ডৰ ভিতৰত {device}ত দেখুওৱা পিনটো দিয়ক।}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device}এ তলত দিয়া পিনটোৰ জৰিয়তে আপোনাৰ ডিভাইচৰ সৈতে সংযোগ কৰিব বিচাৰিছে। {countdown} ছেকেণ্ডত গ্ৰহণ কৰক।}one{{device}এ তলত দিয়া পিনটোৰ জৰিয়তে আপোনাৰ ডিভাইচৰ সৈতে সংযোগ কৰিব বিচাৰিছে। {countdown} ছেকেণ্ডত গ্ৰহণ কৰক।}other{{device}এ তলত দিয়া পিনটোৰ জৰিয়তে আপোনাৰ ডিভাইচৰ সৈতে সংযোগ কৰিব বিচাৰিছে। {countdown} ছেকেণ্ডত গ্ৰহণ কৰক।}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"সংযোগ কৰক"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"বাতিল কৰক"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"পিন"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"পিন"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"আপোনাৰ ডিভাইচটো <xliff:g id="DEVICE_NAME">%1$s</xliff:g>ৰ লগত সংযুক্ত হৈ থকাৰ অৱস্থাত অস্থায়ীভাৱে ৱাই-ফাইৰ পৰা সংযোগ বিচ্ছিন্ন হ’ব"</string> <string name="dlg_ok" msgid="254496739491689405">"ঠিক আছে"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"<xliff:g id="SSID">%1$s</xliff:g>ৰ সৈতে সংযোগ কৰিব নোৱাৰি"</string> diff --git a/service/ServiceWifiResources/res/values-az/strings.xml b/service/ServiceWifiResources/res/values-az/strings.xml index b39b9715cc..c74b17abc0 100644 --- a/service/ServiceWifiResources/res/values-az/strings.xml +++ b/service/ServiceWifiResources/res/values-az/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Kimə:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Tələb olunan PİN kodu daxil edin:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PİN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Cihaz bağlantısı"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Cihaz bağlantısı"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Dəvət <xliff:g id="DEVICE_NAME">%1$s</xliff:g> cihazına göndərildi."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Qoşulmaq üçün bu PIN-i <xliff:g id="DEVICE_NAME">%1$s</xliff:g> cihazına daxil edin."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"OK"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Cihaz bağlantısı"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Cihaz bağlantısı"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Cihaz bağlantısı"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> cihazınıza qoşulmaq istəyir."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Qoşulmaq üçün <xliff:g id="DEVICE_NAME">%1$s</xliff:g> cihazında göstərilən PIN-i daxil edin."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> aşağıdakı PIN ilə cihazınıza qoşulmaq istəyir."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} cihazınıza qoşulmaq istəyir. {countdown} saniyə ərzində qəbul edin.}other{{device} cihazınıza qoşulmaq istəyir. {countdown} saniyə ərzində qəbul edin.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Qoşulmaq üçün {countdown} saniyə ərzində {device} cihazında göstərilən PIN-i daxil edin.}other{Qoşulmaq üçün {countdown} saniyə ərzində {device} cihazında göstərilən PIN-i daxil edin.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} aşağıdakı PIN ilə cihazınıza qoşulmaq istəyir. {countdown} saniyə ərzində qəbul edin.}other{{device} aşağıdakı PIN ilə cihazınıza qoşulmaq istəyir. {countdown} saniyə ərzində qəbul edin.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Qoşulun"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Ləğv edin"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> cihazına qoşulanda cihaz Wi-Fi-dan müvəqqəti ayrılacaq"</string> <string name="dlg_ok" msgid="254496739491689405">"OK"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"<xliff:g id="SSID">%1$s</xliff:g> şəbəkəsinə qoşulmaq mümkün deyil"</string> diff --git a/service/ServiceWifiResources/res/values-b+sr+Latn/strings.xml b/service/ServiceWifiResources/res/values-b+sr+Latn/strings.xml index 267e4bd55f..7c182356d2 100644 --- a/service/ServiceWifiResources/res/values-b+sr+Latn/strings.xml +++ b/service/ServiceWifiResources/res/values-b+sr+Latn/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Kome:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Unesite potrebni PIN:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Veza uređaja"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Veza uređaja"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Poslat je poziv za: <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Da biste se povezali, unesite ovaj PIN na uređaju <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"Potvrdi"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Veza uređaja"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Veza uređaja"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Veza uređaja"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> želi da se poveže sa uređajem."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Da biste se povezali, unesite PIN prikazan na uređaju <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> želi da se poveže sa vašim uređajem pomoću sledećeg PIN-a."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} želi da se poveže sa vašim uređajem. Prihvatite za {countdown} sekundu.}one{{device} želi da se poveže sa vašim uređajem. Prihvatite za {countdown} sekundu.}few{{device} želi da se poveže sa vašim uređajem. Prihvatite za {countdown} sekunde.}other{{device} želi da se poveže sa vašim uređajem. Prihvatite za {countdown} sekundi.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Da biste se povezali, unesite PIN prikazan na uređaju {device} u roku od {countdown} sekunde.}one{Da biste se povezali, unesite PIN prikazan na uređaju {device} u roku od {countdown} sekunde.}few{Da biste se povezali, unesite PIN prikazan na uređaju {device} u roku od {countdown} sekunde.}other{Da biste se povezali, unesite PIN prikazan na uređaju {device} u roku od {countdown} sekundi.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} želi da se poveže sa vašim uređajem pomoću sledećeg PIN-a. Prihvatite za {countdown} sekundu.}one{{device} želi da se poveže sa vašim uređajem pomoću sledećeg PIN-a. Prihvatite za {countdown} sekundu.}few{{device} želi da se poveže sa vašim uređajem pomoću sledećeg PIN-a. Prihvatite za {countdown} sekunde.}other{{device} želi da se poveže sa vašim uređajem pomoću sledećeg PIN-a. Prihvatite za {countdown} sekundi.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Poveži"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Otkaži"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Uređaj će privremeno prekinuti vezu sa WiFi mrežom dok je povezan sa uređajem <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="dlg_ok" msgid="254496739491689405">"Potvrdi"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Povezivanje na mrežu <xliff:g id="SSID">%1$s</xliff:g> nije uspelo"</string> diff --git a/service/ServiceWifiResources/res/values-be/strings.xml b/service/ServiceWifiResources/res/values-be/strings.xml index ae333d9730..a99e0bc8f6 100644 --- a/service/ServiceWifiResources/res/values-be/strings.xml +++ b/service/ServiceWifiResources/res/values-be/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Каму:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Увядзіце патрэбны PIN-код:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN-код"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Падключэнне да прылады"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Падключэнне да прылады"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Запрашэнне адпраўлена на прыладу <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Каб падключыцца, увядзіце гэты PIN-код на прыладзе <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"ОК"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Падключэнне да прылады"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Падключэнне да прылады"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Падключэнне да прылады"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"Прылада <xliff:g id="DEVICE_NAME">%1$s</xliff:g> запытвае падключэнне да вашай прылады."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Каб падключыцца, увядзіце PIN-код, паказаны на прыладзе <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"Прылада <xliff:g id="DEVICE_NAME">%1$s</xliff:g> запытвае падключэнне да вашай прылады з наступным PIN-кодам."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{Прылада {device} запытвае падключэнне да вашай прылады. Прыміце на працягу {countdown} секунды.}one{Прылада {device} запытвае падключэнне да вашай прылады. Прыміце на працягу {countdown} секунды.}few{Прылада {device} запытвае падключэнне да вашай прылады. Прыміце на працягу {countdown} секунд.}many{Прылада {device} запытвае падключэнне да вашай прылады. Прыміце на працягу {countdown} секунд.}other{Прылада {device} запытвае падключэнне да вашай прылады. Прыміце на працягу {countdown} секунды.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Каб падключыцца, увядзіце PIN-код, паказаны на прыладзе {device}, на працягу {countdown} секунды.}one{Каб падключыцца, увядзіце PIN-код, паказаны на прыладзе {device}, на працягу {countdown} секунды.}few{Каб падключыцца, увядзіце PIN-код, паказаны на прыладзе {device}, на працягу {countdown} секунд.}many{Каб падключыцца, увядзіце PIN-код, паказаны на прыладзе {device}, на працягу {countdown} секунд.}other{Каб падключыцца, увядзіце PIN-код, паказаны на прыладзе {device}, на працягу {countdown} секунды.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{Прылада {device} запытвае падключэнне да вашай прылады з наступным PIN-кодам. Прыміце на працягу {countdown} секунды.}one{Прылада {device} запытвае падключэнне да вашай прылады з наступным PIN-кодам. Прыміце на працягу {countdown} секунды.}few{Прылада {device} запытвае падключэнне да вашай прылады з наступным PIN-кодам. Прыміце на працягу {countdown} секунд.}many{Прылада {device} запытвае падключэнне да вашай прылады з наступным PIN-кодам. Прыміце на працягу {countdown} секунд.}other{Прылада {device} запытвае падключэнне да вашай прылады з наступным PIN-кодам. Прыміце на працягу {countdown} секунды.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Падключыцца"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Скасаваць"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN-код"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN-код"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Прылада будзе часова заставацца адключанай ад сеткі Wi-Fi, пакуль падключана да прылады \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\""</string> <string name="dlg_ok" msgid="254496739491689405">"ОК"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Не ўдалося падключыцца да сеткі \"<xliff:g id="SSID">%1$s</xliff:g>\""</string> diff --git a/service/ServiceWifiResources/res/values-bg/strings.xml b/service/ServiceWifiResources/res/values-bg/strings.xml index eb2f5f49da..cd257229e0 100644 --- a/service/ServiceWifiResources/res/values-bg/strings.xml +++ b/service/ServiceWifiResources/res/values-bg/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"До:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Въведете задължителния ПИН:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"ПИН:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Връзка на устройството"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Връзка на устройството"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Поканата бе изпратена до <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Въведете този ПИН код на <xliff:g id="DEVICE_NAME">%1$s</xliff:g>, за да се свържете."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"OK"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Връзка на устройството"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Връзка на устройството"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Връзка на устройството"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> иска да се свърже с устройството ви."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Въведете ПИН кода, показан на <xliff:g id="DEVICE_NAME">%1$s</xliff:g>, за да се свържете."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> иска да се свърже с устройството ви чрез следния ПИН код."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} иска да се свърже с устройството ви. Имате {countdown} секунда, за да приемете.}other{{device} иска да се свърже с устройството ви. Имате {countdown} секунди, за да приемете.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Въведете ПИН кода, показан на {device}, в рамките на {countdown} секунда, за да се свържете.}other{Въведете ПИН кода, показан на {device}, в рамките на {countdown} секунди, за да се свържете.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} иска да се свърже с устройството ви чрез следния ПИН код. Имате {countdown} секунда, за да приемете.}other{{device} иска да се свърже с устройството ви чрез следния ПИН код. Имате {countdown} секунди, за да приемете.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Свързване"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Отказ"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"ПИН"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"ПИН"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Връзката с Wi-Fi на устройството ви временно ще бъде прекратена, докато то е свързано с(ъс) <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="dlg_ok" msgid="254496739491689405">"OK"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Не може да се установи връзка с(ъс) <xliff:g id="SSID">%1$s</xliff:g>"</string> diff --git a/service/ServiceWifiResources/res/values-bn/strings.xml b/service/ServiceWifiResources/res/values-bn/strings.xml index f981dc2988..a00451e1e3 100644 --- a/service/ServiceWifiResources/res/values-bn/strings.xml +++ b/service/ServiceWifiResources/res/values-bn/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"প্রাপক:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"প্রয়োজনীয় পিনটি লিখুন:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"পিন:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"ডিভাইস কানেকশন"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"ডিভাইস কানেকশন"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ডিভাইসকে আমন্ত্রণ পাঠানো হয়েছে।"</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"কানেক্ট করতে <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ডিভাইসে পিন লিখুন।"</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"ঠিক আছে"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"ডিভাইস কানেকশন"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"ডিভাইস কানেকশন"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"ডিভাইস কানেকশন"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> আপনার ডিভাইসের সাথে কানেক্ট করতে চাইছে।"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"কানেক্ট করতে <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ডিভাইসে দেখানো পিন লিখুন।"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> নিচে দেওয়া পিন ব্যবহার করে আপনার ডিভাইসের সাথে কানেক্ট করতে চাইছে।"</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} আপনার ডিভাইসের সাথে কানেক্ট করতে চাইছে। {countdown} সেকেন্ডের মধ্যে সম্মতি দিন।}one{{device} আপনার ডিভাইসের সাথে কানেক্ট করতে চাইছে। {countdown} সেকেন্ডের মধ্যে সম্মতি দিন।}other{{device} আপনার ডিভাইসের সাথে কানেক্ট করতে চাইছে। {countdown} সেকেন্ডের মধ্যে সম্মতি দিন।}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{কানেক্ট করতে {countdown} সেকেন্ডের মধ্যে {device} ডিভাইসে দেখানো পিন লিখুন।}one{কানেক্ট করতে {countdown} সেকেন্ডের মধ্যে {device} ডিভাইসে দেখানো পিন লিখুন।}other{কানেক্ট করতে {countdown} সেকেন্ডের মধ্যে {device} ডিভাইসে দেখানো পিন লিখুন।}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} নিচে দেওয়া পিন ব্যবহার করে আপনার ডিভাইসের সাথে কানেক্ট করতে চাইছে। {countdown} সেকেন্ডের মধ্যে সম্মতি দিন।}one{{device} নিচে দেওয়া পিন ব্যবহার করে আপনার ডিভাইসের সাথে কানেক্ট করতে চাইছে। {countdown} সেকেন্ডের মধ্যে সম্মতি দিন।}other{{device} নিচে দেওয়া পিন ব্যবহার করে আপনার ডিভাইসের সাথে কানেক্ট করতে চাইছে। {countdown} সেকেন্ডের মধ্যে সম্মতি দিন।}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"কানেক্ট করুন"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"বাতিল করুন"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"পিন"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"পিন"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>-এ কানেক্ট থাকা অবস্থায় আপনার ডিভাইস সাময়িকভাবে ওয়াই-ফাই থেকে ডিসকানেক্ট হয়ে যাবে"</string> <string name="dlg_ok" msgid="254496739491689405">"ঠিক আছে"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"<xliff:g id="SSID">%1$s</xliff:g>-এর সাথে কানেক্ট করা যাচ্ছে না"</string> diff --git a/service/ServiceWifiResources/res/values-bs/strings.xml b/service/ServiceWifiResources/res/values-bs/strings.xml index ca0ea80d7a..79cddaaff4 100644 --- a/service/ServiceWifiResources/res/values-bs/strings.xml +++ b/service/ServiceWifiResources/res/values-bs/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Prima:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Unesite potrebni PIN:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Veza uređaja"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Veza uređaja"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Pozivnica je poslana na uređaj <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Da se povežete, unesite ovaj PIN na uređaju <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"Uredu"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Veza uređaja"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Veza uređaja"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Veza uređaja"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"Uređaj <xliff:g id="DEVICE_NAME">%1$s</xliff:g> se želi povezati s vašim uređajem."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Da se povežete, unesite PIN prikazan na uređaju <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"Uređaj <xliff:g id="DEVICE_NAME">%1$s</xliff:g> se želi povezati s vašim uređajem sa sljedećim PIN-om."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{Uređaj {device} se želi povezati s vašim uređajem. Prihvatite za {countdown} sekundu.}one{Uređaj {device} se želi povezati s vašim uređajem. Prihvatite za {countdown} sekundu.}few{Uređaj {device} se želi povezati s vašim uređajem. Prihvatite za {countdown} sekunde.}other{Uređaj {device} se želi povezati s vašim uređajem. Prihvatite za {countdown} sekundi.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Da se povežete, unesite PIN prikazan na uređaju {device} u roku od {countdown} sekunde.}one{Da se povežete, unesite PIN prikazan na uređaju {device} u roku od {countdown} sekunde.}few{Da se povežete, unesite PIN prikazan na uređaju {device} u roku od {countdown} sekunde.}other{Da se povežete, unesite PIN prikazan na uređaju {device} u roku od {countdown} sekundi.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{Uređaj {device} se želi povezati s vašim uređajem sa sljedećim PIN-om. Prihvatite za {countdown} sekundu.}one{Uređaj {device} se želi povezati s vašim uređajem sa sljedećim PIN-om. Prihvatite za {countdown} sekundu.}few{Uređaj {device} se želi povezati s vašim uređajem sa sljedećim PIN-om. Prihvatite za {countdown} sekunde.}other{Uređaj {device} se želi povezati s vašim uređajem sa sljedećim PIN-om. Prihvatite za {countdown} sekundi.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Poveži se"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Otkaži"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Povezanost uređaja s WiFi mrežom će se privremeno prekinuti dok je povezan s uređajem <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="dlg_ok" msgid="254496739491689405">"Uredu"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Nije se moguće povezati s mrežom <xliff:g id="SSID">%1$s</xliff:g>"</string> diff --git a/service/ServiceWifiResources/res/values-ca/strings.xml b/service/ServiceWifiResources/res/values-ca/strings.xml index 97483600ba..b3c39a3304 100644 --- a/service/ServiceWifiResources/res/values-ca/strings.xml +++ b/service/ServiceWifiResources/res/values-ca/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Per a:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Introdueix el PIN sol·licitat:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Connexió de dispositius"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Connexió de dispositius"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"S\'ha enviat una invitació a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Introdueix aquest PIN a <xliff:g id="DEVICE_NAME">%1$s</xliff:g> per connectar-te."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"D\'acord"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Connexió de dispositius"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Connexió de dispositius"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Connexió de dispositius"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> vol connectar-se al teu dispositiu."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Introdueix el PIN que es mostra a <xliff:g id="DEVICE_NAME">%1$s</xliff:g> per connectar-te."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> vol connectar-se al teu dispositiu amb el PIN següent."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} vol connectar-se al teu dispositiu. Accepta-ho d\'aquí a {countdown} segon.}many{{device} vol connectar-se al teu dispositiu. Accepta-ho d\'aquí a {countdown} de segons.}other{{device} vol connectar-se al teu dispositiu. Accepta-ho d\'aquí a {countdown} segons.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Introdueix el PIN que es mostra a {device} en un termini d’{countdown} segon per connectar-te.}many{Introdueix el PIN que es mostra a {device} en un termini de {countdown} de segons per connectar-te.}other{Introdueix el PIN que es mostra a {device} en un termini de {countdown} segons per connectar-te.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} vol connectar-se al teu dispositiu amb el PIN següent. Accepta-ho d\'aquí a {countdown} segon.}many{{device} vol connectar-se al teu dispositiu amb el PIN següent. Accepta-ho d\'aquí a {countdown} de segons.}other{{device} vol connectar-se al teu dispositiu amb el PIN següent. Accepta-ho d\'aquí a {countdown} segons.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Connecta"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Cancel·la"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"El dispositiu es desconnectarà temporalment de la Wi‑Fi mentre estigui connectat a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="dlg_ok" msgid="254496739491689405">"D\'acord"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"No es pot connectar a <xliff:g id="SSID">%1$s</xliff:g>"</string> diff --git a/service/ServiceWifiResources/res/values-cs/strings.xml b/service/ServiceWifiResources/res/values-cs/strings.xml index e26e53cedb..bcaa30db32 100644 --- a/service/ServiceWifiResources/res/values-cs/strings.xml +++ b/service/ServiceWifiResources/res/values-cs/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Komu:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Zadejte požadovaný kód PIN:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Připojení zařízení"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Připojení zařízení"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Do zařízení <xliff:g id="DEVICE_NAME">%1$s</xliff:g> se odeslala pozvánka."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"K připojení zadejte na zařízení <xliff:g id="DEVICE_NAME">%1$s</xliff:g> tento PIN."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"OK"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Připojení zařízení"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Připojení zařízení"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Připojení zařízení"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"Zařízení <xliff:g id="DEVICE_NAME">%1$s</xliff:g> se chce připojit k vašemu zařízení."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Pokud se chcete připojit, zadejte PIN zobrazený na zařízení <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"Zařízení <xliff:g id="DEVICE_NAME">%1$s</xliff:g> se chce připojit k vašemu zařízení s následujícím kódem PIN."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{Zařízení {device} se chce připojit k vašemu zařízení. Na přijetí máte {countdown} sekundu.}few{Zařízení {device} se chce připojit k vašemu zařízení. Na přijetí máte {countdown} sekundy.}many{Zařízení {device} se chce připojit k vašemu zařízení. Na přijetí máte {countdown} sekundy.}other{Zařízení {device} se chce připojit k vašemu zařízení. Na přijetí máte {countdown} sekund.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{K připojení zadejte do {countdown} sekundy PIN zobrazený na zařízení {device}.}few{K připojení zadejte do {countdown} sekund PIN zobrazený na zařízení {device}.}many{K připojení zadejte do {countdown} sekundy PIN zobrazený na zařízení {device}.}other{K připojení zadejte do {countdown} sekund PIN zobrazený na zařízení {device}.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{Zařízení {device} se chce připojit k vašemu zařízení s následujícím kódem PIN. Na přijetí máte {countdown} sekundu.}few{Zařízení {device} se chce připojit k vašemu zařízení s následujícím kódem PIN. Na přijetí máte {countdown} sekundy.}many{Zařízení {device} se chce připojit k vašemu zařízení s následujícím kódem PIN. Na přijetí máte {countdown} sekundy.}other{Zařízení {device} se chce připojit k vašemu zařízení s následujícím kódem PIN. Na přijetí máte {countdown} sekund.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Připojit"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Zrušit"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Zatímco bude zařízení připojeno k zařízení <xliff:g id="DEVICE_NAME">%1$s</xliff:g>, dočasně se odpojí od Wi-Fi"</string> <string name="dlg_ok" msgid="254496739491689405">"OK"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"K síti <xliff:g id="SSID">%1$s</xliff:g> se nelze připojit"</string> diff --git a/service/ServiceWifiResources/res/values-da/strings.xml b/service/ServiceWifiResources/res/values-da/strings.xml index 6a27388ae7..aba3b75ca2 100644 --- a/service/ServiceWifiResources/res/values-da/strings.xml +++ b/service/ServiceWifiResources/res/values-da/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Til:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Skriv den påkrævede pinkode:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"Pinkode:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Enhedsforbindelse"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Enhedsforbindelse"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Der er sendt en invitation til <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Angiv denne pinkode på <xliff:g id="DEVICE_NAME">%1$s</xliff:g> for at oprette tilknytning."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"OK"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Enhedsforbindelse"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Enhedsforbindelse"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Enhedsforbindelse"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> vil gerne oprette tilknytning til din enhed."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Angiv den pinkode, der vises på <xliff:g id="DEVICE_NAME">%1$s</xliff:g>, for at oprette tilknytning."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> vil gerne oprette tilknytning til din enhed med følgende pinkode."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} vil gerne oprette tilknytning til din enhed. Acceptér inden for {countdown} sekund.}one{{device} vil gerne oprette tilknytning til din enhed. Acceptér inden for {countdown} sekund.}other{{device} vil gerne oprette tilknytning til din enhed. Acceptér inden for {countdown} sekunder.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Angiv den pinkode, der vises på {device}, inden for {countdown} sekund for at oprette tilknytning.}one{Angiv den pinkode, der vises på {device}, inden for {countdown} sekund for at oprette tilknytning.}other{Angiv den pinkode, der vises på {device}, inden for {countdown} sekunder for at oprette tilknytning.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} vil gerne oprette tilknytning til din enhed med følgende pinkode. Acceptér inden for {countdown} sekund.}one{{device} vil gerne oprette tilknytning til din enhed med følgende pinkode. Acceptér inden for {countdown} sekund.}other{{device} vil gerne oprette tilknytning til din enhed med følgende pinkode. Acceptér inden for {countdown} sekunder.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Opret tilknytning"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Annuller"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"Pinkode"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"Pinkode"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Enhedens Wi-Fi-forbindelse afbrydes midlertidigt, når den er tilknyttet <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="dlg_ok" msgid="254496739491689405">"OK"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Der kan ikke oprettes forbindelse til <xliff:g id="SSID">%1$s</xliff:g>"</string> diff --git a/service/ServiceWifiResources/res/values-de/strings.xml b/service/ServiceWifiResources/res/values-de/strings.xml index 4032ff10e2..3db2c4042f 100644 --- a/service/ServiceWifiResources/res/values-de/strings.xml +++ b/service/ServiceWifiResources/res/values-de/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"An:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Gib die PIN ein:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Geräteverbindung"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Geräteverbindung"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Einladung an das Gerät „<xliff:g id="DEVICE_NAME">%1$s</xliff:g>“ gesendet."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Gib diese PIN auf dem Gerät „<xliff:g id="DEVICE_NAME">%1$s</xliff:g>“ ein, um eine Verbindung herzustellen."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"Ok"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Geräteverbindung"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Geräteverbindung"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Geräteverbindung"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"Das Gerät „<xliff:g id="DEVICE_NAME">%1$s</xliff:g>“ möchte eine Verbindung mit deinem Gerät herstellen."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Gib die auf dem Gerät „<xliff:g id="DEVICE_NAME">%1$s</xliff:g>“ angezeigte PIN ein, um eine Verbindung herzustellen."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"Das Gerät „<xliff:g id="DEVICE_NAME">%1$s</xliff:g>“ möchte sich mit der folgenden PIN mit deinem Gerät verbinden."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{Das Gerät „{device}“ möchte eine Verbindung mit deinem Gerät herstellen. In {countdown} Sekunde akzeptieren.}other{Das Gerät „{device}“ möchte eine Verbindung mit deinem Gerät herstellen. In {countdown} Sekunden akzeptieren.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Gib die auf dem Gerät „{device}“ angezeigte PIN innerhalb von {countdown} Sekunde ein, um eine Verbindung herzustellen.}other{Gib die auf dem Gerät „{device}“ angezeigte PIN innerhalb von {countdown} Sekunden ein, um eine Verbindung herzustellen.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{Das Gerät „{device}“ möchte sich mit der folgenden PIN mit deinem Gerät verbinden. In {countdown} Sekunde akzeptieren.}other{Das Gerät „{device}“ möchte sich mit der folgenden PIN mit deinem Gerät verbinden. In {countdown} Sekunden akzeptieren.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Verbinden"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Abbrechen"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Dein Gerät wird vorübergehend vom WLAN getrennt, während es mit „<xliff:g id="DEVICE_NAME">%1$s</xliff:g>“ verbunden ist"</string> <string name="dlg_ok" msgid="254496739491689405">"Ok"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Verbindung zu <xliff:g id="SSID">%1$s</xliff:g> nicht möglich"</string> diff --git a/service/ServiceWifiResources/res/values-el/strings.xml b/service/ServiceWifiResources/res/values-el/strings.xml index 1e3a8e0b0e..e3cbfc2ddb 100644 --- a/service/ServiceWifiResources/res/values-el/strings.xml +++ b/service/ServiceWifiResources/res/values-el/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Προς:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Πληκτρολογήστε τον απαιτούμενο κωδικό PIN:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Σύνδεση συσκευής"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Σύνδεση συσκευής"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Η πρόσκληση εστάλη στη συσκευή <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Εισαγάγετε αυτό το PIN στη συσκευή <xliff:g id="DEVICE_NAME">%1$s</xliff:g> για σύνδεση."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"ΟΚ"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Σύνδεση συσκευής"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Σύνδεση συσκευής"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Σύνδεση συσκευής"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"Η συσκευή <xliff:g id="DEVICE_NAME">%1$s</xliff:g> θέλει να συνδεθεί στη συσκευή σας."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Εισαγάγετε το PIN που εμφανίζεται στη συσκευή <xliff:g id="DEVICE_NAME">%1$s</xliff:g> για σύνδεση."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"Η συσκευή <xliff:g id="DEVICE_NAME">%1$s</xliff:g> θέλει να συνδεθεί στη συσκευή σας με τον ακόλουθο κωδικό PIN."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{Η συσκευή {device} θέλει να συνδεθεί στη συσκευή σας. Αποδοχή σε {countdown} δευτερόλεπτο.}other{Η συσκευή {device} θέλει να συνδεθεί στη συσκευή σας. Αποδοχή σε {countdown} δευτερόλεπτα.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Εισαγάγετε το PIN που εμφανίζεται στη συσκευή {device} εντός {countdown} δευτερολέπτου για σύνδεση.}other{Εισαγάγετε το PIN που εμφανίζεται στη συσκευή {device} εντός {countdown} δευτερολέπτων για σύνδεση.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{Η συσκευή {device} θέλει να συνδεθεί στη συσκευή σας με τον ακόλουθο κωδικό PIN. Αποδοχή σε {countdown} δευτερόλεπτο.}other{Η συσκευή {device} θέλει να συνδεθεί στη συσκευή σας με τον ακόλουθο κωδικό PIN. Αποδοχή σε {countdown} δευτερόλεπτα.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Σύνδεση"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Ακύρωση"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Η συσκευή σας θα αποσυνδεθεί προσωρινά από το δίκτυο Wi-Fi ενώ είναι συνδεδεμένη στη συσκευή <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="dlg_ok" msgid="254496739491689405">"ΟΚ"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Δεν είναι δυνατή η σύνδεση με <xliff:g id="SSID">%1$s</xliff:g>"</string> diff --git a/service/ServiceWifiResources/res/values-en-rAU/strings.xml b/service/ServiceWifiResources/res/values-en-rAU/strings.xml index 03afd3affa..377f775449 100644 --- a/service/ServiceWifiResources/res/values-en-rAU/strings.xml +++ b/service/ServiceWifiResources/res/values-en-rAU/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"To:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Type the required PIN:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Device connection"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Device connection"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Invitation sent to <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Enter this PIN on <xliff:g id="DEVICE_NAME">%1$s</xliff:g> to connect."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"OK"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Device connection"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Device connection"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Device connection"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> wants to connect to your device."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Enter the PIN shown on <xliff:g id="DEVICE_NAME">%1$s</xliff:g> to connect."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> wants to connect to your device with the following PIN."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} wants to connect to your device. Accept in {countdown} second.}other{{device} wants to connect to your device. Accept in {countdown} seconds.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Enter the PIN shown on {device} within {countdown} second to connect.}other{Enter the PIN shown on {device} within {countdown} seconds to connect.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} wants to connect to your device with the following PIN. Accept in {countdown} second.}other{{device} wants to connect to your device with the following PIN. Accept in {countdown} seconds.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Connect"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Cancel"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Your device will temporarily disconnect from Wi-Fi while it\'s connected to <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="dlg_ok" msgid="254496739491689405">"OK"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Can\'t connect to <xliff:g id="SSID">%1$s</xliff:g>"</string> diff --git a/service/ServiceWifiResources/res/values-en-rCA/strings.xml b/service/ServiceWifiResources/res/values-en-rCA/strings.xml index d15645c2c7..c2b91ef1da 100644 --- a/service/ServiceWifiResources/res/values-en-rCA/strings.xml +++ b/service/ServiceWifiResources/res/values-en-rCA/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"To:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Type the required PIN:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Device connection"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Device connection"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Invitation sent to <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Enter this PIN on <xliff:g id="DEVICE_NAME">%1$s</xliff:g> to connect."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"OK"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Device connection"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Device connection"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Device connection"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> wants to connect to your device."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Enter the PIN shown on <xliff:g id="DEVICE_NAME">%1$s</xliff:g> to connect."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> wants to connect to your device with the following PIN."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} wants to connect to your device. Accept in {countdown} second.}other{{device} wants to connect to your device. Accept in {countdown} seconds.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Enter the PIN shown on {device} within {countdown} second to connect.}other{Enter the PIN shown on {device} within {countdown} seconds to connect.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} wants to connect to your device with the following PIN. Accept in {countdown} second.}other{{device} wants to connect to your device with the following PIN. Accept in {countdown} seconds.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Connect"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Cancel"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Your device will temporarily disconnect from Wi-Fi while it\'s connected to <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="dlg_ok" msgid="254496739491689405">"OK"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Can\'t connect to <xliff:g id="SSID">%1$s</xliff:g>"</string> diff --git a/service/ServiceWifiResources/res/values-en-rGB/strings.xml b/service/ServiceWifiResources/res/values-en-rGB/strings.xml index 03afd3affa..377f775449 100644 --- a/service/ServiceWifiResources/res/values-en-rGB/strings.xml +++ b/service/ServiceWifiResources/res/values-en-rGB/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"To:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Type the required PIN:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Device connection"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Device connection"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Invitation sent to <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Enter this PIN on <xliff:g id="DEVICE_NAME">%1$s</xliff:g> to connect."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"OK"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Device connection"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Device connection"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Device connection"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> wants to connect to your device."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Enter the PIN shown on <xliff:g id="DEVICE_NAME">%1$s</xliff:g> to connect."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> wants to connect to your device with the following PIN."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} wants to connect to your device. Accept in {countdown} second.}other{{device} wants to connect to your device. Accept in {countdown} seconds.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Enter the PIN shown on {device} within {countdown} second to connect.}other{Enter the PIN shown on {device} within {countdown} seconds to connect.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} wants to connect to your device with the following PIN. Accept in {countdown} second.}other{{device} wants to connect to your device with the following PIN. Accept in {countdown} seconds.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Connect"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Cancel"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Your device will temporarily disconnect from Wi-Fi while it\'s connected to <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="dlg_ok" msgid="254496739491689405">"OK"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Can\'t connect to <xliff:g id="SSID">%1$s</xliff:g>"</string> diff --git a/service/ServiceWifiResources/res/values-en-rIN/strings.xml b/service/ServiceWifiResources/res/values-en-rIN/strings.xml index 03afd3affa..377f775449 100644 --- a/service/ServiceWifiResources/res/values-en-rIN/strings.xml +++ b/service/ServiceWifiResources/res/values-en-rIN/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"To:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Type the required PIN:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Device connection"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Device connection"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Invitation sent to <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Enter this PIN on <xliff:g id="DEVICE_NAME">%1$s</xliff:g> to connect."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"OK"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Device connection"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Device connection"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Device connection"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> wants to connect to your device."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Enter the PIN shown on <xliff:g id="DEVICE_NAME">%1$s</xliff:g> to connect."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> wants to connect to your device with the following PIN."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} wants to connect to your device. Accept in {countdown} second.}other{{device} wants to connect to your device. Accept in {countdown} seconds.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Enter the PIN shown on {device} within {countdown} second to connect.}other{Enter the PIN shown on {device} within {countdown} seconds to connect.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} wants to connect to your device with the following PIN. Accept in {countdown} second.}other{{device} wants to connect to your device with the following PIN. Accept in {countdown} seconds.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Connect"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Cancel"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Your device will temporarily disconnect from Wi-Fi while it\'s connected to <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="dlg_ok" msgid="254496739491689405">"OK"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Can\'t connect to <xliff:g id="SSID">%1$s</xliff:g>"</string> diff --git a/service/ServiceWifiResources/res/values-es-rUS/strings.xml b/service/ServiceWifiResources/res/values-es-rUS/strings.xml index 72f5b3ec61..77d1c5f410 100644 --- a/service/ServiceWifiResources/res/values-es-rUS/strings.xml +++ b/service/ServiceWifiResources/res/values-es-rUS/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Para:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Escribe el PIN solicitado:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Conexión del dispositivo"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Conexión del dispositivo"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Invitación enviada a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Ingresa este PIN en <xliff:g id="DEVICE_NAME">%1$s</xliff:g> para conectarte."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"Aceptar"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Conexión del dispositivo"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Conexión del dispositivo"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Conexión del dispositivo"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> quiere conectarse a tu dispositivo."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Ingresa el PIN que se muestra en <xliff:g id="DEVICE_NAME">%1$s</xliff:g> para conectarte."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> quiere conectarse a tu dispositivo con el siguiente PIN."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} quiere conectarse a tu dispositivo. Aceptar en {countdown} segundo.}many{{device} quiere conectarse a tu dispositivo. Aceptar en {countdown} de segundos.}other{{device} quiere conectarse a tu dispositivo. Aceptar en {countdown} segundos.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Ingresa el PIN que se muestra en {device} en un plazo de {countdown} segundo para conectarte.}many{Ingresa el PIN que se muestra en {device} en un plazo de {countdown} de segundos para conectarte.}other{Ingresa el PIN que se muestra en {device} en un plazo de {countdown} segundos para conectarte.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} quiere conectarse a tu dispositivo con el siguiente PIN. Aceptar en {countdown} segundo.}many{{device} quiere conectarse a tu dispositivo con el siguiente PIN. Aceptar en {countdown} de segundos.}other{{device} quiere conectarse a tu dispositivo con el siguiente PIN. Aceptar en {countdown} segundos.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Conectar"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Cancelar"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Se desconectará temporalmente el dispositivo de la red Wi-Fi mientras esté conectado a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="dlg_ok" msgid="254496739491689405">"Aceptar"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"No se puede establecer conexión con <xliff:g id="SSID">%1$s</xliff:g>"</string> diff --git a/service/ServiceWifiResources/res/values-es/strings.xml b/service/ServiceWifiResources/res/values-es/strings.xml index 33f0d5e984..debb7040f9 100644 --- a/service/ServiceWifiResources/res/values-es/strings.xml +++ b/service/ServiceWifiResources/res/values-es/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Para:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Escribe el PIN solicitado:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Conexión del dispositivo"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Conexión del dispositivo"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Invitación enviada a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Introduce este PIN en <xliff:g id="DEVICE_NAME">%1$s</xliff:g> para conectarte."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"Aceptar"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Conexión del dispositivo"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Conexión del dispositivo"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Conexión del dispositivo"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> quiere conectarse a tu dispositivo."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Introduce el PIN que se muestra en <xliff:g id="DEVICE_NAME">%1$s</xliff:g> para conectarte."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> quiere conectarse a tu dispositivo con el siguiente PIN."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} quiere conectarse a tu dispositivo. Acepta en {countdown} segundo.}many{{device} quiere conectarse a tu dispositivo. Acepta en {countdown} segundos.}other{{device} quiere conectarse a tu dispositivo. Acepta en {countdown} segundos.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Introduce el PIN que se muestra en {device} en un plazo de {countdown} segundo para conectarte.}many{Introduce el PIN que se muestra en {device} en un plazo de {countdown} segundos para conectarte.}other{Introduce el PIN que se muestra en {device} en un plazo de {countdown} segundos para conectarte.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} quiere conectarse a tu dispositivo con el siguiente PIN. Acepta en {countdown} segundo.}many{{device} quiere conectarse a tu dispositivo con el siguiente PIN. Acepta en {countdown} segundos.}other{{device} quiere conectarse a tu dispositivo con el siguiente PIN. Acepta en {countdown} segundos.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Conectar"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Cancelar"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Tu dispositivo se desconectará temporalmente de la red Wi-Fi mientras esté conectado a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="dlg_ok" msgid="254496739491689405">"Aceptar"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"No se puede conectar a <xliff:g id="SSID">%1$s</xliff:g>"</string> diff --git a/service/ServiceWifiResources/res/values-et/strings.xml b/service/ServiceWifiResources/res/values-et/strings.xml index 444af98731..c31121dc0b 100644 --- a/service/ServiceWifiResources/res/values-et/strings.xml +++ b/service/ServiceWifiResources/res/values-et/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Saaja:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Sisestage nõutav PIN-kood:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN-kood:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Seadme ühendus"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Seadme ühendus"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Kutse saadeti seadmele <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Ühenduse loomiseks sisestage see PIN-kood seadmesse<xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"OK"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Seadme ühendus"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Seadme ühendus"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Seadme ühendus"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> soovib teie seadmega ühendust luua."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Ühenduse loomiseks sisestage seadmes <xliff:g id="DEVICE_NAME">%1$s</xliff:g> kuvatav kood."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> soovib teie seadmega ühendust luua, kasutades järgmist PIN-koodi."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} soovib teie seadmega ühendust luua. Võtke vastu {countdown} sekundi jooksul.}other{{device} soovib teie seadmega ühendust luua. Võtke vastu {countdown} sekundi jooksul.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Ühenduse loomiseks sisestage seadmes {device} kuvatav PIN-kood {countdown} sekundi jooksul.}other{Ühenduse loomiseks sisestage seadmes {device} kuvatav PIN-kood {countdown} sekundi jooksul.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} soovib teie seadmega ühendust luua, kasutades järgmist PIN-koodi. Võtke vastu {countdown} sekundi jooksul.}other{{device} soovib teie seadmega ühendust luua, kasutades järgmist PIN-koodi. Võtke vastu {countdown} sekundi jooksul.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Ühenda"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Tühista"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN-kood"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN-kood"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Seadmega <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ühenduse loomisel katkestatakse ajutiselt teie seadme ühendus WiFi-võrguga."</string> <string name="dlg_ok" msgid="254496739491689405">"OK"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Võrguga <xliff:g id="SSID">%1$s</xliff:g> ei õnnestu ühendada"</string> diff --git a/service/ServiceWifiResources/res/values-eu/strings.xml b/service/ServiceWifiResources/res/values-eu/strings.xml index 42cf712171..68c97c3d42 100644 --- a/service/ServiceWifiResources/res/values-eu/strings.xml +++ b/service/ServiceWifiResources/res/values-eu/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Hartzaileak:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Idatzi beharrezko PINa:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PINa:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Gailuen konexioa"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Gailuen konexioa"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Gonbidapena bidali da <xliff:g id="DEVICE_NAME">%1$s</xliff:g> gailura."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Konektatzeko, idatzi PIN hau <xliff:g id="DEVICE_NAME">%1$s</xliff:g> gailuan."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"Ados"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Gailuen konexioa"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Gailuen konexioa"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Gailuen konexioa"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> gailuak zure gailura konektatu nahi du."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Konektatzeko, idatzi <xliff:g id="DEVICE_NAME">%1$s</xliff:g> gailuan agertzen den PINa."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> gailuak zure gailura konektatu nahi du PIN honekin."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} gailuak zure gailura konektatu nahi du. {countdown} segundo duzu onartzeko.}other{{device} gailuak zure gailura konektatu nahi du. {countdown} segundo dituzu onartzeko.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Konektatzeko, idatzi {device} gailuan agertzen den PINa {countdown} segundo igaro aurretik.}other{Konektatzeko, idatzi {device} gailuan agertzen den PINa {countdown} segundo igaro aurretik.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} gailuak zure gailura konektatu nahi du PIN honekin. {countdown} segundo duzu onartzeko.}other{{device} gailuak zure gailura konektatu nahi du PIN honekin. {countdown} segundo dituzu onartzeko.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Konektatu"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Utzi"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PINa"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PINa"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Gailua wifi-saretik deskonektatuko da aldi batez <xliff:g id="DEVICE_NAME">%1$s</xliff:g> gailura konektatuta dagoen bitartean"</string> <string name="dlg_ok" msgid="254496739491689405">"Ados"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Ezin da konektatu <xliff:g id="SSID">%1$s</xliff:g> sarera"</string> diff --git a/service/ServiceWifiResources/res/values-fa/strings.xml b/service/ServiceWifiResources/res/values-fa/strings.xml index c11fd9820f..a1c788103d 100644 --- a/service/ServiceWifiResources/res/values-fa/strings.xml +++ b/service/ServiceWifiResources/res/values-fa/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"به:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"پین لازم را تایپ کنید:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"پین:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"اتصال دستگاه"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"اتصال دستگاه"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"دعوت به <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ارسال شد."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"برای متصل شدن، این پین را در <xliff:g id="DEVICE_NAME">%1$s</xliff:g> وارد کنید."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"تأیید"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"اتصال دستگاه"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"اتصال دستگاه"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"اتصال دستگاه"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> میخواهد به دستگاهتان متصل شود."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"برای متصل شدن، پین نمایش دادهشده روی <xliff:g id="DEVICE_NAME">%1$s</xliff:g> را وارد کنید."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> میخواهد با پین زیر به دستگاهتان متصل شود."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} میخواهد به دستگاهتان متصل شود. تا {countdown} ثانیه دیگر برای پذیرفتن فرصت دارید.}one{{device} میخواهد به دستگاهتان متصل شود. تا {countdown} ثانیه دیگر برای پذیرفتن فرصت دارید.}other{{device} میخواهد به دستگاهتان متصل شود. تا {countdown} ثانیه دیگر برای پذیرفتن فرصت دارید.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{برای متصل شدن، پین نمایش دادهشده روی {device} را تا {countdown} ثانیه دیگر وارد کنید.}one{برای متصل شدن، پین نمایش دادهشده روی {device} را تا {countdown} ثانیه دیگر وارد کنید.}other{برای متصل شدن، پین نمایش دادهشده روی {device} را تا {countdown} ثانیه دیگر وارد کنید.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} میخواهد با پین زیر به دستگاهتان متصل شود. تا {countdown} ثانیه دیگر برای پذیرفتن فرصت دارید.}one{{device} میخواهد با پین زیر به دستگاهتان متصل شود. تا {countdown} ثانیه دیگر برای پذیرفتن فرصت دارید.}other{{device} میخواهد با پین زیر به دستگاهتان متصل شود. تا {countdown} ثانیه دیگر برای پذیرفتن فرصت دارید.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"متصل شدن"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"لغو کردن"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"پین"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"پین"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"وقتی دستگاهتان به <xliff:g id="DEVICE_NAME">%1$s</xliff:g> متصل است، اتصال آن به Wi-Fi موقتاً قطع میشود"</string> <string name="dlg_ok" msgid="254496739491689405">"تأیید"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"اتصال به <xliff:g id="SSID">%1$s</xliff:g> امکانپذیر نیست"</string> diff --git a/service/ServiceWifiResources/res/values-fi/strings.xml b/service/ServiceWifiResources/res/values-fi/strings.xml index dcda428a8f..4fe27267d3 100644 --- a/service/ServiceWifiResources/res/values-fi/strings.xml +++ b/service/ServiceWifiResources/res/values-fi/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Kohde:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Kirjoita pyydetty PIN-koodi:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN-koodi:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Laitteen yhteys"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Laitteen yhteys"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Kutsu lähetetty seuraavalle henkilölle: <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Lisää tämä PIN-koodi laitteeseen (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>), jotta voit yhdistää sen."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"OK"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Laitteen yhteys"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Laitteen yhteys"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Laitteen yhteys"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> haluaa yhdistää laitteeseen."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Yhdistä lisäämällä PIN-koodi, joka näkyy laitteessa <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> haluaa yhdistää laitteeseen seuraavalla PIN-koodilla."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} haluaa yhdistää laitteeseen. Hyväksy {countdown} sekunnissa.}other{{device} haluaa yhdistää laitteeseen. Hyväksy {countdown} sekunnissa.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Yhdistä laitteeseen lisäämällä laitteessa ({device}) näkyvä PIN-koodi {countdown} sekunnin kuluessa.}other{Yhdistä laitteeseen ({device}) lisäämällä PIN-koodi {countdown} sekunnin kuluessa.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} haluaa yhdistää laitteeseen seuraavalla PIN-koodilla. Hyväksy {countdown} sekunnissa.}other{{device} haluaa yhdistää laitteeseen seuraavalla PIN-koodilla. Hyväksy {countdown} sekunnissa.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Yhdistä"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Peru"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Laitteen Wi-Fi-yhteys katkeaa tilapäisesti, kun siihen on yhdistetty <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="dlg_ok" msgid="254496739491689405">"OK"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Verkkoon <xliff:g id="SSID">%1$s</xliff:g> ei saada yhteyttä"</string> diff --git a/service/ServiceWifiResources/res/values-fr-feminine/strings.xml b/service/ServiceWifiResources/res/values-fr-feminine/strings.xml new file mode 100644 index 0000000000..bc890db014 --- /dev/null +++ b/service/ServiceWifiResources/res/values-fr-feminine/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- 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. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string-array name="wifi_eap_error_message_code_32763_carrier_overrides"> + <item msgid="591026649262091217">"<xliff:g id="CARRIER_ID_PREFIX">:::1839:::</xliff:g> <xliff:g id="SSID">%1$s</xliff:g> : vous êtes déjà connectée à Verizon Wi-Fi Access. (Erreur 32763)"</item> + </string-array> +</resources> diff --git a/service/ServiceWifiResources/res/values-fr-masculine/strings.xml b/service/ServiceWifiResources/res/values-fr-masculine/strings.xml new file mode 100644 index 0000000000..9ed7470594 --- /dev/null +++ b/service/ServiceWifiResources/res/values-fr-masculine/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- 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. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string-array name="wifi_eap_error_message_code_32763_carrier_overrides"> + <item msgid="591026649262091217">"<xliff:g id="CARRIER_ID_PREFIX">:::1839:::</xliff:g> <xliff:g id="SSID">%1$s</xliff:g> : vous êtes déjà connecté à Verizon Wi-Fi Access. (Erreur 32763)"</item> + </string-array> +</resources> diff --git a/service/ServiceWifiResources/res/values-fr-neuter/strings.xml b/service/ServiceWifiResources/res/values-fr-neuter/strings.xml new file mode 100644 index 0000000000..025a654e2d --- /dev/null +++ b/service/ServiceWifiResources/res/values-fr-neuter/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- 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. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string-array name="wifi_eap_error_message_code_32763_carrier_overrides"> + <item msgid="591026649262091217">"<xliff:g id="CARRIER_ID_PREFIX">:::1839:::</xliff:g> <xliff:g id="SSID">%1$s</xliff:g> : vous êtes déjà connecté·e à Verizon Wi-Fi Access. (Erreur 32763)"</item> + </string-array> +</resources> diff --git a/service/ServiceWifiResources/res/values-fr-rCA-feminine/strings.xml b/service/ServiceWifiResources/res/values-fr-rCA-feminine/strings.xml new file mode 100644 index 0000000000..e90236a14b --- /dev/null +++ b/service/ServiceWifiResources/res/values-fr-rCA-feminine/strings.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- 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. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string-array name="wifi_eap_error_message_code_32763_carrier_overrides"> + <item msgid="591026649262091217">"<xliff:g id="CARRIER_ID_PREFIX">:::1839:::</xliff:g><xliff:g id="SSID">%1$s</xliff:g> : vous êtes déjà connectée à Verizon Wi-Fi Access. (Erreur = 32763)"</item> + </string-array> + <string name="wifi_ca_cert_dialog_preT_continue_text" msgid="9118713368838029797">"Rester connectée"</string> +</resources> diff --git a/service/ServiceWifiResources/res/values-fr-rCA-masculine/strings.xml b/service/ServiceWifiResources/res/values-fr-rCA-masculine/strings.xml new file mode 100644 index 0000000000..6d357d7e41 --- /dev/null +++ b/service/ServiceWifiResources/res/values-fr-rCA-masculine/strings.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- 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. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string-array name="wifi_eap_error_message_code_32763_carrier_overrides"> + <item msgid="591026649262091217">"<xliff:g id="CARRIER_ID_PREFIX">:::1839:::</xliff:g><xliff:g id="SSID">%1$s</xliff:g> : vous êtes déjà connecté à Verizon Wi-Fi Access. (Erreur = 32763)"</item> + </string-array> + <string name="wifi_ca_cert_dialog_preT_continue_text" msgid="9118713368838029797">"Rester connecté"</string> +</resources> diff --git a/service/ServiceWifiResources/res/values-fr-rCA-neuter/strings.xml b/service/ServiceWifiResources/res/values-fr-rCA-neuter/strings.xml new file mode 100644 index 0000000000..1c3e4f2f68 --- /dev/null +++ b/service/ServiceWifiResources/res/values-fr-rCA-neuter/strings.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- 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. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string-array name="wifi_eap_error_message_code_32763_carrier_overrides"> + <item msgid="591026649262091217">"<xliff:g id="CARRIER_ID_PREFIX">:::1839:::</xliff:g><xliff:g id="SSID">%1$s</xliff:g> : votre connexion à Verizon Wi-Fi Access est déjà établie. (Erreur = 32763)"</item> + </string-array> + <string name="wifi_ca_cert_dialog_preT_continue_text" msgid="9118713368838029797">"Rester connecté·e"</string> +</resources> diff --git a/service/ServiceWifiResources/res/values-fr-rCA/strings.xml b/service/ServiceWifiResources/res/values-fr-rCA/strings.xml index 30c082acf1..7ecfb0fb0b 100644 --- a/service/ServiceWifiResources/res/values-fr-rCA/strings.xml +++ b/service/ServiceWifiResources/res/values-fr-rCA/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"À :"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Saisissez le NIP requis :"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"NIP :"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Connexion à l\'appareil"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Connexion à l\'appareil"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Invitation envoyée à <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Entrez ce code sur <xliff:g id="DEVICE_NAME">%1$s</xliff:g> pour vous connecter."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"OK"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Connexion à l\'appareil"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Connexion à l\'appareil"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Connexion à l\'appareil"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> souhaite se connecter à votre appareil."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Entrez le NIP affiché sur <xliff:g id="DEVICE_NAME">%1$s</xliff:g> pour vous connecter."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> souhaite se connecter à votre appareil avec le NIP suivant."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} souhaite se connecter à votre appareil. Acceptez dans {countdown} seconde.}one{{device} souhaite se connecter à votre appareil. Acceptez dans {countdown} seconde.}many{{device} souhaite se connecter à votre appareil. Acceptez dans {countdown} de secondes.}other{{device} souhaite se connecter à votre appareil. Acceptez dans {countdown} secondes.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Entrez le NIP affiché sur {device} dans un délai de {countdown} seconde pour vous connecter.}one{Entrez le NIP affiché sur {device} dans un délai de {countdown} seconde pour vous connecter.}many{Entrez le NIP affiché sur {device} dans un délai de {countdown} de secondes pour vous connecter.}other{Entrez le NIP affiché sur {device} dans un délai de {countdown} secondes pour vous connecter.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} souhaite se connecter à votre appareil avec le NIP suivant. Acceptez dans {countdown} seconde.}one{{device} souhaite se connecter à votre appareil avec le NIP suivant. Acceptez dans {countdown} seconde.}many{{device} souhaite se connecter à votre appareil avec le NIP suivant. Acceptez dans {countdown} de secondes.}other{{device} souhaite se connecter à votre appareil avec le NIP suivant. Acceptez dans {countdown} secondes.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Se connecter"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Annuler"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"NIP"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"NIP"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Votre appareil sera déconnecté temporairement de la connexion Wi-Fi lorsqu\'il sera connecté à <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="dlg_ok" msgid="254496739491689405">"OK"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Impossible de se connecter à <xliff:g id="SSID">%1$s</xliff:g>"</string> diff --git a/service/ServiceWifiResources/res/values-fr/strings.xml b/service/ServiceWifiResources/res/values-fr/strings.xml index cbc4374ae2..17cdb908bf 100644 --- a/service/ServiceWifiResources/res/values-fr/strings.xml +++ b/service/ServiceWifiResources/res/values-fr/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"À :"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Saisissez le code PIN requis :"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"Code :"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Connexion de l\'appareil"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Connexion de l\'appareil"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Invitation envoyée à <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Saisissez ce code sur <xliff:g id="DEVICE_NAME">%1$s</xliff:g> pour vous connecter."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"OK"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Connexion de l\'appareil"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Connexion de l\'appareil"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Connexion de l\'appareil"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> souhaite se connecter à votre appareil."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Saisissez le code qui s\'affiche sur <xliff:g id="DEVICE_NAME">%1$s</xliff:g> pour vous connecter."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> souhaite se connecter à votre appareil avec le code suivant."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} souhaite se connecter à votre appareil. {countdown} seconde restante pour accepter.}one{{device} souhaite se connecter à votre appareil. {countdown} seconde restante pour accepter.}many{{device} souhaite se connecter à votre appareil. {countdown} de secondes restantes pour accepter.}other{{device} souhaite se connecter à votre appareil. {countdown} secondes restantes pour accepter.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Il vous reste {countdown} seconde pour saisir le code qui s\'affiche sur {device} et vous connecter.}one{Il vous reste {countdown} seconde pour saisir le code qui s\'affiche sur {device} et vous connecter.}many{Il vous reste {countdown} de secondes pour saisir le code qui s\'affiche sur {device} et vous connecter.}other{Il vous reste {countdown} secondes pour saisir le code qui s\'affiche sur {device} et vous connecter.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} souhaite se connecter à votre appareil avec le code suivant. {countdown} seconde restante pour accepter.}one{{device} souhaite se connecter à votre appareil avec le code suivant. {countdown} seconde restante pour accepter.}many{{device} souhaite se connecter à votre appareil avec le code suivant. {countdown} de secondes restantes pour accepter.}other{{device} souhaite se connecter à votre appareil avec le code suivant. {countdown} secondes restantes pour accepter.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Connecter"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Annuler"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"Code"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"Code"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Tant que votre appareil sera connecté à <xliff:g id="DEVICE_NAME">%1$s</xliff:g>, il sera temporairement déconnecté du Wi-Fi"</string> <string name="dlg_ok" msgid="254496739491689405">"OK"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Impossible de se connecter à <xliff:g id="SSID">%1$s</xliff:g>"</string> diff --git a/service/ServiceWifiResources/res/values-gl/strings.xml b/service/ServiceWifiResources/res/values-gl/strings.xml index 5123f9a754..41aa779eef 100644 --- a/service/ServiceWifiResources/res/values-gl/strings.xml +++ b/service/ServiceWifiResources/res/values-gl/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Para:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Escribe o PIN obrigatorio:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Conexión entre dispositivos"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Conexión entre dispositivos"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Invitación enviada a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Para establecer a conexión, mete este PIN en <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"Aceptar"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Conexión entre dispositivos"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Conexión entre dispositivos"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Conexión entre dispositivos"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> quere conectarse ao teu dispositivo."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Para establecer a conexión, mete o PIN que aparece en <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> quere conectarse ao teu dispositivo co seguinte PIN."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} quere conectarse ao teu dispositivo. Acepta en {countdown} segundo.}other{{device} quere conectarse ao teu dispositivo. Acepta en {countdown} segundos.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Para establecer a conexión, mete o PIN que aparece en {device} nun prazo de {countdown} segundo.}other{Para establecer a conexión, mete o PIN que aparece en {device} nun prazo de {countdown} segundos.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} quere conectarse ao teu dispositivo co seguinte PIN. Acepta en {countdown} segundo.}other{{device} quere conectarse ao teu dispositivo co seguinte PIN. Acepta en {countdown} segundos.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Conectar"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Cancelar"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"O dispositivo desconectarase temporalmente da wifi mentres estea conectado a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="dlg_ok" msgid="254496739491689405">"Aceptar"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Non se puido establecer conexión co <xliff:g id="SSID">%1$s</xliff:g>"</string> diff --git a/service/ServiceWifiResources/res/values-gu/strings.xml b/service/ServiceWifiResources/res/values-gu/strings.xml index 3ef9523ea7..90e0c4dcf3 100644 --- a/service/ServiceWifiResources/res/values-gu/strings.xml +++ b/service/ServiceWifiResources/res/values-gu/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"પ્રતિ:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"આવશ્યક પિન લખો:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"પિન:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"ડિવાઇસ કનેક્શન"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"ડિવાઇસ કનેક્શન"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>ને આમંત્રણ મોકલ્યું."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"કનેક્ટ કરવા માટે <xliff:g id="DEVICE_NAME">%1$s</xliff:g> પર આ પિન દાખલ કરો."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"ઓકે"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"ડિવાઇસ કનેક્શન"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"ડિવાઇસ કનેક્શન"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"ડિવાઇસ કનેક્શન"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> તમારા ડિવાઇસ સાથે કનેક્ટ થવા માગે છે."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"કનેક્ટ કરવા માટે <xliff:g id="DEVICE_NAME">%1$s</xliff:g> પર બતાવવામાં આવેલો પિન દાખલ કરો."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"નીચે જણાવેલા પિન સાથે <xliff:g id="DEVICE_NAME">%1$s</xliff:g> તમારા ડિવાઇસ સાથે કનેક્ટ થવા માગે છે."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} તમારા ડિવાઇસ સાથે કનેક્ટ થવા માગે છે. {countdown} સેકન્ડમાં સ્વીકારો.}one{{device} તમારા ડિવાઇસ સાથે કનેક્ટ થવા માગે છે. {countdown} સેકન્ડમાં સ્વીકારો.}other{{device} તમારા ડિવાઇસ સાથે કનેક્ટ થવા માગે છે. {countdown} સેકન્ડમાં સ્વીકારો.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{કનેક્ટ કરવા માટે {device} પર બતાવવામાં આવેલો પિન {countdown} સેકન્ડમાં દાખલ કરો.}one{કનેક્ટ કરવા માટે {device} પર બતાવવામાં આવેલો પિન {countdown} સેકન્ડમાં દાખલ કરો.}other{કનેક્ટ કરવા માટે {device} પર બતાવવામાં આવેલો પિન {countdown} સેકન્ડમાં દાખલ કરો.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{નીચે જણાવેલા પિન સાથે {device} તમારા ડિવાઇસ સાથે કનેક્ટ થવા માગે છે. {countdown} સેકન્ડમાં સ્વીકારો.}one{નીચે જણાવેલા પિન સાથે {device} તમારા ડિવાઇસ સાથે કનેક્ટ થવા માગે છે. {countdown} સેકન્ડમાં સ્વીકારો.}other{નીચે જણાવેલા પિન સાથે {device} તમારા ડિવાઇસ સાથે કનેક્ટ થવા માગે છે. {countdown} સેકન્ડમાં સ્વીકારો.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"કનેક્ટ કરો"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"રદ કરો"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"પિન"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"પિન"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"જ્યારે તમારું ડિવાઇસ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> સાથે કનેક્ટેડ હોય, ત્યારે તેને વાઇ-ફાઇથી હંગામી રીતે ડિસ્કનેક્ટ કરવામાં આવશે"</string> <string name="dlg_ok" msgid="254496739491689405">"ઓકે"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"<xliff:g id="SSID">%1$s</xliff:g> સાથે કનેક્ટ કરી શકાતું નથી"</string> diff --git a/service/ServiceWifiResources/res/values-hi/strings.xml b/service/ServiceWifiResources/res/values-hi/strings.xml index 0b6ba44e9a..732d4ddf66 100644 --- a/service/ServiceWifiResources/res/values-hi/strings.xml +++ b/service/ServiceWifiResources/res/values-hi/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"प्रति:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"आवश्यक पिन लिखें:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"पिन:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"डिवाइस कनेक्शन"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"डिवाइस कनेक्शन"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> पर न्योता भेजा गया."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"कनेक्ट करने के लिए, <xliff:g id="DEVICE_NAME">%1$s</xliff:g> पर यह पिन डालें."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"ठीक है"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"डिवाइस कनेक्शन"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"डिवाइस कनेक्शन"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"डिवाइस कनेक्शन"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"आपको अपने डिवाइस से <xliff:g id="DEVICE_NAME">%1$s</xliff:g> को कनेक्ट करने का अनुरोध मिला है."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"कनेक्ट करने के लिए, <xliff:g id="DEVICE_NAME">%1$s</xliff:g> पर दिखाया गया पिन डालें."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"आपको इस पिन की मदद से <xliff:g id="DEVICE_NAME">%1$s</xliff:g> को अपने डिवाइस से कनेक्ट करने का अनुरोध मिला है."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{आपको अपने डिवाइस से {device} को कनेक्ट करने का अनुरोध मिला है. इस अनुरोध को {countdown} सेकंड में स्वीकार करें.}one{आपको अपने डिवाइस से {device} को कनेक्ट करने का अनुरोध मिला है. इसे {countdown} सेकंड में स्वीकार करें.}other{आपको अपने डिवाइस से {device} को कनेक्ट करने का अनुरोध मिला है. इसे {countdown} सेकंड में स्वीकार करें.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{कनेक्ट करने के लिए, {device} पर दिखाए गए पिन को {countdown} सेकंड के अंदर डालें.}one{कनेक्ट करने के लिए, {device} पर दिखाए गए पिन को {countdown} सेकंड के अंदर डालें.}other{कनेक्ट करने के लिए, {device} पर दिखाए गए पिन को {countdown} सेकंड के अंदर डालें.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{आपको इस पिन की मदद से {device} को अपने डिवाइस से कनेक्ट करने का अनुरोध मिला है. इस अनुरोध को {countdown} सेकंड में स्वीकार करें.}one{आपको इस पिन की मदद से {device} को अपने डिवाइस से कनेक्ट करने का अनुरोध मिला है. इस अनुरोध को {countdown} सेकंड में स्वीकार करें.}other{आपको इस पिन की मदद से {device} को अपने डिवाइस से कनेक्ट करने का अनुरोध मिला है. इस अनुरोध को {countdown} सेकंड में स्वीकार करें.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"कनेक्ट करें"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"रद्द करें"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"पिन"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"पिन"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> से कनेक्ट रहने के दौरान आपका डिवाइस, वाई-फ़ाई से अस्थायी रूप से डिसकनेक्ट हो जाएगा"</string> <string name="dlg_ok" msgid="254496739491689405">"ठीक है"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"<xliff:g id="SSID">%1$s</xliff:g> से कनेक्ट नहीं किया जा सकता"</string> diff --git a/service/ServiceWifiResources/res/values-hr/strings.xml b/service/ServiceWifiResources/res/values-hr/strings.xml index dd3bfba71e..bf88b909a3 100644 --- a/service/ServiceWifiResources/res/values-hr/strings.xml +++ b/service/ServiceWifiResources/res/values-hr/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Prima:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Upišite potreban PIN:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Veza uređaja"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Veza uređaja"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Pozivnica je poslana na uređaj <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Unesite ovaj PIN na uređaju <xliff:g id="DEVICE_NAME">%1$s</xliff:g> da biste se povezali."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"U redu"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Veza uređaja"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Veza uređaja"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Veza uređaja"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"Uređaj <xliff:g id="DEVICE_NAME">%1$s</xliff:g> želi se povezati s vašim uređajem."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Unesite PIN prikazan na uređaju <xliff:g id="DEVICE_NAME">%1$s</xliff:g> da biste se povezali."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"Uređaj <xliff:g id="DEVICE_NAME">%1$s</xliff:g> želi se povezati s vašim uređajem pomoću sljedećeg PIN-a."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{Uređaj {device} želi se povezati s vašim uređajem. Prihvatite za {countdown} sekundu.}one{Uređaj {device} želi se povezati s vašim uređajem. Prihvatite za {countdown} sekundu.}few{Uređaj {device} želi se povezati s vašim uređajem. Prihvatite za {countdown} sekunde.}other{Uređaj {device} želi se povezati s vašim uređajem. Prihvatite za {countdown} sekundi.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Unesite PIN prikazan na uređaju {device} u roku od {countdown} sekunde da biste se povezali.}one{Unesite PIN prikazan na uređaju {device} u roku od {countdown} sekunde da biste se povezali.}few{Unesite PIN prikazan na uređaju {device} u roku od {countdown} sekunde da biste se povezali.}other{Unesite PIN prikazan na uređaju {device} u roku od {countdown} sekundi da biste se povezali.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{Uređaj {device} želi se povezati s vašim uređajem pomoću sljedećeg PIN-a. Prihvatite za {countdown} sekundu.}one{Uređaj {device} želi se povezati s vašim uređajem pomoću sljedećeg PIN-a. Prihvatite za {countdown} sekundu.}few{Uređaj {device} želi se povezati s vašim uređajem pomoću sljedećeg PIN-a. Prihvatite za {countdown} sekunde.}other{Uređaj {device} želi se povezati s vašim uređajem pomoću sljedećeg PIN-a. Prihvatite za {countdown} sekundi.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Poveži"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Odustani"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Vaš će uređaj privremeno prekinuti vezu s Wi-Fijem dok je povezan s uređajem <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="dlg_ok" msgid="254496739491689405">"U redu"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Povezivanje s mrežom <xliff:g id="SSID">%1$s</xliff:g> nije uspjelo"</string> diff --git a/service/ServiceWifiResources/res/values-hu/strings.xml b/service/ServiceWifiResources/res/values-hu/strings.xml index bd7e938bef..e12768bf79 100644 --- a/service/ServiceWifiResources/res/values-hu/strings.xml +++ b/service/ServiceWifiResources/res/values-hu/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Címzett:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Adja meg a szükséges PIN kódot:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN-kód:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Eszközkapcsolat"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Eszközkapcsolat"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Meghívó elküldve a következő eszköznek: <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"A csatlakozáshoz adja meg ezt a PIN-kódot a(z) <xliff:g id="DEVICE_NAME">%1$s</xliff:g> eszközön."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"OK"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Eszközkapcsolat"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Eszközkapcsolat"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Eszközkapcsolat"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"A(z) <xliff:g id="DEVICE_NAME">%1$s</xliff:g> csatlakozni szeretne az Ön eszközéhez."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"A csatlakozáshoz adja meg a(z) <xliff:g id="DEVICE_NAME">%1$s</xliff:g> eszközön megjelenő PIN-kódot."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"A(z) <xliff:g id="DEVICE_NAME">%1$s</xliff:g> a következő PIN-kóddal szeretne csatlakozni az Ön eszközéhez."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{A(z) {device} csatlakozni szeretne az Ön eszközéhez. {countdown} másodperc maradt az elfogadásra.}other{A(z) {device} csatlakozni szeretne az Ön eszközéhez. {countdown} másodperc maradt az elfogadásra.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{A csatlakozáshoz {countdown} másodpercen belül adja meg a(z) {device} eszközön megjelenő PIN-kódot.}other{A csatlakozáshoz {countdown} másodpercen belül adja meg a(z) {device} eszközön megjelenő PIN-kódot.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{A(z) {device} a következő PIN-kóddal szeretne csatlakozni az Ön eszközéhez. {countdown} másodperc maradt az elfogadásra.}other{A(z) {device} a következő PIN-kóddal szeretne csatlakozni az Ön eszközéhez. {countdown} másodperc maradt az elfogadásra.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Csatlakozás"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Mégse"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN-kód"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN-kód"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Az eszköze ideiglenesen bontja a Wi-Fi-kapcsolatot arra az időre, amíg a(z) <xliff:g id="DEVICE_NAME">%1$s</xliff:g> eszközhöz csatlakozik"</string> <string name="dlg_ok" msgid="254496739491689405">"OK"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Nem lehet csatlakozni a következőhöz: <xliff:g id="SSID">%1$s</xliff:g>"</string> diff --git a/service/ServiceWifiResources/res/values-hy/strings.xml b/service/ServiceWifiResources/res/values-hy/strings.xml index 84738c1722..70400c50dd 100644 --- a/service/ServiceWifiResources/res/values-hy/strings.xml +++ b/service/ServiceWifiResources/res/values-hy/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Ում`"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Մուտքագրեք պահանջվող PIN-ը:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN-ը՝"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Սարքի միացում"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Սարքի միացում"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Հրավերն ուղարկվել է <xliff:g id="DEVICE_NAME">%1$s</xliff:g> սարքին։"</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Միանալու համար մուտքագրեք այս PIN կոդը <xliff:g id="DEVICE_NAME">%1$s</xliff:g> սարքում։"</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"Եղավ"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Սարքի միացում"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Սարքի միացում"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Սարքի միացում"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> սարքն ուզում է միանալ ձեր սարքին։"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Միանալու համար մուտքագրեք <xliff:g id="DEVICE_NAME">%1$s</xliff:g> սարքում ցուցադրվող PIN կոդը։"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> սարքն ուզում է միանալ ձեր սարքին հետևյալ PIN կոդով։"</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} սարքն ուզում է միանալ ձեր սարքին։ Մնաց {countdown} վայրկյան։}one{{device} սարքն ուզում է միանալ ձեր սարքին։ Մնաց {countdown} վայրկյան։}other{{device} սարքն ուզում է միանալ ձեր սարքին։ Մնաց {countdown} վայրկյան։}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Միանալու համար {countdown} վայրկյանի ընթացքում մուտքագրեք {device} սարքում ցուցադրվող PIN կոդը։}one{Միանալու համար {countdown} վայրկյանի ընթացքում մուտքագրեք {device} սարքում ցուցադրվող PIN կոդը։}other{Միանալու համար {countdown} վայրկյանի ընթացքում մուտքագրեք {device} սարքում ցուցադրվող PIN կոդը։}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} սարքն ուզում է միանալ ձեր սարքին հետևյալ PIN կոդով։ Մնաց {countdown} վայրկյան։}one{{device} սարքն ուզում է միանալ ձեր սարքին հետևյալ PIN կոդով։ Մնաց {countdown} վայրկյան։}other{{device} սարքն ուզում է միանալ ձեր սարքին հետևյալ PIN կոդով։ Մնաց {countdown} վայրկյան։}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Միանալ"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Չեղարկել"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN կոդ"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN կոդ"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Ձեր սարքը ժամանակավորապես կանջատվի Wi-Fi-ից, քանի դեռ միացված է <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-ին"</string> <string name="dlg_ok" msgid="254496739491689405">"Եղավ"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Չհաջողվեց միանալ <xliff:g id="SSID">%1$s</xliff:g> սարքին"</string> diff --git a/service/ServiceWifiResources/res/values-in/strings.xml b/service/ServiceWifiResources/res/values-in/strings.xml index 5e818035dd..ccab0c8598 100644 --- a/service/ServiceWifiResources/res/values-in/strings.xml +++ b/service/ServiceWifiResources/res/values-in/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Kepada:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Ketik PIN yang diminta:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Koneksi perangkat"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Koneksi perangkat"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Undangan dikirim ke <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Masukkan PIN ini di <xliff:g id="DEVICE_NAME">%1$s</xliff:g> untuk menghubungkan."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"Oke"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Koneksi perangkat"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Koneksi perangkat"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Koneksi perangkat"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ingin terhubung ke perangkat Anda."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Masukkan PIN yang ditampilkan di <xliff:g id="DEVICE_NAME">%1$s</xliff:g> untuk menghubungkan."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ingin terhubung ke perangkat Anda dengan PIN berikut."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} ingin terhubung ke perangkat Anda. Terima dalam {countdown} detik.}other{{device} ingin terhubung ke perangkat Anda. Terima dalam {countdown} detik.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Masukkan PIN yang ditampilkan di {device} dalam {countdown} detik untuk menghubungkan.}other{Masukkan PIN yang ditampilkan di {device} dalam {countdown} detik untuk menghubungkan.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} ingin terhubung ke perangkat Anda dengan PIN berikut. Terima dalam {countdown} detik.}other{{device} ingin terhubung ke perangkat Anda dengan PIN berikut. Terima dalam {countdown} detik.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Hubungkan"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Batal"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Koneksi perangkat akan terputus sementara dari Wi-Fi saat terhubung ke <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="dlg_ok" msgid="254496739491689405">"Oke"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Tidak dapat terhubung ke <xliff:g id="SSID">%1$s</xliff:g>"</string> diff --git a/service/ServiceWifiResources/res/values-is/strings.xml b/service/ServiceWifiResources/res/values-is/strings.xml index ebb850acea..47b4188dd8 100644 --- a/service/ServiceWifiResources/res/values-is/strings.xml +++ b/service/ServiceWifiResources/res/values-is/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Til:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Sláðu inn PIN-númerið sem er krafist:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN-númer:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Tenging tækis"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Tenging tækis"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Boð var sent í <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Sláðu þetta PIN-númer inn í <xliff:g id="DEVICE_NAME">%1$s</xliff:g> til að tengjast."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"Í lagi"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Tenging tækis"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Tenging tækis"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Tenging tækis"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> vill tengjast tækinu þínu."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Sláðu inn PIN-númerið sem birtist í <xliff:g id="DEVICE_NAME">%1$s</xliff:g> til að tengjast."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> vill tengjast tækinu þínu með eftirfarandi PIN-númeri."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} vill tengjast tækinu þínu. Samþykktu áður en {countdown} sekúnda er liðin.}one{{device} vill tengjast tækinu þínu. Samþykktu áður en {countdown} sekúnda er liðin.}other{{device} vill tengjast tækinu þínu. Samþykktu áður en {countdown} sekúndur eru liðnar.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Sláðu inn PIN-númerið sem birtist í {device} innan {countdown} sekúndu til að tengjast.}one{Sláðu inn PIN-númerið sem birtist í {device} innan {countdown} sekúndu til að tengjast.}other{Sláðu inn PIN-númerið sem birtist í {device} innan {countdown} sekúndna til að tengjast.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} vill tengjast tækinu þínu með eftirfarandi PIN-númeri. Samþykktu áður en {countdown} sekúnda er liðin.}one{{device} vill tengjast tækinu þínu með eftirfarandi PIN-númeri. Samþykktu áður en {countdown} sekúnda er liðin.}other{{device} vill tengjast tækinu þínu með eftirfarandi PIN-númeri. Samþykktu áður en {countdown} sekúndur eru liðnar.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Tengja"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Hætta við"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN-númer"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN-númer"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Tækið þitt mun tímabundið aftengjast Wi-Fi á meðan það er tengt við <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="dlg_ok" msgid="254496739491689405">"Í lagi"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Ekki hægt að tengjast <xliff:g id="SSID">%1$s</xliff:g>"</string> diff --git a/service/ServiceWifiResources/res/values-it/strings.xml b/service/ServiceWifiResources/res/values-it/strings.xml index 1d6ef13476..3c9a6acb36 100644 --- a/service/ServiceWifiResources/res/values-it/strings.xml +++ b/service/ServiceWifiResources/res/values-it/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"A:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Inserisci il PIN richiesto:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Connessione dispositivo"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Connessione dispositivo"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Invito inviato a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Inserisci questo PIN su <xliff:g id="DEVICE_NAME">%1$s</xliff:g> per connetterti."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"Ok"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Connessione dispositivo"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Connessione dispositivo"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Connessione dispositivo"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> vuole connettersi al tuo dispositivo."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Inserisci il PIN mostrato su <xliff:g id="DEVICE_NAME">%1$s</xliff:g> per connetterti."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> vuole connettersi al tuo dispositivo con il seguente PIN."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} vuole connettersi al tuo dispositivo. Accetta entro {countdown} secondo.}many{{device} vuole connettersi al tuo dispositivo. Accetta entro {countdown} di secondi.}other{{device} vuole connettersi al tuo dispositivo. Accetta entro {countdown} secondi.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Inserisci il PIN mostrato su {device} entro {countdown} secondo per connetterti.}many{Inserisci il PIN mostrato su {device} entro {countdown} di secondi per connetterti.}other{Inserisci il PIN mostrato su {device} entro {countdown} secondi per connetterti.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} vuole connettersi al tuo dispositivo con il seguente PIN. Accetta entro {countdown} secondo.}many{{device} vuole connettersi al tuo dispositivo con il seguente PIN. Accetta entro {countdown} secondi.}other{{device} vuole connettersi al tuo dispositivo con il seguente PIN. Accetta entro {countdown} secondi.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Connetti"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Annulla"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Il dispositivo verrà scollegato temporaneamente dalla rete Wi-Fi mentre è connesso a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="dlg_ok" msgid="254496739491689405">"Ok"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Impossibile connettersi alla rete <xliff:g id="SSID">%1$s</xliff:g>"</string> diff --git a/service/ServiceWifiResources/res/values-iw/strings.xml b/service/ServiceWifiResources/res/values-iw/strings.xml index df298ff6a4..03edc4dec7 100644 --- a/service/ServiceWifiResources/res/values-iw/strings.xml +++ b/service/ServiceWifiResources/res/values-iw/strings.xml @@ -21,7 +21,7 @@ <string name="wifi_available_title_connecting" msgid="7233590022728579868">"התחברות לרשת Wi-Fi"</string> <string name="wifi_available_title_connected" msgid="6329493859989844201">"בוצע חיבור לרשת Wi-Fi"</string> <string name="wifi_available_title_failed_to_connect" msgid="4840833680513368639">"לא ניתן היה להתחבר לרשת Wi-Fi"</string> - <string name="wifi_available_content_failed_to_connect" msgid="4330035988269701861">"יש להקיש כדי לראות את כל הרשתות"</string> + <string name="wifi_available_content_failed_to_connect" msgid="4330035988269701861">"יש ללחוץ כדי לראות את כל הרשתות"</string> <string name="wifi_available_action_connect" msgid="5636634933476946222">"התחברות"</string> <string name="wifi_available_action_all_networks" msgid="8491109932336522211">"כל הרשתות"</string> <string name="notification_channel_network_status" msgid="1631786866932924838">"סטטוס הרשת"</string> @@ -60,10 +60,28 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"אל:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"יש להקליד את קוד האימות הנדרש:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"קוד אימות:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"חיבור בין המכשירים"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"חיבור בין המכשירים"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"נשלחה הזמנה אל <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"כדי להתחבר, צריך להזין את קוד האימות הזה במכשיר <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"אישור"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"חיבור בין המכשירים"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"חיבור בין המכשירים"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"חיבור בין המכשירים"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"המכשיר <xliff:g id="DEVICE_NAME">%1$s</xliff:g> מבקש להתחבר למכשיר שלך."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"כדי להתחבר, צריך להזין את קוד האימות שמוצג במכשיר <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"המכשיר <xliff:g id="DEVICE_NAME">%1$s</xliff:g> מבקש להתחבר למכשיר שלך באמצעות קוד האימות הבא."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{מכשיר {device} מבקש להתחבר למכשיר שלך. ניתן לאשר תוך שנייה ({countdown}).}one{מכשיר {device} מבקש להתחבר למכשיר שלך. ניתן לאשר תוך {countdown} שניות.}two{מכשיר {device} מבקש להתחבר למכשיר שלך. ניתן לאשר תוך {countdown} שניות.}other{מכשיר {device} מבקש להתחבר למכשיר שלך. ניתן לאשר תוך {countdown} שניות.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{כדי להתחבר, צריך להזין את קוד האימות שמוצג במכשיר {device} תוך שנייה ({countdown}).}one{כדי להתחבר, צריך להזין את קוד האימות שמוצג במכשיר {device} תוך {countdown} שניות.}two{כדי להתחבר, צריך להזין את קוד האימות שמוצג במכשיר {device} תוך {countdown} שניות.}other{כדי להתחבר, צריך להזין את קוד האימות שמוצג במכשיר {device} תוך {countdown} שניות.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{המכשיר {device} מבקש להתחבר למכשיר שלך באמצעות קוד האימות הבא. ניתן לאשר תוך שנייה ({countdown}).}one{המכשיר {device} מבקש להתחבר למכשיר שלך באמצעות קוד האימות הבא. ניתן לאשר תוך {countdown} שניות.}two{המכשיר {device} מבקש להתחבר למכשיר שלך באמצעות קוד האימות הבא. ניתן לאשר תוך {countdown} שניות.}other{המכשיר {device} מבקש להתחבר למכשיר שלך באמצעות קוד האימות הבא. ניתן לאשר תוך {countdown} שניות.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"חיבור"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"ביטול"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"קוד אימות"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"קוד אימות"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"המכשיר יתנתק מרשת ה-Wi-Fi באופן זמני כשהוא מחובר אל <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="dlg_ok" msgid="254496739491689405">"אישור"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"לא ניתן להתחבר אל <xliff:g id="SSID">%1$s</xliff:g>"</string> - <string name="wifi_cannot_connect_with_randomized_mac_message" msgid="4834133226521813352">"יש להקיש כדי לשנות את הגדרות הפרטיות ולנסות שוב"</string> + <string name="wifi_cannot_connect_with_randomized_mac_message" msgid="4834133226521813352">"יש ללחוץ כדי לשנות את הגדרות הפרטיות ולנסות שוב"</string> <string name="wifi_disable_mac_randomization_dialog_title" msgid="2054540994993681606">"לשנות את הגדרות הפרטיות?"</string> <string name="wifi_disable_mac_randomization_dialog_message" msgid="8874064864332248988">"כדי לבצע חיבור, עליך לאפשר ל-<xliff:g id="SSID">%1$s</xliff:g> להשתמש בכתובת ה-MAC של המכשיר, שהיא מזהה ייחודי. הגדרת הפרטיות לרשת הזו משתמשת כרגע במזהה רנדומלי. \n\nשינוי זה עשוי לאפשר למכשירים בקרבת מקום לעקוב אחר מיקום המכשיר שלך."</string> <string name="wifi_disable_mac_randomization_dialog_confirm_text" msgid="6954419863076751626">"שינוי ההגדרות"</string> @@ -117,7 +135,7 @@ <item msgid="2705387186478280792">"<xliff:g id="CARRIER_ID_PREFIX">:::1839:::</xliff:g><xliff:g id="SSID">%1$s</xliff:g> : יש בעיה בהתחברות ל-Wi-Fi של Verizon. (קוד שגיאה = לא ידוע)"</item> </string-array> <string name="wifi_softap_auto_shutdown_timeout_expired_title" msgid="4896534374569504484">"נקודת האינטרנט (hotspot) נכבתה"</string> - <string name="wifi_softap_auto_shutdown_timeout_expired_summary" msgid="7975476698140267728">"אין מכשירים מחוברים. יש להקיש כדי לשנות."</string> + <string name="wifi_softap_auto_shutdown_timeout_expired_summary" msgid="7975476698140267728">"אין מכשירים מחוברים. יש ללחוץ כדי לשנות."</string> <string name="wifi_sim_required_title" msgid="2262227800991155459">"Wi-Fi מנותק"</string> <string name="wifi_sim_required_message" msgid="284812212346125745">"יש להכניס SIM של <xliff:g id="CARRIER_NAME">%2$s</xliff:g> כדי להתחבר אל <xliff:g id="SSID">%1$s</xliff:g>"</string> <string name="wifi_interface_priority_title" msgid="5117627874976875544">"האפליקציה <xliff:g id="APP">%1$s</xliff:g> מבקשת להשתמש במשאב רשת"</string> @@ -141,7 +159,7 @@ <string name="wifi_ca_cert_dialog_message_signature_name_text" msgid="8455163626514894233">"טביעת אצבע SHA-256:\n<xliff:g id="VALUE">%1$s</xliff:g>\n\n"</string> <string name="wifi_ca_cert_dialog_message_contact_text" msgid="5799083318641183815">"איש קשר:\n<xliff:g id="VALUE">%1$s</xliff:g>\n\n"</string> <string name="wifi_ca_cert_notification_title" msgid="537569884930429796">"צריך לאמת את הרשת"</string> - <string name="wifi_ca_cert_notification_message" msgid="6413248690626616961">"צריך לעיין בפרטי הרשת <xliff:g id="SSID">%1$s</xliff:g> לפני שמתחברים אליה. מקישים כדי להמשיך."</string> + <string name="wifi_ca_cert_notification_message" msgid="6413248690626616961">"צריך לעיין בפרטי הרשת <xliff:g id="SSID">%1$s</xliff:g> לפני שמתחברים אליה. לוחצים כדי להמשיך."</string> <string name="wifi_ca_cert_failed_to_install_ca_cert" msgid="4864192219789736195">"הניסיון להתקנת האישור נכשל."</string> <string name="wifi_tofu_invalid_cert_chain_title" msgid="332710627417595752">"לא ניתן להתחבר אל <xliff:g id="VALUE">%1$s</xliff:g>"</string> <string name="wifi_tofu_invalid_cert_chain_message" msgid="7047987920029432392">"שרשרת האישורים של השרת לא חוקית."</string> @@ -152,7 +170,7 @@ <string name="wifi_ca_cert_dialog_preT_message_hint" msgid="5682518783200852031">"חסר אישור לרשת <xliff:g id="SSID">%1$s</xliff:g>."</string> <string name="wifi_ca_cert_dialog_preT_message_link" msgid="6325483132538546884">"הוראות להוספת אישורים"</string> <string name="wifi_ca_cert_notification_preT_title" msgid="7255129934648316663">"לא ניתן לאמת את הרשת הזו"</string> - <string name="wifi_ca_cert_notification_preT_message" msgid="4565553176090475724">"חסר אישור לרשת <xliff:g id="SSID">%1$s</xliff:g>. יש להקיש כדי ללמוד איך להוסיף אישורים."</string> + <string name="wifi_ca_cert_notification_preT_message" msgid="4565553176090475724">"חסר אישור לרשת <xliff:g id="SSID">%1$s</xliff:g>. יש ללחוץ כדי ללמוד איך להוסיף אישורים."</string> <string name="wifi_ca_cert_notification_preT_continue_text" msgid="1525418430746943670">"יש להתחבר בכל זאת"</string> <string name="wifi_ca_cert_notification_preT_abort_text" msgid="8307996031461071854">"אין להתחבר"</string> <string name="wifi_enable_request_dialog_title" msgid="3577459145316177148">"לאפשר לאפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> להפעיל את ה-Wi‑Fi?"</string> diff --git a/service/ServiceWifiResources/res/values-ja/strings.xml b/service/ServiceWifiResources/res/values-ja/strings.xml index 6c31f68c3d..10b78e35b6 100644 --- a/service/ServiceWifiResources/res/values-ja/strings.xml +++ b/service/ServiceWifiResources/res/values-ja/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"受信側:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"必要なPINを入力してください:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"デバイスの接続"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"デバイスの接続"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> に招待状を送信しました。"</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"接続するには、この PIN を <xliff:g id="DEVICE_NAME">%1$s</xliff:g> に入力してください。"</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"OK"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"デバイスの接続"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"デバイスの接続"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"デバイスの接続"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> がデバイスへの接続をリクエストしています。"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"接続するには、<xliff:g id="DEVICE_NAME">%1$s</xliff:g> に表示されている PIN を入力してください。"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> が次の PIN を使用してデバイスへの接続をリクエストしています。"</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} がデバイスへの接続をリクエストしています。{countdown} 秒以内に承諾してください。}other{{device} がデバイスへの接続をリクエストしています。{countdown} 秒以内に承諾してください。}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{接続するには、{device} に表示されている PIN を {countdown} 秒以内に入力してください。}other{接続するには、{device} に表示されている PIN を {countdown} 秒以内に入力してください。}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} が次の PIN を使用してデバイスへの接続をリクエストしています。{countdown} 秒以内に承諾してください。}other{{device} が次の PIN を使用してデバイスへの接続をリクエストしています。{countdown} 秒以内に承諾してください。}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"接続"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"キャンセル"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"デバイスが <xliff:g id="DEVICE_NAME">%1$s</xliff:g> に接続されている間は一時的に Wi-Fi 接続が解除されます"</string> <string name="dlg_ok" msgid="254496739491689405">"OK"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"<xliff:g id="SSID">%1$s</xliff:g> に接続できません"</string> diff --git a/service/ServiceWifiResources/res/values-ka/strings.xml b/service/ServiceWifiResources/res/values-ka/strings.xml index 0ae523bc61..8cc59fdb0b 100644 --- a/service/ServiceWifiResources/res/values-ka/strings.xml +++ b/service/ServiceWifiResources/res/values-ka/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"მიმღები:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"შეიყვანეთ საჭირო PIN:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"პინ-კოდი:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"მოწყობილობის დაკავშირება"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"მოწყობილობის დაკავშირება"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>-ს გაეგზავნა მოსაწვევი."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"დასაკავშირებლად შეიყვანეთ PIN-კოდი <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-ზე."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"კარგი"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"მოწყობილობის დაკავშირება"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"მოწყობილობის დაკავშირება"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"მოწყობილობის დაკავშირება"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>-ს სურს თქვენს მოწყობილობასთან დაკავშირება."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"დასაკავშირებლად შეიყვანეთ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-ზე ნაჩვენები PIN-კოდი."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>-ს სურს თქვენს მოწყობილობასთან დაკავშირება შემდეგი PIN-კოდით."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device}-ს სურს თქვენს მოწყობილობასთან დაკავშირება. {countdown} წამში დათანხმება.}other{{device}-ს სურს თქვენს მოწყობილობასთან დაკავშირება. {countdown} წამში დათანხმება.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{დასაკავშირებლად შეიყვანეთ {device}-ზე {countdown} წამში ნაჩვენები PIN-კოდი.}other{დასაკავშირებლად შეიყვანეთ {device}-ზე {countdown} წამში ნაჩვენები PIN-კოდი.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device}-ს სურს თქვენს მოწყობილობასთან დაკავშირება შემდეგი PIN-კოდით. {countdown} წამში დათანხმება.}other{{device}-ს სურს თქვენს მოწყობილობასთან დაკავშირება შემდეგი PIN-კოდით. {countdown} წამში დათანხმება.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"დაკავშირება"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"გაუქმება"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN-კოდი"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN-კოდი"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"თქვენი მოწყობილობა დროებით გაითიშება Wi-Fi-დან, სანამ ის <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-თან იქნება დაკავშირებული"</string> <string name="dlg_ok" msgid="254496739491689405">"OK"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"<xliff:g id="SSID">%1$s</xliff:g>-თან დაკავშირება ვერ ხერხდება"</string> diff --git a/service/ServiceWifiResources/res/values-kk/strings.xml b/service/ServiceWifiResources/res/values-kk/strings.xml index 33fcbeb648..53c2950f27 100644 --- a/service/ServiceWifiResources/res/values-kk/strings.xml +++ b/service/ServiceWifiResources/res/values-kk/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Кімге:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Өтінілген PIN кодты теру:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Құрылғыны қосу"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Құрылғыны қосу"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> құрылғысына шақыру жіберілді."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Қосылу үшін осы PIN кодын <xliff:g id="DEVICE_NAME">%1$s</xliff:g> құрылғысына енгізіңіз."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"Жарайды"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Құрылғыны қосу"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Құрылғыны қосу"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Құрылғыны қосу"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> сіздің құрылғыңызға қосылғысы келеді."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Қосылу үшін <xliff:g id="DEVICE_NAME">%1$s</xliff:g> құрылғысында көрсетілген PIN кодын енгізіңіз."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> келесі PIN коды арқылы сіздің құрылғыңызға қосылғысы келеді."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} сіздің құрылғыңызға қосылғысы келеді. Қабылдауға {countdown} секунд қалды.}other{{device} сіздің құрылғыңызға қосылғысы келеді. Қабылдауға {countdown} секунд қалды.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Қосылу үшін {countdown} секунд ішінде {device} құрылғысында көрсетілген PIN кодын енгізіңіз.}other{Қосылу үшін {countdown} секунд ішінде {device} құрылғысында көрсетілген PIN кодын енгізіңіз.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} келесі PIN коды арқылы сіздің құрылғыңызға қосылғысы келеді. Қабылдауға {countdown} секунд қалды.}other{{device} келесі PIN коды арқылы сіздің құрылғыңызға қосылғысы келеді. Қабылдауға {countdown} секунд қалды.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Қосылу"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Бас тарту"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN коды"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN коды"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> құрылғысына қосулы тұрғанда, құрылғыңыз Wi-Fi желісінен уақытша ажырайды."</string> <string name="dlg_ok" msgid="254496739491689405">"Жарайды"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"<xliff:g id="SSID">%1$s</xliff:g> құрылғысына қосылмайды."</string> diff --git a/service/ServiceWifiResources/res/values-km/strings.xml b/service/ServiceWifiResources/res/values-km/strings.xml index 6a63936625..9a33bbd2d6 100644 --- a/service/ServiceWifiResources/res/values-km/strings.xml +++ b/service/ServiceWifiResources/res/values-km/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"ទៅ៖"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"បញ្ចូលកូដ PIN ដែលទាមទារ៖"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"កូដ PIN ៖"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"ការតភ្ជាប់ឧបករណ៍"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"ការតភ្ជាប់ឧបករណ៍"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"បានផ្ញើការអញ្ជើញទៅ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>។"</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"បញ្ចូលកូដ PIN នេះនៅលើ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ដើម្បីភ្ជាប់។"</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"យល់ព្រម"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"ការតភ្ជាប់ឧបករណ៍"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"ការតភ្ជាប់ឧបករណ៍"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"ការតភ្ជាប់ឧបករណ៍"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ចង់ភ្ជាប់ទៅឧបករណ៍របស់អ្នក។"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"បញ្ចូលកូដ PIN ដែលបង្ហាញនៅលើ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ដើម្បីភ្ជាប់។"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ចង់ភ្ជាប់ទៅឧបករណ៍របស់អ្នកដោយប្រើកូដ PIN ខាងក្រោម។"</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} ចង់ភ្ជាប់ទៅឧបករណ៍របស់អ្នក។ ទទួលយកក្នុងរយៈពេល {countdown} វិនាទី។}other{{device} ចង់ភ្ជាប់ទៅឧបករណ៍របស់អ្នក។ ទទួលយកក្នុងរយៈពេល {countdown} វិនាទី។}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{បញ្ចូលកូដ PIN ដែលបង្ហាញនៅលើ {device} ក្នុងរយៈពេល{countdown} វិនាទី ដើម្បីភ្ជាប់។}other{បញ្ចូលកូដ PIN ដែលបង្ហាញនៅលើ {device} ក្នុងរយៈពេល{countdown} វិនាទី ដើម្បីភ្ជាប់។}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} ចង់ភ្ជាប់ទៅឧបករណ៍របស់អ្នកដោយប្រើកូដ PIN ខាងក្រោម។ ទទួលយកក្នុងរយៈពេល {countdown} វិនាទី។}other{{device} ចង់ភ្ជាប់ទៅឧបករណ៍របស់អ្នកដោយប្រើកូដ PIN ខាងក្រោម។ ទទួលយកក្នុងរយៈពេល {countdown} វិនាទី។}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"ភ្ជាប់"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"បោះបង់"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"កូដ PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"កូដ PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"ឧបករណ៍របស់អ្នកនឹងផ្ដាច់ពី Wi-Fi ជាបណ្ដោះអាសន្ន នៅពេលឧបករណ៍នេះភ្ជាប់ជាមួយ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="dlg_ok" msgid="254496739491689405">"យល់ព្រម"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"មិនអាចភ្ជាប់ជាមួយ <xliff:g id="SSID">%1$s</xliff:g> បានទេ"</string> diff --git a/service/ServiceWifiResources/res/values-kn/strings.xml b/service/ServiceWifiResources/res/values-kn/strings.xml index 5bc762e2e2..5ced65b008 100644 --- a/service/ServiceWifiResources/res/values-kn/strings.xml +++ b/service/ServiceWifiResources/res/values-kn/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"ಗೆ:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"ಅಗತ್ಯವಿರುವ ಪಿನ್ ಟೈಪ್ ಮಾಡಿ:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"ಪಿನ್:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"ಸಾಧನದ ಕನೆಕ್ಷನ್"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"ಸಾಧನದ ಕನೆಕ್ಷನ್"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ಗೆ ಆಹ್ವಾನವನ್ನು ಕಳುಹಿಸಲಾಗಿದೆ."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"ಸಂಪರ್ಕಿಸಲು <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ನಲ್ಲಿ ಈ ಪಿನ್ ಅನ್ನು ನಮೂದಿಸಿ."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"ಸರಿ"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"ಸಾಧನದ ಕನೆಕ್ಷನ್"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"ಸಾಧನದ ಕನೆಕ್ಷನ್"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"ಸಾಧನದ ಕನೆಕ್ಷನ್"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ನಿಮ್ಮ ಸಾಧನಕ್ಕೆ ಕನೆಕ್ಟ್ ಆಗಲು ಬಯಸುತ್ತದೆ."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"ಕನೆಕ್ಟ್ ಮಾಡಲು <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ಸಾಧನದಲ್ಲಿ ನಿರ್ಮಿಸಲಾದ ಪಿನ್ ಅನ್ನು ನಮೂದಿಸಿ."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"ಈ ಕೆಳಗಿನ ಪಿನ್ನೊಂದಿಗೆ ನಿಮ್ಮ ಸಾಧನಕ್ಕೆ ಕನೆಕ್ಟ್ ಆಗಲು <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ಬಯಸುತ್ತದೆ."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} ನಿಮ್ಮ ಸಾಧನಕ್ಕೆ ಕನೆಕ್ಟ್ ಆಗಲು ಬಯಸುತ್ತದೆ. {countdown} ಸೆಕೆಂಡ್ನಲ್ಲಿ ಸ್ವೀಕರಿಸಿ.}one{{device} ನಿಮ್ಮ ಸಾಧನಕ್ಕೆ ಕನೆಕ್ಟ್ ಆಗಲು ಬಯಸುತ್ತದೆ. {countdown} ಸೆಕೆಂಡ್ಗಳಲ್ಲಿ ಸ್ವೀಕರಿಸಿ.}other{{device} ನಿಮ್ಮ ಸಾಧನಕ್ಕೆ ಕನೆಕ್ಟ್ ಆಗಲು ಬಯಸುತ್ತದೆ. {countdown} ಸೆಕೆಂಡ್ಗಳಲ್ಲಿ ಸ್ವೀಕರಿಸಿ.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{ಕನೆಕ್ಟ್ ಮಾಡಲು {countdown} ಸೆಕೆಂಡ್ನಲ್ಲಿ {device} ನಲ್ಲಿ ತೋರಿಸಿರುವ ಪಿನ್ ಅನ್ನು ನಮೂದಿಸಿ.}one{ಕನೆಕ್ಟ್ ಮಾಡಲು {countdown} ಸೆಕೆಂಡ್ಗಳಲ್ಲಿ {device} ನಲ್ಲಿ ತೋರಿಸಿರುವ ಪಿನ್ ಅನ್ನು ನಮೂದಿಸಿ.}other{ಕನೆಕ್ಟ್ ಮಾಡಲು {countdown} ಸೆಕೆಂಡ್ಗಳಲ್ಲಿ {device} ನಲ್ಲಿ ತೋರಿಸಿರುವ ಪಿನ್ ಅನ್ನು ನಮೂದಿಸಿ.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{ಈ ಕೆಳಗಿನ ಪಿನ್ನೊಂದಿಗೆ ನಿಮ್ಮ ಸಾಧನಕ್ಕೆ ಕನೆಕ್ಟ್ ಆಗಲು {device} ಬಯಸುತ್ತದೆ. {countdown} ಸೆಕೆಂಡ್ನಲ್ಲಿ ಸ್ವೀಕರಿಸಿ.}one{ಈ ಕೆಳಗಿನ ಪಿನ್ನೊಂದಿಗೆ ನಿಮ್ಮ ಸಾಧನಕ್ಕೆ ಕನೆಕ್ಟ್ ಆಗಲು {device} ಬಯಸುತ್ತದೆ. {countdown} ಸೆಕೆಂಡ್ಗಳಲ್ಲಿ ಸ್ವೀಕರಿಸಿ.}other{ಈ ಕೆಳಗಿನ ಪಿನ್ನೊಂದಿಗೆ ನಿಮ್ಮ ಸಾಧನಕ್ಕೆ ಕನೆಕ್ಟ್ ಆಗಲು {device} ಬಯಸುತ್ತದೆ. {countdown} ಸೆಕೆಂಡ್ಗಳಲ್ಲಿ ಸ್ವೀಕರಿಸಿ.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"ಕನೆಕ್ಟ್ ಮಾಡಿ"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"ರದ್ದುಮಾಡಿ"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"ಪಿನ್"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"ಪಿನ್"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"ನಿಮ್ಮ ಸಾಧನವು <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ಗೆ ಕನೆಕ್ಟ್ ಆಗಿರುವಾಗ, ವೈ-ಫೈನಿಂದ ಅದು ತಾತ್ಕಾಲಿಕವಾಗಿ ಡಿಸ್ಕನೆಕ್ಟ್ ಆಗುತ್ತದೆ"</string> <string name="dlg_ok" msgid="254496739491689405">"ಸರಿ"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"<xliff:g id="SSID">%1$s</xliff:g> ಗೆ ಕನೆಕ್ಟ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string> diff --git a/service/ServiceWifiResources/res/values-ko/strings.xml b/service/ServiceWifiResources/res/values-ko/strings.xml index e77fb11b13..9dc1307c70 100644 --- a/service/ServiceWifiResources/res/values-ko/strings.xml +++ b/service/ServiceWifiResources/res/values-ko/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"수신:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"필수 PIN 입력:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"기기 연결"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"기기 연결"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>에 초대를 보냈습니다."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"연결하려면 <xliff:g id="DEVICE_NAME">%1$s</xliff:g>에 이 PIN을 입력하세요."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"확인"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"기기 연결"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"기기 연결"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"기기 연결"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>에서 내 기기에 연결하려고 합니다."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"연결하려면 <xliff:g id="DEVICE_NAME">%1$s</xliff:g>에 표시된 PIN을 입력하세요."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>에서 다음 PIN을 사용하여 내 기기에 연결하려고 합니다."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device}에서 내 기기에 연결하려고 합니다. {countdown}초 이내에 수락하세요.}other{{device}에서 내 기기에 연결하려고 합니다. {countdown}초 이내에 수락하세요.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{연결하려면 {countdown}초 이내에 {device}에 표시된 PIN을 입력하세요.}other{연결하려면 {countdown}초 이내에 {device}에 표시된 PIN을 입력하세요.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device}에서 다음 PIN을 사용하여 내 기기에 연결하려고 합니다. {countdown}초 이내에 수락하세요.}other{{device}에서 다음 PIN을 사용하여 내 기기에 연결하려고 합니다. {countdown}초 이내에 수락하세요.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"연결"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"취소"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>에 연결되어 있는 동안 기기의 Wi-Fi 연결이 일시적으로 해제됩니다"</string> <string name="dlg_ok" msgid="254496739491689405">"확인"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"<xliff:g id="SSID">%1$s</xliff:g>에 연결할 수 없습니다."</string> diff --git a/service/ServiceWifiResources/res/values-ky/strings.xml b/service/ServiceWifiResources/res/values-ky/strings.xml index e8c22b77fc..59b886cd40 100644 --- a/service/ServiceWifiResources/res/values-ky/strings.xml +++ b/service/ServiceWifiResources/res/values-ky/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Алуучу:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Талап кылынган PIN\'ди териңиз:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Түзмөктү туташтыруу"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Түзмөктү туташтыруу"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Чакыруу <xliff:g id="DEVICE_NAME">%1$s</xliff:g> түзмөгүнө жөнөтүлдү."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Туташтыруу үчүн ушул PIN кодду <xliff:g id="DEVICE_NAME">%1$s</xliff:g> түзмөгүнө киргизиңиз."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"Жарайт"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Түзмөктү туташтыруу"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Түзмөктү туташтыруу"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Түзмөктү туташтыруу"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> түзмөгүңүзгө туташканы жатат."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Туташтыруу үчүн <xliff:g id="DEVICE_NAME">%1$s</xliff:g> түзмөгүндө көрсөтүлгөн PIN кодду киргизиңиз."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> түзмөгүңүзгө төмөнкү PIN код менен туташканы жатат."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} түзмөгүңүзгө туташканы жатат. {countdown} секундда кабыл алыңыз.}other{{device} түзмөгүңүзгө туташканы жатат. {countdown} секундда кабыл алыңыз.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Туташтыруу үчүн {device} түзмөгүндө көрсөтүлгөн PIN кодду {countdown} секунддун ичинде киргизиңиз.}other{Туташтыруу үчүн {device} түзмөгүндө көрсөтүлгөн PIN кодду {countdown} секунддун ичинде киргизиңиз.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} түзмөгүңүзгө төмөнкү PIN код менен туташканы жатат. {countdown} секундда кабыл алыңыз.}other{{device} түзмөгүңүзгө төмөнкү PIN код менен туташканы жатат. {countdown} секундда кабыл алыңыз.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Туташтыруу"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Жокко чыгаруу"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN код"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN код"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Түзмөгүңүз <xliff:g id="DEVICE_NAME">%1$s</xliff:g> түзмөгүнө туташып турганда убактылуу Wi-Fi\'дан ажыратылат"</string> <string name="dlg_ok" msgid="254496739491689405">"Жарайт"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"<xliff:g id="SSID">%1$s</xliff:g> менен туташпай жатат"</string> diff --git a/service/ServiceWifiResources/res/values-lo/strings.xml b/service/ServiceWifiResources/res/values-lo/strings.xml index 4d99e108cd..3bcd4899e6 100644 --- a/service/ServiceWifiResources/res/values-lo/strings.xml +++ b/service/ServiceWifiResources/res/values-lo/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"ຈາກ:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"ພິມລະຫັດ PIN:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"ການເຊື່ອມຕໍ່ອຸປະກອນ"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"ການເຊື່ອມຕໍ່ອຸປະກອນ"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"ສົ່ງຄຳເຊີນໄປຫາ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ແລ້ວ."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"ໃສ່ PIN ນີ້ຢູ່ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ເພື່ອເຊື່ອມຕໍ່."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"ຕົກລົງ"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"ການເຊື່ອມຕໍ່ອຸປະກອນ"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"ການເຊື່ອມຕໍ່ອຸປະກອນ"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"ການເຊື່ອມຕໍ່ອຸປະກອນ"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ຕ້ອງການເຊື່ອມຕໍ່ກັບອຸປະກອນຂອງທ່ານ."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"ໃສ່ PIN ທີ່ສະແດງຢູ່ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ເພື່ອເຊື່ອມຕໍ່."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ຕ້ອງການເຊື່ອມຕໍ່ກັບອຸປະກອນຂອງທ່ານດ້ວຍ PIN ຕໍ່ໄປນີ້."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} ຕ້ອງການເຊື່ອມຕໍ່ກັບອຸປະກອນຂອງທ່ານ. ຕອບຮັບພາຍໃນ {countdown} ວິນາທີ.}other{{device} ຕ້ອງການເຊື່ອມຕໍ່ກັບອຸປະກອນຂອງທ່ານ. ຕອບຮັບພາຍໃນ {countdown} ວິນາທີ.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{ໃສ່ PIN ທີ່ສະແດງຢູ່ {device} ພາຍໃນ {countdown} ວິນາທີເພື່ອເຊື່ອມຕໍ່.}other{ໃສ່ PIN ທີ່ສະແດງຢູ່ {device} ພາຍໃນ {countdown} ວິນາທີເພື່ອເຊື່ອມຕໍ່.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} ຕ້ອງການເຊື່ອມຕໍ່ກັບອຸປະກອນຂອງທ່ານດ້ວຍ PIN ຕໍ່ໄປນີ້. ຕອບຮັບພາຍໃນ {countdown} ວິນາທີ.}other{{device} ຕ້ອງການເຊື່ອມຕໍ່ກັບອຸປະກອນຂອງທ່ານດ້ວຍ PIN ຕໍ່ໄປນີ້. ຕອບຮັບພາຍໃນ {countdown} ວິນາທີ.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"ເຊື່ອມຕໍ່"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"ຍົກເລີກ"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"ອຸປະກອນຂອງທ່ານຈະຕັດການເຊື່ອມຕໍ່ຈາກ Wi-Fi ເປັນການຊົ່ວຄາວໃນລະຫວ່າງທີ່ມັນເຊື່ອມຕໍ່ຫາ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="dlg_ok" msgid="254496739491689405">"ຕົກລົງ"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"ບໍ່ສາມາດເຊື່ອມຕໍ່ຫາ <xliff:g id="SSID">%1$s</xliff:g> ໄດ້"</string> diff --git a/service/ServiceWifiResources/res/values-lt/strings.xml b/service/ServiceWifiResources/res/values-lt/strings.xml index e496179c51..5efb0107a9 100644 --- a/service/ServiceWifiResources/res/values-lt/strings.xml +++ b/service/ServiceWifiResources/res/values-lt/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Skirta:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Įveskite reikiamą PIN kodą:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN kodas:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Įrenginio ryšys"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Įrenginio ryšys"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Kvietimas išsiųstas į <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Įveskite šį PIN kodą <xliff:g id="DEVICE_NAME">%1$s</xliff:g>, kad prisijungtumėte."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"Gerai"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Įrenginio ryšys"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Įrenginio ryšys"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Įrenginio ryšys"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> nori prisijungti prie jūsų įrenginio."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Įveskite <xliff:g id="DEVICE_NAME">%1$s</xliff:g> rodomą PIN kodą, kad prisijungtumėte."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> nori prisijungti prie jūsų įrenginio naudojant toliau nurodytą PIN kodą."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} nori prisijungti prie jūsų įrenginio. Priimkite per {countdown} sekundę.}one{{device} nori prisijungti prie jūsų įrenginio. Priimkite per {countdown} sekundę.}few{{device} nori prisijungti prie jūsų įrenginio. Priimkite per {countdown} sekundes.}many{{device} nori prisijungti prie jūsų įrenginio. Priimkite per {countdown} sekundės.}other{{device} nori prisijungti prie jūsų įrenginio. Priimkite per {countdown} sekundžių.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Įveskite {device} rodomą PIN kodą per {countdown} sekundę, kad prisijungtumėte.}one{Įveskite {device} rodomą PIN kodą per {countdown} sekundę, kad prisijungtumėte.}few{Įveskite {device} rodomą PIN kodą per {countdown} sekundes, kad prisijungtumėte.}many{Įveskite {device} rodomą PIN kodą per {countdown} sekundės, kad prisijungtumėte.}other{Įveskite {device} rodomą PIN kodą per {countdown} sekundžių, kad prisijungtumėte.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} nori prisijungti prie jūsų įrenginio naudojant toliau nurodytą PIN kodą. Priimkite per {countdown} sekundę.}one{{device} nori prisijungti prie jūsų įrenginio naudojant toliau nurodytą PIN kodą. Priimkite per {countdown} sekundę.}few{{device} nori prisijungti prie jūsų įrenginio naudojant toliau nurodytą PIN kodą. Priimkite per {countdown} sekundes.}many{{device} nori prisijungti prie jūsų įrenginio naudojant toliau nurodytą PIN kodą. Priimkite per {countdown} sekundės.}other{{device} nori prisijungti prie jūsų įrenginio naudojant toliau nurodytą PIN kodą. Priimkite per {countdown} sekundžių.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Prisijungti"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Atšaukti"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN kodas"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN kodas"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Įrenginys bus laikinai atjungtas nuo „Wi-Fi“, kol bus prijungtas prie „<xliff:g id="DEVICE_NAME">%1$s</xliff:g>“"</string> <string name="dlg_ok" msgid="254496739491689405">"Gerai"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Nepavyksta prisijungti prie „<xliff:g id="SSID">%1$s</xliff:g>“"</string> diff --git a/service/ServiceWifiResources/res/values-lv/strings.xml b/service/ServiceWifiResources/res/values-lv/strings.xml index 6523fd3bdc..360569fd00 100644 --- a/service/ServiceWifiResources/res/values-lv/strings.xml +++ b/service/ServiceWifiResources/res/values-lv/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Kam:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Ierakstiet pieprasīto PIN:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Ierīču saistīšana"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Ierīču saistīšana"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Uzaicinājums nosūtīts ierīcei <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Lai izveidotu savienojumu, ievadiet šo PIN kodu ierīcē <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"Labi"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Ierīču saistīšana"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Ierīču saistīšana"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Ierīču saistīšana"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> vēlas izveidot savienojumu ar jūsu ierīci."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Lai izveidotu savienojumu, ievadiet PIN kodu, kas redzams ierīcē <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> vēlas izveidot savienojumu ar jūsu ierīci, izmantojot tālāk norādīto PIN kodu."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} vēlas izveidot savienojumu ar jūsu ierīci. Atlikusi {countdown} sekunde, lai pieņemtu.}zero{{device} vēlas izveidot savienojumu ar jūsu ierīci. Atlikušas {countdown} sekundes, lai pieņemtu.}one{{device} vēlas izveidot savienojumu ar jūsu ierīci. Atlikusi {countdown} sekunde, lai pieņemtu.}other{{device} vēlas izveidot savienojumu ar jūsu ierīci. Atlikušas {countdown} sekundes, lai pieņemtu.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Lai izveidotu savienojumu, {countdown} sekundes laikā ievadiet ierīcē {device} parādīto PIN kodu.}zero{Lai izveidotu savienojumu, {countdown} sekunžu laikā ievadiet ierīcē {device} parādīto PIN kodu.}one{Lai izveidotu savienojumu, {countdown} sekundes laikā ievadiet ierīcē {device} parādīto PIN kodu.}other{Lai izveidotu savienojumu, {countdown} sekunžu laikā ievadiet ierīcē {device} parādīto PIN kodu.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} vēlas izveidot savienojumu ar jūsu ierīci, izmantojot tālāk norādīto PIN kodu. Atlikusi {countdown} sekunde, lai pieņemtu.}zero{{device} vēlas izveidot savienojumu ar jūsu ierīci, izmantojot tālāk norādīto PIN kodu. Atlikušas {countdown} sekundes, lai pieņemtu.}one{{device} vēlas izveidot savienojumu ar jūsu ierīci, izmantojot tālāk norādīto PIN kodu. Atlikusi {countdown} sekunde, lai pieņemtu.}other{{device} vēlas izveidot savienojumu ar jūsu ierīci, izmantojot tālāk norādīto PIN kodu. Atlikušas {countdown} sekundes, lai pieņemtu.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Izveidot savienojumu"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Atcelt"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN kods"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN kods"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Jūsu ierīce tiks īslaicīgi atvienota no Wi-Fi tīkla, kamēr ir izveidots savienojums ar ierīci <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> <string name="dlg_ok" msgid="254496739491689405">"Labi"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Nevar izveidot savienojumu ar: <xliff:g id="SSID">%1$s</xliff:g>"</string> diff --git a/service/ServiceWifiResources/res/values-mk/strings.xml b/service/ServiceWifiResources/res/values-mk/strings.xml index f8db829ddb..98a8689a0a 100644 --- a/service/ServiceWifiResources/res/values-mk/strings.xml +++ b/service/ServiceWifiResources/res/values-mk/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"До:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Внеси го бараниот PIN:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Врска со уред"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Врска со уред"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Испратена е покана до <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Внесете го PIN-кодов на <xliff:g id="DEVICE_NAME">%1$s</xliff:g> за да се поврзете."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"Во ред"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Врска со уред"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Врска со уред"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Врска со уред"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> сака да се поврзе со вашиот уред."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Внесете го PIN-кодот прикажан на <xliff:g id="DEVICE_NAME">%1$s</xliff:g> за да се поврзете."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> сака да се поврзе со вашиот уред со следниов PIN-код."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} сака да се поврзе со вашиот уред. Прифатете за {countdown} секунда.}one{{device} сака да се поврзе со вашиот уред. Прифатете за {countdown} секунда.}other{{device} сака да се поврзе со вашиот уред. Прифатете за {countdown} секунди.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Внесете го PIN-кодот на {device} во рок од {countdown} секунда за да се поврзете.}one{Внесете го PIN-кодот на {device} во рок од {countdown} секунда за да се поврзете.}other{Внесете го PIN-кодот на {device} во рок од {countdown} секунди за да се поврзете.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} сака да се поврзе со вашиот уред со следниов PIN-код. Прифатете за {countdown} секунда.}one{{device} сака да се поврзе со вашиот уред со следниов PIN-код. Прифатете за {countdown} секунда.}other{{device} сака да се поврзе со вашиот уред со следниов PIN-код. Прифатете за {countdown} секунди.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Поврзи"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Откажи"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Ќе се прекине врската на вашиот уред со Wi-Fi привремено додека е поврзан со <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="dlg_ok" msgid="254496739491689405">"Во ред"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Не може да се поврзе на „<xliff:g id="SSID">%1$s</xliff:g>“"</string> diff --git a/service/ServiceWifiResources/res/values-ml/strings.xml b/service/ServiceWifiResources/res/values-ml/strings.xml index 18f1ada5b2..1455f32e9c 100644 --- a/service/ServiceWifiResources/res/values-ml/strings.xml +++ b/service/ServiceWifiResources/res/values-ml/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"ടു:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"ആവശ്യമായ പിൻ ടൈപ്പുചെയ്യുക:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"പിൻ:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"ഉപകരണ കണക്ഷൻ"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"ഉപകരണ കണക്ഷൻ"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> എന്നതിലേക്ക് ക്ഷണം അയച്ചു."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"കണക്റ്റ് ചെയ്യാൻ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> എന്നതിൽ ഈ പിൻ നൽകുക."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"ശരി"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"ഉപകരണ കണക്ഷൻ"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"ഉപകരണ കണക്ഷൻ"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"ഉപകരണ കണക്ഷൻ"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> നിങ്ങളുടെ ഉപകരണത്തിലേക്ക് കണക്റ്റ് ചെയ്യാൻ ആഗ്രഹിക്കുന്നു."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"കണക്റ്റ് ചെയ്യാൻ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> എന്നതിൽ കാണിക്കുന്ന പിൻ നൽകുക."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ഇനിപ്പറയുന്ന പിൻ ഉപയോഗിച്ച് നിങ്ങളുടെ ഉപകരണത്തിലേക്ക് കണക്റ്റ് ചെയ്യാൻ ആഗ്രഹിക്കുന്നു."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} നിങ്ങളുടെ ഉപകരണത്തിലേക്ക് കണക്റ്റ് ചെയ്യാൻ ആഗ്രഹിക്കുന്നു. {countdown} സെക്കൻഡിൽ അംഗീകരിക്കുക.}other{{device} നിങ്ങളുടെ ഉപകരണത്തിലേക്ക് കണക്റ്റ് ചെയ്യാൻ ആഗ്രഹിക്കുന്നു. {countdown} സെക്കൻഡിൽ അംഗീകരിക്കുക.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{കണക്റ്റ് ചെയ്യാൻ {countdown} സെക്കൻഡിനുള്ളിൽ {device} എന്നതിൽ കാണിക്കുന്ന പിൻ നൽകുക.}other{കണക്റ്റ് ചെയ്യാൻ {countdown} സെക്കൻഡിനുള്ളിൽ {device} എന്നതിൽ കാണിക്കുന്ന പിൻ നൽകുക.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} ഇനിപ്പറയുന്ന പിൻ ഉപയോഗിച്ച് നിങ്ങളുടെ ഉപകരണത്തിലേക്ക് കണക്റ്റ് ചെയ്യാൻ ആഗ്രഹിക്കുന്നു. {countdown} സെക്കൻഡിൽ അംഗീകരിക്കുക.}other{{device} ഇനിപ്പറയുന്ന പിൻ ഉപയോഗിച്ച് നിങ്ങളുടെ ഉപകരണത്തിലേക്ക് കണക്റ്റ് ചെയ്യാൻ ആഗ്രഹിക്കുന്നു. {countdown} സെക്കൻഡിൽ അംഗീകരിക്കുക.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"കണക്റ്റ് ചെയ്യുക"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"റദ്ദാക്കുക"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"പിൻ"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"പിൻ"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"നിങ്ങളുടെ ഉപകരണം <xliff:g id="DEVICE_NAME">%1$s</xliff:g> എന്നതുമായി കണക്റ്റ് ചെയ്തിരിക്കുമ്പോൾ, അത് വൈഫൈയിൽ നിന്ന് താൽക്കാലികമായി വിച്ഛേദിക്കും"</string> <string name="dlg_ok" msgid="254496739491689405">"ശരി"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"<xliff:g id="SSID">%1$s</xliff:g>-ലേക്ക് കണക്റ്റ് ചെയ്യാനാവുന്നില്ല"</string> diff --git a/service/ServiceWifiResources/res/values-mn/strings.xml b/service/ServiceWifiResources/res/values-mn/strings.xml index 293e732b10..d036fa8f42 100644 --- a/service/ServiceWifiResources/res/values-mn/strings.xml +++ b/service/ServiceWifiResources/res/values-mn/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Хэнд:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Шаардлагатай ПИН-г бичнэ үү:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"ПИН:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Төхөөрөмжийн холболт"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Төхөөрөмжийн холболт"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> руу урилга илгээсэн."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Холбогдохын тулд энэ ПИН-г <xliff:g id="DEVICE_NAME">%1$s</xliff:g> дээр оруулна уу."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"OK"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Төхөөрөмжийн холболт"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Төхөөрөмжийн холболт"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Төхөөрөмжийн холболт"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> таны төхөөрөмжид холбогдохыг хүсэж байна."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Холбогдохын тулд <xliff:g id="DEVICE_NAME">%1$s</xliff:g> дээр харуулсан ПИН-г оруулна уу."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> дараах ПИН-ээр таны төхөөрөмжтэй холбогдохыг хүсэж байна."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} таны төхөөрөмжид холбогдохыг хүсэж байна. {countdown} секундийн дотор зөвшөөрнө үү.}other{{device} таны төхөөрөмжид холбогдохыг хүсэж байна. {countdown} секундийн дотор зөвшөөрнө үү.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Холбогдохын тулд {countdown} секундийн дотор {device} дээр харуулсан ПИН-г оруулна уу.}other{Холбогдохын тулд {countdown} секундийн дотор {device} дээр харуулсан ПИН-г оруулна уу.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} дараах ПИН-ээр таны төхөөрөмжтэй холбогдохыг хүсэж байна. {countdown} секундийн дотор зөвшөөрнө үү.}other{{device} дараах ПИН-ээр таны төхөөрөмжтэй холбогдохыг хүсэж байна. {countdown} секундийн дотор зөвшөөрнө үү.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Холбогдох"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Цуцлах"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"ПИН"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"ПИН"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Таны төхөөрөмж <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-д холбогдсон үедээ Wi-Fi-с түр сална"</string> <string name="dlg_ok" msgid="254496739491689405">"OK"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"<xliff:g id="SSID">%1$s</xliff:g>-д холбогдож чадсангүй"</string> diff --git a/service/ServiceWifiResources/res/values-mr/strings.xml b/service/ServiceWifiResources/res/values-mr/strings.xml index 5cdd82f0d2..ec418c2c5f 100644 --- a/service/ServiceWifiResources/res/values-mr/strings.xml +++ b/service/ServiceWifiResources/res/values-mr/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"प्रति:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"आवश्यक पिन टाइप करा:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"पिन:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"डिव्हाइस कनेक्शन"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"डिव्हाइस कनेक्शन"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> यांना आमंत्रण पाठवले आहे."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"कनेक्ट करण्यासाठी <xliff:g id="DEVICE_NAME">%1$s</xliff:g> वर हा पिन एंटर करा."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"ओके"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"डिव्हाइस कनेक्शन"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"डिव्हाइस कनेक्शन"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"डिव्हाइस कनेक्शन"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ला तुमच्या डिव्हाइसशी कनेक्ट करायचे आहे."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"कनेक्ट करण्यासाठी <xliff:g id="DEVICE_NAME">%1$s</xliff:g> वर दाखवलेला पिन एंटर करा."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ला पुढील पिन वापरून तुमच्या डिव्हाइसशी कनेक्ट करायचे आहे."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} ला तुमच्या डिव्हाइसशी कनेक्ट करायचे आहे. {countdown} सेकंदामध्ये स्वीकारा.}other{{device} ला तुमच्या डिव्हाइसशी कनेक्ट करायचे आहे. {countdown} सेकंदांमध्ये स्वीकारा.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{कनेक्ट करण्यासाठी, {countdown} सेकंदाच्या आत {device} वर दाखवलेला पिन एंटर करा.}other{कनेक्ट करण्यासाठी, {countdown} सेकंदांच्या आत {device} वर दाखवलेला पिन एंटर करा.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} ला पुढील पिन वापरून तुमच्या डिव्हाइसशी कनेक्ट करायचे आहे. {countdown} सेकंदामध्ये स्वीकारा.}other{{device} ला पुढील पिन वापरून तुमच्या डिव्हाइसशी कनेक्ट करायचे आहे. {countdown} सेकंदांमध्ये स्वीकारा.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"कनेक्ट करा"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"रद्द करा"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"पिन"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"पिन"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"तुमचे डिव्हाइस <xliff:g id="DEVICE_NAME">%1$s</xliff:g> शी कनेक्ट केलेले असताना वाय-फायवरून तात्पुरते डिस्कनेक्ट होईल"</string> <string name="dlg_ok" msgid="254496739491689405">"ठीक"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"<xliff:g id="SSID">%1$s</xliff:g> शी कनेक्ट करता आले नाही"</string> diff --git a/service/ServiceWifiResources/res/values-ms/strings.xml b/service/ServiceWifiResources/res/values-ms/strings.xml index 66e5b8b5c6..c4e459d444 100644 --- a/service/ServiceWifiResources/res/values-ms/strings.xml +++ b/service/ServiceWifiResources/res/values-ms/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Kepada:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Taipkan PIN yang diperlukan:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Sambungan peranti"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Sambungan peranti"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Undangan dihantar kepada <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Masukkan PIN ini pada <xliff:g id="DEVICE_NAME">%1$s</xliff:g> untuk membuat sambungan."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"OK"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Sambungan peranti"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Sambungan peranti"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Sambungan peranti"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> mahu menyambung kepada peranti anda."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Masukkan PIN yang dipaparkan pada <xliff:g id="DEVICE_NAME">%1$s</xliff:g> untuk menyambung."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> mahu menyambung kepada peranti anda dengan PIN yang berikut."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} mahu menyambung kepada peranti anda. Terima dalam masa {countdown} saat.}other{{device} mahu menyambung kepada peranti anda. Terima dalam masa {countdown} saat.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Masukkan PIN yang dipaparkan pada {device} dalam masa {countdown} saat untuk membuat sambungan.}other{Masukkan PIN yang dipaparkan pada {device} dalam masa {countdown} saat untuk membuat sambungan.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} mahu menyambung kepada peranti anda dengan PIN yang berikut. Terima dalam masa {countdown} saat.}other{{device} mahu menyambung kepada peranti anda dengan PIN yang berikut. Terima dalam masa {countdown} saat.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Sambung"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Batal"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Peranti anda akan diputuskan sambungan daripada Wi-Fi untuk sementara waktu semasa peranti ini disambungkan kepada <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="dlg_ok" msgid="254496739491689405">"OK"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Tidak dapat menyambung ke <xliff:g id="SSID">%1$s</xliff:g>"</string> diff --git a/service/ServiceWifiResources/res/values-my/strings.xml b/service/ServiceWifiResources/res/values-my/strings.xml index ea57a2a317..5ae53cd313 100644 --- a/service/ServiceWifiResources/res/values-my/strings.xml +++ b/service/ServiceWifiResources/res/values-my/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"သို့:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"လိုအပ်သော ပင် နံပါတ် ရိုက်ရန်:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"ပင် နံပါတ်:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"စက်ပစ္စည်းချိတ်ဆက်မှု"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"စက်ပစ္စည်းချိတ်ဆက်မှု"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"ဖိတ်ကြားချက်ကို <xliff:g id="DEVICE_NAME">%1$s</xliff:g> သို့ ပို့လိုက်သည်။"</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"ချိတ်ဆက်ရန်အတွက် <xliff:g id="DEVICE_NAME">%1$s</xliff:g> တွင် ဤပင်နံပါတ်ကို ထည့်ပါ။"</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"OK"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"စက်ပစ္စည်းချိတ်ဆက်မှု"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"စက်ပစ္စည်းချိတ်ဆက်မှု"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"စက်ပစ္စည်းချိတ်ဆက်မှု"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> က သင့်စက်ကို ချိတ်ဆက်လိုသည်။"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"ချိတ်ဆက်ရန်အတွက် <xliff:g id="DEVICE_NAME">%1$s</xliff:g> တွင် ပြထားသော ပင်နံပါတ်ကို ထည့်ပါ။"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> က အောက်ပါပင်နံပါတ်သုံး၍ သင့်စက်ကို ချိတ်ဆက်လိုသည်။"</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} က သင့်စက်ကို ချိတ်ဆက်လိုသည်။ {countdown} စက္ကန့်အတွင်း လက်ခံပါ။}other{{device} က သင့်စက်ကို ချိတ်ဆက်လိုသည်။ {countdown} စက္ကန့်အတွင်း လက်ခံပါ။}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{ချိတ်ဆက်ရန်အတွက် {device} တွင် ပြထားသော ပင်နံပါတ်ကို {countdown} အတွင်း ထည့်ပါ။}other{ချိတ်ဆက်ရန်အတွက် {device} တွင် ပြထားသော ပင်နံပါတ်ကို {countdown} အတွင်း ထည့်ပါ။}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} က အောက်ပါပင်နံပါတ်သုံး၍ သင့်စက်ကို ချိတ်ဆက်လိုသည်။ {countdown} စက္ကန့်အတွင်း လက်ခံပါ။}other{{device} က အောက်ပါပင်နံပါတ်သုံး၍ သင့်စက်ကို ချိတ်ဆက်လိုသည်။ {countdown} စက္ကန့်အတွင်း လက်ခံပါ။}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"ချိတ်ဆက်ရန်"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"မလုပ်တော့"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"ပင်နံပါတ်"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"ပင်နံပါတ်"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"သင့်စက်သည် <xliff:g id="DEVICE_NAME">%1$s</xliff:g> သို့ ချိတ်ဆက်ထားစဉ် Wi-Fi နှင့် ချိတ်ဆက်မှုကို ယာယီဖြုတ်ထားမည်"</string> <string name="dlg_ok" msgid="254496739491689405">"OK"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"<xliff:g id="SSID">%1$s</xliff:g> နှင့် ချိတ်ဆက်၍မရပါ။"</string> diff --git a/service/ServiceWifiResources/res/values-nb/strings.xml b/service/ServiceWifiResources/res/values-nb/strings.xml index f97c308e43..36a08e500a 100644 --- a/service/ServiceWifiResources/res/values-nb/strings.xml +++ b/service/ServiceWifiResources/res/values-nb/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Til:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Skriv inn påkrevd PIN-kode:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Enhetstilkobling"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Enhetstilkobling"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Invitasjonen er sendt til <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Skriv inn denne PIN-koden på <xliff:g id="DEVICE_NAME">%1$s</xliff:g> for å koble til."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"OK"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Enhetstilkobling"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Enhetstilkobling"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Enhetstilkobling"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> vil koble til enheten din."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Skriv inn PIN-koden som vises på <xliff:g id="DEVICE_NAME">%1$s</xliff:g>, for å koble til."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> vil koble til enheten din med den følgende PIN-koden."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} vil koble til enheten din. Godta om {countdown} sekund.}other{{device} vil koble til enheten din. Godta om {countdown} sekunder.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Skriv inn PIN-koden som vises på {device}, innen {countdown} sekund for å koble til.}other{Skriv inn PIN-koden som vises på {device}, innen {countdown} sekunder for å koble til.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} vil koble til enheten din med den følgende PIN-koden. Godta om {countdown} sekund.}other{{device} vil koble til enheten din med den følgende PIN-koden. Godta om {countdown} sekunder.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Koble til"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Avbryt"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN-kode"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN-kode"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Enheten din blir midlertidig koblet fra wifi mens den er koblet til <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="dlg_ok" msgid="254496739491689405">"OK"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Kan ikke koble til <xliff:g id="SSID">%1$s</xliff:g>"</string> @@ -155,7 +173,7 @@ <string name="wifi_ca_cert_notification_preT_message" msgid="4565553176090475724">"Nettverket <xliff:g id="SSID">%1$s</xliff:g> mangler et sertifikat. Trykk for å finne ut hvordan du legger til sertifikater."</string> <string name="wifi_ca_cert_notification_preT_continue_text" msgid="1525418430746943670">"Koble til likevel"</string> <string name="wifi_ca_cert_notification_preT_abort_text" msgid="8307996031461071854">"Ikke koble til"</string> - <string name="wifi_enable_request_dialog_title" msgid="3577459145316177148">"Vil du gi <xliff:g id="APP_NAME">%1$s</xliff:g> tillatelse til å slå på Wi‑Fi?"</string> + <string name="wifi_enable_request_dialog_title" msgid="3577459145316177148">"Vil du gi <xliff:g id="APP_NAME">%1$s</xliff:g> tillatelse til å slå på wifi?"</string> <string name="wifi_enable_request_dialog_message" msgid="6395169178524938278">"Du kan slå av Wi‑Fi i hurtiginnstillingene"</string> <string name="wifi_enable_request_dialog_positive_button" msgid="6050832555821470466">"Tillat"</string> <string name="wifi_enable_request_dialog_negative_button" msgid="4754219902374918882">"Ikke tillat"</string> diff --git a/service/ServiceWifiResources/res/values-ne/strings.xml b/service/ServiceWifiResources/res/values-ne/strings.xml index aa5ac4b49c..daeffdf6cc 100644 --- a/service/ServiceWifiResources/res/values-ne/strings.xml +++ b/service/ServiceWifiResources/res/values-ne/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"प्रापक:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"आवश्यक PIN टाइप गर्नुहोस्:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"डिभाइसको कनेक्सन"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"डिभाइसको कनेक्सन"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> मा निम्तो पठाइएको छ।"</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"कनेक्ट गर्नका निम्ति यो PIN <xliff:g id="DEVICE_NAME">%1$s</xliff:g> मा हाल्नुहोस्।"</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"ठिक छ"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"डिभाइसको कनेक्सन"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"डिभाइसको कनेक्सन"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"डिभाइसको कनेक्सन"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> तपाईंको डिभाइसमा कनेक्ट गर्न चाहन्छ।"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"कनेक्ट गर्नका निम्ति <xliff:g id="DEVICE_NAME">%1$s</xliff:g> मा देखाइएको PIN हाल्नुहोस्।"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> निम्न PIN प्रयोग गरी तपाईंको डिभाइसमा कनेक्ट गर्न चाहन्छ।"</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} तपाईंको डिभाइसमा कनेक्ट गर्न चाहन्छ। {countdown} सेकेन्डपछि स्वीकार गर्नुहोस्।}other{{device} तपाईंको डिभाइसमा कनेक्ट गर्न चाहन्छ। {countdown} सेकेन्डपछि स्वीकार गर्नुहोस्।}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{कनेक्ट गर्नका निम्ति {countdown} सेकेन्डभित्र {device} मा देखाइएको PIN हाल्नुहोस्।}other{कनेक्ट गर्नका निम्ति {countdown} सेकेन्डभित्र {device} मा देखाइएको PIN हाल्नुहोस्।}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} निम्न PIN प्रयोग गरी तपाईंको डिभाइसमा कनेक्ट गर्न चाहन्छ। {countdown} सेकेन्डपछि स्वीकार गर्नुहोस्।}other{{device} निम्न PIN प्रयोग गरी तपाईंको डिभाइसमा कनेक्ट गर्न चाहन्छ। {countdown} सेकेन्डपछि स्वीकार गर्नुहोस्।}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"कनेक्ट गर्नुहोस्"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"रद्द गर्नुहोस्"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"तपाईंको डिभाइस <xliff:g id="DEVICE_NAME">%1$s</xliff:g> मा कनेक्ट गरिएको बेलामा यो केही बेर Wi-Fi बाट डिस्कनेक्ट हुने छ"</string> <string name="dlg_ok" msgid="254496739491689405">"ठिक छ"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"<xliff:g id="SSID">%1$s</xliff:g> मा जडान गर्न सकिएन"</string> diff --git a/service/ServiceWifiResources/res/values-night/styles.xml b/service/ServiceWifiResources/res/values-night/styles.xml index 9279ee3780..a97049517e 100644 --- a/service/ServiceWifiResources/res/values-night/styles.xml +++ b/service/ServiceWifiResources/res/values-night/styles.xml @@ -23,4 +23,18 @@ <item name="android:inputType">number</item> <item name="android:textColor">@android:color/primary_text_dark</item> </style> + + <style name="wifi_p2p_dialog2" parent="@android:style/Theme.DeviceDefault.Dialog.Alert" /> + <style name="wifi_p2p_dialog2_pin_label"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:textColor">@android:color/secondary_text_dark</item> + <item name="android:textSize">14sp</item> + </style> + <style name="wifi_p2p_dialog2_pin"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:textSize">18sp</item> + <item name="android:textColor">@android:color/primary_text_dark</item> + </style> </resources> diff --git a/service/ServiceWifiResources/res/values-nl/strings.xml b/service/ServiceWifiResources/res/values-nl/strings.xml index 4c6e2d7da9..353139452a 100644 --- a/service/ServiceWifiResources/res/values-nl/strings.xml +++ b/service/ServiceWifiResources/res/values-nl/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Naar:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Voer de gewenste pincode in:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"Pincode"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Apparaatverbinding"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Apparaatverbinding"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Uitnodiging verstuurd naar <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Voer deze pincode in op <xliff:g id="DEVICE_NAME">%1$s</xliff:g> om verbinding te maken."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"OK"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Apparaatverbinding"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Apparaatverbinding"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Apparaatverbinding"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> wil verbinding maken met je apparaat."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Voer de pincode in die op <xliff:g id="DEVICE_NAME">%1$s</xliff:g> wordt getoond om verbinding te maken."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> wil verbinding maken met je apparaat met de volgende pincode."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} wil verbinding maken met je apparaat. Accepteren over {countdown} seconde.}other{{device} wil verbinding maken met je apparaat. Accepteren over {countdown} seconden.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Voer binnen {countdown} seconde de pincode in die op {device} wordt getoond om verbinding te maken.}other{Voer binnen {countdown} seconden de pincode in die op {device} wordt getoond om verbinding te maken.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} wil verbinding maken met je apparaat met de volgende pincode. Accepteren over {countdown} seconde.}other{{device} wil verbinding maken met je apparaat met de volgende pincode. Accepteren over {countdown} seconden.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Verbinden"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Annuleren"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"Pincode"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"Pincode"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Je apparaat verbreekt tijdelijk de verbinding met wifi terwijl het verbonden is met <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="dlg_ok" msgid="254496739491689405">"OK"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Kan niet verbinden met <xliff:g id="SSID">%1$s</xliff:g>"</string> diff --git a/service/ServiceWifiResources/res/values-or/strings.xml b/service/ServiceWifiResources/res/values-or/strings.xml index 6abb1e6c34..0860d7958b 100644 --- a/service/ServiceWifiResources/res/values-or/strings.xml +++ b/service/ServiceWifiResources/res/values-or/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"ପ୍ରାପ୍ତେଷୁ:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"ଆବଶ୍ୟକ PIN ଟାଇପ୍ କରନ୍ତୁ:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"ଡିଭାଇସ କନେକ୍ସନ"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"ଡିଭାଇସ କନେକ୍ସନ"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>କୁ ଆମନ୍ତ୍ରଣ ପଠାଯାଇଛି।"</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"କନେକ୍ଟ କରିବାକୁ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>ରେ ଏହି PIN ଲେଖନ୍ତୁ।"</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"ଠିକ ଅଛି"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"ଡିଭାଇସ କନେକ୍ସନ"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"ଡିଭାଇସ କନେକ୍ସନ"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"ଡିଭାଇସ କନେକ୍ସନ"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ଆପଣଙ୍କ ଡିଭାଇସ ସହ କନେକ୍ଟ କରିବାକୁ ଚାହୁଁଛି।"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"କନେକ୍ଟ କରିବାକୁ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>ରେ ଦେଖାଯାଉଥିବା PIN ଲେଖନ୍ତୁ।"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ନିମ୍ନୋକ୍ତ PIN ମାଧ୍ୟମରେ ଆପଣଙ୍କ ଡିଭାଇସ ସହ କନେକ୍ଟ କରିବାକୁ ଚାହୁଁଛି।"</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} ଆପଣଙ୍କ ଡିଭାଇସ ସହ କନେକ୍ଟ କରିବାକୁ ଚାହୁଁଛି। {countdown} ସେକେଣ୍ଡରେ ଗ୍ରହଣ କରନ୍ତୁ।}other{{device} ଆପଣଙ୍କ ଡିଭାଇସ ସହ କନେକ୍ଟ କରିବାକୁ ଚାହୁଁଛି। {countdown} ସେକେଣ୍ଡରେ ଗ୍ରହଣ କରନ୍ତୁ।}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{କନେକ୍ଟ କରିବାକୁ {countdown} ସେକେଣ୍ଡ ମଧ୍ୟରେ {device}ରେ ଦେଖାଯାଉଥିବା PIN ଲେଖନ୍ତୁ।}other{କନେକ୍ଟ କରିବାକୁ {countdown} ସେକେଣ୍ଡ ମଧ୍ୟରେ {device}ରେ ଦେଖାଯାଉଥିବା PIN ଲେଖନ୍ତୁ।}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} ନିମ୍ନୋକ୍ତ PIN ମାଧ୍ୟମରେ ଆପଣଙ୍କ ଡିଭାଇସ ସହ କନେକ୍ଟ କରିବାକୁ ଚାହୁଁଛି। {countdown} ସେକେଣ୍ଡରେ ଗ୍ରହଣ କରନ୍ତୁ।}other{{device} ନିମ୍ନୋକ୍ତ PIN ମାଧ୍ୟମରେ ଆପଣଙ୍କ ଡିଭାଇସ ସହ କନେକ୍ଟ କରିବାକୁ ଚାହୁଁଛି। {countdown} ସେକେଣ୍ଡରେ ଗ୍ରହଣ କରନ୍ତୁ।}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"କନେକ୍ଟ କରନ୍ତୁ"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"ବାତିଲ କରନ୍ତୁ"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"ଆପଣଙ୍କ ଡିଭାଇସ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ସହ କନେକ୍ଟ ଥିବା ସମୟରେ ଏହା ଅସ୍ଥାୟୀ ଭାବେ ୱାଇ-ଫାଇରୁ ଡିସକନେକ୍ଟ ହୋଇଯିବ"</string> <string name="dlg_ok" msgid="254496739491689405">"ଠିକ ଅଛି"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"<xliff:g id="SSID">%1$s</xliff:g> ସହିତ ସଂଯୋଗ କରାଯାଇପାରିବ ନାହିଁ"</string> diff --git a/service/ServiceWifiResources/res/values-pa/strings.xml b/service/ServiceWifiResources/res/values-pa/strings.xml index 2589be4766..4c64a5215d 100644 --- a/service/ServiceWifiResources/res/values-pa/strings.xml +++ b/service/ServiceWifiResources/res/values-pa/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"ਵੱਲ:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"ਲੋੜੀਂਦਾ ਪਿੰਨ ਟਾਈਪ ਕਰੋ:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"ਪਿੰਨ:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"ਡੀਵਾਈਸ ਕਨੈਕਸ਼ਨ"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"ਡੀਵਾਈਸ ਕਨੈਕਸ਼ਨ"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ਨੂੰ ਸੱਦਾ ਭੇਜਿਆ ਗਿਆ।"</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"ਕਨੈਕਟ ਕਰਨ ਲਈ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> \'ਤੇ ਇਹ ਪਿੰਨ ਦਾਖਲ ਕਰੋ।"</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"ਠੀਕ ਹੈ"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"ਡੀਵਾਈਸ ਕਨੈਕਸ਼ਨ"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"ਡੀਵਾਈਸ ਕਨੈਕਸ਼ਨ"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"ਡੀਵਾਈਸ ਕਨੈਕਸ਼ਨ"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਨਾਲ ਕਨੈਕਟ ਕਰਨਾ ਚਾਹੁੰਦਾ ਹੈ।"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"ਕਨੈਕਟ ਕਰਨ ਲਈ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> \'ਤੇ ਦਿਖਾਇਆ ਗਿਆ ਪਿੰਨ ਦਾਖਲ ਕਰੋ।"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ਅੱਗੇ ਦਿੱਤੇ ਪਿੰਨ ਨਾਲ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਨਾਲ ਕਨੈਕਟ ਕਰਨਾ ਚਾਹੁੰਦਾ ਹੈ।"</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਨਾਲ ਕਨੈਕਟ ਕਰਨਾ ਚਾਹੁੰਦਾ ਹੈ। {countdown} ਸਕਿੰਟ ਵਿੱਚ ਸਵੀਕਾਰ ਕਰੋ।}one{{device} ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਨਾਲ ਕਨੈਕਟ ਕਰਨਾ ਚਾਹੁੰਦਾ ਹੈ। {countdown} ਸਕਿੰਟ ਵਿੱਚ ਸਵੀਕਾਰ ਕਰੋ।}other{{device} ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਨਾਲ ਕਨੈਕਟ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹਨ। {countdown} ਸਕਿੰਟਾਂ ਵਿੱਚ ਸਵੀਕਾਰ ਕਰੋ।}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{ਕਨੈਕਟ ਕਰਨ ਲਈ {countdown} ਸਕਿੰਟ ਵਿੱਚ {device} \'ਤੇ ਦਿਖਾਇਆ ਗਿਆ ਪਿੰਨ ਦਾਖਲ ਕਰੋ।}one{ਕਨੈਕਟ ਕਰਨ ਲਈ {countdown} ਸਕਿੰਟ ਵਿੱਚ {device} \'ਤੇ ਦਿਖਾਇਆ ਗਿਆ ਪਿੰਨ ਦਾਖਲ ਕਰੋ।}other{ਕਨੈਕਟ ਕਰਨ ਲਈ {countdown} ਸਕਿੰਟਾਂ ਵਿੱਚ {device} \'ਤੇ ਦਿਖਾਇਆ ਗਿਆ ਪਿੰਨ ਦਾਖਲ ਕਰੋ।}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} ਅੱਗੇ ਦਿੱਤੇ ਪਿੰਨ ਨਾਲ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਨਾਲ ਕਨੈਕਟ ਕਰਨਾ ਚਾਹੁੰਦਾ ਹੈ। {countdown} ਸਕਿੰਟ ਵਿੱਚ ਸਵੀਕਾਰ ਕਰੋ।}one{{device} ਅੱਗੇ ਦਿੱਤੇ ਪਿੰਨ ਨਾਲ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਨਾਲ ਕਨੈਕਟ ਕਰਨਾ ਚਾਹੁੰਦਾ ਹੈ। {countdown} ਸਕਿੰਟ ਵਿੱਚ ਸਵੀਕਾਰ ਕਰੋ।}other{{device} ਅੱਗੇ ਦਿੱਤੇ ਪਿੰਨ ਨਾਲ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਨਾਲ ਕਨੈਕਟ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹਨ। {countdown} ਸਕਿੰਟਾਂ ਵਿੱਚ ਸਵੀਕਾਰ ਕਰੋ।}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"ਕਨੈਕਟ ਕਰੋ"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"ਰੱਦ ਕਰੋ"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"ਪਿੰਨ"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"ਪਿੰਨ"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ਨਾਲ ਕਨੈਕਟ ਹੋਣ \'ਤੇ, ਤੁਹਾਡਾ ਡੀਵਾਈਸ ਕੁਝ ਸਮੇਂ ਲਈ ਵਾਈ-ਫਾਈ ਤੋਂ ਡਿਸਕਨੈਕਟ ਹੋ ਜਾਵੇਗਾ"</string> <string name="dlg_ok" msgid="254496739491689405">"ਠੀਕ ਹੈ"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"<xliff:g id="SSID">%1$s</xliff:g> ਨਾਲ ਕਨੈਕਟ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ"</string> diff --git a/service/ServiceWifiResources/res/values-pl/strings.xml b/service/ServiceWifiResources/res/values-pl/strings.xml index e3afb51815..85c9402f20 100644 --- a/service/ServiceWifiResources/res/values-pl/strings.xml +++ b/service/ServiceWifiResources/res/values-pl/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Do:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Wpisz wymagany kod PIN:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"Kod PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Połączenie urządzenia"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Połączenie urządzenia"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Zaproszenie zostało wysłane na urządzenie <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Aby się połączyć, wpisz ten kod PIN na urządzeniu <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"OK"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Połączenie urządzenia"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Połączenie urządzenia"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Połączenie urządzenia"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> chce połączyć się z Twoim urządzeniem."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Aby się połączyć, wpisz kod PIN widoczny na urządzeniu <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> chce połączyć się z Twoim urządzeniem za pomocą tego kodu PIN."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} chce połączyć się z Twoim urządzeniem. Zaakceptuj w ciągu {countdown} sekundy.}few{{device} chce połączyć się z Twoim urządzeniem. Zaakceptuj w ciągu {countdown} sekund.}many{{device} chce połączyć się z Twoim urządzeniem. Zaakceptuj w ciągu {countdown} sekund.}other{{device} chce połączyć się z Twoim urządzeniem. Zaakceptuj w ciągu {countdown} sekundy.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Aby się połączyć, w ciągu {countdown} sekundy wpisz kod PIN widoczny na urządzeniu {device}.}few{Aby się połączyć, w ciągu {countdown} sekund wpisz kod PIN widoczny na urządzeniu {device}.}many{Aby się połączyć, w ciągu {countdown} sekund wpisz kod PIN widoczny na urządzeniu {device}.}other{Aby się połączyć, w ciągu {countdown} sekundy wpisz kod PIN widoczny na urządzeniu {device}.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} chce połączyć się z Twoim urządzeniem za pomocą tego kodu PIN. Zaakceptuj w ciągu {countdown} sekundy.}few{{device} chce połączyć się z Twoim urządzeniem za pomocą tego kodu PIN. Zaakceptuj w ciągu {countdown} sekund.}many{{device} chce połączyć się z Twoim urządzeniem za pomocą tego kodu PIN. Zaakceptuj w ciągu {countdown} sekund.}other{{device} chce połączyć się z Twoim urządzeniem za pomocą tego kodu PIN. Zaakceptuj w ciągu {countdown} sekundy.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Połącz"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Anuluj"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"Kod PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"Kod PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Gdy urządzenie zostanie podłączone do urządzenia <xliff:g id="DEVICE_NAME">%1$s</xliff:g>, tymczasowo rozłączy się z Wi-Fi"</string> <string name="dlg_ok" msgid="254496739491689405">"OK"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Nie można połączyć się z siecią <xliff:g id="SSID">%1$s</xliff:g>"</string> diff --git a/service/ServiceWifiResources/res/values-pt-rBR/strings.xml b/service/ServiceWifiResources/res/values-pt-rBR/strings.xml index 2f2060c0e2..f545a10bef 100644 --- a/service/ServiceWifiResources/res/values-pt-rBR/strings.xml +++ b/service/ServiceWifiResources/res/values-pt-rBR/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Para:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Digite o PIN obrigatório:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Conexão do dispositivo"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Conexão do dispositivo"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Convite enviado para o <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Digite este PIN no <xliff:g id="DEVICE_NAME">%1$s</xliff:g> para se conectar."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"OK"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Conexão do dispositivo"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Conexão do dispositivo"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Conexão do dispositivo"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"O <xliff:g id="DEVICE_NAME">%1$s</xliff:g> quer se conectar ao seu dispositivo."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Digite o PIN mostrado no <xliff:g id="DEVICE_NAME">%1$s</xliff:g> para se conectar."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"O <xliff:g id="DEVICE_NAME">%1$s</xliff:g> quer se conectar ao seu dispositivo com o seguinte PIN."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{O {device} quer se conectar ao seu dispositivo. Aceite em {countdown} segundo.}one{O {device} quer se conectar ao seu dispositivo. Aceite em {countdown} segundo.}many{O {device} quer se conectar ao seu dispositivo. Aceite em {countdown} de segundos.}other{O {device} quer se conectar ao seu dispositivo. Aceite em {countdown} segundos.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Digite o PIN mostrado no {device} em até {countdown} segundo para se conectar.}one{Digite o PIN mostrado no {device} em até {countdown} segundo para se conectar.}many{Digite o PIN mostrado no {device} em até {countdown} de segundos para se conectar.}other{Digite o PIN mostrado no {device} em até {countdown} segundos para se conectar.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{O {device} quer se conectar ao seu dispositivo com o seguinte PIN. Aceite em {countdown} segundo.}one{O {device} quer se conectar ao seu dispositivo com o seguinte PIN. Aceite em {countdown} segundo.}many{O {device} quer se conectar ao seu dispositivo com o seguinte PIN. Aceite em {countdown} de segundos.}other{O {device} quer se conectar ao seu dispositivo com o seguinte PIN. Aceite em {countdown} segundos.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Conectar"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Cancelar"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"O dispositivo vai desconectar o Wi-Fi temporariamente enquanto estiver conectado ao <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="dlg_ok" msgid="254496739491689405">"OK"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Não foi possível se conectar à rede <xliff:g id="SSID">%1$s</xliff:g>"</string> diff --git a/service/ServiceWifiResources/res/values-pt-rPT/strings.xml b/service/ServiceWifiResources/res/values-pt-rPT/strings.xml index 6b56d85906..086254b4cf 100644 --- a/service/ServiceWifiResources/res/values-pt-rPT/strings.xml +++ b/service/ServiceWifiResources/res/values-pt-rPT/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Para:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Introduza o PIN solicitado:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Ligação do dispositivo"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Ligação do dispositivo"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Convite enviado para o dispositivo <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Introduza este PIN no dispositivo <xliff:g id="DEVICE_NAME">%1$s</xliff:g> para estabelecer ligação."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"OK"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Ligação do dispositivo"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Ligação do dispositivo"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Ligação do dispositivo"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"O dispositivo <xliff:g id="DEVICE_NAME">%1$s</xliff:g> quer estabelecer ligação ao seu dispositivo."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Introduza o PIN apresentado no dispositivo <xliff:g id="DEVICE_NAME">%1$s</xliff:g> para estabelecer ligação."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"O dispositivo <xliff:g id="DEVICE_NAME">%1$s</xliff:g> quer estabelecer ligação ao seu dispositivo com o seguinte PIN."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{O dispositivo {device} quer estabelecer ligação ao seu dispositivo. Aceite dentro de {countdown} segundo.}many{O dispositivo {device} quer estabelecer ligação ao seu dispositivo. Aceite dentro de {countdown} segundos.}other{O dispositivo {device} quer estabelecer ligação ao seu dispositivo. Aceite dentro de {countdown} segundos.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Introduza o PIN apresentado no dispositivo {device} no prazo de {countdown} segundo para estabelecer ligação.}many{Introduza o PIN apresentado no dispositivo {device} no prazo de {countdown} segundos para estabelecer ligação.}other{Introduza o PIN apresentado no dispositivo {device} no prazo de {countdown} segundos para estabelecer ligação.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{O dispositivo {device} quer estabelecer ligação ao seu dispositivo com o seguinte PIN. Aceite dentro de {countdown} segundo.}many{O dispositivo {device} quer estabelecer ligação ao seu dispositivo com o seguinte PIN. Aceite dentro de {countdown} segundos.}other{O dispositivo {device} quer estabelecer ligação ao seu dispositivo com o seguinte PIN. Aceite dentro de {countdown} segundos.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Ligar"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Cancelar"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"O seu dispositivo vai desligar-se temporariamente da rede Wi-Fi enquanto está ligado ao dispositivo <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> <string name="dlg_ok" msgid="254496739491689405">"OK"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Não é possível ligar a <xliff:g id="SSID">%1$s</xliff:g>."</string> diff --git a/service/ServiceWifiResources/res/values-pt/strings.xml b/service/ServiceWifiResources/res/values-pt/strings.xml index 2f2060c0e2..f545a10bef 100644 --- a/service/ServiceWifiResources/res/values-pt/strings.xml +++ b/service/ServiceWifiResources/res/values-pt/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Para:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Digite o PIN obrigatório:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Conexão do dispositivo"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Conexão do dispositivo"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Convite enviado para o <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Digite este PIN no <xliff:g id="DEVICE_NAME">%1$s</xliff:g> para se conectar."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"OK"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Conexão do dispositivo"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Conexão do dispositivo"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Conexão do dispositivo"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"O <xliff:g id="DEVICE_NAME">%1$s</xliff:g> quer se conectar ao seu dispositivo."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Digite o PIN mostrado no <xliff:g id="DEVICE_NAME">%1$s</xliff:g> para se conectar."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"O <xliff:g id="DEVICE_NAME">%1$s</xliff:g> quer se conectar ao seu dispositivo com o seguinte PIN."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{O {device} quer se conectar ao seu dispositivo. Aceite em {countdown} segundo.}one{O {device} quer se conectar ao seu dispositivo. Aceite em {countdown} segundo.}many{O {device} quer se conectar ao seu dispositivo. Aceite em {countdown} de segundos.}other{O {device} quer se conectar ao seu dispositivo. Aceite em {countdown} segundos.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Digite o PIN mostrado no {device} em até {countdown} segundo para se conectar.}one{Digite o PIN mostrado no {device} em até {countdown} segundo para se conectar.}many{Digite o PIN mostrado no {device} em até {countdown} de segundos para se conectar.}other{Digite o PIN mostrado no {device} em até {countdown} segundos para se conectar.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{O {device} quer se conectar ao seu dispositivo com o seguinte PIN. Aceite em {countdown} segundo.}one{O {device} quer se conectar ao seu dispositivo com o seguinte PIN. Aceite em {countdown} segundo.}many{O {device} quer se conectar ao seu dispositivo com o seguinte PIN. Aceite em {countdown} de segundos.}other{O {device} quer se conectar ao seu dispositivo com o seguinte PIN. Aceite em {countdown} segundos.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Conectar"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Cancelar"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"O dispositivo vai desconectar o Wi-Fi temporariamente enquanto estiver conectado ao <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="dlg_ok" msgid="254496739491689405">"OK"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Não foi possível se conectar à rede <xliff:g id="SSID">%1$s</xliff:g>"</string> diff --git a/service/ServiceWifiResources/res/values-ro/strings.xml b/service/ServiceWifiResources/res/values-ro/strings.xml index 40271f5458..80291ea853 100644 --- a/service/ServiceWifiResources/res/values-ro/strings.xml +++ b/service/ServiceWifiResources/res/values-ro/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Către:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Introdu codul PIN necesar:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"Cod PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Conexiunea dispozitivului"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Conexiunea dispozitivului"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Invitația a fost trimisă la <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Introdu acest PIN pe <xliff:g id="DEVICE_NAME">%1$s</xliff:g> pentru a te conecta."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"OK"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Conexiunea dispozitivului"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Conexiunea dispozitivului"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Conexiunea dispozitivului"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> solicită conectarea la dispozitivul tău."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Introdu PIN-ul afișat pe <xliff:g id="DEVICE_NAME">%1$s</xliff:g> pentru a te conecta."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> solicită conectarea la dispozitivul tău cu următorul PIN."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} solicită conectarea la dispozitivul tău. Acceptă în {countdown} secundă.}few{{device} solicită conectarea la dispozitivul tău. Acceptă în {countdown} secunde.}other{{device} solicită conectarea la dispozitivul tău. Acceptă în {countdown} de secunde.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Introdu PIN-ul afișat pe {device} în {countdown} secundă pentru a te conecta.}few{Introdu PIN-ul afișat pe {device} în {countdown} secunde pentru a te conecta.}other{Introdu PIN-ul afișat pe {device} în {countdown} de secunde pentru a te conecta.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} solicită conectarea la dispozitivul tău cu următorul PIN. Acceptă în {countdown} secundă.}few{{device} solicită conectarea la dispozitivul tău cu următorul PIN. Acceptă în {countdown} secunde.}other{{device} solicită conectarea la dispozitivul tău cu următorul PIN. Acceptă în {countdown} de secunde.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Conectează"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Anulează"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Dispozitivul se va deconecta temporar de la Wi-Fi cât timp este conectat la <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="dlg_ok" msgid="254496739491689405">"OK"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Nu se poate conecta la <xliff:g id="SSID">%1$s</xliff:g>"</string> diff --git a/service/ServiceWifiResources/res/values-ru/strings.xml b/service/ServiceWifiResources/res/values-ru/strings.xml index a47cd0e788..e83fadbdfd 100644 --- a/service/ServiceWifiResources/res/values-ru/strings.xml +++ b/service/ServiceWifiResources/res/values-ru/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Кому:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Введите PIN-код:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN-код:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Подключение к устройству"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Подключение к устройству"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Приглашение отправлено пользователю устройства \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\"."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Чтобы подключиться, введите этот PIN-код на устройстве \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\"."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"ОК"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Подключение к устройству"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Подключение к устройству"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Подключение к устройству"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"К вашему устройству хочет подключиться пользователь устройства \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\"."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Чтобы подключиться, введите PIN-код, указанный на устройстве \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\"."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"К вашему устройству хочет подключиться пользователь устройства \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\". Для этого потребуется указанный далее PIN-код."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{К вашему устройству хочет подключиться пользователь устройства \"{device}\". У вас есть {countdown} секунда, чтобы дать согласие.}one{К вашему устройству хочет подключиться пользователь устройства \"{device}\". У вас есть {countdown} секунда, чтобы дать согласие.}few{К вашему устройству хочет подключиться пользователь устройства \"{device}\". У вас есть {countdown} секунды, чтобы дать согласие.}many{К вашему устройству хочет подключиться пользователь устройства \"{device}\". У вас есть {countdown} секунд, чтобы дать согласие.}other{К вашему устройству хочет подключиться пользователь устройства \"{device}\". У вас есть {countdown} секунды, чтобы дать согласие.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Чтобы подключиться, в течение {countdown} секунды введите PIN-код, указанный на устройстве \"{device}\".}one{Чтобы подключиться, в течение {countdown} секунды введите PIN-код, указанный на устройстве \"{device}\".}few{Чтобы подключиться, в течение {countdown} секунд введите PIN-код, указанный на устройстве \"{device}\".}many{Чтобы подключиться, в течение {countdown} секунд введите PIN-код, указанный на устройстве \"{device}\".}other{Чтобы подключиться, в течение {countdown} секунды введите PIN-код, указанный на устройстве \"{device}\".}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{К вашему устройству хочет подключиться пользователь устройства \"{device}\". Для этого нужен указанный далее PIN-код. У вас есть {countdown} секунда, чтобы дать согласие.}one{К вашему устройству хочет подключиться пользователь устройства \"{device}\". Для этого нужен указанный далее PIN-код. У вас есть {countdown} секунда, чтобы дать согласие.}few{К вашему устройству хочет подключиться пользователь устройства \"{device}\". Для этого нужен указанный далее PIN-код. У вас есть {countdown} секунды, чтобы дать согласие.}many{К вашему устройству хочет подключиться пользователь устройства \"{device}\". Для этого нужен указанный далее PIN-код. У вас есть {countdown} секунд, чтобы дать согласие.}other{К вашему устройству хочет подключиться пользователь устройства \"{device}\". Для этого нужен указанный далее PIN-код. У вас есть {countdown} секунды, чтобы дать согласие.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Подключиться"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Отмена"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN-код"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN-код"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"На время подключения к устройству <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ваше устройство отключится от сети Wi-Fi."</string> <string name="dlg_ok" msgid="254496739491689405">"ОК"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Не удалось подключиться к устройству \"<xliff:g id="SSID">%1$s</xliff:g>\""</string> diff --git a/service/ServiceWifiResources/res/values-si/strings.xml b/service/ServiceWifiResources/res/values-si/strings.xml index d9c7cb4bd2..4e3c1cd984 100644 --- a/service/ServiceWifiResources/res/values-si/strings.xml +++ b/service/ServiceWifiResources/res/values-si/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"වෙත:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"අවශ්ය PIN එක ටයිප් කරන්න:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"උපාංග සම්බන්ධතාව"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"උපාංග සම්බන්ධතාව"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> වෙත ආරාධනය යැවුවා."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"සම්බන්ධ වීමට <xliff:g id="DEVICE_NAME">%1$s</xliff:g> මත මෙම PIN ඇතුළු කරන්න."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"හරි"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"උපාංග සම්බන්ධතාව"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"උපාංග සම්බන්ධතාව"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"උපාංග සම්බන්ධතාව"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> හට ඔබේ උපාංගයට සම්බන්ධ වීමට අවශ්යයි."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"සම්බන්ධ වීමට <xliff:g id="DEVICE_NAME">%1$s</xliff:g> හි පෙන්වා ඇති PIN ඇතුළු කරන්න."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> හට පහත PIN අංකය සමග ඔබේ උපාංගයට සම්බන්ධ වීමට අවශ්යයි."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} හට ඔබේ උපාංගයට සම්බන්ධ වීමට අවශ්යයි. තත්පර {countdown}කින් පිළිගන්න.}one{{device} හට ඔබේ උපාංගයට සම්බන්ධ වීමට අවශ්යයි. තත්පර {countdown}කින් පිළිගන්න.}other{{device} හට ඔබේ උපාංගයට සම්බන්ධ වීමට අවශ්යයි. තත්පර {countdown}කින් පිළිගන්න.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{සම්බන්ධ වීමට තත්පර {countdown}ක් ඇතුළත {device} හි පෙන්වා ඇති PIN ඇතුළු කරන්න.}one{සම්බන්ධ වීමට තත්පර {countdown}ක් ඇතුළත {device} හි පෙන්වා ඇති PIN ඇතුළු කරන්න.}other{සම්බන්ධ වීමට තත්පර {countdown}ක් ඇතුළත {device} හි පෙන්වා ඇති PIN ඇතුළු කරන්න.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} හට පහත PIN අංකය සමග ඔබේ උපාංගයට සම්බන්ධ වීමට අවශ්යයි. තත්පර {countdown}කින් පිළිගන්න.}one{{device} හට පහත PIN අංකය සමග ඔබේ උපාංගයට සම්බන්ධ වීමට අවශ්යයි. තත්පර {countdown}කින් පිළිගන්න.}other{{device} හට පහත PIN අංකය සමග ඔබේ උපාංගයට සම්බන්ධ වීමට අවශ්යයි. තත්පර {countdown}කින් පිළිගන්න.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"සම්බන්ධ කරන්න"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"අවලංගු කරන්න"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> වෙත සම්බන්ධ වී ඇති අතරේ ඔබේ උපාංගය Wi-Fi වෙතින් තාවකාලිකව විසන්ධි වනු ඇත"</string> <string name="dlg_ok" msgid="254496739491689405">"හරි"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"<xliff:g id="SSID">%1$s</xliff:g> වෙත සම්බන්ධ විය නොහැකිය"</string> diff --git a/service/ServiceWifiResources/res/values-sk/strings.xml b/service/ServiceWifiResources/res/values-sk/strings.xml index 397dc9f33c..84521b65ce 100644 --- a/service/ServiceWifiResources/res/values-sk/strings.xml +++ b/service/ServiceWifiResources/res/values-sk/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Komu:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Zadajte požadovaný kód PIN:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Pripojenie zariadenia"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Pripojenie zariadenia"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Pozvánka bola odoslaná do zariadenia <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Ak sa chcete pripojiť, zadajte tento kód PIN v zariadení <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"OK"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Pripojenie zariadenia"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Pripojenie zariadenia"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Pripojenie zariadenia"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> sa chce pripojiť k vášmu zariadeniu."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Zadajte kód PIN zobrazený v zariadení <xliff:g id="DEVICE_NAME">%1$s</xliff:g> a pripojte sa."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> sa chce pripojiť k vášmu zariadeniu pomocou nasledujúceho kódu PIN."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} sa chce pripojiť k vášmu zariadeniu. Prijmite o {countdown} sekundu.}few{{device} sa chce pripojiť k vášmu zariadeniu. Prijmite o {countdown} sekundy.}many{{device} sa chce pripojiť k vášmu zariadeniu. Prijmite o {countdown} sekundy.}other{{device} sa chce pripojiť k vášmu zariadeniu. Prijmite o {countdown} sekúnd.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Ak sa chcete pripojiť, do {countdown} sekundy zadajte kód PIN zobrazený v zariadení {device}.}few{Ak sa chcete pripojiť, do {countdown} sekúnd zadajte kód PIN zobrazený v zariadení {device}.}many{Ak sa chcete pripojiť, do {countdown} sekundy zadajte kód PIN zobrazený v zariadení {device}.}other{Ak sa chcete pripojiť, do {countdown} sekúnd zadajte kód PIN zobrazený v zariadení {device}.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} sa chce pripojiť k vášmu zariadeniu pomocou nasledujúceho kódu PIN. Prijmite o {countdown} sekundu.}few{{device} sa chce pripojiť k vášmu zariadeniu pomocou nasledujúceho kódu PIN. Prijmite o {countdown} sekundy.}many{{device} sa chce pripojiť k vášmu zariadeniu pomocou nasledujúceho kódu PIN. Prijmite o {countdown} sekundy.}other{{device} sa chce pripojiť k vášmu zariadeniu pomocou nasledujúceho kódu PIN. Prijmite o {countdown} sekúnd.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Pripojiť"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Zrušiť"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Počas pripojenia k zariadeniu <xliff:g id="DEVICE_NAME">%1$s</xliff:g> sa vaše zariadenie dočasne odpojí od siete Wi‑Fi"</string> <string name="dlg_ok" msgid="254496739491689405">"OK"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"K sieti <xliff:g id="SSID">%1$s</xliff:g> sa nedá pripojiť"</string> diff --git a/service/ServiceWifiResources/res/values-sl/strings.xml b/service/ServiceWifiResources/res/values-sl/strings.xml index e05b77dae6..8cdff4827e 100644 --- a/service/ServiceWifiResources/res/values-sl/strings.xml +++ b/service/ServiceWifiResources/res/values-sl/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Za:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Vnesite zahtevano kodo PIN:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Povezava naprave"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Povezava naprave"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Povabilo je bilo poslano v napravo <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Vnesite to kodo PIN v napravi <xliff:g id="DEVICE_NAME">%1$s</xliff:g>, če želite vzpostaviti povezavo."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"V redu"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Povezava naprave"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Povezava naprave"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Povezava naprave"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> se želi povezati z vašo napravo."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Vnesite kodo PIN, prikazano v napravi <xliff:g id="DEVICE_NAME">%1$s</xliff:g>, če želite vzpostaviti povezavo."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> se želi povezati z vašo napravo s to kodo PIN."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} se želi povezati z vašo napravo. Sprejmite v roku {countdown} sekunde.}one{{device} se želi povezati z vašo napravo. Sprejmite v roku {countdown} sekunde.}two{{device} se želi povezati z vašo napravo. Sprejmite v roku {countdown} sekund.}few{{device} se želi povezati z vašo napravo. Sprejmite v roku {countdown} sekund.}other{{device} se želi povezati z vašo napravo. Sprejmite v roku {countdown} sekund.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{V roku {countdown} sekunde vnesite kodo PIN, prikazano v napravi {device}, če želite vzpostaviti povezavo.}one{V roku {countdown} sekunde vnesite kodo PIN, prikazano v napravi {device}, če želite vzpostaviti povezavo.}two{V roku {countdown} sekund vnesite kodo PIN, prikazano v napravi {device}, če želite vzpostaviti povezavo.}few{V roku {countdown} sekund vnesite kodo PIN, prikazano v napravi {device}, če želite vzpostaviti povezavo.}other{V roku {countdown} sekund vnesite kodo PIN, prikazano v napravi {device}, če želite vzpostaviti povezavo.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} se želi povezati z vašo napravo s to kodo PIN. Sprejmite v roku {countdown} sekunde.}one{{device} se želi povezati z vašo napravo s to kodo PIN. Sprejmite v roku {countdown} sekunde.}two{{device} se želi povezati z vašo napravo s to kodo PIN. Sprejmite v roku {countdown} sekund.}few{{device} se želi povezati z vašo napravo s to kodo PIN. Sprejmite v roku {countdown} sekund.}other{{device} se želi povezati z vašo napravo s to kodo PIN. Sprejmite v roku {countdown} sekund.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Poveži"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Prekliči"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Naprava bo začasno prekinila povezavo z omrežjem Wi-Fi, medtem ko je povezana z napravo <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="dlg_ok" msgid="254496739491689405">"V redu"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Povezava z omrežjem <xliff:g id="SSID">%1$s</xliff:g> ni mogoča"</string> diff --git a/service/ServiceWifiResources/res/values-sq/strings.xml b/service/ServiceWifiResources/res/values-sq/strings.xml index a71a892754..18b379e7ca 100644 --- a/service/ServiceWifiResources/res/values-sq/strings.xml +++ b/service/ServiceWifiResources/res/values-sq/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Për:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Shkruaj PIN-in e kërkuar:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN-i:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Lidhja e pajisjes"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Lidhja e pajisjes"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Ftesa u dërgua te <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Fut këtë kod PIN në <xliff:g id="DEVICE_NAME">%1$s</xliff:g> për ta lidhur."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"Në rregull"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Lidhja e pajisjes"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Lidhja e pajisjes"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Lidhja e pajisjes"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> dëshiron të lidhet me pajisjen tënde."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Fut kodin PIN që shfaqet në <xliff:g id="DEVICE_NAME">%1$s</xliff:g> për ta lidhur."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> dëshiron të lidhet me pajisjen tënde me kodin e mëposhtëm PIN."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} dëshiron të lidhet me pajisjen tënde. Prano pas {countdown} sekonde.}other{{device} dëshiron të lidhet me pajisjen tënde. Prano pas {countdown} sekondash.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Fut kodin PIN që shfaqet në {device} brenda {countdown} sekonde për ta lidhur.}other{Fut kodin PIN që shfaqet në {device} brenda {countdown} sekondash për ta lidhur.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} dëshiron të lidhet me pajisjen tënde me kodin e mëposhtëm PIN. Prano pas {countdown} sekonde.}other{{device} dëshiron të lidhet me pajisjen tënde me kodin e mëposhtëm PIN. Prano pas {countdown} sekondash.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Lidh"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Anulo"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"Kodi PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"Kodi PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Pajisja jote do të shkëputet përkohësisht nga Wi-Fi ndërkohë që është e lidhur me <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="dlg_ok" msgid="254496739491689405">"Në rregull"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Nuk mund të lidhet me <xliff:g id="SSID">%1$s</xliff:g>"</string> diff --git a/service/ServiceWifiResources/res/values-sr/strings.xml b/service/ServiceWifiResources/res/values-sr/strings.xml index 2f5a6ef217..bc40eec20b 100644 --- a/service/ServiceWifiResources/res/values-sr/strings.xml +++ b/service/ServiceWifiResources/res/values-sr/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Коме:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Унесите потребни PIN:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Веза уређаја"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Веза уређаја"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Послат је позив за: <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Да бисте се повезали, унесите овај PIN на уређају <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"Потврди"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Веза уређаја"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Веза уређаја"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Веза уређаја"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> жели да се повеже са уређајем."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Да бисте се повезали, унесите PIN приказан на уређају <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> жели да се повеже са вашим уређајем помоћу следећег PIN-а."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} жели да се повеже са вашим уређајем. Прихватите за {countdown} секунду.}one{{device} жели да се повеже са вашим уређајем. Прихватите за {countdown} секунду.}few{{device} жели да се повеже са вашим уређајем. Прихватите за {countdown} секунде.}other{{device} жели да се повеже са вашим уређајем. Прихватите за {countdown} секунди.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Да бисте се повезали, унесите PIN приказан на уређају {device} у року од {countdown} секунде.}one{Да бисте се повезали, унесите PIN приказан на уређају {device} у року од {countdown} секунде.}few{Да бисте се повезали, унесите PIN приказан на уређају {device} у року од {countdown} секунде.}other{Да бисте се повезали, унесите PIN приказан на уређају {device} у року од {countdown} секунди.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} жели да се повеже са вашим уређајем помоћу следећег PIN-а. Прихватите за {countdown} секунду.}one{{device} жели да се повеже са вашим уређајем помоћу следећег PIN-а. Прихватите за {countdown} секунду.}few{{device} жели да се повеже са вашим уређајем помоћу следећег PIN-а. Прихватите за {countdown} секунде.}other{{device} жели да се повеже са вашим уређајем помоћу следећег PIN-а. Прихватите за {countdown} секунди.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Повежи"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Откажи"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Уређај ће привремено прекинути везу са WiFi мрежом док је повезан са уређајем <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="dlg_ok" msgid="254496739491689405">"Потврди"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Повезивање на мрежу <xliff:g id="SSID">%1$s</xliff:g> није успело"</string> diff --git a/service/ServiceWifiResources/res/values-sv/strings.xml b/service/ServiceWifiResources/res/values-sv/strings.xml index d02a82d58b..d67dacd8a8 100644 --- a/service/ServiceWifiResources/res/values-sv/strings.xml +++ b/service/ServiceWifiResources/res/values-sv/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Till:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Ange den obligatoriska PIN-koden:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN-kod:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Enhetsanslutning"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Enhetsanslutning"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Inbjudan har skickats till <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Ange den här pinkoden på <xliff:g id="DEVICE_NAME">%1$s</xliff:g> för att ansluta."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"OK"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Enhetsanslutning"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Enhetsanslutning"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Enhetsanslutning"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> vill ansluta till din enhet."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Ange pinkoden som visas på <xliff:g id="DEVICE_NAME">%1$s</xliff:g> för att ansluta."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> vill ansluta till din enhet med följande pinkod."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} vill ansluta till din enhet. Godkänn inom {countdown} sekund.}other{{device} vill ansluta till din enhet. Godkänn inom {countdown} sekunder.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Ange pinkoden som visas på {device} inom {countdown} sekund för att ansluta.}other{Ange pinkoden som visas på {device} inom {countdown} sekunder för att ansluta.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} vill ansluta till din enhet med följande pinkod. Godkänn inom {countdown} sekund.}other{{device} vill ansluta till din enhet med följande pinkod. Godkänn inom {countdown} sekunder.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Anslut"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Avbryt"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"Pinkod"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"Pinkod"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Enheten kopplas tillfälligt från wifi när den är ansluten till <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="dlg_ok" msgid="254496739491689405">"OK"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Det gick inte att ansluta till <xliff:g id="SSID">%1$s</xliff:g>"</string> diff --git a/service/ServiceWifiResources/res/values-sw/strings.xml b/service/ServiceWifiResources/res/values-sw/strings.xml index b63f476ffa..b30efb19d2 100644 --- a/service/ServiceWifiResources/res/values-sw/strings.xml +++ b/service/ServiceWifiResources/res/values-sw/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Kwa:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Charaza PIN inayohitajika:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Muunganisho wa vifaa"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Muunganisho wa vifaa"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Mwaliko umetumwa kwenye <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Weka PIN hii kwenye <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ili uunganishe."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"Sawa"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Muunganisho wa vifaa"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Muunganisho wa vifaa"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Muunganisho wa vifaa"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> inataka kuunganisha kwenye kifaa chako."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Weka PIN inayoonyeshwa kwenye <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ili uunganishe."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> inataka kuunganisha kwenye kifaa chako kwa kutumia PIN ifuatayo."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} inataka kuunganisha kwenye kifaa chako. Kubali baada ya sekunde {countdown}.}other{{device} inataka kuunganisha kwenye kifaa chako. Kubali baada ya sekunde {countdown}.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Weka PIN inayoonyeshwa kwenye {device} ndani ya sekunde {countdown} ili uunganishe.}other{Weka PIN inayoonyeshwa kwenye {device} ndani ya sekunde {countdown} ili uunganishe.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} inataka kuunganisha kwenye kifaa chako kwa kutumia PIN ifuatayo. Kubali baada ya sekunde {countdown}.}other{{device} inataka kuunganisha kwenye kifaa chako kwa kutumia PIN ifuatayo. Kubali baada ya sekunde {countdown}.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Unganisha"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Acha"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Kifaa chako kitatenganishwa na Wi-Fi kwa muda wakati kimeunganishwa kwenye <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="dlg_ok" msgid="254496739491689405">"Sawa"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Imeshindwa kuunganisha kwenye <xliff:g id="SSID">%1$s</xliff:g>"</string> diff --git a/service/ServiceWifiResources/res/values-ta/strings.xml b/service/ServiceWifiResources/res/values-ta/strings.xml index 005dfe55a5..ac64422a2c 100644 --- a/service/ServiceWifiResources/res/values-ta/strings.xml +++ b/service/ServiceWifiResources/res/values-ta/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"பெறுநர்:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"பின்வரும் அவசியமான பின்னை உள்ளிடவும்:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"பின்:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"சாதன இணைப்பு"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"சாதன இணைப்பு"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> சாதனத்திற்கு அழைப்பு அனுப்பப்பட்டது."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"இணைப்பதற்கு இந்தப் பின்னை <xliff:g id="DEVICE_NAME">%1$s</xliff:g> சாதனத்தில் டைப் செய்யவும்."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"சரி"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"சாதன இணைப்பு"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"சாதன இணைப்பு"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"சாதன இணைப்பு"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"உங்கள் சாதனத்துடன் இணைக்கும்படி <xliff:g id="DEVICE_NAME">%1$s</xliff:g> கேட்கிறது."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"இணைப்பதற்கு <xliff:g id="DEVICE_NAME">%1$s</xliff:g> சாதனத்தில் கட்டப்படும் பின்னை டைப் செய்யவும்."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"காட்டப்படும் பின்னைக் கொண்டு உங்கள் சாதனத்துடன் இணைக்கும்படி <xliff:g id="DEVICE_NAME">%1$s</xliff:g> கேட்கிறது."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{உங்கள் சாதனத்துடன் இணைக்கும்படி {device} கேட்கிறது. {countdown} வினாடியில் ஏற்கவும்.}other{உங்கள் சாதனத்துடன் இணைக்கும்படி {device} கேட்கிறது. {countdown} வினாடிகளில் ஏற்கவும்.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{இணைப்பதற்கு {device} சாதனத்தில் காட்டப்படும் பின்னை {countdown} வினாடிக்குள் டைப் செய்யவும்.}other{இணைப்பதற்கு {device} சாதனத்தில் காட்டப்படும் பின்னை {countdown} வினாடிகளுக்குள் டைப் செய்யவும்.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{காட்டப்படும் பின்னைக் கொண்டு உங்கள் சாதனத்துடன் இணைக்கும்படி {device} கேட்கிறது. {countdown} வினாடியில் ஏற்கவும்.}other{காட்டப்படும் பின்னைக் கொண்டு உங்கள் சாதனத்துடன் இணைக்கும்படி {device} கேட்கிறது. {countdown} வினாடிகளில் ஏற்கவும்.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"இணை"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"ரத்துசெய்"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"பின்"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"பின்"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> உடன் இணைக்கப்பட்டிருக்கும்போது உங்கள் சாதனம் வைஃபை இணைப்பில் இருந்து தற்காலிகமாகத் துண்டிக்கப்படும்"</string> <string name="dlg_ok" msgid="254496739491689405">"சரி"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"<xliff:g id="SSID">%1$s</xliff:g> உடன் இணைக்க முடியவில்லை"</string> diff --git a/service/ServiceWifiResources/res/values-te/strings.xml b/service/ServiceWifiResources/res/values-te/strings.xml index ec40546f41..baa41465aa 100644 --- a/service/ServiceWifiResources/res/values-te/strings.xml +++ b/service/ServiceWifiResources/res/values-te/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"వీరికి:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"అవసరమైన పిన్ను టైప్ చేయండి:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"పిన్:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"పరికర కనెక్షన్"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"పరికర కనెక్షన్"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>కు ఆహ్వానం పంపబడింది."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"కనెక్ట్ చేయడానికి ఈ PINను <xliff:g id="DEVICE_NAME">%1$s</xliff:g>లో ఎంటర్ చేయండి."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"సరే"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"పరికర కనెక్షన్"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"పరికర కనెక్షన్"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"పరికర కనెక్షన్"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> మీ పరికరానికి కనెక్ట్ చేయాలనుకుంటున్నారు."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"కనెక్ట్ చేయడానికి <xliff:g id="DEVICE_NAME">%1$s</xliff:g>లో కనిపించే PINను ఎంటర్ చేయండి."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>, కింద ఉన్న PINను ఉపయోగించి మీ పరికరానికి కనెక్ట్ చేయాలనుకుంటున్నారు."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} మీ పరికరానికి కనెక్ట్ చేయాలనుకుంటున్నారు. {countdown} సెకనులో ఆమోదించండి.}other{{device} మీ పరికరానికి కనెక్ట్ చేయాలనుకుంటున్నారు. {countdown} సెకన్లలో ఆమోదించండి.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{కనెక్ట్ చేయడానికి {countdown}లో {device}లో కనిపించే PINను ఎంటర్ చేయండి.}other{కనెక్ట్ చేయడానికి {countdown}లో {device}లో కనిపించే PINను ఎంటర్ చేయండి.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device}, కింద ఉన్న PINను ఉపయోగించి మీ పరికరంతో కనెక్ట్ చేయాలనుకుంటున్నారు. {countdown} సెకనులో ఆమోదించండి.}other{{device}, కింద ఉన్న PINను ఉపయోగించి మీ పరికరంతో కనెక్ట్ చేయాలనుకుంటున్నారు. {countdown} సెకన్లలో ఆమోదించండి.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"కనెక్ట్ చేయండి"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"రద్దు చేయండి"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"మీ పరికరం <xliff:g id="DEVICE_NAME">%1$s</xliff:g>కి కనెక్ట్ అయి ఉన్నప్పుడు తాత్కాలికంగా Wi-Fi నుండి డిస్కనెక్ట్ అవుతుంది"</string> <string name="dlg_ok" msgid="254496739491689405">"సరే"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"<xliff:g id="SSID">%1$s</xliff:g>కు కనెక్ట్ చేయడం సాధ్యపడదు"</string> diff --git a/service/ServiceWifiResources/res/values-th/strings.xml b/service/ServiceWifiResources/res/values-th/strings.xml index 0958cc3ceb..69965b0f29 100644 --- a/service/ServiceWifiResources/res/values-th/strings.xml +++ b/service/ServiceWifiResources/res/values-th/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"ถึง:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"พิมพ์ PIN ที่ต้องการ:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"การเชื่อมต่อของอุปกรณ์"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"การเชื่อมต่อของอุปกรณ์"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"ส่งข้อความเชิญถึง <xliff:g id="DEVICE_NAME">%1$s</xliff:g> แล้ว"</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"ป้อน PIN นี้ลงใน <xliff:g id="DEVICE_NAME">%1$s</xliff:g> เพื่อเชื่อมต่อ"</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"ตกลง"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"การเชื่อมต่อของอุปกรณ์"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"การเชื่อมต่อของอุปกรณ์"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"การเชื่อมต่อของอุปกรณ์"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ต้องการเชื่อมต่อกับอุปกรณ์ของคุณ"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"ป้อน PIN ที่แสดงบน <xliff:g id="DEVICE_NAME">%1$s</xliff:g> เพื่อเชื่อมต่อ"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ต้องการเชื่อมต่อกับอุปกรณ์ของคุณด้วย PIN ต่อไปนี้"</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} ต้องการเชื่อมต่อกับอุปกรณ์ของคุณ ตอบรับภายใน {countdown} วินาที}other{{device} ต้องการเชื่อมต่อกับอุปกรณ์ของคุณ ตอบรับภายใน {countdown} วินาที}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{ป้อน PIN ที่แสดงบน {device} ภายใน {countdown} วินาทีเพื่อเชื่อมต่อ}other{ป้อน PIN ที่แสดงบน {device} ภายใน {countdown} วินาทีเพื่อเชื่อมต่อ}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} ต้องการเชื่อมต่อกับอุปกรณ์ของคุณด้วย PIN ต่อไปนี้ ตอบรับภายใน {countdown} วินาที}other{{device} ต้องการเชื่อมต่อกับอุปกรณ์ของคุณด้วย PIN ต่อไปนี้ ตอบรับภายใน {countdown} วินาที}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"เชื่อมต่อ"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"ยกเลิก"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"อุปกรณ์ของคุณจะยกเลิกการเชื่อมต่อ Wi-Fi ชั่วคราวระหว่างที่เชื่อมต่อกับ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="dlg_ok" msgid="254496739491689405">"ตกลง"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"เชื่อมต่อกับ <xliff:g id="SSID">%1$s</xliff:g> ไม่ได้"</string> diff --git a/service/ServiceWifiResources/res/values-tl/strings.xml b/service/ServiceWifiResources/res/values-tl/strings.xml index 7b16618e41..cff9e6499e 100644 --- a/service/ServiceWifiResources/res/values-tl/strings.xml +++ b/service/ServiceWifiResources/res/values-tl/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Kay:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"I-type ang kinakailangang PIN:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Koneksyon ng device"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Koneksyon ng device"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Naipadala ang imbitasyon kay <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Ilagay ang PIN na ito sa <xliff:g id="DEVICE_NAME">%1$s</xliff:g> para kumonekta."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"OK"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Koneksyon ng device"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Koneksyon ng device"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Koneksyon ng device"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"Gustong kumonekta ng <xliff:g id="DEVICE_NAME">%1$s</xliff:g> sa iyong device."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Ilagay ang ipinapakitang PIN sa <xliff:g id="DEVICE_NAME">%1$s</xliff:g> para kumonekta."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"Gustong kumonekta ng <xliff:g id="DEVICE_NAME">%1$s</xliff:g> sa iyong device gamit ang sumusunod na PIN."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{Gustong kumonekta ng {device} sa iyong device. Tanggapin sa loob ng {countdown} segundo.}one{Gustong kumonekta ng {device} sa iyong device. Tanggapin sa loob ng {countdown} segundo.}other{Gustong kumonekta ng {device} sa iyong device. Tanggapin sa loob ng {countdown} na segundo.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Ilagay ang PIN na ipinapakita sa {device} sa loob ng {countdown} segundo para kumonekta.}one{Ilagay ang PIN na ipinapakita sa {device} sa loob ng {countdown} segundo para kumonekta.}other{Ilagay ang PIN na ipinapakita sa {device} sa loob ng {countdown} na segundo para kumonekta.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{Gustong kumonekta ng {device} sa iyong device gamit ang sumusunod na PIN. Tanggapin sa loob ng {countdown} segundo.}one{Gustong kumonekta ng {device} sa iyong device gamit ang sumusunod na PIN. Tanggapin sa loob ng {countdown} segundo.}other{Gustong kumonekta ng {device} sa iyong device gamit ang sumusunod na PIN. Tanggapin sa loob ng {countdown} na segundo.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Kumonekta"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Kanselahin"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Pansamantalang madidiskonekta sa Wi-Fi ang iyong device habang nakakonekta ito sa <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="dlg_ok" msgid="254496739491689405">"OK"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Hindi makakonekta sa <xliff:g id="SSID">%1$s</xliff:g>"</string> diff --git a/service/ServiceWifiResources/res/values-tr/strings.xml b/service/ServiceWifiResources/res/values-tr/strings.xml index efc4d1b7ad..917f23c0c1 100644 --- a/service/ServiceWifiResources/res/values-tr/strings.xml +++ b/service/ServiceWifiResources/res/values-tr/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Alıcı:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Gerekli PIN\'i yazın:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Cihaz bağlantısı"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Cihaz bağlantısı"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Davetin <xliff:g id="DEVICE_NAME">%1$s</xliff:g> cihazına gönderildi."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Bağlanmak için bu PIN\'i <xliff:g id="DEVICE_NAME">%1$s</xliff:g> cihazına girin."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"Tamam"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Cihaz bağlantısı"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Cihaz bağlantısı"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Cihaz bağlantısı"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>, cihazınıza bağlanmak istiyor."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Bağlanmak için <xliff:g id="DEVICE_NAME">%1$s</xliff:g> cihazında gösterilen PIN\'i girin."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>, aşağıdaki PIN\'i kullanarak cihazınıza bağlanmak istiyor."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device}, cihazınıza bağlanmak istiyor. {countdown} saniye içinde kabul edin.}other{{device}, cihazınıza bağlanmak istiyor. {countdown} saniye içinde kabul edin.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Bağlanmak için {device} cihazında gösterilen PIN\'i {countdown} saniye içinde girin.}other{Bağlanmak için {device} cihazında gösterilen PIN\'i {countdown} saniye içinde girin.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device}, aşağıdaki PIN\'i kullanarak cihazınıza bağlanmak istiyor. {countdown} saniye içinde kabul edin.}other{{device}, aşağıdaki PIN\'i kullanarak cihazınıza bağlanmak istiyor. {countdown} saniye içinde kabul edin.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Bağlan"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"İptal"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Cihazınız <xliff:g id="DEVICE_NAME">%1$s</xliff:g> adlı cihaza bağlıyken kablosuz bağlantısı geçici olarak kesilecek"</string> <string name="dlg_ok" msgid="254496739491689405">"Tamam"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"<xliff:g id="SSID">%1$s</xliff:g> ağına bağlanılamıyor"</string> diff --git a/service/ServiceWifiResources/res/values-uk/strings.xml b/service/ServiceWifiResources/res/values-uk/strings.xml index 456be30cbc..e73df0aaf0 100644 --- a/service/ServiceWifiResources/res/values-uk/strings.xml +++ b/service/ServiceWifiResources/res/values-uk/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Кому:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Введіть потрібний PIN-код:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN-код:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Підключення пристрою"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Підключення пристрою"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Запрошення надіслано на пристрій \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\"."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Щоб підключитися, введіть цей PIN-код на пристрої \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\"."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"OK"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Підключення пристрою"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Підключення пристрою"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Підключення пристрою"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"Пристрій \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\" хоче підключитися до вашого пристрою."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Щоб підключитися, введіть PIN-код, який відображається на екрані пристрою \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\"."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"Пристрій \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\" хоче підключитися до вашого пристрою за допомогою вказаного нижче PIN-коду."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{Пристрій \"{device}\" хоче підключитися до вашого пристрою. У вас є {countdown} секунда, щоб прийняти запит.}one{Пристрій \"{device}\" хоче підключитися до вашого пристрою. У вас є {countdown} секунда, щоб прийняти запит.}few{Пристрій \"{device}\" хоче підключитися до вашого пристрою. У вас є {countdown} секунди, щоб прийняти запит.}many{Пристрій \"{device}\" хоче підключитися до вашого пристрою. У вас є {countdown} секунд, щоб прийняти запит.}other{Пристрій \"{device}\" хоче підключитися до вашого пристрою. У вас є {countdown} секунди, щоб прийняти запит.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Щоб підключитися, протягом {countdown} секунди введіть PIN-код, який відображається на екрані пристрою \"{device}\".}one{Щоб підключитися, протягом {countdown} секунди введіть PIN-код, який відображається на екрані пристрою \"{device}\".}few{Щоб підключитися, протягом {countdown} секунд введіть PIN-код, який відображається на екрані пристрою \"{device}\".}many{Щоб підключитися, протягом {countdown} секунд введіть PIN-код, який відображається на екрані пристрою \"{device}\".}other{Щоб підключитися, протягом {countdown} секунди введіть PIN-код, який відображається на екрані пристрою \"{device}\".}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{Пристрій \"{device}\" хоче підключитися до вашого пристрою за допомогою вказаного нижче PIN-коду. У вас є {countdown} секунда, щоб прийняти запит.}one{Пристрій \"{device}\" хоче підключитися до вашого пристрою за допомогою вказаного нижче PIN-коду. У вас є {countdown} секунда, щоб прийняти запит.}few{Пристрій \"{device}\" хоче підключитися до вашого пристрою за допомогою вказаного нижче PIN-коду. У вас є {countdown} секунди, щоб прийняти запит.}many{Пристрій \"{device}\" хоче підключитися до вашого пристрою за допомогою вказаного нижче PIN-коду. У вас є {countdown} секунд, щоб прийняти запит.}other{Пристрій \"{device}\" хоче підключитися до вашого пристрою за допомогою вказаного нижче PIN-коду. У вас є {countdown} секунди, щоб прийняти запит.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Підключити"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Скасувати"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN-код"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN-код"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"На час підключення до пристрою \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\" ваш пристрій буде від’єднано від мережі Wi-Fi"</string> <string name="dlg_ok" msgid="254496739491689405">"OK"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Не вдалося підключитися до мережі \"<xliff:g id="SSID">%1$s</xliff:g>\""</string> diff --git a/service/ServiceWifiResources/res/values-ur/strings.xml b/service/ServiceWifiResources/res/values-ur/strings.xml index d96adbadbc..25f2a7a54a 100644 --- a/service/ServiceWifiResources/res/values-ur/strings.xml +++ b/service/ServiceWifiResources/res/values-ur/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"بنام:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"مطلوبہ PIN ٹائپ کریں:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"آلے کا کنکشن"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"آلے کا کنکشن"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> کو دعوت نامہ بھیج دیا گیا ہے۔"</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"منسلک ہونے کے لیے <xliff:g id="DEVICE_NAME">%1$s</xliff:g> پر یہ PIN درج کریں۔"</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"ٹھیک ہے"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"آلے کا کنکشن"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"آلے کا کنکشن"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"آلے کا کنکشن"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> آپ کے آلے سے منسلک ہونا چاہتا ہے۔"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"منسلک ہونے کے لیے <xliff:g id="DEVICE_NAME">%1$s</xliff:g> پر دکھایا گیا PIN درج کریں۔"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> درج ذیل PIN کی مدد سے آپ کے آلے سے منسلک ہونا چاہتا ہے۔"</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} آپ کے آلے سے منسلک ہونا چاہتا ہے۔ {countdown} سیکنڈ میں قبول کریں۔}other{{device} آپ کے آلے سے منسلک ہونا چاہتا ہے۔ {countdown} سیکنڈ میں قبول کریں۔}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{منسلک ہونے کے لیے {countdown} سیکنڈ کے اندر {device} پر دکھایا گیا PIN درج کریں۔}other{منسلک ہونے کے لیے {countdown} سیکنڈ کے اندر {device} پر دکھایا گیا PIN درج کریں۔}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} درج ذیل PIN کی مدد سے آپ کے آلے سے منسلک ہونا چاہتا ہے۔ {countdown} سیکنڈ میں قبول کریں۔}other{{device} درج ذیل PIN کی مدد سے آپ کے آلے سے منسلک ہونا چاہتا ہے۔ {countdown} سیکنڈ میں قبول کریں۔}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"منسلک کریں"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"منسوخ کریں"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"آپ کا آلہ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> سے منسلک ہونے پر، عارضی طور پر Wi-Fi سے غیر منسلک ہو جائے گا"</string> <string name="dlg_ok" msgid="254496739491689405">"ٹھیک ہے"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"<xliff:g id="SSID">%1$s</xliff:g> سے منسلک نہیں کر سکتے"</string> diff --git a/service/ServiceWifiResources/res/values-uz/strings.xml b/service/ServiceWifiResources/res/values-uz/strings.xml index ebd27bd65a..0d705fb7ff 100644 --- a/service/ServiceWifiResources/res/values-uz/strings.xml +++ b/service/ServiceWifiResources/res/values-uz/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Kimga:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"So‘ralgan PIN kodni kiriting:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN kod:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Qurilma aloqasi"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Qurilma aloqasi"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Taklifnoma yuborildi: <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Ulash uchun <xliff:g id="DEVICE_NAME">%1$s</xliff:g> qurilmasida bu PIN kodni kiriting."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"OK"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Qurilma aloqasi"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Qurilma aloqasi"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Qurilma aloqasi"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> sizning qurilmangizga ulanmoqchi."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Ulash uchun <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ekranidagi PIN kodni kiriting."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> sizning qurilmangizga mazkur PIN kod orqali ulanmoqchi."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} sizning qurilmangizga ulanmoqchi. {countdown} soniya ichida qabul qiling.}other{{device} sizning qurilmangizga ulanmoqchi. {countdown} soniya ichida qabul qiling.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Ulash uchun {device} ekranidagi PIN kodni {countdown} soniya ichida kiriting.}other{Ulash uchun {device} ekranidagi PIN kodni {countdown} soniya ichida kiriting.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} sizning qurilmangizga mazkur PIN kod orqali ulanmoqchi. {countdown} soniya ichida qabul qiling.}other{{device} sizning qurilmangizga mazkur PIN kod orqali ulanmoqchi. {countdown} soniya ichida qabul qiling.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Ulash"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Bekor qilish"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN kod"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN kod"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Qurilmangiz <xliff:g id="DEVICE_NAME">%1$s</xliff:g> qurilmasiga ulangani uchun u vaqtincha Wi-Fi tarmoqdan uzildi."</string> <string name="dlg_ok" msgid="254496739491689405">"OK"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"<xliff:g id="SSID">%1$s</xliff:g> bilan aloqa oʻrnatilmadi"</string> diff --git a/service/ServiceWifiResources/res/values-vi/strings.xml b/service/ServiceWifiResources/res/values-vi/strings.xml index bd2e416ca5..511890b0e4 100644 --- a/service/ServiceWifiResources/res/values-vi/strings.xml +++ b/service/ServiceWifiResources/res/values-vi/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Người nhận:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Nhập PIN bắt buộc:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"Mã PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Kết nối thiết bị"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Kết nối thiết bị"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Lời mời đã được gửi tới <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Nhập mã PIN này trên <xliff:g id="DEVICE_NAME">%1$s</xliff:g> để kết nối."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"OK"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Kết nối thiết bị"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Kết nối thiết bị"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Kết nối thiết bị"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> muốn kết nối với thiết bị của bạn."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Nhập mã PIN hiển thị trên <xliff:g id="DEVICE_NAME">%1$s</xliff:g> để kết nối."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> muốn kết nối với thiết bị của bạn bằng mã PIN sau đây."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{{device} muốn kết nối với thiết bị của bạn. Chấp nhận sau {countdown} giây.}other{{device} muốn kết nối với thiết bị của bạn. Chấp nhận sau {countdown} giây.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Nhập mã PIN hiển thị trên {device} trong vòng {countdown} giây để kết nối.}other{Nhập mã PIN hiển thị trên {device} trong vòng {countdown} giây để kết nối.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{{device} muốn kết nối với thiết bị của bạn bằng mã PIN sau đây. Chấp nhận sau {countdown} giây.}other{{device} muốn kết nối với thiết bị của bạn bằng mã PIN sau đây. Chấp nhận sau {countdown} giây.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Kết nối"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Huỷ"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"Mã PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"Mã PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Thiết bị của bạn sẽ tạm thời ngắt kết nối với Wi-Fi trong khi kết nối với <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="dlg_ok" msgid="254496739491689405">"OK"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Không thể kết nối với <xliff:g id="SSID">%1$s</xliff:g>"</string> diff --git a/service/ServiceWifiResources/res/values-zh-rCN/strings.xml b/service/ServiceWifiResources/res/values-zh-rCN/strings.xml index 5a0871e7af..397a5cd95f 100644 --- a/service/ServiceWifiResources/res/values-zh-rCN/strings.xml +++ b/service/ServiceWifiResources/res/values-zh-rCN/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"收件人:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"输入所需的PIN码:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN 码:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"设备连接"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"设备连接"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"已向“<xliff:g id="DEVICE_NAME">%1$s</xliff:g>”发送邀请。"</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"请在“<xliff:g id="DEVICE_NAME">%1$s</xliff:g>”上输入此 PIN 码以建立连接。"</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"确定"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"设备连接"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"设备连接"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"设备连接"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"“<xliff:g id="DEVICE_NAME">%1$s</xliff:g>”想要连接到您的设备。"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"请输入“<xliff:g id="DEVICE_NAME">%1$s</xliff:g>”上显示的 PIN 码以建立连接。"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"“<xliff:g id="DEVICE_NAME">%1$s</xliff:g>”想要使用以下 PIN 码连接到您的设备。"</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{“{device}”想要连接到您的设备。{countdown} 秒后接受。}other{“{device}”想要连接到您的设备。{countdown} 秒后接受。}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{请在 {countdown} 秒内输入“{device}”上显示的 PIN 码以建立连接。}other{请在 {countdown} 秒内输入“{device}”上显示的 PIN 码以建立连接。}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{“{device}”想要使用以下 PIN 码连接到您的设备。{countdown} 秒后接受。}other{“{device}”想要使用以下 PIN 码连接到您的设备。{countdown} 秒后接受。}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"连接"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"取消"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN 码"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN 码"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"当设备连接到<xliff:g id="DEVICE_NAME">%1$s</xliff:g>时,它会暂时断开 WLAN 连接"</string> <string name="dlg_ok" msgid="254496739491689405">"确定"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"无法连接到“<xliff:g id="SSID">%1$s</xliff:g>”"</string> diff --git a/service/ServiceWifiResources/res/values-zh-rHK/strings.xml b/service/ServiceWifiResources/res/values-zh-rHK/strings.xml index 635311749b..743108aeb1 100644 --- a/service/ServiceWifiResources/res/values-zh-rHK/strings.xml +++ b/service/ServiceWifiResources/res/values-zh-rHK/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"收件者:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"請輸入必要的 PIN 碼:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN 碼:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"裝置連線"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"裝置連線"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"已傳送邀請至「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」。"</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"在「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」上輸入此 PIN 即可連線。"</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"確定"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"裝置連線"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"裝置連線"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"裝置連線"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」想連結你的裝置。"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"輸入「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」上顯示的 PIN 即可建立連結。"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」想透過以下 PIN 連結你的裝置。"</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{「{device}」想連結你的裝置。請於 {countdown} 秒內接受。}other{「{device}」想連結你的裝置。請於 {countdown} 秒內接受。}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{於 {countdown} 秒內輸入「{device}」上顯示的 PIN,即可建立連結。}other{於 {countdown} 秒內輸入「{device}」上顯示的 PIN,即可建立連結。}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{「{device}」想透過以下 PIN 連結你的裝置。請於 {countdown} 秒內接受。}other{「{device}」想透過以下 PIN 連結你的裝置。請於 {countdown} 秒內接受。}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"連線"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"取消"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"裝置與「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」連線期間將暫時中斷 Wi-Fi 連線"</string> <string name="dlg_ok" msgid="254496739491689405">"確定"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"無法連接「<xliff:g id="SSID">%1$s</xliff:g>」"</string> diff --git a/service/ServiceWifiResources/res/values-zh-rTW/strings.xml b/service/ServiceWifiResources/res/values-zh-rTW/strings.xml index 6aed027f4c..ca5f2df04e 100644 --- a/service/ServiceWifiResources/res/values-zh-rTW/strings.xml +++ b/service/ServiceWifiResources/res/values-zh-rTW/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"收件者:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"請輸入必要的 PIN:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN 碼:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"裝置連線"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"裝置連線"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"已傳送邀請至「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」。"</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"在「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」上輸入這個 PIN 碼即可連線。"</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"確定"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"裝置連線"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"裝置連線"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"裝置連線"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」想連結你的裝置。"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"輸入「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」上顯示的 PIN 碼即可建立連結。"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」想透過下列 PIN 碼連結你的裝置。"</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{「{device}」想連結你的裝置,請在 {countdown} 秒內接受。}other{「{device}」想連結你的裝置,請在 {countdown} 秒內接受。}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{在 {countdown} 秒內輸入「{device}」顯示的 PIN 碼,即可建立連結。}other{在 {countdown} 秒內輸入「{device}」顯示的 PIN 碼,即可建立連結。}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{「{device}」想透過下列 PIN 碼連結你的裝置,請在 {countdown} 秒內接受。}other{「{device}」想透過下列 PIN 碼連結你的裝置,請在 {countdown} 秒內接受。}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"連線"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"取消"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"PIN 碼"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"PIN 碼"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"裝置與「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」連線期間將暫時中斷 Wi-Fi 連線"</string> <string name="dlg_ok" msgid="254496739491689405">"確定"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"無法連線至「<xliff:g id="SSID">%1$s</xliff:g>」"</string> diff --git a/service/ServiceWifiResources/res/values-zu/strings.xml b/service/ServiceWifiResources/res/values-zu/strings.xml index 5820703694..14a732dccc 100644 --- a/service/ServiceWifiResources/res/values-zu/strings.xml +++ b/service/ServiceWifiResources/res/values-zu/strings.xml @@ -60,6 +60,24 @@ <string name="wifi_p2p_to_message" msgid="3809923305696994787">"Ku:"</string> <string name="wifi_p2p_enter_pin_message" msgid="5200220251738047620">"Faka i-PIN edingekayo:"</string> <string name="wifi_p2p_show_pin_message" msgid="1000091690967930798">"PIN:"</string> + <string name="wifi_p2p_dialog2_sent_title" msgid="2336103322429678328">"Uxhumo lwedivayisi"</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin" msgid="511248898541903515">"Uxhumo lwedivayisi"</string> + <string name="wifi_p2p_dialog2_sent_message" msgid="2544736217133674163">"Isimemo sithunyelwe ku-<xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin" msgid="5017076731258834940">"Faka le Phinikhodi ku-<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ukuze uxhumeke."</string> + <string name="wifi_p2p_dialog2_sent_positive_button" msgid="6464227471745156497">"KULUNGILE"</string> + <string name="wifi_p2p_dialog2_received_title" msgid="6897349204607457747">"Uxhumo lwedivayisi"</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin" msgid="6431543271224139601">"Uxhumo lwedivayisi"</string> + <string name="wifi_p2p_dialog2_received_title_display_pin" msgid="5017007825248741433">"Uxhumo lwedivayisi"</string> + <string name="wifi_p2p_dialog2_received_message" msgid="4376275522073306499">"I-<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ifuna ukuxhumana nedivayisi yakho."</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin" msgid="6070908860523284394">"Faka Iphinikhodi eboniswe ku-<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ukuze uxhumeke."</string> + <string name="wifi_p2p_dialog2_received_message_display_pin" msgid="5906560549535982131">"I-<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ifuna ukuxhumana nedivayisi yakho Ngephinikhodi elandelayo."</string> + <string name="wifi_p2p_dialog2_received_message_countdown" msgid="557930403509771814">"{countdown,plural, =1{I-{device} ifuna ukuxhumana nedivayisi yakho. Yamukela kumzuzwana ongu-{countdown}.}one{I-{device} ifuna ukuxhumana nedivayisi yakho. Yamukela emizuzwaneni engu-{countdown}.}other{I-{device} ifuna ukuxhumana nedivayisi yakho. Yamukela emizuzwaneni engu-{countdown}.}}"</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown" msgid="3723666737782219820">"{countdown,plural, =1{Faka Iphinikhodi eboniswe ku-{device} kumzuzwana ongu-{countdown} ukuze uxhumeke.}one{Faka Iphinikhodi eboniswe ku-{device} emizuzwaneni engu-{countdown} ukuze uxhumeke.}other{Faka Iphinikhodi eboniswe ku-{device} emizuzwaneni engu-{countdown} ukuze uxhumeke.}}"</string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown" msgid="1248395771858360837">"{countdown,plural, =1{I-{device} ifuna ukuxhumana nedivayisi yakho Ngephinikhodi elandelayo. Yamukela kumzuzwana ongu-{countdown}.}one{I-{device} ifuna ukuxhumana nedivayisi yakho Ngephinikhodi elandelayo. Yamukela emizuzwaneni engu-{countdown}.}other{I-{device} ifuna ukuxhumana nedivayisi yakho Ngephinikhodi elandelayo. Yamukela emizuzwaneni engu-{countdown}.}}"</string> + <string name="wifi_p2p_dialog2_received_positive_button" msgid="6358592617824573781">"Xhuma"</string> + <string name="wifi_p2p_dialog2_received_negative_button" msgid="4762549789911681138">"Khansela"</string> + <string name="wifi_p2p_dialog2_enter_pin_label" msgid="7410100924400114335">"Iphinikhodi"</string> + <string name="wifi_p2p_dialog2_display_pin_label" msgid="3929561425167093538">"Iphinikhodi"</string> <string name="wifi_p2p_frequency_conflict_message" msgid="8535404941723941766">"Idivayisi yakho izonqamuka okwesikhashana ku-Wi-Fi ngenkathi uxhume ku-<xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="dlg_ok" msgid="254496739491689405">"KULUNGILE"</string> <string name="wifi_cannot_connect_with_randomized_mac_title" msgid="2344570489693915253">"Ayikwazi ukuxhumeka ku-<xliff:g id="SSID">%1$s</xliff:g>"</string> diff --git a/service/ServiceWifiResources/res/values/config.xml b/service/ServiceWifiResources/res/values/config.xml index aaaa4ab5d6..5390b48e86 100644 --- a/service/ServiceWifiResources/res/values/config.xml +++ b/service/ServiceWifiResources/res/values/config.xml @@ -1060,6 +1060,8 @@ <!-- Indicate the time in milliseconds to wait before auto-cancelling a P2P invitation received dialog that the user has not responded to. A value of 0 indicates no timeout. --> <integer translatable="false" name="config_p2pInvitationReceivedDialogTimeoutMs">0</integer> + <!-- Whether to show the timeout in the P2P invitation received dialog --> + <bool translatable="false" name="config_p2pInvitationReceivedDialogShowRemainingTime">true</bool> <!-- Indicates whether or not to play a notification sound upon displaying a P2P invitation received dialog that the user has not responded to. If the device is in vibrate mode, then the device will vibrate instead of playing a sound. --> @@ -1112,6 +1114,14 @@ <item>com.company2.example.test.name2</item> --> </string-array> + <!-- list of package names that the framework will allow to use SoftAp/Aware concurrency. If + this list is empty, then all packages are allowed --> + <string-array translatable="false" name="config_wifiSoftApAwareConcurrencyAllowlist"> + <!-- Below is a sample configuration for this list: + <item>com.company1.example.test.name1</item> + <item>com.company2.example.test.name2</item> + --> + </string-array> <!-- boolean indicating whether the Easy Connect (DPP) AKM is supported --> <bool translatable="false" name ="config_wifiDppAkmSupported">false</bool> <!-- Indicates the number of octets to mask for each BSSID in the SecurityLog output. @@ -1344,7 +1354,19 @@ --> </integer-array> - <!-- Wi-Fi chip supports single link MLO SoftAp instances in the bridged mode --> + + <!-- integer indicating the maximum number of MLDs supported for multi-link operation (MLO) + SoftAp. Each multi-link device (AP MLD) can be operated with multiple links or single link. + It is required to configure it at least 1 for multiple links MLO SoftAp. Default value 0 is + treated as device does NOT support multiple links MLO SoftAp and the maximum + number of MLDs is decided by configuration, config_wifiSoftApSingleLinkMloInBridgedModeSupported. + (i.e. 2 when config_wifiSoftApSingleLinkMloInBridgedModeSupported is true and 1 when + config_wifiSoftApSingleLinkMloInBridgedModeSupported is false.) + --> + <integer translatable="false" name="config_wifiSoftApMaxNumberMLDSupported">0</integer> + + <!-- (Deprecated) Wi-Fi chip supports single link MLO SoftAp instances in the bridged mode. + The value will be used only when config_wifiSoftApMaxNumberMLDSupported isn't configured --> <bool translatable="false" name="config_wifiSoftApSingleLinkMloInBridgedModeSupported">false</bool> <!-- Boolean indicating whether Target Wake Time (TWT) feature is supported or not. This flag @@ -1364,7 +1386,11 @@ feature are included in the array by default. --> <integer-array translatable="false" name="config_wifiDelayedSelectionCarrierIds"> - <item>2032</item> + + <!-- Below is a sample configuration for this list: + <item>1234</item> + <item>5678</item> + --> </integer-array> <!-- Delay time in milliseconds for delayed carrier network selection. @@ -1374,8 +1400,19 @@ <!-- Boolean indicating whether the device supports Wi-Fi Alliance WPA3 Specification version 3.3 Section 14 - RSN Overriding. - Enabling this config allows framework to parse the RSNO IE and RSNO2 IE. - Only enable this flag if Supplicant and driver/firmware supports RSN Overriding. otherwise - the connection may fail or downgrade to WPA2 --> + Enabling this config allows framework to parse the RSNO IE and RSNO2 IE on devices running + Supplicant AIDL interface version lower than 4. Only enable this flag if Supplicant and + driver/firmware supports RSN Overriding. otherwise the connection may fail or downgrade to + WPA2. + If the device is running the AIDL interface version 4 or later, this config item has + no effect. The feature will be enabled/disabled based on the chip capability for RSN + Overriding advertised via Supplicant AIDL wpa driver capability interface --> <bool translatable="false" name ="config_wifiRsnOverridingEnabled">false</bool> + + <!-- Boolean indicating whether to trigger bugreport for WiFi subsystem restart issue --> + <bool translatable="false" name ="config_wifi_subsystem_restart_bugreport_enabled">true</bool> + + <!-- Boolean indicating whether to use the common nl80211 implementation of the WiFi Hal. This + will be used instead of the AIDL or HIDL implementation if enabled. --> + <bool translatable="false" name="config_wifiNl80211HalEnabled">false</bool> </resources> diff --git a/service/ServiceWifiResources/res/values/overlayable.xml b/service/ServiceWifiResources/res/values/overlayable.xml index d499e366a2..7a94faa9b4 100644 --- a/service/ServiceWifiResources/res/values/overlayable.xml +++ b/service/ServiceWifiResources/res/values/overlayable.xml @@ -297,6 +297,7 @@ <item type="bool" name="config_wifiDialogCanceledOnTouchOutside" /> <item type="bool" name="config_showConfirmationDialogForThirdPartyAppsEnablingWifi" /> <item type="integer" name="config_p2pInvitationReceivedDialogTimeoutMs"/> + <item type="bool" name="config_p2pInvitationReceivedDialogShowRemainingTime" /> <item type="bool" name="config_p2pInvitationReceivedDialogNotificationSound"/> <item type="integer" name="config_wifiP2pJoinRequestAuthorizingTimeoutMs" /> <item type="bool" name="config_wifiForcedSoftApRestartWhenCountryCodeChanged" /> @@ -307,6 +308,7 @@ <item type="bool" name="config_wifiUserApprovalNotRequireForDisconnectedP2p" /> <item type="integer" name="config_disconnectedP2pIfaceLowPriorityTimeoutMs" /> <item type="array" name="config_wifiP2pAwareConcurrencyAllowlist" /> + <item type="array" name="config_wifiSoftApAwareConcurrencyAllowlist" /> <item type="bool" name="config_wifiNetworkCentricQosPolicyFeatureEnabled" /> <item type="bool" name="config_wifiApplicationCentricQosPolicyFeatureEnabled" /> <item type="string" name="config_wifiDriverWorldModeCountryCode" /> @@ -344,6 +346,7 @@ <item type="integer" name="config_wifi80211azMaxTimeBetweenNtbMeasurementsMicros"/> <item type="bool" name="config_wifiD2dAllowedControlSupportedWhenInfraStaDisabled" /> <item type="array" name="config_wifiTwtBlockedOuiList" /> + <item type="integer" name="config_wifiSoftApMaxNumberMLDSupported"/> <item type="bool" name="config_wifiSoftApSingleLinkMloInBridgedModeSupported" /> <item type="bool" name="config_wifiTwtSupported" /> <item type="bool" name="config_wifiNetworkSelectionSetTargetBssid" /> @@ -351,6 +354,8 @@ <item type="array" name="config_wifiDelayedSelectionCarrierIds" /> <item type="integer" name="config_wifiDelayedCarrierSelectionTimeMs" /> <item type="bool" name="config_wifiRsnOverridingEnabled" /> + <item type="bool" name="config_wifi_subsystem_restart_bugreport_enabled" /> + <item type="bool" name="config_wifiNl80211HalEnabled" /> <!-- Params from config.xml that can be overlayed --> @@ -387,13 +392,19 @@ <item type="string" name="accept" /> <item type="string" name="decline" /> <item type="string" name="ok" /> - <item type="string" name="wifi_p2p_invitation_sent_title" /> - <item type="string" name="wifi_p2p_invitation_to_connect_title" /> - <item type="string" name="wifi_p2p_invitation_seconds_remaining" /> - <item type="string" name="wifi_p2p_from_message" /> - <item type="string" name="wifi_p2p_to_message" /> - <item type="string" name="wifi_p2p_enter_pin_message" /> - <item type="string" name="wifi_p2p_show_pin_message" /> + <item type="string" name="wifi_p2p_dialog2_sent_title" /> + <item type="string" name="wifi_p2p_dialog2_sent_title_display_pin" /> + <item type="string" name="wifi_p2p_dialog2_sent_message" /> + <item type="string" name="wifi_p2p_dialog2_sent_message_display_pin" /> + <item type="string" name="wifi_p2p_dialog2_sent_positive_button" /> + <item type="string" name="wifi_p2p_dialog2_received_title" /> + <item type="string" name="wifi_p2p_dialog2_received_message" /> + <item type="string" name="wifi_p2p_dialog2_received_message_enter_pin" /> + <item type="string" name="wifi_p2p_dialog2_received_message_display_pin" /> + <item type="string" name="wifi_p2p_dialog2_received_positive_button" /> + <item type="string" name="wifi_p2p_dialog2_received_negative_button" /> + <item type="string" name="wifi_p2p_dialog2_enter_pin_label" /> + <item type="string" name="wifi_p2p_dialog2_display_pin_label" /> <item type="string" name="wifi_p2p_frequency_conflict_message" /> <item type="string" name="dlg_ok" /> <item type="string" name="wifi_cannot_connect_with_randomized_mac_title" /> @@ -417,8 +428,6 @@ <item type="string" name="wifi_eap_error_message_code_32765" /> <item type="string" name="wifi_eap_error_message_code_32766" /> <item type="string" name="wifi_eap_error_message_code_32767" /> - <item type="string" name="wifi_eap_error_message_code_32768" /> - <item type="string" name="wifi_eap_error_message_code_32769" /> <item type="string" name="wifi_eap_error_message_code_16384" /> <item type="string" name="wifi_eap_error_message_code_16385" /> <item type="string" name="wifi_eap_error_message_unknown_error_code" /> @@ -502,6 +511,17 @@ <item type="style" name="wifi_p2p_dialog_row_content" /> <item type="style" name="wifi_p2p_dialog_enter_pin_message" /> <item type="style" name="wifi_p2p_dialog_pin_input" /> + + <item type="style" name="wifi_p2p_dialog2" /> + <item type="style" name="wifi_p2p_dialog2_pin_section" /> + <item type="style" name="wifi_p2p_dialog2_pin_label" /> + <item type="style" name="wifi_p2p_dialog2_pin" /> + <item type="style" name="wifi_p2p_dialog2_enter_pin_section" /> + <item type="style" name="wifi_p2p_dialog2_enter_pin_label" /> + <item type="style" name="wifi_p2p_dialog2_enter_pin" /> + <item type="style" name="wifi_p2p_dialog2_display_pin_section" /> + <item type="style" name="wifi_p2p_dialog2_display_pin_label" /> + <item type="style" name="wifi_p2p_dialog2_display_pin" /> <!-- Params from styles.xml that can be overlayed --> <!-- Params from drawable/ that can be overlayed --> diff --git a/service/ServiceWifiResources/res/values/strings.xml b/service/ServiceWifiResources/res/values/strings.xml index 26624b023e..3660d752b8 100644 --- a/service/ServiceWifiResources/res/values/strings.xml +++ b/service/ServiceWifiResources/res/values/strings.xml @@ -100,6 +100,7 @@ <string name="wifi_p2p_invitation_sent_title">Invitation sent</string> <string name="wifi_p2p_invitation_to_connect_title">Invitation to connect</string> + <!-- Start of strings for legacy P2P Dialog --> <!-- The message of the P2P Invitation Received dialog indicating the seconds left to accept before auto rejection [CHAR_LIMIT=NONE] [ICU SYNTAX] --> <string name="wifi_p2p_invitation_seconds_remaining"> {0, plural, @@ -111,6 +112,47 @@ <string name="wifi_p2p_to_message">To: </string> <string name="wifi_p2p_enter_pin_message">Type the required PIN: </string> <string name="wifi_p2p_show_pin_message">PIN: </string> + <!-- End of strings for legacy P2P Dialog --> + + <!-- Start of strings for P2P Dialog 2 --> + <string name="wifi_p2p_dialog2_sent_title">Device connection</string> + <string name="wifi_p2p_dialog2_sent_title_display_pin">Device connection</string> + <string name="wifi_p2p_dialog2_sent_message">Invitation sent to <xliff:g id="device_name">%1$s</xliff:g>.</string> + <string name="wifi_p2p_dialog2_sent_message_display_pin">Enter this PIN on <xliff:g id="device_name">%1$s</xliff:g> to connect.</string> + <string name="wifi_p2p_dialog2_sent_positive_button">OK</string> + + <string name="wifi_p2p_dialog2_received_title">Device connection</string> + <string name="wifi_p2p_dialog2_received_title_enter_pin">Device connection</string> + <string name="wifi_p2p_dialog2_received_title_display_pin">Device connection</string> + <string name="wifi_p2p_dialog2_received_message"><xliff:g id="device_name">%1$s</xliff:g> wants to connect to your device.</string> + <string name="wifi_p2p_dialog2_received_message_enter_pin">Enter the PIN shown on <xliff:g id="device_name">%1$s</xliff:g> to connect.</string> + <string name="wifi_p2p_dialog2_received_message_display_pin"><xliff:g id="device_name">%1$s</xliff:g> wants to connect to your device with the following PIN.</string> + + <string name="wifi_p2p_dialog2_received_message_countdown"> + {countdown, plural, + =1 {{device} wants to connect to your device. Accept in {countdown} second.} + other {{device} wants to connect to your device. Accept in {countdown} seconds.} + } + </string> + <string name="wifi_p2p_dialog2_received_message_enter_pin_countdown"> + {countdown, plural, + =1 {Enter the PIN shown on {device} within {countdown} second to connect.} + other {Enter the PIN shown on {device} within {countdown} seconds to connect.} + } + </string> + <string name="wifi_p2p_dialog2_received_message_display_pin_countdown"> + {countdown, plural, + =1 {{device} wants to connect to your device with the following PIN. Accept in {countdown} second.} + other {{device} wants to connect to your device with the following PIN. Accept in {countdown} seconds.} + } + </string> + + <string name="wifi_p2p_dialog2_received_positive_button">Connect</string> + <string name="wifi_p2p_dialog2_received_negative_button">Cancel</string> + + <string name="wifi_p2p_dialog2_enter_pin_label">PIN</string> + <string name="wifi_p2p_dialog2_display_pin_label">PIN</string> + <!-- End of strings for P2P Dialog 2 --> <string name="wifi_p2p_frequency_conflict_message">Your device will temporarily disconnect from Wi-Fi while it\'s connected to <xliff:g id="device_name">%1$s</xliff:g></string> <!-- Dialog ok button--> diff --git a/service/ServiceWifiResources/res/values/styles.xml b/service/ServiceWifiResources/res/values/styles.xml index 714d10fe0f..e86aee8417 100644 --- a/service/ServiceWifiResources/res/values/styles.xml +++ b/service/ServiceWifiResources/res/values/styles.xml @@ -63,4 +63,38 @@ <item name="android:inputType">number</item> <item name="android:textColor">@android:color/primary_text_light</item> </style> + + <style name="wifi_p2p_dialog2" parent="@android:style/Theme.DeviceDefault.Light.Dialog.Alert" /> + + <style name="wifi_p2p_dialog2_pin_section"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:orientation">vertical</item> + <item name="android:paddingLeft">24dp</item> + <item name="android:paddingRight">24dp</item> + </style> + <style name="wifi_p2p_dialog2_pin_label" > + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:textColor">@android:color/secondary_text_light</item> + <item name="android:textSize">14sp</item> + </style> + <style name="wifi_p2p_dialog2_pin"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:textColor">@android:color/primary_text_light</item> + <item name="android:textSize">18sp</item> + </style> + + <style name="wifi_p2p_dialog2_enter_pin_section" parent="@style/wifi_p2p_dialog2_pin_section" /> + <style name="wifi_p2p_dialog2_enter_pin_label" parent="@style/wifi_p2p_dialog2_pin_label" /> + <style name="wifi_p2p_dialog2_enter_pin" parent="@style/wifi_p2p_dialog2_pin"> + <item name="android:singleLine">true</item> + <item name="android:maxLength">8</item> + <item name="android:inputType">number</item> + </style> + + <style name="wifi_p2p_dialog2_display_pin_section" parent="@style/wifi_p2p_dialog2_pin_section" /> + <style name="wifi_p2p_dialog2_display_pin_label" parent="@style/wifi_p2p_dialog2_pin_label" /> + <style name="wifi_p2p_dialog2_display_pin" parent="@style/wifi_p2p_dialog2_pin" /> </resources> diff --git a/service/java/com/android/server/wifi/ActiveModeManager.java b/service/java/com/android/server/wifi/ActiveModeManager.java index fddfbd449a..f987cd738b 100644 --- a/service/java/com/android/server/wifi/ActiveModeManager.java +++ b/service/java/com/android/server/wifi/ActiveModeManager.java @@ -20,6 +20,8 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.os.WorkSource; +import androidx.annotation.Keep; + import java.io.FileDescriptor; import java.io.PrintWriter; @@ -144,6 +146,7 @@ public interface ActiveModeManager { /** * Method to get the role for a mode manager. */ + @Keep @Nullable Role getRole(); /** @@ -159,6 +162,7 @@ public interface ActiveModeManager { /** * Method to get the iface name for the mode manager. */ + @Keep String getInterfaceName(); /** diff --git a/service/java/com/android/server/wifi/ActiveModeWarden.java b/service/java/com/android/server/wifi/ActiveModeWarden.java index 6b6b5c00f5..c08388a3cb 100644 --- a/service/java/com/android/server/wifi/ActiveModeWarden.java +++ b/service/java/com/android/server/wifi/ActiveModeWarden.java @@ -47,6 +47,7 @@ import android.net.Network; import android.net.wifi.ISubsystemRestartCallback; import android.net.wifi.IWifiConnectedNetworkScorer; import android.net.wifi.IWifiNetworkStateChangedListener; +import android.net.wifi.IWifiStateChangedListener; import android.net.wifi.SoftApCapability; import android.net.wifi.SoftApConfiguration; import android.net.wifi.SoftApState; @@ -77,6 +78,8 @@ import android.util.LocalLog; import android.util.Log; import android.util.Pair; +import androidx.annotation.Keep; + import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IState; @@ -92,12 +95,14 @@ import com.android.server.wifi.util.ApConfigUtil; import com.android.server.wifi.util.LastCallerInfoManager; import com.android.server.wifi.util.NativeUtil; import com.android.server.wifi.util.WifiPermissionsUtil; +import com.android.wifi.flags.FeatureFlags; import com.android.wifi.resources.R; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.BitSet; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -105,7 +110,6 @@ import java.util.Objects; import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -145,6 +149,7 @@ public class ActiveModeWarden { private final UserManager mUserManager; private final LastCallerInfoManager mLastCallerInfoManager; private final WifiGlobals mWifiGlobals; + private final FeatureFlags mFeatureFlags; private WifiServiceImpl.SoftApCallbackInternal mSoftApCallback; private WifiServiceImpl.SoftApCallbackInternal mLohsCallback; @@ -153,6 +158,8 @@ public class ActiveModeWarden { new RemoteCallbackList<>(); private final RemoteCallbackList<IWifiNetworkStateChangedListener> mWifiNetworkStateChangedListeners = new RemoteCallbackList<>(); + private final RemoteCallbackList<IWifiStateChangedListener> mWifiStateChangedListeners = + new RemoteCallbackList<>(); private boolean mIsMultiplePrimaryBugreportTaken = false; private boolean mIsShuttingdown = false; @@ -171,7 +178,6 @@ public class ActiveModeWarden { private WorkSource mLastPrimaryClientModeManagerRequestorWs = null; @Nullable private WorkSource mLastScanOnlyClientModeManagerRequestorWs = null; - private AtomicLong mSupportedFeatureSet = new AtomicLong(0); private AtomicInteger mBandsSupported = new AtomicInteger(0); // Mutex lock between service Api binder thread and Wifi main thread private final Object mServiceApiLock = new Object(); @@ -179,6 +185,8 @@ public class ActiveModeWarden { private Network mCurrentNetwork; @GuardedBy("mServiceApiLock") private WifiInfo mCurrentConnectionInfo = new WifiInfo(); + @GuardedBy("mServiceApiLock") + private BitSet mSupportedFeatureSet = new BitSet(); @GuardedBy("mServiceApiLock") private final ArraySet<WorkSource> mRequestWs = new ArraySet<>(); @@ -211,7 +219,10 @@ public class ActiveModeWarden { if (mVerboseLoggingEnabled) { Log.d(TAG, "setting wifi state to: " + newState); } - mWifiState.set(newState); + if (mWifiState.get() != newState) { + mWifiState.set(newState); + notifyRemoteWifiStateChangedListeners(); + } break; default: Log.d(TAG, "attempted to set an invalid state: " + newState); @@ -268,6 +279,32 @@ public class ActiveModeWarden { } /** + * See {@link WifiManager#addWifiStateChangedListener(Executor, WifiStateChangedListener)} + */ + public void addWifiStateChangedListener(@NonNull IWifiStateChangedListener listener) { + mWifiStateChangedListeners.register(listener); + } + + /** + * See {@link WifiManager#removeWifiStateChangedListener(WifiStateChangedListener)} + */ + public void removeWifiStateChangedListener(@NonNull IWifiStateChangedListener listener) { + mWifiStateChangedListeners.unregister(listener); + } + + private void notifyRemoteWifiStateChangedListeners() { + final int itemCount = mWifiStateChangedListeners.beginBroadcast(); + for (int i = 0; i < itemCount; i++) { + try { + mWifiStateChangedListeners.getBroadcastItem(i).onWifiStateChanged(); + } catch (RemoteException e) { + Log.e(TAG, "onWifiStateChanged: remote exception -- " + e); + } + } + mWifiStateChangedListeners.finishBroadcast(); + } + + /** * Called from WifiServiceImpl to register a callback for notifications from SoftApManager */ public void registerSoftApCallback(@NonNull WifiServiceImpl.SoftApCallbackInternal callback) { @@ -412,6 +449,7 @@ public class ActiveModeWarden { mUserManager = mWifiInjector.getUserManager(); mLastCallerInfoManager = mWifiInjector.getLastCallerInfoManager(); mWifiGlobals = wifiGlobals; + mFeatureFlags = mWifiInjector.getDeviceConfigFacade().getFeatureFlags(); wifiNative.registerStatusListener(isReady -> { if (!isReady && !mIsShuttingdown) { @@ -680,15 +718,16 @@ public class ActiveModeWarden { /** Begin listening to broadcasts and start the internal state machine. */ public void start() { - mContext.registerReceiver(new BroadcastReceiver() { + BroadcastReceiver locationChangeReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // Location mode has been toggled... trigger with the scan change // update to make sure we are in the correct mode scanAlwaysModeChanged(); } - }, new IntentFilter(LocationManager.MODE_CHANGED_ACTION)); - mContext.registerReceiver(new BroadcastReceiver() { + }; + + BroadcastReceiver airplaneChangedReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { boolean airplaneModeUpdated = mSettingsStore.updateAirplaneModeTracker(); @@ -701,26 +740,51 @@ public class ActiveModeWarden { airplaneModeToggled(); } } - }, new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED)); - mContext.registerReceiver(new BroadcastReceiver() { + }; + + BroadcastReceiver emergencyCallbackModeChangedReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { boolean emergencyMode = intent.getBooleanExtra(TelephonyManager.EXTRA_PHONE_IN_ECM_STATE, false); emergencyCallbackModeChanged(emergencyMode); } - }, new IntentFilter(TelephonyManager.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)); + }; + + BroadcastReceiver emergencyCallStateChangedReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + boolean inCall = intent.getBooleanExtra( + TelephonyManager.EXTRA_PHONE_IN_EMERGENCY_CALL, false); + emergencyCallStateChanged(inCall); + } + }; + + + mContext.registerReceiverForAllUsers(locationChangeReceiver, + new IntentFilter(LocationManager.MODE_CHANGED_ACTION), null, mHandler); boolean trackEmergencyCallState = mResourceCache.getBoolean( R.bool.config_wifi_turn_off_during_emergency_call); - if (trackEmergencyCallState) { - mContext.registerReceiver(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - boolean inCall = intent.getBooleanExtra( - TelephonyManager.EXTRA_PHONE_IN_EMERGENCY_CALL, false); - emergencyCallStateChanged(inCall); - } - }, new IntentFilter(TelephonyManager.ACTION_EMERGENCY_CALL_STATE_CHANGED)); + if (mFeatureFlags.monitorIntentForAllUsers()) { + mContext.registerReceiverForAllUsers(airplaneChangedReceiver, + new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED), null, mHandler); + mContext.registerReceiverForAllUsers(emergencyCallbackModeChangedReceiver, + new IntentFilter(TelephonyManager.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED), + null, mHandler); + if (trackEmergencyCallState) { + mContext.registerReceiverForAllUsers(emergencyCallStateChangedReceiver, + new IntentFilter(TelephonyManager.ACTION_EMERGENCY_CALL_STATE_CHANGED), + null, mHandler); + } + } else { + mContext.registerReceiver(airplaneChangedReceiver, + new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED)); + mContext.registerReceiver(emergencyCallbackModeChangedReceiver, + new IntentFilter(TelephonyManager.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)); + if (trackEmergencyCallState) { + mContext.registerReceiver(emergencyCallStateChangedReceiver, + new IntentFilter(TelephonyManager.ACTION_EMERGENCY_CALL_STATE_CHANGED)); + } } mWifiGlobals.setD2dStaConcurrencySupported( mWifiNative.isP2pStaConcurrencySupported() @@ -816,6 +880,7 @@ public class ActiveModeWarden { } /** Wifi has been toggled. */ + @Keep public void wifiToggled(WorkSource requestorWs) { mWifiController.sendMessage(WifiController.CMD_WIFI_TOGGLED, requestorWs); } @@ -896,6 +961,7 @@ public class ActiveModeWarden { @NonNull public final String ssid; @Nullable public final String bssid; public final boolean didUserApprove; + public boolean preferSecondarySta = false; AdditionalClientModeManagerRequestInfo( @NonNull ExternalClientModeManagerRequestListener listener, @@ -911,7 +977,6 @@ public class ActiveModeWarden { this.ssid = ssid; this.bssid = bssid; this.didUserApprove = didUserApprove; - } } @@ -924,11 +989,12 @@ public class ActiveModeWarden { * 3. The new ClientModeManager - if it was created successfully. * @param requestorWs the WorkSource for this request * @param didUserApprove if user explicitly approve on this request + * @param preferSecondarySta prefer to use secondary CMM for this request if possible */ public void requestLocalOnlyClientModeManager( @NonNull ExternalClientModeManagerRequestListener listener, @NonNull WorkSource requestorWs, @NonNull String ssid, @NonNull String bssid, - boolean didUserApprove) { + boolean didUserApprove, boolean preferSecondarySta) { if (listener == null) { Log.wtf(TAG, "Cannot provide a null ExternalClientModeManagerRequestListener"); return; @@ -938,10 +1004,14 @@ public class ActiveModeWarden { return; } + AdditionalClientModeManagerRequestInfo additionalClientModeManagerRequestInfo = + new AdditionalClientModeManagerRequestInfo(listener, requestorWs, + ROLE_CLIENT_LOCAL_ONLY, ssid, bssid, didUserApprove); + additionalClientModeManagerRequestInfo.preferSecondarySta = preferSecondarySta; + mWifiController.sendMessage( WifiController.CMD_REQUEST_ADDITIONAL_CLIENT_MODE_MANAGER, - new AdditionalClientModeManagerRequestInfo(listener, requestorWs, - ROLE_CLIENT_LOCAL_ONLY, ssid, bssid, didUserApprove)); + additionalClientModeManagerRequestInfo); } /** @@ -1040,6 +1110,7 @@ public class ActiveModeWarden { * calls. * @return Instance of {@link ConcreteClientModeManager} or null. */ + @Keep @Nullable public ConcreteClientModeManager getPrimaryClientModeManagerNullable() { return getClientModeManagerInRole(ROLE_CLIENT_PRIMARY); @@ -1052,6 +1123,7 @@ public class ActiveModeWarden { * calls. * @return Instance of {@link ClientModeManager}. */ + @Keep @NonNull public ClientModeManager getPrimaryClientModeManager() { ClientModeManager cm = getPrimaryClientModeManagerNullable(); @@ -1087,6 +1159,7 @@ public class ActiveModeWarden { } @NonNull + @Keep public List<ClientModeManager> getClientModeManagers() { return new ArrayList<>(mClientModeManagers); } @@ -1152,6 +1225,7 @@ public class ActiveModeWarden { } /** Get any client mode manager in the given role, or null if none was found. */ + @Keep @Nullable public ConcreteClientModeManager getClientModeManagerInRole(ClientRole role) { for (ConcreteClientModeManager manager : mClientModeManagers) { @@ -1340,6 +1414,9 @@ public class ActiveModeWarden { private void stopAllClientModeManagers() { Log.d(TAG, "Shutting down all client mode managers"); for (ConcreteClientModeManager clientModeManager : getClientModeManagersPrimaryLast()) { + if (clientModeManager.getRole() == ROLE_CLIENT_PRIMARY) { + setWifiStateForApiCalls(WIFI_STATE_DISABLING); + } clientModeManager.stop(); } } @@ -1371,7 +1448,9 @@ public class ActiveModeWarden { Log.d(TAG, "Switching all client mode managers"); for (ConcreteClientModeManager clientModeManager : mClientModeManagers) { if (clientModeManager.getRole() != ROLE_CLIENT_PRIMARY - && clientModeManager.getRole() != ROLE_CLIENT_SCAN_ONLY) { + && clientModeManager.getRole() != ROLE_CLIENT_SCAN_ONLY + && clientModeManager.getTargetRole() != ROLE_CLIENT_PRIMARY + && clientModeManager.getTargetRole() != ROLE_CLIENT_SCAN_ONLY) { continue; } if (!switchPrimaryOrScanOnlyClientModeManagerRole(clientModeManager)) { @@ -1470,6 +1549,9 @@ public class ActiveModeWarden { private void shutdownWifi() { Log.d(TAG, "Shutting down all mode managers"); for (ActiveModeManager manager : getActiveModeManagers()) { + if (manager.getRole() == ROLE_CLIENT_PRIMARY) { + setWifiStateForApiCalls(WIFI_STATE_DISABLING); + } manager.stop(); } } @@ -1965,6 +2047,13 @@ public class ActiveModeWarden { setInitialState(mDisabledState); } mWifiMetrics.noteWifiEnabledDuringBoot(mSettingsStore.isWifiToggleEnabled()); + if (mSettingsStore.isWifiToggleEnabled()) { + boolean isWifiWakeOn = mWifiInjector.getWakeupController().isUsable(); + mWifiMetrics.reportWifiStateChanged(true, isWifiWakeOn, false); + if (mVerboseLoggingEnabled) { + Log.d(TAG, "logging wifi is on after boot. wifi wake state=" + isWifiWakeOn); + } + } // Initialize the lower layers before we start. mWifiNative.initialize(); @@ -2432,7 +2521,8 @@ public class ActiveModeWarden { // Special case for holders with ENTER_CAR_MODE_PRIORITIZED. Only give them the // primary STA to avoid the device getting into STA+STA state. // In STA+STA wifi scans will result in high latency in the secondary STA. - if (requestInfo.clientRole == ROLE_CLIENT_LOCAL_ONLY + if (!requestInfo.preferSecondarySta + && requestInfo.clientRole == ROLE_CLIENT_LOCAL_ONLY && requestInfo.requestorWs != null) { WorkSource workSource = requestInfo.requestorWs; for (int i = 0; i < workSource.size(); i++) { @@ -2699,6 +2789,28 @@ public class ActiveModeWarden { // onStopped will move the state machine to "DisabledState". break; } + case CMD_RECOVERY_RESTART_WIFI_CONTINUE: { + log("received CMD_RECOVERY_RESTART_WIFI_CONTINUE when already in " + + "mEnabledState"); + // This could happen when SoftAp is turned on before recovery is complete. + // Simply make sure the primary CMM is on in this case. + if (shouldEnableSta() && !hasPrimaryOrScanOnlyModeManager()) { + startPrimaryOrScanOnlyClientModeManager( + // Assumes user toggled it on from settings before. + mFacade.getSettingsWorkSource(mContext)); + } + int numCallbacks = mRestartCallbacks.beginBroadcast(); + for (int i = 0; i < numCallbacks; i++) { + try { + mRestartCallbacks.getBroadcastItem(i).onSubsystemRestarted(); + } catch (RemoteException e) { + Log.e(TAG, "Failure calling onSubsystemRestarted" + e); + } + } + mRestartCallbacks.finishBroadcast(); + mWifiInjector.getSelfRecovery().onRecoveryCompleted(); + break; + } default: return NOT_HANDLED; } @@ -2734,86 +2846,85 @@ public class ActiveModeWarden { /** * Set the current supported Wifi feature set, called from primary client mode manager. - * @param supportedFeatureSet supported Wifi feature set + * @param wifiNativeFeatureSet feature set retrieved from WifiNative * @param isStaApConcurrencySupported true if Sta+Ap concurrency supported * @param isStaStaConcurrencySupported true if Sta+Sta concurrency supported */ - private void setSupportedFeatureSet(long supportedFeatureSet, + private void setSupportedFeatureSet(BitSet wifiNativeFeatureSet, boolean isStaApConcurrencySupported, boolean isStaStaConcurrencySupported) { - long concurrencyFeatureSet = 0L; + BitSet featureSet = (BitSet) wifiNativeFeatureSet.clone(); + + // Concurrency features if (isStaApConcurrencySupported) { - concurrencyFeatureSet |= WifiManager.WIFI_FEATURE_AP_STA; + featureSet.set(WifiManager.WIFI_FEATURE_AP_STA); } if (isStaStaConcurrencySupported) { if (mResourceCache.getBoolean( R.bool.config_wifiMultiStaLocalOnlyConcurrencyEnabled)) { - concurrencyFeatureSet |= WifiManager.WIFI_FEATURE_ADDITIONAL_STA_LOCAL_ONLY; + featureSet.set(WifiManager.WIFI_FEATURE_ADDITIONAL_STA_LOCAL_ONLY); } if (mResourceCache.getBoolean( R.bool.config_wifiMultiStaNetworkSwitchingMakeBeforeBreakEnabled)) { - concurrencyFeatureSet |= WifiManager.WIFI_FEATURE_ADDITIONAL_STA_MBB; + featureSet.set(WifiManager.WIFI_FEATURE_ADDITIONAL_STA_MBB); } if (mResourceCache.getBoolean( R.bool.config_wifiMultiStaRestrictedConcurrencyEnabled)) { - concurrencyFeatureSet |= WifiManager.WIFI_FEATURE_ADDITIONAL_STA_RESTRICTED; + featureSet.set(WifiManager.WIFI_FEATURE_ADDITIONAL_STA_RESTRICTED); } if (mResourceCache.getBoolean( R.bool.config_wifiMultiStaMultiInternetConcurrencyEnabled)) { - concurrencyFeatureSet |= WifiManager.WIFI_FEATURE_ADDITIONAL_STA_MULTI_INTERNET; + featureSet.set(WifiManager.WIFI_FEATURE_ADDITIONAL_STA_MULTI_INTERNET); } } - long additionalFeatureSet = 0L; - long excludedFeatureSet = 0L; - // Mask the feature set against system properties. - if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_RTT)) { - // flags filled in by vendor HAL, remove if overlay disables it. - excludedFeatureSet |= - (WifiManager.WIFI_FEATURE_D2D_RTT | WifiManager.WIFI_FEATURE_D2AP_RTT); - } - - if (!mResourceCache.getBoolean( - R.bool.config_wifi_p2p_mac_randomization_supported)) { - // flags filled in by vendor HAL, remove if overlay disables it. - excludedFeatureSet |= WifiManager.WIFI_FEATURE_P2P_RAND_MAC; - } + // Additional features if (mResourceCache.getBoolean( R.bool.config_wifi_connected_mac_randomization_supported)) { // no corresponding flags in vendor HAL, set if overlay enables it. - additionalFeatureSet |= WifiManager.WIFI_FEATURE_CONNECTED_RAND_MAC; + featureSet.set(WifiManager.WIFI_FEATURE_CONNECTED_RAND_MAC); } if (ApConfigUtil.isApMacRandomizationSupported(mContext)) { // no corresponding flags in vendor HAL, set if overlay enables it. - additionalFeatureSet |= WifiManager.WIFI_FEATURE_AP_RAND_MAC; + featureSet.set(WifiManager.WIFI_FEATURE_AP_RAND_MAC); } - if (ApConfigUtil.isBridgedModeSupported(mContext, mWifiNative)) { // The bridged mode requires the kernel network modules support. // It doesn't relate the vendor HAL, set if overlay enables it. - additionalFeatureSet |= WifiManager.WIFI_FEATURE_BRIDGED_AP; + featureSet.set(WifiManager.WIFI_FEATURE_BRIDGED_AP); } if (ApConfigUtil.isStaWithBridgedModeSupported(mContext, mWifiNative)) { // The bridged mode requires the kernel network modules support. // It doesn't relate the vendor HAL, set if overlay enables it. - additionalFeatureSet |= WifiManager.WIFI_FEATURE_STA_BRIDGED_AP; + featureSet.set(WifiManager.WIFI_FEATURE_STA_BRIDGED_AP); } if (mWifiGlobals.isWepSupported()) { - additionalFeatureSet |= WifiManager.WIFI_FEATURE_WEP; + featureSet.set(WifiManager.WIFI_FEATURE_WEP); } if (!mWifiGlobals.isWpaPersonalDeprecated()) { // The WPA didn't be deprecated, set it. - additionalFeatureSet |= WifiManager.WIFI_FEATURE_WPA_PERSONAL; + featureSet.set(WifiManager.WIFI_FEATURE_WPA_PERSONAL); } if (mWifiGlobals.isD2dSupportedWhenInfraStaDisabled()) { - additionalFeatureSet |= WifiManager.WIFI_FEATURE_D2D_WHEN_INFRA_STA_DISABLED; + featureSet.set(WifiManager.WIFI_FEATURE_D2D_WHEN_INFRA_STA_DISABLED); } - mSupportedFeatureSet.set( - (supportedFeatureSet | concurrencyFeatureSet | additionalFeatureSet) - & ~excludedFeatureSet); - if (mVerboseLoggingEnabled) { - Log.d(TAG, "setSupportedFeatureSet 0x" + Long.toHexString(mSupportedFeatureSet.get())); + + // Remove capabilities that are disabled by the system properties + if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_RTT)) { + featureSet.clear(WifiManager.WIFI_FEATURE_D2D_RTT); + featureSet.clear(WifiManager.WIFI_FEATURE_D2AP_RTT); + } + if (!mResourceCache.getBoolean( + R.bool.config_wifi_p2p_mac_randomization_supported)) { + featureSet.clear(WifiManager.WIFI_FEATURE_P2P_RAND_MAC); + } + + synchronized (mServiceApiLock) { + mSupportedFeatureSet = featureSet; + if (mVerboseLoggingEnabled) { + Log.d(TAG, "setSupportedFeatureSet to " + mSupportedFeatureSet); + } } } @@ -2821,8 +2932,10 @@ public class ActiveModeWarden { * Get the current supported Wifi feature set. * @return supported Wifi feature set */ - public long getSupportedFeatureSet() { - return mSupportedFeatureSet.get(); + public @NonNull BitSet getSupportedFeatureSet() { + synchronized (mServiceApiLock) { + return mSupportedFeatureSet; + } } /** @@ -2925,4 +3038,26 @@ public class ActiveModeWarden { mSettingsStore.updateSatelliteModeTracker(); mWifiController.sendMessage(WifiController.CMD_SATELLITE_MODE_CHANGED); } + + /** + * Returns the number of multiple link devices (MLD) which are being operated. + */ + public int getCurrentMLDAp() { + if (!SdkLevel.isAtLeastT()) { + return 0; + } + int numberMLD = 0; + for (SoftApManager manager : mSoftApManagers) { + if (manager.isStarted() && manager.getSoftApModeConfiguration() + .getSoftApConfiguration().isIeee80211beEnabled()) { + if (manager.isBridgedMode() && !manager.isUsingMlo()) { + // Non MLO bridged mode, it occupies two MLD APs. + numberMLD += 2; + } else { + numberMLD++; + } + } + } + return numberMLD; + } } diff --git a/service/java/com/android/server/wifi/ClientMode.java b/service/java/com/android/server/wifi/ClientMode.java index 7292de3338..b371884718 100644 --- a/service/java/com/android/server/wifi/ClientMode.java +++ b/service/java/com/android/server/wifi/ClientMode.java @@ -16,12 +16,15 @@ package com.android.server.wifi; +import static com.android.server.wifi.util.GeneralUtil.bitsetToLong; + import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.net.DhcpResultsParcelable; import android.net.MacAddress; import android.net.Network; +import android.net.wifi.BlockingOption; import android.net.wifi.IWifiConnectedNetworkScorer; import android.net.wifi.WifiAnnotations; import android.net.wifi.WifiConfiguration; @@ -35,6 +38,8 @@ import android.os.IBinder; import android.os.Message; import android.os.WorkSource; +import androidx.annotation.Keep; + import com.android.server.wifi.WifiNative.RxFateReport; import com.android.server.wifi.WifiNative.TxFateReport; import com.android.server.wifi.util.ActionListenerWrapper; @@ -43,6 +48,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.BitSet; import java.util.List; import java.util.Set; @@ -65,20 +71,24 @@ public interface ClientMode { void enableVerboseLogging(boolean verbose); + @Keep void connectNetwork(NetworkUpdateResult result, ActionListenerWrapper wrapper, int callingUid, @NonNull String packageName, @Nullable String attributionTag); void saveNetwork(NetworkUpdateResult result, ActionListenerWrapper wrapper, int callingUid, @NonNull String packageName); + @Keep void disconnect(); void reconnect(WorkSource ws); void reassociate(); + @Keep void startConnectToNetwork(int networkId, int uid, String bssid); + @Keep void startRoamToNetwork(int networkId, String bssid); /** When the device mobility changes, update the RSSI polling interval accordingly */ @@ -123,6 +133,7 @@ public interface ClientMode { * Get current Wifi connection information * @return Wifi info */ + @Keep WifiInfo getConnectionInfo(); boolean syncQueryPasspointIcon(long bssid, String fileName); @@ -131,12 +142,23 @@ public interface ClientMode { * Get the current Wifi network information * @return network */ + @Keep Network getCurrentNetwork(); DhcpResultsParcelable syncGetDhcpResultsParcelable(); /** Get the supported feature set synchronously */ - long getSupportedFeatures(); + @NonNull + BitSet getSupportedFeaturesBitSet(); + + /** + * Do not use this method, new features will not be supported by this method. This method is + * only for backward compatible for some OEMs. Please use {@link #getSupportedFeaturesBitSet()} + */ + @Keep + default long getSupportedFeatures() { + return bitsetToLong(getSupportedFeaturesBitSet()); + } boolean syncStartSubscriptionProvisioning(int callingUid, OsuProvider provider, IProvisioningCallback callback); @@ -187,6 +209,7 @@ public interface ClientMode { */ @Nullable String getConnectingBssid(); + @Keep WifiLinkLayerStats getWifiLinkLayerStats(); boolean setPowerSave(@PowerSaveClientType int client, boolean ps); @@ -196,12 +219,14 @@ public interface ClientMode { WifiMulticastLockManager.FilterController getMcastLockManagerFilterController(); + @Keep boolean isConnected(); boolean isConnecting(); boolean isRoaming(); + @Keep boolean isDisconnected(); boolean isSupplicantTransientState(); @@ -368,4 +393,9 @@ public interface ClientMode { * Notify changes in PowerManager#isDeviceIdleMode */ void onIdleModeChanged(boolean isIdle); + + /** + * Block current connect network and add to blocklist + */ + void blockNetwork(BlockingOption option); } diff --git a/service/java/com/android/server/wifi/ClientModeDefaults.java b/service/java/com/android/server/wifi/ClientModeDefaults.java index f526f2dc6a..6a9d1ae409 100644 --- a/service/java/com/android/server/wifi/ClientModeDefaults.java +++ b/service/java/com/android/server/wifi/ClientModeDefaults.java @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.net.DhcpResultsParcelable; import android.net.MacAddress; import android.net.Network; +import android.net.wifi.BlockingOption; import android.net.wifi.IWifiConnectedNetworkScorer; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; @@ -284,4 +285,7 @@ public interface ClientModeDefaults extends ClientMode { @Override default void onIdleModeChanged(boolean isIdle) { } + + @Override + default void blockNetwork(BlockingOption option) { } } diff --git a/service/java/com/android/server/wifi/ClientModeImpl.java b/service/java/com/android/server/wifi/ClientModeImpl.java index 8e97cae460..edbd7e3043 100644 --- a/service/java/com/android/server/wifi/ClientModeImpl.java +++ b/service/java/com/android/server/wifi/ClientModeImpl.java @@ -17,9 +17,11 @@ package com.android.server.wifi; import static android.net.util.KeepalivePacketDataUtil.parseTcpKeepalivePacketData; +import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER; import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.DISABLED_NONE; import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.DISABLED_NO_INTERNET_PERMANENT; import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.DISABLED_NO_INTERNET_TEMPORARY; +import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.DISABLED_TRANSITION_DISABLE_INDICATION; import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.DISABLED_UNWANTED_LOW_RSSI; import static android.net.wifi.WifiManager.WIFI_FEATURE_FILS_SHA256; import static android.net.wifi.WifiManager.WIFI_FEATURE_FILS_SHA384; @@ -33,6 +35,8 @@ import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_PRIMARY; import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_SCAN_ONLY; import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_SECONDARY_LONG_LIVED; import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_SECONDARY_TRANSIENT; +import static com.android.server.wifi.WifiBlocklistMonitor.REASON_APP_DISALLOW; +import static com.android.server.wifi.WifiConfigManager.LINK_CONFIGURATION_BSSID_MATCH_LENGTH; import static com.android.server.wifi.WifiSettingsConfigStore.SECONDARY_WIFI_STA_FACTORY_MAC_ADDRESS; import static com.android.server.wifi.WifiSettingsConfigStore.WIFI_STA_FACTORY_MAC_ADDRESS; import static com.android.server.wifi.proto.WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__ROLE__ROLE_CLIENT_LOCAL_ONLY; @@ -85,6 +89,7 @@ import android.net.shared.ProvisioningConfiguration; import android.net.shared.ProvisioningConfiguration.ScanResultInfo; import android.net.vcn.VcnManager; import android.net.vcn.VcnNetworkPolicyResult; +import android.net.wifi.BlockingOption; import android.net.wifi.IWifiConnectedNetworkScorer; import android.net.wifi.MloLink; import android.net.wifi.ScanResult; @@ -160,6 +165,7 @@ import com.android.server.wifi.proto.nano.WifiMetricsProto; import com.android.server.wifi.proto.nano.WifiMetricsProto.StaEvent; import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiIsUnusableEvent; import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiUsabilityStats; +import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiUsabilityStatsEntry; import com.android.server.wifi.util.ActionListenerWrapper; import com.android.server.wifi.util.InformationElementUtil; import com.android.server.wifi.util.NativeUtil; @@ -182,6 +188,7 @@ import java.net.URL; import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; +import java.util.BitSet; import java.util.Collections; import java.util.List; import java.util.Map; @@ -679,12 +686,6 @@ public class ClientModeImpl extends StateMachine implements ClientMode { // connected before wrong password failure on this network reached this threshold. public static final int THRESHOLD_TO_PERM_WRONG_PASSWORD = 3; - // Maximum duration to continue to log Wifi usability stats after a data stall is triggered. - @VisibleForTesting - public static final long DURATION_TO_WAIT_ADD_STATS_AFTER_DATA_STALL_MS = 30 * 1000; - private long mDataStallTriggerTimeMs = -1; - private int mLastStatusDataStall = WifiIsUnusableEvent.TYPE_UNKNOWN; - @Nullable private StateMachineObituary mObituary = null; @@ -923,6 +924,7 @@ public class ClientModeImpl extends StateMachine implements ClientMode { mRoamingState = new RoamingState(threshold); mDisconnectedState = new DisconnectedState(threshold); + // Code indentation is used to show the hierarchical relationship between states. addState(mConnectableState); { addState(mConnectingOrConnectedState, mConnectableState); { addState(mL2ConnectingState, mConnectingOrConnectedState); @@ -1227,7 +1229,7 @@ public class ClientModeImpl extends StateMachine implements ClientMode { if (config.networkId == mTargetNetworkId || config.networkId == mLastNetworkId) { // Disconnect and let autojoin reselect a new network mFrameworkDisconnectReasonOverride = WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__DISCONNECT_NETWORK_REMOVED; - sendMessage(CMD_DISCONNECT, StaEvent.DISCONNECT_NETWORK_REMOVED); + sendMessageAtFrontOfQueue(CMD_DISCONNECT, StaEvent.DISCONNECT_NETWORK_REMOVED); // Log disconnection here, since the network config won't exist when the // disconnection event is received. String bssid = getConnectedBssidInternal(); @@ -1264,7 +1266,8 @@ public class ClientModeImpl extends StateMachine implements ClientMode { + " triggering disconnect"); mFrameworkDisconnectReasonOverride = WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__DISCONNECT_NETWORK_WIFI7_TOGGLED; - sendMessage(CMD_DISCONNECT, StaEvent.DISCONNECT_NETWORK_WIFI7_TOGGLED); + sendMessageAtFrontOfQueue(CMD_DISCONNECT, + StaEvent.DISCONNECT_NETWORK_WIFI7_TOGGLED); return; } @@ -1281,7 +1284,8 @@ public class ClientModeImpl extends StateMachine implements ClientMode { if (!newConfig.trusted) { Log.w(getTag(), "Network marked untrusted, triggering disconnect"); mFrameworkDisconnectReasonOverride = WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__DISCONNECT_NETWORK_UNTRUSTED; - sendMessage(CMD_DISCONNECT, StaEvent.DISCONNECT_NETWORK_UNTRUSTED); + sendMessageAtFrontOfQueue(CMD_DISCONNECT, + StaEvent.DISCONNECT_NETWORK_UNTRUSTED); return; } } @@ -1289,7 +1293,7 @@ public class ClientModeImpl extends StateMachine implements ClientMode { if (isMetered) { Log.w(getTag(), "Network marked metered, triggering disconnect"); mFrameworkDisconnectReasonOverride = WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__DISCONNECT_NETWORK_METERED; - sendMessage(CMD_DISCONNECT, StaEvent.DISCONNECT_NETWORK_METERED); + sendMessageAtFrontOfQueue(CMD_DISCONNECT, StaEvent.DISCONNECT_NETWORK_METERED); return; } @@ -1300,27 +1304,16 @@ public class ClientModeImpl extends StateMachine implements ClientMode { } @Override - public void onNetworkTemporarilyDisabled(WifiConfiguration config, int disableReason) { - if (disableReason == DISABLED_NO_INTERNET_TEMPORARY) return; - if (config.networkId == mTargetNetworkId || config.networkId == mLastNetworkId) { - // Disconnect and let autojoin reselect a new network - mFrameworkDisconnectReasonOverride = WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__DISCONNECT_TEMP_DISABLED; - sendMessage(CMD_DISCONNECT, StaEvent.DISCONNECT_NETWORK_TEMPORARY_DISABLED); - } - - } - - @Override public void onNetworkPermanentlyDisabled(WifiConfiguration config, int disableReason) { - // For DISABLED_NO_INTERNET_PERMANENT we do not need to remove the network - // because supplicant won't be trying to reconnect. If this is due to a - // preventAutomaticReconnect request from ConnectivityService, that service - // will disconnect as appropriate. - if (disableReason == DISABLED_NO_INTERNET_PERMANENT) return; + if (disableReason != DISABLED_BY_WIFI_MANAGER + && disableReason != DISABLED_TRANSITION_DISABLE_INDICATION) { + return; + } if (config.networkId == mTargetNetworkId || config.networkId == mLastNetworkId) { // Disconnect and let autojoin reselect a new network mFrameworkDisconnectReasonOverride = WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__DISCONNECT_PERM_DISABLED; - sendMessage(CMD_DISCONNECT, StaEvent.DISCONNECT_NETWORK_PERMANENT_DISABLED); + sendMessageAtFrontOfQueue(CMD_DISCONNECT, + StaEvent.DISCONNECT_NETWORK_PERMANENT_DISABLED); } } } @@ -1340,7 +1333,8 @@ public class ClientModeImpl extends StateMachine implements ClientMode { && configuration.carrierMerged == merged) { Log.i(getTag(), "Carrier network offload disabled, triggering disconnect"); mFrameworkDisconnectReasonOverride = WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__DISCONNECT_CARRIER_OFFLOAD_DISABLED; - sendMessage(CMD_DISCONNECT, StaEvent.DISCONNECT_CARRIER_OFFLOAD_DISABLED); + sendMessageAtFrontOfQueue(CMD_DISCONNECT, + StaEvent.DISCONNECT_CARRIER_OFFLOAD_DISABLED); } mWifiConnectivityManager.clearCachedCandidates(); } @@ -1646,14 +1640,14 @@ public class ClientModeImpl extends StateMachine implements ClientMode { } private boolean isLinkLayerStatsSupported() { - return (getSupportedFeatures() & WIFI_FEATURE_LINK_LAYER_STATS) != 0; + return getSupportedFeaturesBitSet().get(WIFI_FEATURE_LINK_LAYER_STATS); } /** * @return true if this device supports WPA3_SAE */ private boolean isWpa3SaeSupported() { - return (getSupportedFeatures() & WIFI_FEATURE_WPA3_SAE) != 0; + return getSupportedFeaturesBitSet().get(WIFI_FEATURE_WPA3_SAE); } /** @@ -1978,7 +1972,7 @@ public class ClientModeImpl extends StateMachine implements ClientMode { /** * Get the supported feature set synchronously */ - public long getSupportedFeatures() { + public @NonNull BitSet getSupportedFeaturesBitSet() { return mWifiNative.getSupportedFeatureSet(mInterfaceName); } @@ -2042,8 +2036,7 @@ public class ClientModeImpl extends StateMachine implements ClientMode { * Check if a TDLS session can be established */ public boolean isTdlsOperationCurrentlyAvailable() { - return (getSupportedFeatures() & WIFI_FEATURE_TDLS) != 0 && isConnected() - && canEnableTdls(); + return getSupportedFeaturesBitSet().get(WIFI_FEATURE_TDLS) && isConnected() && canEnableTdls(); } /** @@ -2278,9 +2271,7 @@ public class ClientModeImpl extends StateMachine implements ClientMode { sb.append(" "); sb.append(Integer.toString(msg.arg2)); if (mWifiInfo.getSSID() != null) { - if (mWifiInfo.getSSID() != null) { - sb.append(" ").append(mWifiInfo.getSSID()); - } + sb.append(" ").append(mWifiInfo.getSSID()); } if (mWifiInfo.getBSSID() != null) { sb.append(" ").append(mWifiInfo.getBSSID()); @@ -2817,9 +2808,6 @@ public class ClientModeImpl extends StateMachine implements ClientMode { * set Tx link speed only if it is valid */ if (newTxLinkSpeed > 0) { - if (newTxLinkSpeed != mWifiInfo.getTxLinkSpeedMbps() && SdkLevel.isAtLeastV()) { - updateNetworkCapabilities = true; - } mWifiInfo.setLinkSpeed(newTxLinkSpeed); mWifiInfo.setTxLinkSpeedMbps(newTxLinkSpeed); } @@ -2827,9 +2815,6 @@ public class ClientModeImpl extends StateMachine implements ClientMode { * set Rx link speed only if it is valid */ if (newRxLinkSpeed > 0) { - if (newRxLinkSpeed != mWifiInfo.getRxLinkSpeedMbps() && SdkLevel.isAtLeastV()) { - updateNetworkCapabilities = true; - } mWifiInfo.setRxLinkSpeedMbps(newRxLinkSpeed); } if (newFrequency > 0) { @@ -4072,9 +4057,11 @@ public class ClientModeImpl extends StateMachine implements ClientMode { WifiConfiguration.NetworkSelectionStatus.DISABLED_DHCP_FAILURE); /* DHCP times out after about 30 seconds, we do a - * disconnect thru supplicant, we will let autojoin retry connecting to the network + * disconnect through supplicant, we will let autojoin retry connecting to the network */ mFrameworkDisconnectReasonOverride = WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__DISCONNECT_IP_PROVISIONING_FAILURE; + mWifiMetrics.logStaEvent(mInterfaceName, StaEvent.TYPE_FRAMEWORK_DISCONNECT, + StaEvent.DISCONNECT_IP_CONFIGURATION_LOST); mWifiNative.disconnect(mInterfaceName); updateCurrentConnectionInfo(); } @@ -4096,7 +4083,7 @@ public class ClientModeImpl extends StateMachine implements ClientMode { mFrameworkDisconnectReasonOverride = WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__DISCONNECT_NUD_FAILURE_ORGANIC; } // Disconnect via supplicant, and let autojoin retry connecting to the network. - mWifiNative.disconnect(mInterfaceName); + sendMessageAtFrontOfQueue(CMD_DISCONNECT, StaEvent.DISCONNECT_IP_REACHABILITY_LOST); updateCurrentConnectionInfo(); } @@ -4170,9 +4157,6 @@ public class ClientModeImpl extends StateMachine implements ClientMode { mWifiMetrics.logStaEvent(mInterfaceName, StaEvent.TYPE_CMD_IP_REACHABILITY_LOST); mWifiMetrics.logWifiIsUnusableEvent(mInterfaceName, WifiIsUnusableEvent.TYPE_IP_REACHABILITY_LOST); - mWifiMetrics.addToWifiUsabilityStatsList(mInterfaceName, - WifiUsabilityStats.LABEL_BAD, - WifiUsabilityStats.TYPE_IP_REACHABILITY_LOST, -1); if (mWifiGlobals.getIpReachabilityDisconnectEnabled()) { handleIpReachabilityLost(lossReason); } else { @@ -4766,10 +4750,9 @@ public class ClientModeImpl extends StateMachine implements ClientMode { break; } if (mWifiP2pConnection.shouldTemporarilyDisconnectWifi()) { - mWifiMetrics.logStaEvent(mInterfaceName, StaEvent.TYPE_FRAMEWORK_DISCONNECT, - StaEvent.DISCONNECT_P2P_DISCONNECT_WIFI_REQUEST); mFrameworkDisconnectReasonOverride = WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__DISCONNECT_P2P_REQUESTED_DISCONNECT; - mWifiNative.disconnect(mInterfaceName); + sendMessageAtFrontOfQueue(CMD_DISCONNECT, + StaEvent.DISCONNECT_P2P_DISCONNECT_WIFI_REQUEST); } else { mWifiNative.reconnect(mInterfaceName); } @@ -5023,7 +5006,8 @@ public class ClientModeImpl extends StateMachine implements ClientMode { loge("Disconnecting from Passpoint network due to an issue with the " + "Terms and Conditions URL"); mFrameworkDisconnectReasonOverride = WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__DISCONNECT_PASSPOINT_TAC; - sendMessage(CMD_DISCONNECT, StaEvent.DISCONNECT_PASSPOINT_TAC); + sendMessageAtFrontOfQueue(CMD_DISCONNECT, + StaEvent.DISCONNECT_PASSPOINT_TAC); } break; case WifiMonitor.HS20_REMEDIATION_EVENT: @@ -5121,8 +5105,8 @@ public class ClientModeImpl extends StateMachine implements ClientMode { case CMD_CONNECTING_WATCHDOG_TIMER: case WifiMonitor.NETWORK_NOT_FOUND_EVENT: case CMD_ROAM_WATCHDOG_TIMER: { - // no-op: all messages must be handled in the base state in case it was missed - // in one of the child states. + // no-op: all messages must be handled in the base state if they were not + // handled in one of the child states. break; } case CMD_ACCEPT_EAP_SERVER_CERTIFICATE: @@ -5330,7 +5314,7 @@ public class ClientModeImpl extends StateMachine implements ClientMode { if (vcnNetworkPolicy.isTeardownRequested()) { mFrameworkDisconnectReasonOverride = WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__DISCONNECT_VNC_REQUEST; - sendMessage(CMD_DISCONNECT, StaEvent.DISCONNECT_VCN_REQUEST); + sendMessageAtFrontOfQueue(CMD_DISCONNECT, StaEvent.DISCONNECT_VCN_REQUEST); } final NetworkCapabilities vcnCapability = vcnNetworkPolicy.getNetworkCapabilities(); if (!vcnCapability.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)) { @@ -5779,7 +5763,8 @@ public class ClientModeImpl extends StateMachine implements ClientMode { logw("Connected to unknown networkId " + mLastNetworkId + ", disconnecting..."); mFrameworkDisconnectReasonOverride = WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__DISCONNECT_UNKNOWN_NETWORK; - sendMessage(CMD_DISCONNECT, StaEvent.DISCONNECT_UNKNOWN_NETWORK); + sendMessageAtFrontOfQueue(CMD_DISCONNECT, + StaEvent.DISCONNECT_UNKNOWN_NETWORK); break; } handleNetworkConnectionEventInfo(config, connectionInfo); @@ -6042,7 +6027,8 @@ public class ClientModeImpl extends StateMachine implements ClientMode { // disconnect the network. if (disconnectRequired) { mFrameworkDisconnectReasonOverride = WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__DISCONNECT_NETWORK_UNTRUSTED; - sendMessage(CMD_DISCONNECT, StaEvent.DISCONNECT_NETWORK_UNTRUSTED); + sendMessageAtFrontOfQueue(CMD_DISCONNECT, + StaEvent.DISCONNECT_NETWORK_UNTRUSTED); } break; } @@ -6398,9 +6384,8 @@ public class ClientModeImpl extends StateMachine implements ClientMode { WifiConfiguration.NetworkSelectionStatus .DISABLED_AUTHENTICATION_NO_CREDENTIALS); } - mWifiMetrics.logStaEvent(mInterfaceName, StaEvent.TYPE_FRAMEWORK_DISCONNECT, - StaEvent.DISCONNECT_GENERIC); - mWifiNative.disconnect(mInterfaceName); + sendMessageAtFrontOfQueue(CMD_DISCONNECT, + StaEvent.DISCONNECT_NO_CREDENTIALS); } break; } @@ -6449,7 +6434,8 @@ public class ClientModeImpl extends StateMachine implements ClientMode { // In the TOFU flow, the user approval dialog is now displayed and the // network remains disconnected and disabled until it is approved. mFrameworkDisconnectReasonOverride = WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__DISCONNECT_NETWORK_UNTRUSTED; - sendMessage(CMD_DISCONNECT, StaEvent.DISCONNECT_NETWORK_UNTRUSTED); + sendMessageAtFrontOfQueue(CMD_DISCONNECT, + StaEvent.DISCONNECT_NETWORK_UNTRUSTED); mLeafCertSent = true; } break; @@ -6477,13 +6463,15 @@ public class ClientModeImpl extends StateMachine implements ClientMode { if (config == null) { logw("Connected to a network that's already been removed " + mLastNetworkId + ", disconnecting..."); - sendMessage(CMD_DISCONNECT, StaEvent.DISCONNECT_UNKNOWN_NETWORK); + sendMessageAtFrontOfQueue(CMD_DISCONNECT, StaEvent.DISCONNECT_UNKNOWN_NETWORK); return; } mRssiPollToken++; if (mEnableRssiPolling) { sendMessage(CMD_RSSI_POLL, mRssiPollToken, 0); + mWifiMetrics.logAsynchronousEvent(mInterfaceName, + WifiUsabilityStatsEntry.CAPTURE_EVENT_TYPE_RSSI_POLLING_ENABLED); } else { updateLinkLayerStatsRssiAndScoreReport(); } @@ -6593,12 +6581,9 @@ public class ClientModeImpl extends StateMachine implements ClientMode { if (getConnectedWifiConfigurationInternal() == null || mNetworkAgent == null) { // The current config may have been removed while we were connecting, // trigger a disconnect to clear up state. - reportConnectionAttemptEnd( - WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION, - WifiMetricsProto.ConnectionEvent.HLF_NONE, - WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, 0); mFrameworkDisconnectReasonOverride = WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__DISCONNECT_NETWORK_REMOVED; - mWifiNative.disconnect(mInterfaceName); + sendMessageAtFrontOfQueue(CMD_DISCONNECT, + StaEvent.DISCONNECT_UNKNOWN_NETWORK); } else { handleSuccessfulIpConfiguration(); transitionTo(mL3ConnectedState); @@ -6631,9 +6616,8 @@ public class ClientModeImpl extends StateMachine implements ClientMode { WifiDiagnostics.REPORT_REASON_REACHABILITY_LOST); mWifiMetrics.logWifiIsUnusableEvent(mInterfaceName, WifiIsUnusableEvent.TYPE_IP_REACHABILITY_LOST); - mWifiMetrics.addToWifiUsabilityStatsList(mInterfaceName, - WifiUsabilityStats.LABEL_BAD, - WifiUsabilityStats.TYPE_IP_REACHABILITY_LOST, -1); + mWifiMetrics.logAsynchronousEvent(mInterfaceName, + WifiUsabilityStatsEntry.CAPTURE_EVENT_TYPE_IP_REACHABILITY_LOST, -1); if (mWifiGlobals.getIpReachabilityDisconnectEnabled()) { handleIpReachabilityLost(-1); } else { @@ -6645,15 +6629,17 @@ public class ClientModeImpl extends StateMachine implements ClientMode { if (!isFromCurrentIpClientCallbacks(message)) break; mWifiDiagnostics.triggerBugReportDataCapture( WifiDiagnostics.REPORT_REASON_REACHABILITY_FAILURE); + mWifiMetrics.logAsynchronousEvent(mInterfaceName, + WifiUsabilityStatsEntry.CAPTURE_EVENT_TYPE_IP_REACHABILITY_FAILURE, + ((ReachabilityLossInfoParcelable) message.obj).reason); handleIpReachabilityFailure((ReachabilityLossInfoParcelable) message.obj); break; } case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST: { if (mWifiP2pConnection.shouldTemporarilyDisconnectWifi()) { - mWifiMetrics.logStaEvent(mInterfaceName, StaEvent.TYPE_FRAMEWORK_DISCONNECT, - StaEvent.DISCONNECT_P2P_DISCONNECT_WIFI_REQUEST); mFrameworkDisconnectReasonOverride = WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__DISCONNECT_P2P_REQUESTED_DISCONNECT; - mWifiNative.disconnect(mInterfaceName); + sendMessageAtFrontOfQueue(CMD_DISCONNECT, + StaEvent.DISCONNECT_P2P_DISCONNECT_WIFI_REQUEST); } break; } @@ -6707,7 +6693,7 @@ public class ClientModeImpl extends StateMachine implements ClientMode { } case CMD_ONESHOT_RSSI_POLL: { if (!mEnableRssiPolling) { - updateLinkLayerStatsRssiDataStallScoreReport(); + updateLinkLayerStatsRssiDataStallScoreReport(true); } break; } @@ -6719,7 +6705,7 @@ public class ClientModeImpl extends StateMachine implements ClientMode { break; } if (message.arg1 == mRssiPollToken) { - updateLinkLayerStatsRssiDataStallScoreReport(); + updateLinkLayerStatsRssiDataStallScoreReport(false); mWifiScoreCard.noteSignalPoll(mWifiInfo); // Update the polling interval as needed before sending the delayed message // so that the next polling can happen after the updated interval @@ -6751,10 +6737,14 @@ public class ClientModeImpl extends StateMachine implements ClientMode { updateLinkLayerStatsRssiSpeedFrequencyCapabilities(txBytes, rxBytes); sendMessageDelayed(obtainMessage(CMD_RSSI_POLL, mRssiPollToken, 0), mWifiGlobals.getPollRssiIntervalMillis()); + mWifiMetrics.logAsynchronousEvent(mInterfaceName, + WifiUsabilityStatsEntry.CAPTURE_EVENT_TYPE_RSSI_POLLING_ENABLED); } else { mRssiMonitor.setShortPollRssiInterval(); removeMessages(CMD_RSSI_POLL); + mWifiMetrics.logAsynchronousEvent(mInterfaceName, + WifiUsabilityStatsEntry.CAPTURE_EVENT_TYPE_RSSI_POLLING_DISABLED); } break; } @@ -6914,8 +6904,10 @@ public class ClientModeImpl extends StateMachine implements ClientMode { /** * Fetches link stats, updates Wifi Data Stall, Score Card and Score Report. + * + * oneshot indicates that this update request came from CMD_ONESHOT_RSSI_POLL. */ - private WifiLinkLayerStats updateLinkLayerStatsRssiDataStallScoreReport() { + private WifiLinkLayerStats updateLinkLayerStatsRssiDataStallScoreReport(boolean oneshot) { // Get Info and continue polling long txBytes; long rxBytes; @@ -6928,12 +6920,19 @@ public class ClientModeImpl extends StateMachine implements ClientMode { } WifiLinkLayerStats stats = updateLinkLayerStatsRssiSpeedFrequencyCapabilities(txBytes, rxBytes); - mWifiMetrics.updateWifiUsabilityStatsEntries(mInterfaceName, mWifiInfo, stats); // checkDataStallAndThroughputSufficiency() should be called before // mWifiScoreReport.calculateAndReportScore() which needs the latest throughput int statusDataStall = mWifiDataStall.checkDataStallAndThroughputSufficiency( mInterfaceName, mLastConnectionCapabilities, mLastLinkLayerStats, stats, mWifiInfo, txBytes, rxBytes); + // This function will update stats that are used for WifiUsabilityStatsEntry and + // logScorerPredictionResult, so it should be called before + // mWifiMetrics.updateWifiUsabilityStatsEntries and + // mWifiMetrics.logScorerPredictionResult + mWifiMetrics.updateWiFiEvaluationAndScorerStats(mWifiScoreReport.getLingering(), + mWifiInfo, mLastConnectionCapabilities); + mWifiMetrics.updateWifiUsabilityStatsEntries(mInterfaceName, mWifiInfo, stats, oneshot, + statusDataStall); if (getClientRoleForMetrics(getConnectedWifiConfiguration()) == WIFI_CONNECTION_RESULT_REPORTED__ROLE__ROLE_CLIENT_PRIMARY) { mWifiMetrics.logScorerPredictionResult(mWifiInjector.hasActiveModem(), @@ -6941,29 +6940,9 @@ public class ClientModeImpl extends StateMachine implements ClientMode { mWifiCarrierInfoManager.isMobileDataEnabled(), mWifiGlobals.getPollRssiIntervalMillis(), mWifiScoreReport.getAospScorerPredictionStatusForEvaluation(), - mWifiScoreReport.getExternalScorerPredictionStatusForEvaluation(), - mWifiScoreReport.getLingering(), - mWifiInfo, mLastConnectionCapabilities); + mWifiScoreReport.getExternalScorerPredictionStatusForEvaluation()); mWifiScoreReport.clearScorerPredictionStatusForEvaluation(); } - - if (mDataStallTriggerTimeMs == -1 - && statusDataStall != WifiIsUnusableEvent.TYPE_UNKNOWN) { - mDataStallTriggerTimeMs = mClock.getElapsedSinceBootMillis(); - mLastStatusDataStall = statusDataStall; - } - if (mDataStallTriggerTimeMs != -1) { - long elapsedTime = mClock.getElapsedSinceBootMillis() - - mDataStallTriggerTimeMs; - if (elapsedTime >= DURATION_TO_WAIT_ADD_STATS_AFTER_DATA_STALL_MS) { - mDataStallTriggerTimeMs = -1; - mWifiMetrics.addToWifiUsabilityStatsList(mInterfaceName, - WifiUsabilityStats.LABEL_BAD, - convertToUsabilityStatsTriggerType(mLastStatusDataStall), - -1); - mLastStatusDataStall = WifiIsUnusableEvent.TYPE_UNKNOWN; - } - } // Send the update score to network agent. mWifiScoreReport.calculateAndReportScore(); @@ -7273,9 +7252,8 @@ public class ClientModeImpl extends StateMachine implements ClientMode { mRoamFailCount++; handleNetworkDisconnect(false, WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__ROAM_WATCHDOG_TIMER); - mWifiMetrics.logStaEvent(mInterfaceName, StaEvent.TYPE_FRAMEWORK_DISCONNECT, + sendMessageAtFrontOfQueue(CMD_DISCONNECT, StaEvent.DISCONNECT_ROAM_WATCHDOG_TIMER); - mWifiNative.disconnect(mInterfaceName); transitionTo(mDisconnectedState); } break; @@ -7367,7 +7345,7 @@ public class ClientModeImpl extends StateMachine implements ClientMode { @Override public void enterImpl() { if (mVerboseLoggingEnabled) { - log("Enter ConnectedState mScreenOn=" + mScreenOn); + log("Enter ConnectedState mScreenOn=" + mScreenOn); } reportConnectionAttemptEnd( @@ -7426,8 +7404,6 @@ public class ClientModeImpl extends StateMachine implements ClientMode { switch (message.what) { case CMD_UNWANTED_NETWORK: { if (message.arg1 == NETWORK_STATUS_UNWANTED_DISCONNECT) { - mWifiMetrics.logStaEvent(mInterfaceName, StaEvent.TYPE_FRAMEWORK_DISCONNECT, - StaEvent.DISCONNECT_UNWANTED); if (mClientModeManager.getRole() == ROLE_CLIENT_SECONDARY_TRANSIENT && mClientModeManager.getPreviousRole() == ROLE_CLIENT_PRIMARY) { mWifiMetrics.incrementMakeBeforeBreakLingerCompletedCount( @@ -7452,7 +7428,7 @@ public class ClientModeImpl extends StateMachine implements ClientMode { DISABLED_UNWANTED_LOW_RSSI); } mFrameworkDisconnectReasonOverride = WifiStatsLog.WIFI_DISCONNECT_REPORTED__FAILURE_CODE__DISCONNECT_UNWANTED_BY_CONNECTIVITY; - mWifiNative.disconnect(mInterfaceName); + sendMessageAtFrontOfQueue(CMD_DISCONNECT, StaEvent.DISCONNECT_UNWANTED); } else if (message.arg1 == NETWORK_STATUS_UNWANTED_DISABLE_AUTOJOIN || message.arg1 == NETWORK_STATUS_UNWANTED_VALIDATION_FAILED) { Log.d(getTag(), (message.arg1 == NETWORK_STATUS_UNWANTED_DISABLE_AUTOJOIN @@ -7564,11 +7540,6 @@ public class ClientModeImpl extends StateMachine implements ClientMode { } case WifiMonitor.NETWORK_DISCONNECTION_EVENT: { DisconnectEventInfo eventInfo = (DisconnectEventInfo) message.obj; - reportConnectionAttemptEnd( - WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION, - WifiMetricsProto.ConnectionEvent.HLF_NONE, - WifiMetricsProto.ConnectionEvent.FAILURE_REASON_UNKNOWN, - eventInfo.reasonCode); if (unexpectedDisconnectedReason(eventInfo.reasonCode)) { mWifiDiagnostics.triggerBugReportDataCapture( WifiDiagnostics.REPORT_REASON_UNEXPECTED_DISCONNECT); @@ -8085,21 +8056,21 @@ public class ClientModeImpl extends StateMachine implements ClientMode { * @return true if this device supports FILS-SHA256 */ private boolean isFilsSha256Supported() { - return (getSupportedFeatures() & WIFI_FEATURE_FILS_SHA256) != 0; + return getSupportedFeaturesBitSet().get(WIFI_FEATURE_FILS_SHA256); } /** * @return true if this device supports FILS-SHA384 */ private boolean isFilsSha384Supported() { - return (getSupportedFeatures() & WIFI_FEATURE_FILS_SHA384) != 0; + return getSupportedFeaturesBitSet().get(WIFI_FEATURE_FILS_SHA384); } /** * @return true if this device supports Trust On First Use */ private boolean isTrustOnFirstUseSupported() { - return (getSupportedFeatures() & WIFI_FEATURE_TRUST_ON_FIRST_USE) != 0; + return getSupportedFeaturesBitSet().get(WIFI_FEATURE_TRUST_ON_FIRST_USE); } /** @@ -8913,4 +8884,38 @@ public class ClientModeImpl extends StateMachine implements ClientMode { mWifiInjector.getActiveModeWarden().updateCurrentConnectionInfo(); } } + + @Override + public void blockNetwork(BlockingOption option) { + if (mLastNetworkId == WifiConfiguration.INVALID_NETWORK_ID) { + Log.e(TAG, "Calling blockNetwork when disconnected"); + return; + } + WifiConfiguration configuration = mWifiConfigManager.getConfiguredNetwork(mLastNetworkId); + if (configuration == null) { + Log.e(TAG, "No available config for networkId: " + mLastNetworkId); + return; + } + if (mLastBssid == null) { + Log.e(TAG, "No available BSSID for networkId: " + mLastNetworkId); + return; + } + if (option.isBlockingBssidOnly()) { + mWifiBlocklistMonitor.blockBssidForDurationMs(mLastBssid, + mWifiConfigManager.getConfiguredNetwork(mLastNetworkId), + option.getBlockingTimeSeconds() * 1000L, REASON_APP_DISALLOW, 0); + } else { + ScanDetailCache scanDetailCache = mWifiConfigManager + .getScanDetailCacheForNetwork(mLastNetworkId); + for (String bssid : scanDetailCache.keySet()) { + if (bssid.regionMatches(true, 0, mLastBssid, 0, + LINK_CONFIGURATION_BSSID_MATCH_LENGTH)) { + mWifiBlocklistMonitor.blockBssidForDurationMs(bssid, + mWifiConfigManager.getConfiguredNetwork(mLastNetworkId), + option.getBlockingTimeSeconds() * 1000L, REASON_APP_DISALLOW, 0); + } + } + } + mWifiBlocklistMonitor.updateAndGetBssidBlocklistForSsids(Set.of(configuration.SSID)); + } } diff --git a/service/java/com/android/server/wifi/Clock.java b/service/java/com/android/server/wifi/Clock.java index 63e958b3ca..1a235287cb 100644 --- a/service/java/com/android/server/wifi/Clock.java +++ b/service/java/com/android/server/wifi/Clock.java @@ -18,6 +18,8 @@ package com.android.server.wifi; import android.os.SystemClock; +import java.time.Instant; + import javax.annotation.concurrent.ThreadSafe; /** @@ -68,4 +70,8 @@ public class Clock { public void sleep(long ms) { SystemClock.sleep(ms); } + + public Instant getCurrentInstant() { + return Instant.now(); + } } diff --git a/service/java/com/android/server/wifi/ConcreteClientModeManager.java b/service/java/com/android/server/wifi/ConcreteClientModeManager.java index 33c5f3142f..c7094f84b0 100644 --- a/service/java/com/android/server/wifi/ConcreteClientModeManager.java +++ b/service/java/com/android/server/wifi/ConcreteClientModeManager.java @@ -16,6 +16,7 @@ package com.android.server.wifi; +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; @@ -30,6 +31,7 @@ import android.net.MacAddress; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkRequest; +import android.net.wifi.BlockingOption; import android.net.wifi.IWifiConnectedNetworkScorer; import android.net.wifi.WifiAnnotations; import android.net.wifi.WifiConfiguration; @@ -59,6 +61,8 @@ import android.telephony.ims.stub.ImsRegistrationImplBase; import android.text.TextUtils; import android.util.Log; +import androidx.annotation.Keep; + import com.android.internal.util.IState; import com.android.internal.util.State; import com.android.internal.util.StateMachine; @@ -75,6 +79,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.BitSet; import java.util.List; import java.util.Set; @@ -197,6 +202,7 @@ public class ConcreteClientModeManager implements ClientModeManager { * Sets whether this ClientModeManager is for secondary STA with internet. * @param secondaryInternet whether the ClientModeManager is for secondary internet. */ + @Keep public void setSecondaryInternet(boolean secondaryInternet) { // TODO: b/197670907 : Add client role ROLE_CLIENT_SECONDARY_INTERNET if (mRole == ROLE_CLIENT_SECONDARY_LONG_LIVED) { @@ -208,6 +214,7 @@ public class ConcreteClientModeManager implements ClientModeManager { * Sets whether this ClientModeManager is for DBS AP multi internet. * @param isDbs whether the ClientModeManager is connecting to to the same SSID as primary. */ + @Keep public void setSecondaryInternetDbsAp(boolean isDbs) { // TODO: b/197670907 : Add client role ROLE_CLIENT_SECONDARY_INTERNET if (mRole == ROLE_CLIENT_SECONDARY_LONG_LIVED) { @@ -585,6 +592,9 @@ public class ConcreteClientModeManager implements ClientModeManager { mTargetRoleChangeInfo = new RoleChangeInfo(role, requestorWs, modeListener); if (role == ROLE_CLIENT_SCAN_ONLY) { // Switch client mode manager to scan only mode. + if (mRole == ROLE_CLIENT_PRIMARY) { + mWifiInjector.getActiveModeWarden().setWifiStateForApiCalls(WIFI_STATE_DISABLING); + } mStateMachine.sendMessage( ClientModeStateMachine.CMD_SWITCH_TO_SCAN_ONLY_MODE); } else { @@ -1479,8 +1489,8 @@ public class ConcreteClientModeManager implements ClientModeManager { } @Override - public long getSupportedFeatures() { - return getClientMode().getSupportedFeatures(); + public @NonNull BitSet getSupportedFeaturesBitSet() { + return getClientMode().getSupportedFeaturesBitSet(); } @Override @@ -1724,4 +1734,9 @@ public class ConcreteClientModeManager implements ClientModeManager { public void onIdleModeChanged(boolean isIdle) { getClientMode().onIdleModeChanged(isIdle); } + + @Override + public void blockNetwork(BlockingOption option) { + getClientMode().blockNetwork(option); + } } diff --git a/service/java/com/android/server/wifi/DefaultClientModeManager.java b/service/java/com/android/server/wifi/DefaultClientModeManager.java index 450dd1e87e..f5f46ab003 100644 --- a/service/java/com/android/server/wifi/DefaultClientModeManager.java +++ b/service/java/com/android/server/wifi/DefaultClientModeManager.java @@ -16,11 +16,13 @@ package com.android.server.wifi; +import android.annotation.NonNull; import android.annotation.Nullable; import android.os.WorkSource; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.BitSet; /** * This is used for creating a public {@link ClientModeManager} instance when wifi is off. @@ -81,8 +83,8 @@ public class DefaultClientModeManager implements ClientModeManager, ClientModeDe } @Override - public long getSupportedFeatures() { - return 0L; + public @NonNull BitSet getSupportedFeaturesBitSet() { + return new BitSet(); } @Override diff --git a/service/java/com/android/server/wifi/DppManager.java b/service/java/com/android/server/wifi/DppManager.java index c8bf111b9d..553d716a7e 100644 --- a/service/java/com/android/server/wifi/DppManager.java +++ b/service/java/com/android/server/wifi/DppManager.java @@ -74,6 +74,7 @@ public class DppManager { private static final String DPP_TIMEOUT_TAG = TAG + " Request Timeout"; private static final int DPP_TIMEOUT_MS = 40_000; // 40 seconds private static final int DPP_RESPONDER_TIMEOUT_MS = 300_000; // 5 minutes + private static final int DPP_ENROLLEE_CONN_STATUS_RESULT_TX_TIMEOUT_MS = 16_000; // 16 seconds public static final int DPP_AUTH_ROLE_INACTIVE = -1; public static final int DPP_AUTH_ROLE_INITIATOR = 0; public static final int DPP_AUTH_ROLE_RESPONDER = 1; @@ -740,7 +741,11 @@ public class DppManager { if (!mDppRequestInfo.connStatusRequested) { cleanupDppResources(); } else { - Log.d(TAG, "Wait for enrollee to send connection status"); + Log.d(TAG, "Wait " + (DPP_ENROLLEE_CONN_STATUS_RESULT_TX_TIMEOUT_MS / 1000) + + " seconds for enrollee to send connection status"); + mDppTimeoutMessage.cancel(); + mDppTimeoutMessage.schedule(mClock.getElapsedSinceBootMillis() + + DPP_ENROLLEE_CONN_STATUS_RESULT_TX_TIMEOUT_MS); } } diff --git a/service/java/com/android/server/wifi/HalDeviceManager.java b/service/java/com/android/server/wifi/HalDeviceManager.java index 8d0d9c44b6..a251af8578 100644 --- a/service/java/com/android/server/wifi/HalDeviceManager.java +++ b/service/java/com/android/server/wifi/HalDeviceManager.java @@ -19,7 +19,7 @@ package com.android.server.wifi; import static com.android.server.wifi.HalDeviceManagerUtil.jsonToStaticChipInfo; import static com.android.server.wifi.HalDeviceManagerUtil.staticChipInfoToJson; import static com.android.server.wifi.WifiSettingsConfigStore.WIFI_STATIC_CHIP_INFO; -import static com.android.server.wifi.util.GeneralUtil.bitsetToLong; +import static com.android.server.wifi.util.GeneralUtil.longToBitset; import android.annotation.IntDef; import android.annotation.NonNull; @@ -83,8 +83,9 @@ public class HalDeviceManager { private final FeatureFlags mFeatureFlags; private boolean mDbg = false; - public static final long CHIP_CAPABILITY_ANY = 0L; - private static final long CHIP_CAPABILITY_UNINITIALIZED = -1L; + public static final BitSet CHIP_CAPABILITY_ANY = new BitSet(); + // TODO: Determine if CHIP_CAPABILITY_UNINITIALIZED can be replaced with an empty BitSet + private static final BitSet CHIP_CAPABILITY_UNINITIALIZED = longToBitset(-1L); private static final int DBS_24G_5G_MASK = WifiScanner.WIFI_BAND_24_GHZ | WifiScanner.WIFI_BAND_5_GHZ; @@ -322,8 +323,9 @@ public class HalDeviceManager { * @param concreteClientModeManager ConcreteClientModeManager requesting the interface. * @return A newly created interface - or null if the interface could not be created. */ - public WifiStaIface createStaIface( - long requiredChipCapabilities, + @VisibleForTesting + protected WifiStaIface createStaIface( + @NonNull BitSet requiredChipCapabilities, @Nullable InterfaceDestroyedListener destroyedListener, @Nullable Handler handler, @NonNull WorkSource requestorWs, @NonNull ConcreteClientModeManager concreteClientModeManager) { @@ -332,7 +334,8 @@ public class HalDeviceManager { return null; } WifiStaIface staIface = (WifiStaIface) createIface(HDM_CREATE_IFACE_STA, - requiredChipCapabilities, destroyedListener, handler, requestorWs, null); + requiredChipCapabilities, destroyedListener, handler, requestorWs, null, + false /* isUsingMultiLinkOperation */); if (staIface != null) { mClientModeManagers.put(getName(staIface), concreteClientModeManager); } @@ -367,7 +370,7 @@ public class HalDeviceManager { * Create AP interface if possible (see createStaIface doc). */ public WifiApIface createApIface( - long requiredChipCapabilities, + @NonNull BitSet requiredChipCapabilities, @Nullable InterfaceDestroyedListener destroyedListener, @Nullable Handler handler, @NonNull WorkSource requestorWs, boolean isBridged, @NonNull SoftApManager softApManager, @NonNull List<OuiKeyedData> vendorData) { @@ -377,7 +380,7 @@ public class HalDeviceManager { } WifiApIface apIface = (WifiApIface) createIface(isBridged ? HDM_CREATE_IFACE_AP_BRIDGE : HDM_CREATE_IFACE_AP, requiredChipCapabilities, destroyedListener, - handler, requestorWs, vendorData); + handler, requestorWs, vendorData, softApManager.isUsingMlo()); if (apIface != null) { mSoftApManagers.put(getName(apIface), softApManager); } @@ -387,12 +390,14 @@ public class HalDeviceManager { /** * Create P2P interface if possible (see createStaIface doc). */ - public String createP2pIface( - long requiredChipCapabilities, + @VisibleForTesting + protected String createP2pIface( + @NonNull BitSet requiredChipCapabilities, @Nullable InterfaceDestroyedListener destroyedListener, @Nullable Handler handler, @NonNull WorkSource requestorWs) { WifiP2pIface iface = (WifiP2pIface) createIface(HDM_CREATE_IFACE_P2P, - requiredChipCapabilities, destroyedListener, handler, requestorWs, null); + requiredChipCapabilities, destroyedListener, handler, requestorWs, null, + false /* isUsingMultiLinkOperation */); if (iface == null) { return null; } @@ -419,7 +424,8 @@ public class HalDeviceManager { public WifiNanIface createNanIface(@Nullable InterfaceDestroyedListener destroyedListener, @Nullable Handler handler, @NonNull WorkSource requestorWs) { return (WifiNanIface) createIface(HDM_CREATE_IFACE_NAN, CHIP_CAPABILITY_ANY, - destroyedListener, handler, requestorWs, null); + destroyedListener, handler, requestorWs, null, + false /* isUsingMultiLinkOperation */); } /** @@ -818,8 +824,9 @@ public class HalDeviceManager { * interface using rules based on the requestor app's context. * @return true if the device supports the provided combo, false otherwise. */ - public boolean isItPossibleToCreateIface(@HdmIfaceTypeForCreation int createIfaceType, - long requiredChipCapabilities, WorkSource requestorWs) { + @VisibleForTesting + protected boolean isItPossibleToCreateIface(@HdmIfaceTypeForCreation int createIfaceType, + BitSet requiredChipCapabilities, WorkSource requestorWs) { if (VDBG) { Log.d(TAG, "isItPossibleToCreateIface: createIfaceType=" + createIfaceType + ", requiredChipCapabilities=" + requiredChipCapabilities); @@ -863,7 +870,7 @@ public class HalDeviceManager { */ private List<WifiIfaceInfo> getIfacesToDestroyForRequest( @HdmIfaceTypeForCreation int createIfaceType, boolean queryForNewInterface, - long requiredChipCapabilities, WorkSource requestorWs) { + BitSet requiredChipCapabilities, WorkSource requestorWs) { if (VDBG) { Log.d(TAG, "getIfacesToDestroyForRequest: ifaceType=" + createIfaceType + ", requiredChipCapabilities=" + requiredChipCapabilities @@ -1066,7 +1073,7 @@ public class HalDeviceManager { // Arrays of WifiIfaceInfo indexed by @HdmIfaceTypeForCreation, in order of creation as // returned by WifiChip.getXxxIfaceNames. public WifiIfaceInfo[][] ifaces = new WifiIfaceInfo[CREATE_TYPES_BY_PRIORITY.length][]; - public long chipCapabilities; + public BitSet chipCapabilities = new BitSet(); public List<WifiChip.WifiRadioCombination> radioCombinations = null; // A data structure for the faster band combination lookup. public Set<List<Integer>> bandCombinations = null; @@ -1208,7 +1215,7 @@ public class HalDeviceManager { return null; } - long chipCapabilities = getChipCapabilities(chip); + BitSet chipCapabilities = getChipCapabilities(chip); List<String> ifaceNames = chip.getStaIfaceNames(); if (ifaceNames == null) { @@ -1378,6 +1385,7 @@ public class HalDeviceManager { } } catch (JSONException e) { Log.e(TAG, "Failed to load static chip info from store: " + e); + return new StaticChipInfo[0]; } return staticChipInfos; } @@ -1390,7 +1398,6 @@ public class HalDeviceManager { WifiChipInfo chipInfo = chipInfos[i]; staticChipInfos[i] = new StaticChipInfo( chipInfo.chipId, - chipInfo.chipCapabilities, chipInfo.availableModes); } return staticChipInfos; @@ -1616,8 +1623,9 @@ public class HalDeviceManager { } private WifiHal.WifiInterface createIface(@HdmIfaceTypeForCreation int createIfaceType, - long requiredChipCapabilities, InterfaceDestroyedListener destroyedListener, - Handler handler, WorkSource requestorWs, @Nullable List<OuiKeyedData> vendorData) { + BitSet requiredChipCapabilities, InterfaceDestroyedListener destroyedListener, + Handler handler, WorkSource requestorWs, @Nullable List<OuiKeyedData> vendorData, + boolean isUsingMultiLinkOperation) { if (mDbg) { Log.d(TAG, "createIface: createIfaceType=" + createIfaceType + ", requiredChipCapabilities=" + requiredChipCapabilities @@ -1628,6 +1636,10 @@ public class HalDeviceManager { + "with NonNull destroyedListener but Null handler"); return null; } + if (requiredChipCapabilities == null) { + Log.wtf(TAG, "createIface received null required chip capabilities"); + return null; + } synchronized (mLock) { WifiChipInfo[] chipInfos = getAllChipInfo(false); @@ -1649,23 +1661,33 @@ public class HalDeviceManager { return createIfaceIfPossible( chipInfos, createIfaceType, requiredChipCapabilities, - destroyedListener, handler, requestorWs, vendorData); + destroyedListener, handler, requestorWs, vendorData, + isUsingMultiLinkOperation); } } - private static boolean isChipCapabilitiesSupported(long currentChipCapabilities, - long requiredChipCapabilities) { - if (requiredChipCapabilities == CHIP_CAPABILITY_ANY) return true; - - if (CHIP_CAPABILITY_UNINITIALIZED == currentChipCapabilities) return true; + @VisibleForTesting + protected static boolean areChipCapabilitiesSupported(BitSet currentChipCapabilities, + BitSet requiredChipCapabilities) { + if (requiredChipCapabilities == null + || requiredChipCapabilities.equals(CHIP_CAPABILITY_ANY)) { + // No capabilities are required for this operation + return true; + } + if (currentChipCapabilities.equals(CHIP_CAPABILITY_UNINITIALIZED)) { + return true; + } - return (currentChipCapabilities & requiredChipCapabilities) - == requiredChipCapabilities; + // Check if the chip supports the required capabilities using + // (requiredChipCapabilities & currentChipCapabilities) == requiredChipCapabilities + BitSet tempRequiredCapabilities = (BitSet) requiredChipCapabilities.clone(); + tempRequiredCapabilities.and(currentChipCapabilities); + return tempRequiredCapabilities.equals(requiredChipCapabilities); } private IfaceCreationData getBestIfaceCreationProposal( WifiChipInfo[] chipInfos, @HdmIfaceTypeForCreation int createIfaceType, - long requiredChipCapabilities, WorkSource requestorWs) { + BitSet requiredChipCapabilities, WorkSource requestorWs) { int targetHalIfaceType = HAL_IFACE_MAP.get(createIfaceType); if (VDBG) { Log.d(TAG, "getBestIfaceCreationProposal: chipInfos=" + Arrays.deepToString(chipInfos) @@ -1677,7 +1699,7 @@ public class HalDeviceManager { synchronized (mLock) { IfaceCreationData bestIfaceCreationProposal = null; for (WifiChipInfo chipInfo : chipInfos) { - if (!isChipCapabilitiesSupported( + if (!areChipCapabilitiesSupported( chipInfo.chipCapabilities, requiredChipCapabilities)) { continue; } @@ -1749,15 +1771,17 @@ public class HalDeviceManager { private WifiHal.WifiInterface createIfaceIfPossible( WifiChipInfo[] chipInfos, @HdmIfaceTypeForCreation int createIfaceType, - long requiredChipCapabilities, InterfaceDestroyedListener destroyedListener, - Handler handler, WorkSource requestorWs, @Nullable List<OuiKeyedData> vendorData) { + BitSet requiredChipCapabilities, InterfaceDestroyedListener destroyedListener, + Handler handler, WorkSource requestorWs, @Nullable List<OuiKeyedData> vendorData, + boolean isUsingMultiLinkOperation) { int targetHalIfaceType = HAL_IFACE_MAP.get(createIfaceType); if (VDBG) { Log.d(TAG, "createIfaceIfPossible: chipInfos=" + Arrays.deepToString(chipInfos) + ", createIfaceType=" + createIfaceType + ", targetHalIfaceType=" + targetHalIfaceType + ", requiredChipCapabilities=" + requiredChipCapabilities - + ", requestorWs=" + requestorWs); + + ", requestorWs=" + requestorWs + + ", isUsingMultiLinkOperation" + isUsingMultiLinkOperation); } if (vendorData != null && !vendorData.isEmpty()) { Log.d(TAG, "Request includes vendor data. ifaceType=" + createIfaceType @@ -1769,7 +1793,7 @@ public class HalDeviceManager { if (bestIfaceCreationProposal != null) { WifiHal.WifiInterface iface = executeChipReconfiguration(bestIfaceCreationProposal, - createIfaceType, vendorData); + createIfaceType, vendorData, isUsingMultiLinkOperation); if (iface == null) { // If the chip reconfiguration failed, we'll need to clean up internal state. Log.e(TAG, "Teardown Wifi internal state"); @@ -1876,6 +1900,51 @@ public class HalDeviceManager { return false; } + private boolean isRequestorAllowedToUseApNanConcurrency(WorkSource requestorWs) { + String[] allowlistArray = mContext.getResources().getStringArray( + R.array.config_wifiSoftApAwareConcurrencyAllowlist); + if (allowlistArray == null || allowlistArray.length == 0) { + // No allowlist defined, so allow. + return true; + } + List<String> allowlist = Arrays.asList(allowlistArray); + for (int i = 0; i < requestorWs.size(); i++) { + if (allowlist.contains(requestorWs.getPackageName(i))) { + return true; + } + } + return false; + } + + /** + * Remove AP from the combo if NAN requested (or NAN if AP is requested) if the current + * requestor is not allowed to use AP/NAN concurrency. + */ + @NonNull + private int[] removeApNanConcurrencyIfNotAllowed( + @NonNull int[] chipCreateTypeCombo, + @HdmIfaceTypeForCreation int requestedCreateType, + WorkSource requestorWs) { + if (isRequestorAllowedToUseApNanConcurrency(requestorWs)) { + return chipCreateTypeCombo; + } + + int[] newCombo = chipCreateTypeCombo.clone(); + switch (requestedCreateType) { + case HDM_CREATE_IFACE_AP: + case HDM_CREATE_IFACE_AP_BRIDGE: + newCombo[HDM_CREATE_IFACE_NAN] = 0; + break; + case HDM_CREATE_IFACE_NAN: + newCombo[HDM_CREATE_IFACE_AP] = 0; + newCombo[HDM_CREATE_IFACE_AP_BRIDGE] = 0; + break; + default: + break; + } + return newCombo; + } + /** * Checks whether the input chip-create-type-combo can support the requested create type: * if not then returns null, if yes then returns information containing the list of interfaces @@ -1923,6 +1992,10 @@ public class HalDeviceManager { } } + // Remove AP/NAN concurrency if the requestor isn't on the allowlist. + chipCreateTypeCombo = removeApNanConcurrencyIfNotAllowed( + chipCreateTypeCombo, requestedCreateType, requestorWs); + IfaceCreationData ifaceCreationData = new IfaceCreationData(); ifaceCreationData.chipInfo = chipInfo; ifaceCreationData.chipModeId = chipModeId; @@ -2414,7 +2487,8 @@ public class HalDeviceManager { * Returns the newly created interface or a null on any error. */ private WifiHal.WifiInterface executeChipReconfiguration(IfaceCreationData ifaceCreationData, - @HdmIfaceTypeForCreation int createIfaceType, @Nullable List<OuiKeyedData> vendorData) { + @HdmIfaceTypeForCreation int createIfaceType, @Nullable List<OuiKeyedData> vendorData, + boolean isUsingMultiLinkOperation) { if (mDbg) { Log.d(TAG, "executeChipReconfiguration: ifaceCreationData=" + ifaceCreationData + ", createIfaceType=" + createIfaceType); @@ -2488,7 +2562,8 @@ public class HalDeviceManager { iface = ifaceCreationData.chipInfo.chip.createStaIface(); break; case HDM_CREATE_IFACE_AP_BRIDGE: - iface = ifaceCreationData.chipInfo.chip.createBridgedApIface(vendorData); + iface = ifaceCreationData.chipInfo.chip.createBridgedApIface(vendorData, + isUsingMultiLinkOperation); break; case HDM_CREATE_IFACE_AP: iface = ifaceCreationData.chipInfo.chip.createApIface(vendorData); @@ -2858,18 +2933,18 @@ public class HalDeviceManager { * @param wifiChip WifiChip to get the features for. * @return Bitset of WifiManager.WIFI_FEATURE_* values. */ - private long getChipCapabilities(@NonNull WifiChip wifiChip) { - if (wifiChip == null) return 0; + private BitSet getChipCapabilities(@NonNull WifiChip wifiChip) { + if (wifiChip == null) return new BitSet(); WifiChip.Response<BitSet> capsResp = wifiChip.getCapabilitiesBeforeIfacesExist(); if (capsResp.getStatusCode() == WifiHal.WIFI_STATUS_SUCCESS) { - return bitsetToLong(capsResp.getValue()); + return capsResp.getValue(); } else if (capsResp.getStatusCode() != WifiHal.WIFI_STATUS_ERROR_REMOTE_EXCEPTION) { // Non-remote exception here is likely because HIDL HAL < v1.5 // does not support getting capabilities before creating an interface. return CHIP_CAPABILITY_UNINITIALIZED; } else { // remote exception - return 0; + return new BitSet(); } } diff --git a/service/java/com/android/server/wifi/HalDeviceManagerUtil.java b/service/java/com/android/server/wifi/HalDeviceManagerUtil.java index 5897307522..09aa486ef0 100644 --- a/service/java/com/android/server/wifi/HalDeviceManagerUtil.java +++ b/service/java/com/android/server/wifi/HalDeviceManagerUtil.java @@ -33,15 +33,12 @@ import java.util.List; public class HalDeviceManagerUtil { static class StaticChipInfo { private int mChipId; - private long mChipCapabilities; private @NonNull ArrayList<WifiChip.ChipMode> mAvailableModes = new ArrayList<>(); StaticChipInfo( int chipId, - long chipCapabilities, @NonNull ArrayList<WifiChip.ChipMode> availableModes) { mChipId = chipId; - mChipCapabilities = chipCapabilities; if (availableModes != null) { mAvailableModes = availableModes; } @@ -51,24 +48,18 @@ public class HalDeviceManagerUtil { return mChipId; } - long getChipCapabilities() { - return mChipCapabilities; - } - ArrayList<WifiChip.ChipMode> getAvailableModes() { return mAvailableModes; } } private static final String KEY_CHIP_ID = "chipId"; - private static final String KEY_CHIP_CAPABILITIES = "chipCapabilities"; private static final String KEY_AVAILABLE_MODES = "availableModes"; static JSONObject staticChipInfoToJson(@NonNull StaticChipInfo staticChipInfo) throws JSONException { JSONObject jsonObject = new JSONObject(); jsonObject.put(KEY_CHIP_ID, staticChipInfo.getChipId()); - jsonObject.put(KEY_CHIP_CAPABILITIES, staticChipInfo.getChipCapabilities()); JSONArray availableModesJson = new JSONArray(); for (WifiChip.ChipMode mode : staticChipInfo.getAvailableModes()) { availableModesJson.put(chipModeToJson(mode)); @@ -80,12 +71,11 @@ public class HalDeviceManagerUtil { static StaticChipInfo jsonToStaticChipInfo(JSONObject jsonObject) throws JSONException { ArrayList<WifiChip.ChipMode> availableModes = new ArrayList<>(); int chipId = jsonObject.getInt(KEY_CHIP_ID); - long chipCapabilities = jsonObject.getLong(KEY_CHIP_CAPABILITIES); JSONArray modesJson = jsonObject.getJSONArray(KEY_AVAILABLE_MODES); for (int i = 0; i < modesJson.length(); i++) { availableModes.add(jsonToChipMode(modesJson.getJSONObject(i))); } - return new StaticChipInfo(chipId, chipCapabilities, availableModes); + return new StaticChipInfo(chipId, availableModes); } private static final String KEY_ID = "id"; diff --git a/service/java/com/android/server/wifi/HostapdHal.java b/service/java/com/android/server/wifi/HostapdHal.java index 7d7b2bfda2..1bf664e2aa 100644 --- a/service/java/com/android/server/wifi/HostapdHal.java +++ b/service/java/com/android/server/wifi/HostapdHal.java @@ -28,6 +28,7 @@ import com.android.server.wifi.WifiNative.HostapdDeathEventHandler; import com.android.server.wifi.WifiNative.SoftApHalCallback; import java.io.PrintWriter; +import java.util.List; import javax.annotation.concurrent.ThreadSafe; @@ -160,13 +161,16 @@ public class HostapdHal { * @return true on success, false otherwise. */ public boolean addAccessPoint(@NonNull String ifaceName, @NonNull SoftApConfiguration config, - boolean isMetered, @NonNull Runnable onFailureListener) { + boolean isMetered, boolean isUsingMultiLinkOperation, + @NonNull List<String> instanceIdentities, + @NonNull Runnable onFailureListener) { synchronized (mLock) { String methodStr = "addAccessPoint"; if (mIHostapd == null) { return handleNullIHostapd(methodStr); } - return mIHostapd.addAccessPoint(ifaceName, config, isMetered, onFailureListener); + return mIHostapd.addAccessPoint(ifaceName, config, isMetered, isUsingMultiLinkOperation, + instanceIdentities, onFailureListener); } } @@ -288,6 +292,23 @@ public class HostapdHal { } } + /** + * See comments for + * {@link IHostapdHal#removeLinkFromMultipleLinkBridgedApIface(String, String)}. + */ + public void removeLinkFromMultipleLinkBridgedApIface(@NonNull String ifaceName, + @NonNull String apIfaceInstance) { + synchronized (mLock) { + String methodStr = "removeLinkFromMultipleLinkBridgedApIface"; + if (mIHostapd == null) { + handleNullIHostapd(methodStr); + return; + } + mIHostapd.removeLinkFromMultipleLinkBridgedApIface( + ifaceName, apIfaceInstance); + } + } + private boolean handleNullIHostapd(String methodStr) { Log.e(TAG, "Cannot call " + methodStr + " because mIHostapd is null."); return false; diff --git a/service/java/com/android/server/wifi/HostapdHalAidlImp.java b/service/java/com/android/server/wifi/HostapdHalAidlImp.java index c689998078..d73880f973 100644 --- a/service/java/com/android/server/wifi/HostapdHalAidlImp.java +++ b/service/java/com/android/server/wifi/HostapdHalAidlImp.java @@ -16,6 +16,7 @@ package com.android.server.wifi; import android.annotation.NonNull; +import android.annotation.SuppressLint; import android.hardware.wifi.hostapd.ApInfo; import android.hardware.wifi.hostapd.BandMask; import android.hardware.wifi.hostapd.ChannelBandwidth; @@ -32,6 +33,7 @@ import android.hardware.wifi.hostapd.Ieee80211ReasonCode; import android.hardware.wifi.hostapd.IfaceParams; import android.hardware.wifi.hostapd.NetworkParams; import android.net.MacAddress; +import android.net.wifi.DeauthenticationReasonCode; import android.net.wifi.OuiKeyedData; import android.net.wifi.ScanResult; import android.net.wifi.SoftApConfiguration; @@ -40,6 +42,7 @@ import android.net.wifi.SoftApInfo; import android.net.wifi.WifiAnnotations; import android.net.wifi.WifiContext; import android.net.wifi.WifiManager; +import android.net.wifi.util.Environment; import android.net.wifi.util.WifiResourceCache; import android.os.Handler; import android.os.IBinder; @@ -56,6 +59,7 @@ import com.android.server.wifi.WifiNative.SoftApHalCallback; import com.android.server.wifi.util.ApConfigUtil; import com.android.server.wifi.util.HalAidlUtil; import com.android.server.wifi.util.NativeUtil; +import com.android.wifi.flags.Flags; import com.android.wifi.resources.R; import java.io.PrintWriter; @@ -232,7 +236,8 @@ public class HostapdHalAidlImp implements IHostapdHal { */ @Override public boolean addAccessPoint(@NonNull String ifaceName, @NonNull SoftApConfiguration config, - boolean isMetered, Runnable onFailureListener) { + boolean isMetered, boolean isUsingMultiLinkOperation, List<String> instanceIdentities, + Runnable onFailureListener) { synchronized (mLock) { final String methodStr = "addAccessPoint"; Log.d(TAG, methodStr + ": " + ifaceName); @@ -240,7 +245,8 @@ public class HostapdHalAidlImp implements IHostapdHal { return false; } try { - IfaceParams ifaceParams = prepareIfaceParams(ifaceName, config); + IfaceParams ifaceParams = prepareIfaceParams(ifaceName, config, + isUsingMultiLinkOperation, instanceIdentities); NetworkParams nwParams = prepareNetworkParams(isMetered, config); if (ifaceParams == null || nwParams == null) { Log.e(TAG, "addAccessPoint parameters could not be prepared."); @@ -416,7 +422,10 @@ public class HostapdHalAidlImp implements IHostapdHal { callback.onInfoChanged(info.apIfaceInstance, info.freqMhz, mapHalChannelBandwidthToSoftApInfo(info.channelBandwidth), mapHalGenerationToWifiStandard(info.generation), - MacAddress.fromBytes(info.apIfaceInstanceMacAddress), vendorData); + MacAddress.fromBytes(info.apIfaceInstanceMacAddress), + (Flags.mloSap() && info.mldMacAddress != null) + ? MacAddress.fromBytes(info.mldMacAddress) : null, + vendorData); } mActiveInstances.add(info.apIfaceInstance); } catch (IllegalArgumentException iae) { @@ -433,8 +442,12 @@ public class HostapdHalAidlImp implements IHostapdHal { + " isConnected: " + info.isConnected); SoftApHalCallback callback = mSoftApHalCallbacks.get(info.ifaceName); if (callback != null) { + int disconnectReasonCode = isServiceVersionAtLeast(3) && !info.isConnected + ? mapHalToFrameworkDeauthenticationReasonCode(info.disconnectReasonCode) + : DeauthenticationReasonCode.REASON_UNKNOWN; callback.onConnectedClientsChanged(info.apIfaceInstance, - MacAddress.fromBytes(info.clientAddress), info.isConnected); + MacAddress.fromBytes(info.clientAddress), info.isConnected, + disconnectReasonCode); } } catch (IllegalArgumentException iae) { Log.e(TAG, " Invalid clientAddress, " + iae); @@ -856,6 +869,176 @@ public class HostapdHalAidlImp implements IHostapdHal { } } + /** + * Convert from a HAL DeauthenticationReasonCode to its framework equivalent. + * + * @param deauthenticationReasonCode The deauthentication reason code defined in HAL. + * @return The corresponding {@link DeauthenticationReasonCode}. + */ + @VisibleForTesting + @WifiAnnotations.SoftApDisconnectReason int mapHalToFrameworkDeauthenticationReasonCode( + int deauthenticationReasonCode) { + return switch (deauthenticationReasonCode) { + case android.hardware.wifi.common.DeauthenticationReasonCode.HOSTAPD_NO_REASON -> + DeauthenticationReasonCode.REASON_UNKNOWN; + case android.hardware.wifi.common.DeauthenticationReasonCode.UNSPECIFIED -> + DeauthenticationReasonCode.REASON_UNSPECIFIED; + case android.hardware.wifi.common.DeauthenticationReasonCode.PREV_AUTH_NOT_VALID -> + DeauthenticationReasonCode.REASON_PREV_AUTH_NOT_VALID; + case android.hardware.wifi.common.DeauthenticationReasonCode.DEAUTH_LEAVING -> + DeauthenticationReasonCode.REASON_DEAUTH_LEAVING; + case android.hardware.wifi.common + .DeauthenticationReasonCode.DISASSOC_DUE_TO_INACTIVITY -> + DeauthenticationReasonCode.REASON_DISASSOC_DUE_TO_INACTIVITY; + case android.hardware.wifi.common.DeauthenticationReasonCode.DISASSOC_AP_BUSY -> + DeauthenticationReasonCode.REASON_DISASSOC_AP_BUSY; + case android.hardware.wifi.common + .DeauthenticationReasonCode.CLASS2_FRAME_FROM_NONAUTH_STA -> + DeauthenticationReasonCode.REASON_CLASS2_FRAME_FROM_NONAUTH_STA; + case android.hardware.wifi.common + .DeauthenticationReasonCode.CLASS3_FRAME_FROM_NONASSOC_STA -> + DeauthenticationReasonCode.REASON_CLASS3_FRAME_FROM_NONASSOC_STA; + case android.hardware.wifi.common.DeauthenticationReasonCode.DISASSOC_STA_HAS_LEFT -> + DeauthenticationReasonCode.REASON_DISASSOC_STA_HAS_LEFT; + case android.hardware.wifi.common + .DeauthenticationReasonCode.STA_REQ_ASSOC_WITHOUT_AUTH -> + DeauthenticationReasonCode.REASON_STA_REQ_ASSOC_WITHOUT_AUTH; + case android.hardware.wifi.common + .DeauthenticationReasonCode.PWR_CAPABILITY_NOT_VALID -> + DeauthenticationReasonCode.REASON_PWR_CAPABILITY_NOT_VALID; + case android.hardware.wifi.common + .DeauthenticationReasonCode.SUPPORTED_CHANNEL_NOT_VALID -> + DeauthenticationReasonCode.REASON_SUPPORTED_CHANNEL_NOT_VALID; + case android.hardware.wifi.common.DeauthenticationReasonCode.BSS_TRANSITION_DISASSOC -> + DeauthenticationReasonCode.REASON_BSS_TRANSITION_DISASSOC; + case android.hardware.wifi.common.DeauthenticationReasonCode.INVALID_IE -> + DeauthenticationReasonCode.REASON_INVALID_IE; + case android.hardware.wifi.common.DeauthenticationReasonCode.MICHAEL_MIC_FAILURE -> + DeauthenticationReasonCode.REASON_MICHAEL_MIC_FAILURE; + case android.hardware.wifi.common + .DeauthenticationReasonCode.FOURWAY_HANDSHAKE_TIMEOUT -> + DeauthenticationReasonCode.REASON_FOURWAY_HANDSHAKE_TIMEOUT; + case android.hardware.wifi.common + .DeauthenticationReasonCode.GROUP_KEY_UPDATE_TIMEOUT -> + DeauthenticationReasonCode.REASON_GROUP_KEY_UPDATE_TIMEOUT; + case android.hardware.wifi.common.DeauthenticationReasonCode.IE_IN_4WAY_DIFFERS -> + DeauthenticationReasonCode.REASON_IE_IN_4WAY_DIFFERS; + case android.hardware.wifi.common.DeauthenticationReasonCode.GROUP_CIPHER_NOT_VALID -> + DeauthenticationReasonCode.REASON_GROUP_CIPHER_NOT_VALID; + case android.hardware.wifi.common + .DeauthenticationReasonCode.PAIRWISE_CIPHER_NOT_VALID -> + DeauthenticationReasonCode.REASON_PAIRWISE_CIPHER_NOT_VALID; + case android.hardware.wifi.common.DeauthenticationReasonCode.AKMP_NOT_VALID -> + DeauthenticationReasonCode.REASON_AKMP_NOT_VALID; + case android.hardware.wifi.common + .DeauthenticationReasonCode.UNSUPPORTED_RSN_IE_VERSION -> + DeauthenticationReasonCode.REASON_UNSUPPORTED_RSN_IE_VERSION; + case android.hardware.wifi.common.DeauthenticationReasonCode.INVALID_RSN_IE_CAPAB -> + DeauthenticationReasonCode.REASON_INVALID_RSN_IE_CAPAB; + case android.hardware.wifi.common.DeauthenticationReasonCode.IEEE_802_1X_AUTH_FAILED -> + DeauthenticationReasonCode.REASON_IEEE_802_1X_AUTH_FAILED; + case android.hardware.wifi.common.DeauthenticationReasonCode.CIPHER_SUITE_REJECTED -> + DeauthenticationReasonCode.REASON_CIPHER_SUITE_REJECTED; + case android.hardware.wifi.common + .DeauthenticationReasonCode.TDLS_TEARDOWN_UNREACHABLE -> + DeauthenticationReasonCode.REASON_TDLS_TEARDOWN_UNREACHABLE; + case android.hardware.wifi.common + .DeauthenticationReasonCode.TDLS_TEARDOWN_UNSPECIFIED -> + DeauthenticationReasonCode.REASON_TDLS_TEARDOWN_UNSPECIFIED; + case android.hardware.wifi.common.DeauthenticationReasonCode.SSP_REQUESTED_DISASSOC -> + DeauthenticationReasonCode.REASON_SSP_REQUESTED_DISASSOC; + case android.hardware.wifi.common + .DeauthenticationReasonCode.NO_SSP_ROAMING_AGREEMENT -> + DeauthenticationReasonCode.REASON_NO_SSP_ROAMING_AGREEMENT; + case android.hardware.wifi.common.DeauthenticationReasonCode.BAD_CIPHER_OR_AKM -> + DeauthenticationReasonCode.REASON_BAD_CIPHER_OR_AKM; + case android.hardware.wifi.common + .DeauthenticationReasonCode.NOT_AUTHORIZED_THIS_LOCATION -> + DeauthenticationReasonCode.REASON_NOT_AUTHORIZED_THIS_LOCATION; + case android.hardware.wifi.common + .DeauthenticationReasonCode.SERVICE_CHANGE_PRECLUDES_TS -> + DeauthenticationReasonCode.REASON_SERVICE_CHANGE_PRECLUDES_TS; + case android.hardware.wifi.common.DeauthenticationReasonCode.UNSPECIFIED_QOS_REASON -> + DeauthenticationReasonCode.REASON_UNSPECIFIED_QOS_REASON; + case android.hardware.wifi.common.DeauthenticationReasonCode.NOT_ENOUGH_BANDWIDTH -> + DeauthenticationReasonCode.REASON_NOT_ENOUGH_BANDWIDTH; + case android.hardware.wifi.common.DeauthenticationReasonCode.DISASSOC_LOW_ACK -> + DeauthenticationReasonCode.REASON_DISASSOC_LOW_ACK; + case android.hardware.wifi.common.DeauthenticationReasonCode.EXCEEDED_TXOP -> + DeauthenticationReasonCode.REASON_EXCEEDED_TXOP; + case android.hardware.wifi.common.DeauthenticationReasonCode.STA_LEAVING -> + DeauthenticationReasonCode.REASON_STA_LEAVING; + case android.hardware.wifi.common.DeauthenticationReasonCode.END_TS_BA_DLS -> + DeauthenticationReasonCode.REASON_END_TS_BA_DLS; + case android.hardware.wifi.common.DeauthenticationReasonCode.UNKNOWN_TS_BA -> + DeauthenticationReasonCode.REASON_UNKNOWN_TS_BA; + case android.hardware.wifi.common.DeauthenticationReasonCode.TIMEOUT -> + DeauthenticationReasonCode.REASON_TIMEOUT; + case android.hardware.wifi.common.DeauthenticationReasonCode.PEERKEY_MISMATCH -> + DeauthenticationReasonCode.REASON_PEERKEY_MISMATCH; + case android.hardware.wifi.common + .DeauthenticationReasonCode.AUTHORIZED_ACCESS_LIMIT_REACHED -> + DeauthenticationReasonCode.REASON_AUTHORIZED_ACCESS_LIMIT_REACHED; + case android.hardware.wifi.common + .DeauthenticationReasonCode.EXTERNAL_SERVICE_REQUIREMENTS -> + DeauthenticationReasonCode.REASON_EXTERNAL_SERVICE_REQUIREMENTS; + case android.hardware.wifi.common + .DeauthenticationReasonCode.INVALID_FT_ACTION_FRAME_COUNT -> + DeauthenticationReasonCode.REASON_INVALID_FT_ACTION_FRAME_COUNT; + case android.hardware.wifi.common.DeauthenticationReasonCode.INVALID_PMKID -> + DeauthenticationReasonCode.REASON_INVALID_PMKID; + case android.hardware.wifi.common.DeauthenticationReasonCode.INVALID_MDE -> + DeauthenticationReasonCode.REASON_INVALID_MDE; + case android.hardware.wifi.common.DeauthenticationReasonCode.INVALID_FTE -> + DeauthenticationReasonCode.REASON_INVALID_FTE; + case android.hardware.wifi.common.DeauthenticationReasonCode.MESH_PEERING_CANCELLED -> + DeauthenticationReasonCode.REASON_MESH_PEERING_CANCELLED; + case android.hardware.wifi.common.DeauthenticationReasonCode.MESH_MAX_PEERS -> + DeauthenticationReasonCode.REASON_MESH_MAX_PEERS; + case android.hardware.wifi.common + .DeauthenticationReasonCode.MESH_CONFIG_POLICY_VIOLATION -> + DeauthenticationReasonCode.REASON_MESH_CONFIG_POLICY_VIOLATION; + case android.hardware.wifi.common.DeauthenticationReasonCode.MESH_CLOSE_RCVD -> + DeauthenticationReasonCode.REASON_MESH_CLOSE_RCVD; + case android.hardware.wifi.common.DeauthenticationReasonCode.MESH_MAX_RETRIES -> + DeauthenticationReasonCode.REASON_MESH_MAX_RETRIES; + case android.hardware.wifi.common.DeauthenticationReasonCode.MESH_CONFIRM_TIMEOUT -> + DeauthenticationReasonCode.REASON_MESH_CONFIRM_TIMEOUT; + case android.hardware.wifi.common.DeauthenticationReasonCode.MESH_INVALID_GTK -> + DeauthenticationReasonCode.REASON_MESH_INVALID_GTK; + case android.hardware.wifi.common + .DeauthenticationReasonCode.MESH_INCONSISTENT_PARAMS -> + DeauthenticationReasonCode.REASON_MESH_INCONSISTENT_PARAMS; + case android.hardware.wifi.common + .DeauthenticationReasonCode.MESH_INVALID_SECURITY_CAP -> + DeauthenticationReasonCode.REASON_MESH_INVALID_SECURITY_CAP; + case android.hardware.wifi.common + .DeauthenticationReasonCode.MESH_PATH_ERROR_NO_PROXY_INFO -> + DeauthenticationReasonCode.REASON_MESH_PATH_ERROR_NO_PROXY_INFO; + case android.hardware.wifi.common + .DeauthenticationReasonCode.MESH_PATH_ERROR_NO_FORWARDING_INFO -> + DeauthenticationReasonCode.REASON_MESH_PATH_ERROR_NO_FORWARDING_INFO; + case android.hardware.wifi.common + .DeauthenticationReasonCode.MESH_PATH_ERROR_DEST_UNREACHABLE -> + DeauthenticationReasonCode.REASON_MESH_PATH_ERROR_DEST_UNREACHABLE; + case android.hardware.wifi.common + .DeauthenticationReasonCode.MAC_ADDRESS_ALREADY_EXISTS_IN_MBSS -> + DeauthenticationReasonCode.REASON_MAC_ADDRESS_ALREADY_EXISTS_IN_MBSS; + case android.hardware.wifi.common + .DeauthenticationReasonCode.MESH_CHANNEL_SWITCH_REGULATORY_REQ -> + DeauthenticationReasonCode.REASON_MESH_CHANNEL_SWITCH_REGULATORY_REQ; + case android.hardware.wifi.common + .DeauthenticationReasonCode.MESH_CHANNEL_SWITCH_UNSPECIFIED -> + DeauthenticationReasonCode.REASON_MESH_CHANNEL_SWITCH_UNSPECIFIED; + default -> { + Log.e(TAG, "Invalid DeauthenticationReasonCode: " + + deauthenticationReasonCode); + yield DeauthenticationReasonCode.REASON_UNKNOWN; + } + }; + } + + @SuppressLint("NewApi") private NetworkParams prepareNetworkParams(boolean isMetered, SoftApConfiguration config) { NetworkParams nwParams = new NetworkParams(); @@ -885,6 +1068,9 @@ public class HostapdHalAidlImp implements IHostapdHal { nwParams.encryptionType = getEncryptionType(config); nwParams.passphrase = (config.getPassphrase() != null) ? config.getPassphrase() : ""; + if (Flags.apIsolate() && isServiceVersionAtLeast(3) && Environment.isSdkAtLeastB()) { + nwParams.isClientIsolationEnabled = config.isClientIsolationEnabled(); + } if (nwParams.ssid == null || nwParams.passphrase == null) { return null; @@ -892,12 +1078,18 @@ public class HostapdHalAidlImp implements IHostapdHal { return nwParams; } - private IfaceParams prepareIfaceParams(String ifaceName, SoftApConfiguration config) + private IfaceParams prepareIfaceParams(String ifaceName, SoftApConfiguration config, + boolean isUsingMultiLinkOperation, List<String> instanceIdentities) throws IllegalArgumentException { IfaceParams ifaceParams = new IfaceParams(); ifaceParams.name = ifaceName; ifaceParams.hwModeParams = prepareHwModeParams(config); ifaceParams.channelParams = prepareChannelParamsList(config); + ifaceParams.usesMlo = isUsingMultiLinkOperation; + if (instanceIdentities != null) { + ifaceParams.instanceIdentities = + instanceIdentities.toArray(new String[instanceIdentities.size()]); + } if (ifaceParams.name == null || ifaceParams.hwModeParams == null || ifaceParams.channelParams == null) { return null; @@ -1005,9 +1197,33 @@ public class HostapdHalAidlImp implements IHostapdHal { /** * Dump information about the AIDL implementation. * - * TODO (b/202302891) Log version information once we freeze the AIDL interface */ public void dump(PrintWriter pw) { - pw.println("AIDL interface version: 1 (initial)"); + pw.println("AIDL interface version: " + mServiceVersion); + } + + /** + * See comments for + * {@link IHostapdHal#removeLinkFromMultipleLinkBridgedApIface(String, String)}. + */ + public void removeLinkFromMultipleLinkBridgedApIface(@NonNull String ifaceName, + @NonNull String apIfaceInstance) { + if (!isServiceVersionAtLeast(3)) { + return; + } + synchronized (mLock) { + final String methodStr = "removeLinkFromMultipleLinkBridgedApIface"; + if (!checkHostapdAndLogFailure(methodStr)) { + return; + } + Log.i(TAG, "Remove link: " + apIfaceInstance + " from AP iface: " + ifaceName); + try { + mIHostapd.removeLinkFromMultipleLinkBridgedApIface(ifaceName, apIfaceInstance); + } catch (RemoteException e) { + handleRemoteException(e, methodStr); + } catch (ServiceSpecificException e) { + handleServiceSpecificException(e, methodStr); + } + } } } diff --git a/service/java/com/android/server/wifi/HostapdHalHidlImp.java b/service/java/com/android/server/wifi/HostapdHalHidlImp.java index ce74efe1fd..4e675fdc45 100644 --- a/service/java/com/android/server/wifi/HostapdHalHidlImp.java +++ b/service/java/com/android/server/wifi/HostapdHalHidlImp.java @@ -26,6 +26,7 @@ import android.hardware.wifi.hostapd.V1_3.Generation; import android.hidl.manager.V1_0.IServiceManager; import android.hidl.manager.V1_0.IServiceNotification; import android.net.MacAddress; +import android.net.wifi.DeauthenticationReasonCode; import android.net.wifi.ScanResult; import android.net.wifi.SoftApConfiguration; import android.net.wifi.SoftApConfiguration.BandType; @@ -437,7 +438,9 @@ public class HostapdHalHidlImp implements IHostapdHal { */ @Override public boolean addAccessPoint(@NonNull String ifaceName, @NonNull SoftApConfiguration config, - boolean isMetered, @NonNull Runnable onFailureListener) { + boolean isMetered, boolean isUsingMultiLinkOperation, + @NonNull List<String> instanceIdentities, + @NonNull Runnable onFailureListener) { synchronized (mLock) { final String methodStr = "addAccessPoint"; IHostapd.IfaceParams ifaceParamsV1_0 = prepareIfaceParamsV1_0(ifaceName, config); @@ -1282,6 +1285,7 @@ public class HostapdHalHidlImp implements IHostapdHal { mapHalBandwidthToSoftApInfo(bandwidth), mapHalGenerationToWifiStandard(generation), MacAddress.fromBytes(apIfaceInstanceMacAddress), + null, Collections.emptyList()); } } catch (IllegalArgumentException iae) { @@ -1299,7 +1303,8 @@ public class HostapdHalHidlImp implements IHostapdHal { SoftApHalCallback callback = mSoftApHalCallbacks.get(ifaceName); if (callback != null) { callback.onConnectedClientsChanged(apIfaceInstance, - MacAddress.fromBytes(clientAddress), isConnected); + MacAddress.fromBytes(clientAddress), isConnected, + DeauthenticationReasonCode.REASON_UNKNOWN); } } catch (IllegalArgumentException iae) { Log.e(TAG, " Invalid clientAddress, " + iae); diff --git a/service/java/com/android/server/wifi/IHostapdHal.java b/service/java/com/android/server/wifi/IHostapdHal.java index a93cff2610..20879fd3fb 100644 --- a/service/java/com/android/server/wifi/IHostapdHal.java +++ b/service/java/com/android/server/wifi/IHostapdHal.java @@ -23,6 +23,7 @@ import com.android.server.wifi.WifiNative.HostapdDeathEventHandler; import com.android.server.wifi.WifiNative.SoftApHalCallback; import java.io.PrintWriter; +import java.util.List; /** Abstraction of HAL interface */ interface IHostapdHal { @@ -53,11 +54,13 @@ interface IHostapdHal { * @param ifaceName Name of the interface. * @param config Configuration to use for the AP. * @param isMetered Indicates the network is metered or not. Ignored in AIDL imp. + * @param isUsingMultiLinkOperation Indicate the AP is using MLO or not. * @param onFailureListener A runnable to be triggered on failure. * @return true on success, false otherwise. */ boolean addAccessPoint(@NonNull String ifaceName, @NonNull SoftApConfiguration config, boolean isMetered, + boolean isUsingMultiLinkOperation, @NonNull List<String> instanceIdentities, Runnable onFailureListener); /** @@ -128,4 +131,15 @@ interface IHostapdHal { * Dump information about the specific implementation. */ void dump(PrintWriter pw); + + /** + * Removes an existing link from multiple link device which the current AP resides on. + * Note: It is being implemented for AIDL interface only. + * + * @param ifaceName Name of the iface. + * @param apIfaceInstance The identity of the link which associated to the multiple link device + * that the current AP resides on. + */ + default void removeLinkFromMultipleLinkBridgedApIface(@NonNull String ifaceName, + @NonNull String apIfaceInstance) {}; } diff --git a/service/java/com/android/server/wifi/ISupplicantStaIfaceHal.java b/service/java/com/android/server/wifi/ISupplicantStaIfaceHal.java index 96e703dfcb..fcd4c9d52c 100644 --- a/service/java/com/android/server/wifi/ISupplicantStaIfaceHal.java +++ b/service/java/com/android/server/wifi/ISupplicantStaIfaceHal.java @@ -18,11 +18,16 @@ package com.android.server.wifi; import android.annotation.NonNull; +import android.annotation.Nullable; import android.net.MacAddress; import android.net.wifi.MscsParams; import android.net.wifi.QosPolicyParams; import android.net.wifi.SecurityParams; import android.net.wifi.WifiConfiguration; +import android.net.wifi.usd.PublishConfig; +import android.net.wifi.usd.SubscribeConfig; + +import com.android.server.wifi.usd.UsdRequestManager; import java.util.ArrayList; import java.util.BitSet; @@ -853,4 +858,96 @@ interface ISupplicantStaIfaceHal { * @param ifaceName Name of the interface. */ default void disableMscs(String ifaceName) {} + + /** + * Returns true if this device supports RSN Overriding, false otherwise. + */ + default boolean isRsnOverridingSupported(@NonNull String ifaceName) { + return false; + } + + /** + * Returns USD capabilities for the interface. + * + * @param ifaceName Name of the interface. + * @return UsdCapabilities if available, otherwise null. + */ + @Nullable + default SupplicantStaIfaceHal.UsdCapabilitiesInternal getUsdCapabilities(String ifaceName) { + return null; + } + + /** + * Starts USD publish operation. + * + * @param interfaceName Name of the interface. + * @param cmdId An id for this command. + * @param publishConfig Publish configuration. + * @return true if succeeds otherwise false. + */ + default boolean startUsdPublish(String interfaceName, int cmdId, PublishConfig publishConfig) { + return false; + } + + /** + * Registers USD event callbacks. + * + * @param usdEventsCallback event callbacks which need to be registered. + */ + default void registerUsdEventsCallback( + UsdRequestManager.UsdNativeEventsCallback usdEventsCallback) {} + + /** + * Starts USD subscribe operation. + * + * @param interfaceName Name of the interface. + * @param cmdId An id for this command. + * @param subscribeConfig Subscribe configuration. + * @return true if succeeds otherwise false. + */ + default boolean startUsdSubscribe(String interfaceName, int cmdId, + SubscribeConfig subscribeConfig) { + return false; + } + + /** + * Updates an ongoing USD publish operation. + * + * @param interfaceName Name of the interface. + * @param publishId publish id for this session. + * @param ssi Service specific info. + */ + default void updateUsdPublish(String interfaceName, int publishId, byte[] ssi) {} + + /** + * Cancels an ongoing USD publish. + * + * @param interfaceName Name of the interface. + * @param publishId publish id for the session. + */ + default void cancelUsdPublish(String interfaceName, int publishId) {} + + /** + * Cancels an ongoing USD subscribe. + * + * @param interfaceName Name of the interface. + * @param subscribeId Subscribe id for the session. + */ + default void cancelUsdSubscribe(String interfaceName, int subscribeId) {} + + /** + * Sends message on a USD publish/subscribe session. + * + * @param interfaceName Name of the interface. + * @param ownId Id for the session. + * @param peerId Id for the peer session. + * @param peerMacAddress Mac address of the peer session. + * @param message Bytes of data to send. + * @return true if succeeds otherwise false. + */ + default boolean sendUsdMessage(String interfaceName, int ownId, int peerId, + MacAddress peerMacAddress, + byte[] message) { + return false; + } } diff --git a/service/java/com/android/server/wifi/MboOceController.java b/service/java/com/android/server/wifi/MboOceController.java index 2b5e9102f2..6301d66233 100644 --- a/service/java/com/android/server/wifi/MboOceController.java +++ b/service/java/com/android/server/wifi/MboOceController.java @@ -25,6 +25,8 @@ import android.util.Log; import com.android.server.wifi.SupplicantStaIfaceHal.MboAssocDisallowedReasonCode; +import java.util.BitSet; + /** * MboOceController is responsible for controlling MBO and OCE operations. */ @@ -60,9 +62,9 @@ public class MboOceController { if (clientModeManager == null) { return; } - long supportedFeatures = clientModeManager.getSupportedFeatures(); - mIsMboSupported = (supportedFeatures & WIFI_FEATURE_MBO) != 0; - mIsOceSupported = (supportedFeatures & WIFI_FEATURE_OCE) != 0; + BitSet supportedFeatures = clientModeManager.getSupportedFeaturesBitSet(); + mIsMboSupported = supportedFeatures.get(WIFI_FEATURE_MBO); + mIsOceSupported = supportedFeatures.get(WIFI_FEATURE_OCE); mEnabled = true; if (mVerboseLoggingEnabled) { Log.d(TAG, "Enable MBO-OCE MBO support: " + mIsMboSupported diff --git a/service/java/com/android/server/wifi/MultiInternetWifiNetworkFactory.java b/service/java/com/android/server/wifi/MultiInternetWifiNetworkFactory.java index cb149b7bc5..88aa3c6e29 100644 --- a/service/java/com/android/server/wifi/MultiInternetWifiNetworkFactory.java +++ b/service/java/com/android/server/wifi/MultiInternetWifiNetworkFactory.java @@ -182,7 +182,8 @@ public class MultiInternetWifiNetworkFactory extends NetworkFactory { NetworkRequestState nrs = new NetworkRequestState(networkRequest, new WifiNetworkSpecifier( wns.ssidPatternMatcher, wns.bssidPatternMatcher, wns.getBand(), - wns.wifiConfiguration, wns.getPreferredChannelFrequenciesMhz()), + wns.wifiConfiguration, wns.getPreferredChannelFrequenciesMhz(), + wns.isPreferSecondarySta()), isFromSetting, isFromForegroundApp, isFromForegroundAppOrService); diff --git a/service/java/com/android/server/wifi/NetworkUpdateResult.java b/service/java/com/android/server/wifi/NetworkUpdateResult.java index 78ee193ecb..0dec54577a 100644 --- a/service/java/com/android/server/wifi/NetworkUpdateResult.java +++ b/service/java/com/android/server/wifi/NetworkUpdateResult.java @@ -22,6 +22,8 @@ import static android.net.wifi.WifiManager.AddNetworkResult.STATUS_SUCCESS; import android.net.wifi.WifiManager; +import androidx.annotation.Keep; + import java.util.Objects; public class NetworkUpdateResult { @@ -32,6 +34,7 @@ public class NetworkUpdateResult { private final boolean mIsNewNetwork; private final @WifiManager.AddNetworkResult.AddNetworkStatusCode int mStatusCode; + @Keep public NetworkUpdateResult(int netId) { this(netId, netId != INVALID_NETWORK_ID ? STATUS_SUCCESS : STATUS_ADD_WIFI_CONFIG_FAILURE, false, false, false, false); @@ -61,6 +64,7 @@ public class NetworkUpdateResult { return new NetworkUpdateResult(INVALID_NETWORK_ID); } + @Keep public int getNetworkId() { return mNetId; } @@ -81,6 +85,7 @@ public class NetworkUpdateResult { return mIsNewNetwork; } + @Keep public boolean isSuccess() { return mStatusCode == STATUS_SUCCESS; } diff --git a/service/java/com/android/server/wifi/RunnerHandler.java b/service/java/com/android/server/wifi/RunnerHandler.java index 78b7b12a4f..0329031014 100644 --- a/service/java/com/android/server/wifi/RunnerHandler.java +++ b/service/java/com/android/server/wifi/RunnerHandler.java @@ -52,6 +52,7 @@ public class RunnerHandler extends Handler { // TODO: b/246623192 Add Wifi metric for Runner state overruns. private final LocalLog mLocalLog; + private boolean mVerboseLoggingEnabled = false; /** * The Runner handler Constructor @@ -73,6 +74,11 @@ public class RunnerHandler extends Handler { mIgnoredMethods.add("handleMessage"); } + /** Enable/disable verbose logging. */ + public void enableVerboseLogging(boolean verboseEnabled) { + mVerboseLoggingEnabled = verboseEnabled; + } + private String getSignature(StackTraceElement[] elements, Runnable callback) { StringBuilder sb = new StringBuilder(); for (StackTraceElement e : elements) { @@ -114,7 +120,8 @@ public class RunnerHandler extends Handler { public void dispatchMessage(@NonNull Message msg) { final Bundle bundle = msg.getData(); final String signature = bundle.getString(KEY_SIGNATURE); - if (signature != null) { + boolean traceEvent = mVerboseLoggingEnabled; + if (signature != null && traceEvent) { Trace.traceBegin(Trace.TRACE_TAG_NETWORK, signature); } // The message sent to front of the queue has when=0, get from the bundle in that case. @@ -122,10 +129,10 @@ public class RunnerHandler extends Handler { final long start = SystemClock.uptimeMillis(); final long scheduleLatency = start - when; super.dispatchMessage(msg); - if (signature != null) { + final long runTime = SystemClock.uptimeMillis() - start; + if (signature != null && traceEvent) { Trace.traceEnd(Trace.TRACE_TAG_NETWORK); } - final long runTime = SystemClock.uptimeMillis() - start; final String signatureToLog = signature != null ? signature : "unknown"; if (runTime > mRunningTimeThresholdInMilliseconds) { mLocalLog.log(signatureToLog + " was running for " + runTime); diff --git a/service/java/com/android/server/wifi/RunnerState.java b/service/java/com/android/server/wifi/RunnerState.java index e6a8573826..4ec5b62d9c 100644 --- a/service/java/com/android/server/wifi/RunnerState.java +++ b/service/java/com/android/server/wifi/RunnerState.java @@ -20,6 +20,7 @@ import static com.android.server.wifi.proto.WifiStatsLog.WIFI_THREAD_TASK_EXECUT import android.annotation.NonNull; import android.os.Message; +import android.os.SystemClock; import android.os.Trace; import android.util.LocalLog; @@ -48,6 +49,7 @@ public abstract class RunnerState extends State { private final int mRunningTimeThresholdInMilliseconds; // TODO: b/246623192 Add Wifi metric for Runner state overruns. private final LocalLog mLocalLog; + private final WifiInjector mWifiInjector; /** * The Runner state Constructor @@ -56,37 +58,55 @@ public abstract class RunnerState extends State { public RunnerState(int threshold, @NonNull LocalLog localLog) { mRunningTimeThresholdInMilliseconds = threshold; mLocalLog = localLog; + mWifiInjector = WifiInjector.getInstance(); + } + + private boolean isVerboseLoggingEnabled() { + return mWifiInjector.isVerboseLoggingEnabled(); } @Override public boolean processMessage(Message message) { - long startTime = System.currentTimeMillis(); - String signatureToLog = getMessageLogRec(message); if (signatureToLog == null) { signatureToLog = getMessageLogRec(message.what); } - Trace.traceBegin(Trace.TRACE_TAG_NETWORK, signatureToLog); + boolean traceEvent = isVerboseLoggingEnabled(); + if (traceEvent) { + Trace.traceBegin(Trace.TRACE_TAG_NETWORK, signatureToLog); + } + + long startTime = SystemClock.uptimeMillis(); + // TODO(b/295398783): Support deferMessage and sendMessageAtFrontOfQueue where when is 0; + long scheduleLatency = message.getWhen() != 0 ? startTime - message.getWhen() : 0; boolean ret = processMessageImpl(message); - Trace.traceEnd(Trace.TRACE_TAG_NETWORK); - long runTime = System.currentTimeMillis() - startTime; + long runTime = SystemClock.uptimeMillis() - startTime; + if (traceEvent) { + Trace.traceEnd(Trace.TRACE_TAG_NETWORK); + } if (runTime > mRunningTimeThresholdInMilliseconds) { mLocalLog.log(signatureToLog + " was running for " + runTime + " ms"); } - if (runTime > METRICS_THRESHOLD_MILLIS) { - WifiStatsLog.write(WIFI_THREAD_TASK_EXECUTED, (int) runTime, 0, signatureToLog); + if (runTime > METRICS_THRESHOLD_MILLIS || scheduleLatency > METRICS_THRESHOLD_MILLIS) { + WifiStatsLog.write(WIFI_THREAD_TASK_EXECUTED, (int) runTime, (int) scheduleLatency, + signatureToLog); } return ret; } @Override public void enter() { - long startTime = System.currentTimeMillis(); String signatureToLog = getMessageLogRec(STATE_ENTER_CMD); - Trace.traceBegin(Trace.TRACE_TAG_NETWORK, signatureToLog); + boolean traceEvent = isVerboseLoggingEnabled(); + if (traceEvent) { + Trace.traceBegin(Trace.TRACE_TAG_NETWORK, signatureToLog); + } + long startTime = SystemClock.uptimeMillis(); enterImpl(); - Trace.traceEnd(Trace.TRACE_TAG_NETWORK); - long runTime = System.currentTimeMillis() - startTime; + long runTime = SystemClock.uptimeMillis() - startTime; + if (traceEvent) { + Trace.traceEnd(Trace.TRACE_TAG_NETWORK); + } if (runTime > mRunningTimeThresholdInMilliseconds) { mLocalLog.log(signatureToLog + " was running for " + runTime + " ms"); } @@ -97,12 +117,18 @@ public abstract class RunnerState extends State { @Override public void exit() { - long startTime = System.currentTimeMillis(); String signatureToLog = getMessageLogRec(STATE_EXIT_CMD); - Trace.traceBegin(Trace.TRACE_TAG_NETWORK, signatureToLog); + boolean traceEvent = isVerboseLoggingEnabled(); + if (traceEvent) { + Trace.traceBegin(Trace.TRACE_TAG_NETWORK, signatureToLog); + } + long startTime = SystemClock.uptimeMillis(); exitImpl(); - Trace.traceEnd(Trace.TRACE_TAG_NETWORK); - long runTime = System.currentTimeMillis() - startTime; + long runTime = SystemClock.uptimeMillis() - startTime; + if (traceEvent) { + Trace.traceEnd(Trace.TRACE_TAG_NETWORK); + } + if (runTime > mRunningTimeThresholdInMilliseconds) { mLocalLog.log(signatureToLog + " was running for " + runTime + " ms"); } diff --git a/service/java/com/android/server/wifi/ScanDetail.java b/service/java/com/android/server/wifi/ScanDetail.java index 59eb16af85..2858d9a215 100644 --- a/service/java/com/android/server/wifi/ScanDetail.java +++ b/service/java/com/android/server/wifi/ScanDetail.java @@ -20,6 +20,7 @@ import android.net.wifi.AnqpInformationElement; import android.net.wifi.ScanResult; import android.net.wifi.WifiSsid; +import androidx.annotation.Keep; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -62,6 +63,8 @@ public class ScanDetail { boolean is80211McResponder = false; boolean isTwtResponder = false; boolean is11azNtbResponder = false; + boolean isSecureHeLtfSupported = false; + boolean isRangingFrameProtectionRequired = false; if (networkDetail != null) { hessid = networkDetail.getHESSID(); anqpDomainId = networkDetail.getAnqpDomainID(); @@ -77,6 +80,8 @@ public class ScanDetail { is80211McResponder = networkDetail.is80211McResponderSupport(); isTwtResponder = networkDetail.isIndividualTwtSupported(); is11azNtbResponder = networkDetail.is80211azNtbResponder(); + isSecureHeLtfSupported = networkDetail.isSecureHeLtfSupported(); + isRangingFrameProtectionRequired = networkDetail.isRangingFrameProtectionRequired(); } sBuilder.clear(); mScanResult = sBuilder @@ -91,6 +96,8 @@ public class ScanDetail { .setTsf(tsf) .setIsTwtResponder(isTwtResponder) .setIs80211azNtbRTTResponder(is11azNtbResponder) + .setSecureHeLtfSupported(isSecureHeLtfSupported) + .setRangingFrameProtectionRequired(isRangingFrameProtectionRequired) .build(); mSeen = System.currentTimeMillis(); mScanResult.seen = mSeen; @@ -177,6 +184,7 @@ public class ScanDetail { } } + @Keep public ScanResult getScanResult() { return mScanResult; } diff --git a/service/java/com/android/server/wifi/ScanDetailCache.java b/service/java/com/android/server/wifi/ScanDetailCache.java index 90dd4d060f..dcfb423ab5 100644 --- a/service/java/com/android/server/wifi/ScanDetailCache.java +++ b/service/java/com/android/server/wifi/ScanDetailCache.java @@ -20,6 +20,8 @@ import android.annotation.NonNull; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; +import androidx.annotation.Keep; + import com.android.server.wifi.hotspot2.NetworkDetail; import java.util.ArrayList; @@ -93,6 +95,7 @@ public class ScanDetailCache { mMap.remove(bssid); } + @Keep int size() { return mMap.size(); } @@ -105,6 +108,7 @@ public class ScanDetailCache { return mMap.keySet(); } + @Keep Collection<ScanDetail> values() { return mMap.values(); } diff --git a/service/java/com/android/server/wifi/ScanOnlyModeImpl.java b/service/java/com/android/server/wifi/ScanOnlyModeImpl.java index 6449b6a18d..c0b58e7b50 100644 --- a/service/java/com/android/server/wifi/ScanOnlyModeImpl.java +++ b/service/java/com/android/server/wifi/ScanOnlyModeImpl.java @@ -18,6 +18,8 @@ package com.android.server.wifi; import android.annotation.NonNull; +import java.util.BitSet; + /** * Used to respond to calls to ClientMode interface when ClientModeImpl is not up * i.e. in scan only mode. @@ -40,7 +42,7 @@ public class ScanOnlyModeImpl implements ClientModeDefaults { } @Override - public long getSupportedFeatures() { + public @NonNull BitSet getSupportedFeaturesBitSet() { return mWifiNative.getSupportedFeatureSet(mIfaceName); } diff --git a/service/java/com/android/server/wifi/ScanRequestProxy.java b/service/java/com/android/server/wifi/ScanRequestProxy.java index 38cf93767a..72609b08c4 100644 --- a/service/java/com/android/server/wifi/ScanRequestProxy.java +++ b/service/java/com/android/server/wifi/ScanRequestProxy.java @@ -41,6 +41,8 @@ import android.util.Log; import android.util.LruCache; import android.util.Pair; +import androidx.annotation.Keep; + import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.modules.utils.build.SdkLevel; @@ -569,6 +571,7 @@ public class ScanRequestProxy { * a list of {@link ScanResult} objects. * @return the list of results */ + @Keep public List<ScanResult> getScanResults() { // return a copy to prevent external modification return new ArrayList<>(combineScanResultsCache().values()); diff --git a/service/java/com/android/server/wifi/SoftApManager.java b/service/java/com/android/server/wifi/SoftApManager.java index 5afaf78c82..828b3114ad 100644 --- a/service/java/com/android/server/wifi/SoftApManager.java +++ b/service/java/com/android/server/wifi/SoftApManager.java @@ -66,8 +66,11 @@ import com.android.server.wifi.coex.CoexManager; import com.android.server.wifi.coex.CoexManager.CoexListener; import com.android.server.wifi.util.ApConfigUtil; import com.android.server.wifi.util.WaitingState; +import com.android.wifi.flags.Flags; import com.android.wifi.resources.R; +import com.google.common.collect.ImmutableList; + import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.annotation.Retention; @@ -217,6 +220,15 @@ public class SoftApManager implements ActiveModeManager { private boolean mVerboseLoggingEnabled = false; + // Whether this device supports multiple link operation in a single MLD. + private boolean mIsMLDApSupportMLO = false; + + // Whether this SoftApManager (i.e. this AP interface) is using multiple link operation. + private boolean mIsUsingMlo = false; + + private int mMaximumNumberOfMLDSupported; + private int mCurrentExistingMLD; + /** * The specified configuration passed in during initialization or during a configuration update * that doesn't require a restart. @@ -234,6 +246,10 @@ public class SoftApManager implements ActiveModeManager { */ @Nullable private SoftApConfiguration mCurrentSoftApConfiguration; + /** + * Whether the configuration being used is the user's persistent SoftApConfiguration. + */ + private boolean mIsUsingPersistentSoftApConfiguration = false; @NonNull private Map<String, SoftApInfo> mCurrentSoftApInfoMap = new HashMap<>(); @@ -250,7 +266,7 @@ public class SoftApManager implements ActiveModeManager { private final SarManager mSarManager; - private String mStartTimestamp; + private long mStartTimestampMs; private long mDefaultShutdownTimeoutMillis; @@ -284,6 +300,8 @@ public class SoftApManager implements ActiveModeManager { private boolean mIsSoftApStartedEventWritten = false; + private int mMaxConnectedClients = 0; + /** * A map stores shutdown timeouts for each Soft Ap instance. * There are three timeout messages now. @@ -319,7 +337,8 @@ public class SoftApManager implements ActiveModeManager { public void onInfoChanged(String apIfaceInstance, int frequency, @WifiAnnotations.Bandwidth int bandwidth, @WifiAnnotations.WifiStandard int generation, - MacAddress apIfaceInstanceMacAddress, + @Nullable MacAddress apIfaceInstanceMacAddress, + @Nullable MacAddress mldAddress, @NonNull List<OuiKeyedData> vendorData) { SoftApInfo apInfo = new SoftApInfo(); apInfo.setFrequency(frequency); @@ -328,6 +347,9 @@ public class SoftApManager implements ActiveModeManager { if (apIfaceInstanceMacAddress != null) { apInfo.setBssid(apIfaceInstanceMacAddress); } + if (mldAddress != null) { + apInfo.setMldAddress(mldAddress); + } apInfo.setApInstanceIdentifier(apIfaceInstance != null ? apIfaceInstance : mApInterfaceName); if (SdkLevel.isAtLeastV() && vendorData != null && !vendorData.isEmpty()) { @@ -339,10 +361,10 @@ public class SoftApManager implements ActiveModeManager { @Override public void onConnectedClientsChanged(String apIfaceInstance, MacAddress clientAddress, - boolean isConnected) { + boolean isConnected, @WifiAnnotations.SoftApDisconnectReason int disconnectReason) { if (clientAddress != null) { WifiClient client = new WifiClient(clientAddress, apIfaceInstance != null - ? apIfaceInstance : mApInterfaceName); + ? apIfaceInstance : mApInterfaceName, disconnectReason); mStateMachine.sendMessage(SoftApStateMachine.CMD_ASSOCIATED_STATIONS_CHANGED, isConnected ? 1 : 0, 0, client); } else { @@ -472,9 +494,11 @@ public class SoftApManager implements ActiveModeManager { mWifiApConfigStore = wifiApConfigStore; mCurrentSoftApConfiguration = apConfig.getSoftApConfiguration(); mCurrentSoftApCapability = apConfig.getCapability(); + // null is a valid input and means we use the user-configured tethering settings. if (mCurrentSoftApConfiguration == null) { mCurrentSoftApConfiguration = mWifiApConfigStore.getApConfiguration(); + mIsUsingPersistentSoftApConfiguration = true; // may still be null if we fail to load the default config } // Store mode configuration before update the configuration. @@ -512,6 +536,12 @@ public class SoftApManager implements ActiveModeManager { updateSafeChannelFrequencyList(); mId = id; mRole = role; + // chip support it && overlay configuration is set. + mIsMLDApSupportMLO = mWifiNative.isMLDApSupportMLO(); + mMaximumNumberOfMLDSupported = ApConfigUtil.getMaximumSupportedMLD( + mContext, mWifiNative.isMultipleMLDSupportedOnSap()); + mCurrentExistingMLD = mActiveModeWarden.getCurrentMLDAp(); + mIsUsingMlo = useMultilinkMloSoftAp(); enableVerboseLogging(verboseLoggingEnabled); mStateMachine.sendMessage(SoftApStateMachine.CMD_START, requestorWs); } @@ -534,13 +564,36 @@ public class SoftApManager implements ActiveModeManager { mStateMachine.sendMessage(SoftApStateMachine.CMD_STOP); } + public boolean isUsingMlo() { + return mIsUsingMlo; + } + + private boolean useMultilinkMloSoftAp() { + if (!Flags.mloSap()) { + return false; + } + if (SdkLevel.isAtLeastT() && mCurrentSoftApConfiguration != null + && mCurrentSoftApConfiguration.isIeee80211beEnabled() + && isBridgedMode() && mIsMLDApSupportMLO) { + + if (ApConfigUtil.is11beAllowedForThisConfiguration( + null /* Wiphy capability can be ignored for MLO case*/, + mContext, mCurrentSoftApConfiguration, true /* isBridgedMode */, + mMaximumNumberOfMLDSupported, mCurrentExistingMLD, + true /* isMLDApSupportMLO */)) { + return true; + } + } + return false; + } + private boolean isOweTransition() { return (SdkLevel.isAtLeastT() && mCurrentSoftApConfiguration != null && mCurrentSoftApConfiguration.getSecurityType() == SoftApConfiguration.SECURITY_TYPE_WPA3_OWE_TRANSITION); } - private boolean isBridgedMode() { + public boolean isBridgedMode() { return (SdkLevel.isAtLeastS() && mCurrentSoftApConfiguration != null && (mCurrentSoftApConfiguration.getBands().length > 1)); } @@ -663,6 +716,13 @@ public class SoftApManager implements ActiveModeManager { } /** + * Return true when current softap state is enabled. + */ + public boolean isStarted() { + return mCurrentApState == WifiManager.WIFI_AP_STATE_ENABLED; + } + + /** * Dump info about this softap manager. */ @Override @@ -684,7 +744,7 @@ public class SoftApManager implements ActiveModeManager { pw.println("mBridgedModeOpportunisticsShutdownTimeoutEnabled: " + mBridgedModeOpportunisticsShutdownTimeoutEnabled); pw.println("mCurrentSoftApInfoMap " + mCurrentSoftApInfoMap); - pw.println("mStartTimestamp: " + mStartTimestamp); + pw.println("mStartTimestamp: " + FORMATTER.format(new Date(mStartTimestampMs))); pw.println("mSafeChannelFrequencyList: " + mSafeChannelFrequencyList.stream() .map(Object::toString) .collect(Collectors.joining(","))); @@ -844,7 +904,6 @@ public class SoftApManager implements ActiveModeManager { } else { Log.d(getTag(), "startSoftAp: band " + mCurrentSoftApConfiguration.getBand()); } - updateApState(WifiManager.WIFI_AP_STATE_ENABLING, WifiManager.WIFI_AP_STATE_DISABLED, 0); @@ -882,14 +941,14 @@ public class SoftApManager implements ActiveModeManager { localConfigBuilder.build(), mSpecifiedModeConfiguration.getTargetMode() == WifiManager.IFACE_IP_MODE_TETHERED, - mSoftApHalCallback); + mSoftApHalCallback, mIsUsingMlo); if (startResult != START_RESULT_SUCCESS) { Log.e(getTag(), "Soft AP start failed"); return startResult; } mWifiDiagnostics.startLogging(mApInterfaceName); - mStartTimestamp = FORMATTER.format(new Date(System.currentTimeMillis())); + mStartTimestampMs = mWifiInjector.getClock().getWallClockMillis(); Log.d(getTag(), "Soft AP is started "); return START_RESULT_SUCCESS; @@ -1232,8 +1291,8 @@ public class SoftApManager implements ActiveModeManager { .setBand(newSingleApBand) .build(); } - } else if (!isCountryCodeChanged - && mRole == ROLE_SOFTAP_TETHERED && isBridgedApAvailable()) { + } else if (!isCountryCodeChanged && isBridgedApAvailable() + && mIsUsingPersistentSoftApConfiguration) { // Try upgrading config to 2 + 5 GHz Dual Band if the available config // bands only include 2 or 5 Ghz. This is to handle cases where the // config was previously set to single band in a CC that didn't support @@ -1276,11 +1335,10 @@ public class SoftApManager implements ActiveModeManager { == InterfaceConflictManager.ICM_SKIP_COMMAND_WAIT_FOR_USER) { break; } - mApInterfaceName = mWifiNative.setupInterfaceForSoftApMode( mWifiNativeInterfaceCallback, mRequestorWs, mCurrentSoftApConfiguration.getBand(), isBridgeRequired(), - SoftApManager.this, getVendorData()); + SoftApManager.this, getVendorData(), mIsUsingMlo); if (TextUtils.isEmpty(mApInterfaceName)) { Log.e(getTag(), "setup failure when creating ap interface."); // Only check if it's possible to create single AP, since a DBS request @@ -1293,13 +1351,15 @@ public class SoftApManager implements ActiveModeManager { break; } - if (SdkLevel.isAtLeastT() + if (!mIsUsingMlo && SdkLevel.isAtLeastT() && mCurrentSoftApConfiguration.isIeee80211beEnabled()) { DeviceWiphyCapabilities capabilities = mWifiNative.getDeviceWiphyCapabilities( mApInterfaceName, isBridgeRequired()); if (!ApConfigUtil.is11beAllowedForThisConfiguration(capabilities, - mContext, mCurrentSoftApConfiguration, isBridgedMode())) { + mContext, mCurrentSoftApConfiguration, isBridgedMode(), + mMaximumNumberOfMLDSupported, mCurrentExistingMLD, + mIsMLDApSupportMLO)) { Log.d(getTag(), "11BE is not allowed," + " removing from configuration"); mCurrentSoftApConfiguration = new SoftApConfiguration.Builder( @@ -1462,7 +1522,7 @@ public class SoftApManager implements ActiveModeManager { mCurrentSoftApCapability, mContext, mWifiNative, null); updateSafeChannelFrequencyList(); int[] oldBands = mCurrentSoftApConfiguration.getBands(); - if (mRole == ROLE_SOFTAP_TETHERED && isBridgedApAvailable()) { + if (isBridgedApAvailable() && mIsUsingPersistentSoftApConfiguration) { mCurrentSoftApConfiguration = ApConfigUtil.upgradeTo2g5gBridgedIfAvailableBandsAreSubset( mCurrentSoftApConfiguration, @@ -1488,7 +1548,7 @@ public class SoftApManager implements ActiveModeManager { mApInterfaceName = mWifiNative.setupInterfaceForSoftApMode( mWifiNativeInterfaceCallback, mRequestorWs, mCurrentSoftApConfiguration.getBand(), isBridgeRequired(), - SoftApManager.this, getVendorData()); + SoftApManager.this, getVendorData(), mIsUsingMlo); if (TextUtils.isEmpty(mApInterfaceName)) { Log.e(getTag(), "setup failure when creating single AP iface"); handleStartSoftApFailure(START_RESULT_FAILURE_GENERAL); @@ -1584,7 +1644,7 @@ public class SoftApManager implements ActiveModeManager { + mCurrentSoftApInfoMap.get(instanceName).getFrequency() + ") from bridged iface " + mApInterfaceName); mWifiNative.removeIfaceInstanceFromBridgedApIface(mApInterfaceName, - instanceName); + instanceName, mIsUsingMlo); // Remove the info and update it. updateSoftApInfo(mCurrentSoftApInfoMap.get(instanceName), true); } @@ -1719,6 +1779,8 @@ public class SoftApManager implements ActiveModeManager { boolean isAllow = checkSoftApClient(mCurrentSoftApConfiguration, client); if (isAllow) { clientList.add(client); + mMaxConnectedClients = clientList.size() > mMaxConnectedClients + ? clientList.size() : mMaxConnectedClients; } else { return; } @@ -1737,6 +1799,15 @@ public class SoftApManager implements ActiveModeManager { + currentInfoWithClientsChanged); if (mSoftApCallback != null) { + if (Flags.softapDisconnectReason() && !isConnected) { + // Client successfully disconnected, should also notify callback + mWifiMetrics.reportOnClientsDisconnected(client.getDisconnectReason(), + mRequestorWs); + mSoftApCallback.onClientsDisconnected( + currentInfoWithClientsChanged, + ImmutableList.of(client)); + } + mSoftApCallback.onConnectedClientsOrInfoChanged(mCurrentSoftApInfoMap, mConnectedClientWithApInfoMap, isBridgeRequired()); } else { @@ -1995,7 +2066,8 @@ public class SoftApManager implements ActiveModeManager { WifiClient client = (WifiClient) message.obj; Log.d(getTag(), "CMD_ASSOCIATED_STATIONS_CHANGED, Client: " + client.getMacAddress().toString() + " isConnected: " - + isConnected); + + isConnected + " disconnectReason: " + + client.getDisconnectReason()); updateConnectedClients(client, isConnected); break; case CMD_AP_INFO_CHANGED: @@ -2027,6 +2099,7 @@ public class SoftApManager implements ActiveModeManager { updateApState(WifiManager.WIFI_AP_STATE_DISABLING, WifiManager.WIFI_AP_STATE_ENABLING, 0); } + writeSoftApStoppedEvent(STOP_EVENT_STOPPED); quitNow(); break; case CMD_START: @@ -2350,6 +2423,8 @@ public class SoftApManager implements ActiveModeManager { if (mCurrentSoftApConfiguration != null) { securityType = mCurrentSoftApConfiguration.getSecurityType(); } + int durationSeconds = + (int) ((mWifiInjector.getClock().getWallClockMillis() - mStartTimestampMs) / 1000); // TODO(b/245824786): Fill out the rest of the fields mWifiMetrics.writeSoftApStoppedEvent( stopEvent, @@ -2360,10 +2435,10 @@ public class SoftApManager implements ActiveModeManager { ApConfigUtil.isStaWithBridgedModeSupported(mContext, mWifiNative), getCurrentStaFreqMhz(), mDefaultShutdownTimeoutMillis > 0, - -1, + durationSeconds, securityType, standard, - -1, + mMaxConnectedClients, mDefaultShutdownIdleInstanceInBridgedModeTimeoutMillis > 0, -1, -1, diff --git a/service/java/com/android/server/wifi/SupplicantStaIfaceCallbackAidlImpl.java b/service/java/com/android/server/wifi/SupplicantStaIfaceCallbackAidlImpl.java index ae98ccbfdc..3b499c2173 100644 --- a/service/java/com/android/server/wifi/SupplicantStaIfaceCallbackAidlImpl.java +++ b/service/java/com/android/server/wifi/SupplicantStaIfaceCallbackAidlImpl.java @@ -29,6 +29,7 @@ import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.HS import static com.android.server.wifi.hotspot2.anqp.Constants.ANQPElementType.HSWANMetrics; import android.annotation.NonNull; +import android.annotation.SuppressLint; import android.content.Context; import android.hardware.wifi.supplicant.AnqpData; import android.hardware.wifi.supplicant.AssociationRejectionData; @@ -56,6 +57,9 @@ import android.hardware.wifi.supplicant.StaIfaceCallbackState; import android.hardware.wifi.supplicant.StaIfaceReasonCode; import android.hardware.wifi.supplicant.StaIfaceStatusCode; import android.hardware.wifi.supplicant.SupplicantStateChangeData; +import android.hardware.wifi.supplicant.UsdMessageInfo; +import android.hardware.wifi.supplicant.UsdServiceDiscoveryInfo; +import android.hardware.wifi.supplicant.UsdTerminateReasonCode; import android.hardware.wifi.supplicant.WpsConfigError; import android.hardware.wifi.supplicant.WpsErrorIndication; import android.net.MacAddress; @@ -64,6 +68,8 @@ import android.net.wifi.SupplicantState; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; import android.net.wifi.WifiSsid; +import android.net.wifi.usd.SessionCallback; +import android.os.Handler; import android.os.Process; import android.util.Log; @@ -75,6 +81,7 @@ import com.android.server.wifi.hotspot2.WnmData; import com.android.server.wifi.hotspot2.anqp.ANQPElement; import com.android.server.wifi.hotspot2.anqp.ANQPParser; import com.android.server.wifi.hotspot2.anqp.Constants; +import com.android.server.wifi.usd.UsdRequestManager; import com.android.server.wifi.util.HalAidlUtil; import com.android.server.wifi.util.NativeUtil; @@ -101,17 +108,19 @@ class SupplicantStaIfaceCallbackAidlImpl extends ISupplicantStaIfaceCallback.Stu // Current SSID in WifiSsid.toString() format. May be translated to UTF-8 if SSID translation // is enabled. private String mCurrentSsid = null; + private final Handler mEventHandler; SupplicantStaIfaceCallbackAidlImpl(@NonNull SupplicantStaIfaceHalAidlImpl staIfaceHal, @NonNull String ifaceName, @NonNull Object lock, @NonNull Context context, @NonNull WifiMonitor wifiMonitor, - @NonNull SsidTranslator ssidTranslator) { + @NonNull SsidTranslator ssidTranslator, Handler eventHandler) { mStaIfaceHal = staIfaceHal; mIfaceName = ifaceName; mLock = lock; mContext = context; mWifiMonitor = wifiMonitor; mSsidTranslator = ssidTranslator; + mEventHandler = eventHandler; } @Override @@ -374,15 +383,13 @@ class SupplicantStaIfaceCallbackAidlImpl extends ISupplicantStaIfaceCallback.Stu // was not successfully verified is indicated with a status code of 15. This // typically happens when the entered password is wrong. So treat status code // of 15 as incorrect password. - // Some implementations also send status code of 1 for incorrect password. But - // this is a generic status code and can't be treated as incorrect password all - // the time. So treat status code of 1 as incorrect password only if the STA - // was not connected to this network before. In this case, we will - // send an authentication failure event up. + // Some implementations also send status code of 1 for incorrect password. For + // both status codes, broadcast authentication failure message with reason code + // set to wrong password. ClientModeImpl will notify user for wrong password + // error if the network had never been connected before. if (statusCode == SupplicantStaIfaceHal.StaIfaceStatusCode.CHALLENGE_FAIL - || (statusCode - == SupplicantStaIfaceHal.StaIfaceStatusCode.UNSPECIFIED_FAILURE - && !curConfiguration.getNetworkSelectionStatus().hasEverConnected())) { + || statusCode + == SupplicantStaIfaceHal.StaIfaceStatusCode.UNSPECIFIED_FAILURE) { mStaIfaceHal.logCallback("SAE incorrect password"); isWrongPwd = true; } else { @@ -671,6 +678,149 @@ class SupplicantStaIfaceCallbackAidlImpl extends ISupplicantStaIfaceCallback.Stu } } + @Override + public void onUsdPublishStarted(int cmdId, int publishId) { + mEventHandler.post(() -> { + if (mStaIfaceHal.getUsdEventsCallback() == null) { + Log.e(TAG, "UsdEventsCallback callback is null"); + return; + } + mStaIfaceHal.getUsdEventsCallback().onUsdPublishStarted(cmdId, publishId); + }); + } + + @Override + public void onUsdSubscribeStarted(int cmdId, int subscribeId) { + mEventHandler.post(() -> { + if (mStaIfaceHal.getUsdEventsCallback() == null) { + Log.e(TAG, "UsdEventsCallback callback is null"); + return; + } + mStaIfaceHal.getUsdEventsCallback().onUsdSubscribeStarted(cmdId, subscribeId); + }); + } + + @Override + public void onUsdPublishConfigFailed(int cmdId, int errorCode) { + mEventHandler.post(() -> { + if (mStaIfaceHal.getUsdEventsCallback() == null) { + Log.e(TAG, "UsdEventsCallback callback is null"); + return; + } + mStaIfaceHal.getUsdEventsCallback().onUsdPublishConfigFailed(cmdId, + convertHalToFrameworkUsdConfigErrorCode(errorCode)); + }); + } + + @Override + public void onUsdSubscribeConfigFailed(int cmdId, int errorCode) { + mEventHandler.post(() -> { + if (mStaIfaceHal.getUsdEventsCallback() == null) { + Log.e(TAG, "UsdEventsCallback callback is null"); + return; + } + mStaIfaceHal.getUsdEventsCallback().onUsdSubscribeConfigFailed(cmdId, + convertHalToFrameworkUsdConfigErrorCode(errorCode)); + }); + } + + @Override + public void onUsdPublishTerminated(int publishId, int reasonCode) { + mEventHandler.post(() -> { + if (mStaIfaceHal.getUsdEventsCallback() == null) { + Log.e(TAG, "UsdEventsCallback callback is null"); + return; + } + mStaIfaceHal.getUsdEventsCallback().onUsdPublishTerminated(publishId, + convertHalToFrameworkTerminateReasonCode(reasonCode)); + }); + } + + private int convertHalToFrameworkTerminateReasonCode(int usdHalReasonCode) { + switch (usdHalReasonCode) { + case UsdTerminateReasonCode.USER_REQUEST: + return SessionCallback.TERMINATION_REASON_USER_INITIATED; + default: + return SessionCallback.TERMINATION_REASON_UNKNOWN; + } + } + + @SuppressLint("NewApi") + private static @SessionCallback.FailureCode int + convertHalToFrameworkUsdConfigErrorCode(int errorCode) { + switch (errorCode) { + case UsdConfigErrorCode.FAILURE_TIMEOUT: + return SessionCallback.FAILURE_TIMEOUT; + case UsdConfigErrorCode.FAILURE_NOT_AVAILABLE: + return SessionCallback.FAILURE_NOT_AVAILABLE; + default: + return SessionCallback.FAILURE_UNKNOWN; + } + } + + @Override + public void onUsdSubscribeTerminated(int subscribeId, int reasonCode) { + mEventHandler.post(() -> { + if (mStaIfaceHal.getUsdEventsCallback() == null) { + Log.e(TAG, "UsdEventsCallback callback is null"); + return; + } + mStaIfaceHal.getUsdEventsCallback().onUsdSubscribeTerminated(subscribeId, + convertHalToFrameworkTerminateReasonCode(reasonCode)); + }); + } + + @Override + public void onUsdPublishReplied(UsdServiceDiscoveryInfo info) { + mEventHandler.post(() -> { + if (mStaIfaceHal.getUsdEventsCallback() == null) { + Log.e(TAG, "UsdEventsCallback callback is null"); + return; + } + UsdRequestManager.UsdHalDiscoveryInfo usdHalDiscoveryInfo = + new UsdRequestManager.UsdHalDiscoveryInfo(info.ownId, + info.peerId, + MacAddress.fromBytes(info.peerMacAddress), + info.serviceSpecificInfo, + info.protoType, + info.isFsd, + info.matchFilter); + mStaIfaceHal.getUsdEventsCallback().onUsdPublishReplied(usdHalDiscoveryInfo); + }); + } + + @Override + public void onUsdServiceDiscovered(UsdServiceDiscoveryInfo info) { + mEventHandler.post(() -> { + if (mStaIfaceHal.getUsdEventsCallback() == null) { + Log.e(TAG, "UsdEventsCallback callback is null"); + return; + } + UsdRequestManager.UsdHalDiscoveryInfo usdHalDiscoveryInfo = + new UsdRequestManager.UsdHalDiscoveryInfo(info.ownId, + info.peerId, + MacAddress.fromBytes(info.peerMacAddress), + info.serviceSpecificInfo, + info.protoType, + info.isFsd, + info.matchFilter); + mStaIfaceHal.getUsdEventsCallback().onUsdServiceDiscovered(usdHalDiscoveryInfo); + }); + } + + @Override + public void onUsdMessageReceived(UsdMessageInfo messageInfo) { + mEventHandler.post(() -> { + if (mStaIfaceHal.getUsdEventsCallback() == null) { + Log.e(TAG, "UsdEventsCallback callback is null"); + return; + } + mStaIfaceHal.getUsdEventsCallback().onUsdMessageReceived(messageInfo.ownId, + messageInfo.peerId, MacAddress.fromBytes(messageInfo.peerMacAddress), + messageInfo.message); + }); + } + private @MboOceConstants.BtmResponseStatus int halToFrameworkBtmResponseStatus(int status) { switch (status) { case BssTmStatusCode.ACCEPT: diff --git a/service/java/com/android/server/wifi/SupplicantStaIfaceCallbackHidlImpl.java b/service/java/com/android/server/wifi/SupplicantStaIfaceCallbackHidlImpl.java index facbb7bfe0..7727a96732 100644 --- a/service/java/com/android/server/wifi/SupplicantStaIfaceCallbackHidlImpl.java +++ b/service/java/com/android/server/wifi/SupplicantStaIfaceCallbackHidlImpl.java @@ -333,15 +333,13 @@ abstract class SupplicantStaIfaceCallbackHidlImpl extends ISupplicantStaIfaceCal // was not successfully verified is indicated with a status code of 15. This // typically happens when the entered password is wrong. So treat status code // of 15 as incorrect password. - // Some implementations also send status code of 1 for incorrect password. But - // this is a generic status code and can't be treated as incorrect password all - // the time. So treat status code of 1 as incorrect password only if the STA - // was not connected to this network before. In this case, we will - // send an authentication failure event up. + // Some implementations also send status code of 1 for incorrect password. For + // both status codes, broadcast authentication failure message with reason code + // set to wrong password. ClientModeImpl will notify user for wrong password + // error if the network had never been connected before. if (statusCode == SupplicantStaIfaceHal.StaIfaceStatusCode.CHALLENGE_FAIL - || (statusCode - == SupplicantStaIfaceHal.StaIfaceStatusCode.UNSPECIFIED_FAILURE - && !curConfiguration.getNetworkSelectionStatus().hasEverConnected())) { + || statusCode + == SupplicantStaIfaceHal.StaIfaceStatusCode.UNSPECIFIED_FAILURE) { mStaIfaceHal.logCallback("SAE incorrect password"); isWrongPwd = true; } else { diff --git a/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java b/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java index 7129609f9b..f36f32c640 100644 --- a/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java +++ b/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java @@ -23,11 +23,14 @@ import android.net.wifi.MscsParams; import android.net.wifi.QosPolicyParams; import android.net.wifi.SecurityParams; import android.net.wifi.WifiConfiguration; +import android.net.wifi.usd.PublishConfig; +import android.net.wifi.usd.SubscribeConfig; import android.os.Handler; import android.util.Log; import android.util.Range; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.wifi.usd.UsdRequestManager; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -1955,6 +1958,20 @@ public class SupplicantStaIfaceHal { } /** + * Returns true if this device supports RSN Overriding, false otherwise. + */ + public boolean isRsnOverridingSupported(@NonNull String ifaceName) { + synchronized (mLock) { + String methodStr = "isRsnOverridingSupported"; + if (mStaIfaceHal == null) { + handleNullHal(methodStr); + return false; + } + return mStaIfaceHal.isRsnOverridingSupported(ifaceName); + } + } + + /** * Returns connection capabilities of the current network * * @param ifaceName Name of the interface. @@ -2402,4 +2419,163 @@ public class SupplicantStaIfaceHal { Log.e(TAG, "Cannot call " + methodStr + " because HAL object is null."); return false; } + + public static class UsdCapabilitiesInternal { + /** Whether USD Publisher is supported on this device. */ + public final boolean isUsdPublisherSupported; + /** Whether USD Subscriber is supported on this device. */ + public final boolean isUsdSubscriberSupported; + /** Maximum allowed length (in bytes) for the Service Specific Info (SSI). */ + public final int maxLocalSsiLengthBytes; + /** Maximum allowed length (in bytes) for the service name. */ + public final int maxServiceNameLengthBytes; + /** Maximum allowed length (in bytes) for a match filter. */ + public final int maxMatchFilterLengthBytes; + /** Maximum number of allowed publish sessions. */ + public final int maxNumPublishSessions; + /** Maximum number of allowed subscribe sessions. */ + public final int maxNumSubscribeSessions; + + public UsdCapabilitiesInternal(boolean isUsdPublisherSupported, + boolean isUsdSubscriberSupported, + int maxLocalSsiLengthBytes, int maxServiceNameLengthBytes, + int maxMatchFilterLengthBytes, + int maxNumPublishSessions, int maxNumSubscribeSessions) { + this.isUsdPublisherSupported = isUsdPublisherSupported; + this.isUsdSubscriberSupported = isUsdSubscriberSupported; + this.maxLocalSsiLengthBytes = maxLocalSsiLengthBytes; + this.maxServiceNameLengthBytes = maxServiceNameLengthBytes; + this.maxMatchFilterLengthBytes = maxMatchFilterLengthBytes; + this.maxNumPublishSessions = maxNumPublishSessions; + this.maxNumSubscribeSessions = maxNumSubscribeSessions; + } + + public UsdCapabilitiesInternal() { + this.isUsdPublisherSupported = false; + this.isUsdSubscriberSupported = false; + this.maxLocalSsiLengthBytes = 0; + this.maxServiceNameLengthBytes = 0; + this.maxMatchFilterLengthBytes = 0; + this.maxNumPublishSessions = 0; + this.maxNumSubscribeSessions = 0; + } + } + + /** + * See comments for {@link ISupplicantStaIfaceHal#getUsdCapabilities(String)} + */ + public UsdCapabilitiesInternal getUsdCapabilities(String ifaceName) { + synchronized (mLock) { + String methodStr = "getUsdCapabilities"; + if (mStaIfaceHal == null) { + handleNullHal(methodStr); + return null; + } + return mStaIfaceHal.getUsdCapabilities(ifaceName); + } + } + + /** + * See comments for {@link ISupplicantStaIfaceHal#startUsdPublish(String, int, PublishConfig)} + */ + public boolean startUsdPublish(String interfaceName, int cmdId, PublishConfig publishConfig) { + synchronized (mLock) { + String methodStr = "startUsdPublish"; + if (mStaIfaceHal == null) { + handleNullHal(methodStr); + return false; + } + return mStaIfaceHal.startUsdPublish(interfaceName, cmdId, publishConfig); + } + } + + /** + * See comments for + * {@link WifiNative#registerUsdEventsCallback(UsdRequestManager.UsdNativeEventsCallback)} + */ + public void registerUsdEventsCallback( + UsdRequestManager.UsdNativeEventsCallback usdNativeEventsCallback) { + synchronized (mLock) { + String methodStr = "registerUsdEventsCallback"; + if (mStaIfaceHal == null) { + handleNullHal(methodStr); + return; + } + mStaIfaceHal.registerUsdEventsCallback(usdNativeEventsCallback); + } + } + + /** + * See comments for + * {@link ISupplicantStaIfaceHal#startUsdSubscribe(String, int, SubscribeConfig)} + */ + public boolean startUsdSubscribe(String interfaceName, int cmdId, + SubscribeConfig subscribeConfig) { + synchronized (mLock) { + String methodStr = "startUsdSubscribe"; + if (mStaIfaceHal == null) { + handleNullHal(methodStr); + return false; + } + return mStaIfaceHal.startUsdSubscribe(interfaceName, cmdId, subscribeConfig); + } + } + + /** + * See comments for {@link ISupplicantStaIfaceHal#updateUsdPublish(String, int, byte[])} + */ + public void updateUsdPublish(String interfaceName, int publishId, byte[] ssi) { + synchronized (mLock) { + String methodStr = "updateUsdPublish"; + if (mStaIfaceHal == null) { + handleNullHal(methodStr); + return; + } + mStaIfaceHal.updateUsdPublish(interfaceName, publishId, ssi); + } + } + + /** + * See comments for {@link ISupplicantStaIfaceHal#cancelUsdPublish(String, int)} + */ + public void cancelUsdPublish(String interfaceName, int publishId) { + synchronized (mLock) { + String methodStr = "cancelUsdPublish"; + if (mStaIfaceHal == null) { + handleNullHal(methodStr); + return; + } + mStaIfaceHal.cancelUsdPublish(interfaceName, publishId); + } + } + + /** + * See {@link ISupplicantStaIfaceHal#cancelUsdSubscribe(String, int)} + */ + public void cancelUsdSubscribe(String interfaceName, int subscribeId) { + synchronized (mLock) { + String methodStr = "cancelUsdSubscribe"; + if (mStaIfaceHal == null) { + handleNullHal(methodStr); + return; + } + mStaIfaceHal.cancelUsdSubscribe(interfaceName, subscribeId); + } + } + + /** + * See {@link ISupplicantStaIfaceHal#sendUsdMessage(String, int, int, MacAddress, byte[])} + */ + public boolean sendUsdMessage(String interfaceName, int ownId, int peerId, + MacAddress peerMacAddress, byte[] message) { + synchronized (mLock) { + String methodStr = "sendMessage"; + if (mStaIfaceHal == null) { + handleNullHal(methodStr); + return false; + } + return mStaIfaceHal.sendUsdMessage(interfaceName, ownId, peerId, peerMacAddress, + message); + } + } } diff --git a/service/java/com/android/server/wifi/SupplicantStaIfaceHalAidlImpl.java b/service/java/com/android/server/wifi/SupplicantStaIfaceHalAidlImpl.java index b378188fd6..e50201617f 100644 --- a/service/java/com/android/server/wifi/SupplicantStaIfaceHalAidlImpl.java +++ b/service/java/com/android/server/wifi/SupplicantStaIfaceHalAidlImpl.java @@ -33,13 +33,11 @@ import static android.net.wifi.WifiManager.WIFI_FEATURE_WAPI; import static android.net.wifi.WifiManager.WIFI_FEATURE_WFD_R2; import static android.net.wifi.WifiManager.WIFI_FEATURE_WPA3_SAE; import static android.net.wifi.WifiManager.WIFI_FEATURE_WPA3_SUITE_B; -import static android.os.Build.VERSION.SDK_INT; - -import static com.android.server.wifi.util.GeneralUtil.getCapabilityIndex; import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.content.Context; -import android.hardware.wifi.WifiChannelWidthInMhz; import android.hardware.wifi.supplicant.BtCoexistenceMode; import android.hardware.wifi.supplicant.ConnectionCapabilities; import android.hardware.wifi.supplicant.DebugLevel; @@ -77,6 +75,13 @@ import android.hardware.wifi.supplicant.QosPolicyStatusCode; import android.hardware.wifi.supplicant.RxFilterType; import android.hardware.wifi.supplicant.SignalPollResult; import android.hardware.wifi.supplicant.SupplicantStatusCode; +import android.hardware.wifi.supplicant.UsdCapabilities; +import android.hardware.wifi.supplicant.UsdMessageInfo; +import android.hardware.wifi.supplicant.UsdPublishConfig; +import android.hardware.wifi.supplicant.UsdPublishTransmissionType; +import android.hardware.wifi.supplicant.UsdServiceProtoType; +import android.hardware.wifi.supplicant.UsdSubscribeConfig; +import android.hardware.wifi.supplicant.WifiChannelWidthInMhz; import android.hardware.wifi.supplicant.WifiTechnology; import android.hardware.wifi.supplicant.WpaDriverCapabilitiesMask; import android.hardware.wifi.supplicant.WpsConfigMethods; @@ -93,6 +98,10 @@ import android.net.wifi.WifiKeystore; import android.net.wifi.WifiMigration; import android.net.wifi.WifiSsid; import android.net.wifi.flags.Flags; +import android.net.wifi.usd.Config; +import android.net.wifi.usd.PublishConfig; +import android.net.wifi.usd.SubscribeConfig; +import android.net.wifi.util.Environment; import android.os.Handler; import android.os.IBinder; import android.os.IBinder.DeathRecipient; @@ -104,14 +113,18 @@ import android.util.Log; import android.util.Pair; import com.android.internal.annotations.VisibleForTesting; +import com.android.modules.utils.HandlerExecutor; import com.android.modules.utils.build.SdkLevel; import com.android.server.wifi.mockwifi.MockWifiServiceUtil; +import com.android.server.wifi.usd.UsdNativeManager; +import com.android.server.wifi.usd.UsdRequestManager; import com.android.server.wifi.util.HalAidlUtil; import com.android.server.wifi.util.NativeUtil; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; +import java.util.Arrays; import java.util.BitSet; import java.util.HashMap; import java.util.List; @@ -120,6 +133,7 @@ import java.util.NoSuchElementException; import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.function.IntConsumer; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -186,6 +200,15 @@ public class SupplicantStaIfaceHalAidlImpl implements ISupplicantStaIfaceHal { @VisibleForTesting protected boolean mHasMigratedLegacyKeystoreAliases = false; + @VisibleForTesting + protected KeystoreMigrationStatusConsumer mKeystoreMigrationStatusConsumer; + + /** + * Default implementation of USD events. + * {@link UsdRequestManager#registerUsdEventsCallback(UsdRequestManager.UsdNativeEventsCallback)} will override + * this default implementation. + */ + private UsdNativeManager.UsdEventsCallback mUsdEventsCallback = null; private class SupplicantDeathRecipient implements DeathRecipient { @Override @@ -214,6 +237,26 @@ public class SupplicantStaIfaceHalAidlImpl implements ISupplicantStaIfaceHal { } } + @VisibleForTesting + protected class KeystoreMigrationStatusConsumer implements IntConsumer { + @Override + public void accept(int statusCode) { + synchronized (mLock) { + if (statusCode == WifiMigration.KEYSTORE_MIGRATION_SUCCESS_MIGRATION_NOT_NEEDED + || statusCode + == WifiMigration.KEYSTORE_MIGRATION_SUCCESS_MIGRATION_COMPLETE) { + mHasMigratedLegacyKeystoreAliases = true; + } else { + mHasMigratedLegacyKeystoreAliases = false; + } + Log.i(TAG, "Keystore migration returned with success=" + + mHasMigratedLegacyKeystoreAliases + ", statusCode=" + statusCode); + // Consumer is no longer needed, since the callback has been received + mKeystoreMigrationStatusConsumer = null; + } + } + } + public SupplicantStaIfaceHalAidlImpl(Context context, WifiMonitor monitor, Handler handler, Clock clock, WifiMetrics wifiMetrics, WifiGlobals wifiGlobals, @NonNull SsidTranslator ssidTranslator, WifiInjector wifiInjector) { @@ -298,7 +341,7 @@ public class SupplicantStaIfaceHalAidlImpl implements ISupplicantStaIfaceHal { ISupplicantStaIfaceCallback callback = new SupplicantStaIfaceCallbackAidlImpl( SupplicantStaIfaceHalAidlImpl.this, ifaceName, - new Object(), mContext, mWifiMonitor, mSsidTranslator); + new Object(), mContext, mWifiMonitor, mSsidTranslator, mEventHandler); if (registerCallback(iface, callback)) { mISupplicantStaIfaces.put(ifaceName, iface); // Keep callback in a store to avoid recycling by garbage collector @@ -2593,16 +2636,15 @@ public class SupplicantStaIfaceHalAidlImpl implements ISupplicantStaIfaceHal { BitSet advancedCapabilities = new BitSet(); int keyMgmtCapabilities = getKeyMgmtCapabilities(ifaceName); - advancedCapabilities.set( - getCapabilityIndex(WIFI_FEATURE_PASSPOINT_TERMS_AND_CONDITIONS)); - advancedCapabilities.set(getCapabilityIndex(WIFI_FEATURE_DECORATED_IDENTITY)); + advancedCapabilities.set(WIFI_FEATURE_PASSPOINT_TERMS_AND_CONDITIONS); + advancedCapabilities.set(WIFI_FEATURE_DECORATED_IDENTITY); if (mVerboseLoggingEnabled) { Log.v(TAG, methodStr + ": Passpoint T&C supported"); Log.v(TAG, methodStr + ": RFC 7542 decorated identity supported"); } if ((keyMgmtCapabilities & KeyMgmtMask.SAE) != 0) { - advancedCapabilities.set(getCapabilityIndex(WIFI_FEATURE_WPA3_SAE)); + advancedCapabilities.set(WIFI_FEATURE_WPA3_SAE); if (mVerboseLoggingEnabled) { Log.v(TAG, methodStr + ": SAE supported"); @@ -2610,7 +2652,7 @@ public class SupplicantStaIfaceHalAidlImpl implements ISupplicantStaIfaceHal { } if ((keyMgmtCapabilities & KeyMgmtMask.SUITE_B_192) != 0) { - advancedCapabilities.set(getCapabilityIndex(WIFI_FEATURE_WPA3_SUITE_B)); + advancedCapabilities.set(WIFI_FEATURE_WPA3_SUITE_B); if (mVerboseLoggingEnabled) { Log.v(TAG, methodStr + ": SUITE_B supported"); @@ -2618,7 +2660,7 @@ public class SupplicantStaIfaceHalAidlImpl implements ISupplicantStaIfaceHal { } if ((keyMgmtCapabilities & KeyMgmtMask.OWE) != 0) { - advancedCapabilities.set(getCapabilityIndex(WIFI_FEATURE_OWE)); + advancedCapabilities.set(WIFI_FEATURE_OWE); if (mVerboseLoggingEnabled) { Log.v(TAG, methodStr + ": OWE supported"); @@ -2626,8 +2668,8 @@ public class SupplicantStaIfaceHalAidlImpl implements ISupplicantStaIfaceHal { } if ((keyMgmtCapabilities & KeyMgmtMask.DPP) != 0) { - advancedCapabilities.set(getCapabilityIndex(WIFI_FEATURE_DPP)); - advancedCapabilities.set(getCapabilityIndex(WIFI_FEATURE_DPP_ENROLLEE_RESPONDER)); + advancedCapabilities.set(WIFI_FEATURE_DPP); + advancedCapabilities.set(WIFI_FEATURE_DPP_ENROLLEE_RESPONDER); if (mVerboseLoggingEnabled) { Log.v(TAG, methodStr + ": DPP supported"); @@ -2636,7 +2678,7 @@ public class SupplicantStaIfaceHalAidlImpl implements ISupplicantStaIfaceHal { } if ((keyMgmtCapabilities & KeyMgmtMask.WAPI_PSK) != 0) { - advancedCapabilities.set(getCapabilityIndex(WIFI_FEATURE_WAPI)); + advancedCapabilities.set(WIFI_FEATURE_WAPI); if (mVerboseLoggingEnabled) { Log.v(TAG, methodStr + ": WAPI supported"); @@ -2644,7 +2686,7 @@ public class SupplicantStaIfaceHalAidlImpl implements ISupplicantStaIfaceHal { } if ((keyMgmtCapabilities & KeyMgmtMask.FILS_SHA256) != 0) { - advancedCapabilities.set(getCapabilityIndex(WIFI_FEATURE_FILS_SHA256)); + advancedCapabilities.set(WIFI_FEATURE_FILS_SHA256); if (mVerboseLoggingEnabled) { Log.v(TAG, methodStr + ": FILS_SHA256 supported"); @@ -2652,7 +2694,7 @@ public class SupplicantStaIfaceHalAidlImpl implements ISupplicantStaIfaceHal { } if ((keyMgmtCapabilities & KeyMgmtMask.FILS_SHA384) != 0) { - advancedCapabilities.set(getCapabilityIndex(WIFI_FEATURE_FILS_SHA384)); + advancedCapabilities.set(WIFI_FEATURE_FILS_SHA384); if (mVerboseLoggingEnabled) { Log.v(TAG, methodStr + ": FILS_SHA384 supported"); @@ -2696,14 +2738,14 @@ public class SupplicantStaIfaceHalAidlImpl implements ISupplicantStaIfaceHal { BitSet featureSet = new BitSet(); if ((drvCapabilitiesMask & WpaDriverCapabilitiesMask.SET_TLS_MINIMUM_VERSION) != 0) { - featureSet.set(getCapabilityIndex(WIFI_FEATURE_SET_TLS_MINIMUM_VERSION)); + featureSet.set(WIFI_FEATURE_SET_TLS_MINIMUM_VERSION); if (mVerboseLoggingEnabled) { Log.v(TAG, methodStr + ": EAP-TLS minimum version supported"); } } if ((drvCapabilitiesMask & WpaDriverCapabilitiesMask.TLS_V1_3) != 0) { - featureSet.set(getCapabilityIndex(WIFI_FEATURE_TLS_V1_3)); + featureSet.set(WIFI_FEATURE_TLS_V1_3); if (mVerboseLoggingEnabled) { Log.v(TAG, methodStr + ": EAP-TLS v1.3 supported"); } @@ -2721,12 +2763,12 @@ public class SupplicantStaIfaceHalAidlImpl implements ISupplicantStaIfaceHal { BitSet featureSet = new BitSet(); if ((drvCapabilitiesMask & WpaDriverCapabilitiesMask.MBO) != 0) { - featureSet.set(getCapabilityIndex(WIFI_FEATURE_MBO)); + featureSet.set(WIFI_FEATURE_MBO); if (mVerboseLoggingEnabled) { Log.v(TAG, methodStr + ": MBO supported"); } if ((drvCapabilitiesMask & WpaDriverCapabilitiesMask.OCE) != 0) { - featureSet.set(getCapabilityIndex(WIFI_FEATURE_OCE)); + featureSet.set(WIFI_FEATURE_OCE); if (mVerboseLoggingEnabled) { Log.v(TAG, methodStr + ": OCE supported"); } @@ -2734,14 +2776,14 @@ public class SupplicantStaIfaceHalAidlImpl implements ISupplicantStaIfaceHal { } if ((drvCapabilitiesMask & WpaDriverCapabilitiesMask.SAE_PK) != 0) { - featureSet.set(getCapabilityIndex(WIFI_FEATURE_SAE_PK)); + featureSet.set(WIFI_FEATURE_SAE_PK); if (mVerboseLoggingEnabled) { Log.v(TAG, methodStr + ": SAE-PK supported"); } } if ((drvCapabilitiesMask & WpaDriverCapabilitiesMask.WFD_R2) != 0) { - featureSet.set(getCapabilityIndex(WIFI_FEATURE_WFD_R2)); + featureSet.set(WIFI_FEATURE_WFD_R2); if (mVerboseLoggingEnabled) { Log.v(TAG, methodStr + ": WFD-R2 supported"); } @@ -2749,7 +2791,7 @@ public class SupplicantStaIfaceHalAidlImpl implements ISupplicantStaIfaceHal { if ((drvCapabilitiesMask & WpaDriverCapabilitiesMask.TRUST_ON_FIRST_USE) != 0) { - featureSet.set(getCapabilityIndex(WIFI_FEATURE_TRUST_ON_FIRST_USE)); + featureSet.set(WIFI_FEATURE_TRUST_ON_FIRST_USE); if (mVerboseLoggingEnabled) { Log.v(TAG, methodStr + ": Trust-On-First-Use supported"); } @@ -2762,6 +2804,27 @@ public class SupplicantStaIfaceHalAidlImpl implements ISupplicantStaIfaceHal { } /** + * Returns true if this device supports RSN Overriding, false otherwise. Need service version + * at least 4 or higher. + */ + public boolean isRsnOverridingSupported(@NonNull String ifaceName) { + synchronized (mLock) { + final String methodStr = "isRsnOverridingSupported"; + if (!isServiceVersionAtLeast(4)) { + return false; + } + int drvCapabilitiesMask = getWpaDriverCapabilities(ifaceName); + boolean rsnOverridingSupported = + (drvCapabilitiesMask & WpaDriverCapabilitiesMask.RSN_OVERRIDING) != 0; + if (mVerboseLoggingEnabled) { + Log.v(TAG, methodStr + ": RSN Overriding supported: " + + rsnOverridingSupported); + } + return rsnOverridingSupported; + } + } + + /** * Get the bitmask of supplicant/driver supported features in * AIDL WpaDriverCapabilitiesMask format. */ @@ -4062,6 +4125,7 @@ public class SupplicantStaIfaceHalAidlImpl implements ISupplicantStaIfaceHal { } } + @SuppressLint("NewApi") // Keystore migration API is guarded by an SDK check private void registerNonStandardCertCallback() { synchronized (mLock) { final String methodStr = "registerNonStandardCertCallback"; @@ -4072,11 +4136,14 @@ public class SupplicantStaIfaceHalAidlImpl implements ISupplicantStaIfaceHal { return; } - // TODO: Use SdkLevel API when it exists, rather than the SDK_INT - if (!mHasMigratedLegacyKeystoreAliases && SDK_INT >= 36 + if (!mHasMigratedLegacyKeystoreAliases && Environment.isSdkAtLeastB() && Flags.legacyKeystoreToWifiBlobstoreMigrationReadOnly()) { - WifiMigration.migrateLegacyKeystoreToWifiBlobstore(); - mHasMigratedLegacyKeystoreAliases = true; + if (mKeystoreMigrationStatusConsumer == null) { + // Create global callback temporarily for access in the unit tests + mKeystoreMigrationStatusConsumer = new KeystoreMigrationStatusConsumer(); + } + WifiMigration.migrateLegacyKeystoreToWifiBlobstore( + new HandlerExecutor(mEventHandler), mKeystoreMigrationStatusConsumer); } try { @@ -4091,4 +4158,246 @@ public class SupplicantStaIfaceHalAidlImpl implements ISupplicantStaIfaceHal { } } } + + @Override + public SupplicantStaIfaceHal.UsdCapabilitiesInternal getUsdCapabilities(String ifaceName) { + synchronized (mLock) { + if (!isServiceVersionAtLeast(4)) { + return null; + } + String methodStr = "getUsdCapabilities"; + ISupplicantStaIface iface = checkStaIfaceAndLogFailure(ifaceName, methodStr); + if (iface == null) { + return null; + } + try { + UsdCapabilities usdCapabilities = iface.getUsdCapabilities(); + return new SupplicantStaIfaceHal.UsdCapabilitiesInternal( + usdCapabilities.isUsdPublisherSupported, + usdCapabilities.isUsdSubscriberSupported, + usdCapabilities.maxLocalSsiLengthBytes, + usdCapabilities.maxServiceNameLengthBytes, + usdCapabilities.maxMatchFilterLengthBytes, + usdCapabilities.maxNumPublishSessions, + usdCapabilities.maxNumSubscribeSessions); + } catch (RemoteException e) { + handleRemoteException(e, methodStr); + } catch (ServiceSpecificException e) { + handleServiceSpecificException(e, methodStr); + } + return null; + } + } + + @Override + public boolean startUsdPublish(String interfaceName, int cmdId, PublishConfig publishConfig) { + synchronized (mLock) { + if (!isServiceVersionAtLeast(4)) { + return false; + } + String methodStr = "startUsdPublish"; + ISupplicantStaIface iface = checkStaIfaceAndLogFailure(interfaceName, methodStr); + if (iface == null) { + return false; + } + try { + iface.startUsdPublish(cmdId, frameworkToHalPublishConfig(publishConfig)); + return true; + } catch (RemoteException e) { + handleRemoteException(e, methodStr); + } catch (ServiceSpecificException e) { + handleServiceSpecificException(e, methodStr); + } + return false; + } + } + + private UsdPublishConfig frameworkToHalPublishConfig(PublishConfig frameworkConfig) { + UsdPublishConfig aidlConfig = new UsdPublishConfig(); + // USD publisher is always solicited and unsolicited. + aidlConfig.publishType = UsdPublishConfig.PublishType.SOLICITED_AND_UNSOLICITED; + // USD has FSD enabled always. + aidlConfig.isFsd = true; + aidlConfig.transmissionType = frameworkToHalTransmissionType( + frameworkConfig.getSolicitedTransmissionType()); + aidlConfig.announcementPeriodMillis = frameworkConfig.getAnnouncementPeriodMillis(); + aidlConfig.usdBaseConfig.ttlSec = frameworkConfig.getTtlSeconds(); + int[] freqs = frameworkConfig.getOperatingFrequenciesMhz(); + aidlConfig.usdBaseConfig.defaultFreqMhz = (freqs == null) ? 2437 : freqs[0]; + aidlConfig.usdBaseConfig.freqsMhz = + (freqs == null || freqs.length <= 1) ? null : Arrays.copyOfRange(freqs, 1, + freqs.length); + aidlConfig.usdBaseConfig.serviceName = Arrays.toString(frameworkConfig.getServiceName()); + aidlConfig.usdBaseConfig.serviceSpecificInfo = frameworkConfig.getServiceSpecificInfo(); + aidlConfig.usdBaseConfig.rxMatchFilter = frameworkConfig.getRxMatchFilterTlv(); + aidlConfig.usdBaseConfig.txMatchFilter = frameworkConfig.getTxMatchFilterTlv(); + aidlConfig.usdBaseConfig.serviceProtoType = frameworkToHalProtoType( + frameworkConfig.getServiceProtoType()); + return aidlConfig; + } + + private static int frameworkToHalTransmissionType( + @Config.TransmissionType int transmissionType) { + if (transmissionType == Config.TRANSMISSION_TYPE_MULTICAST) { + return UsdPublishTransmissionType.MULTICAST; + } else { + return UsdPublishTransmissionType.UNICAST; + } + } + + private static int frameworkToHalProtoType(int serviceProtoType) { + return switch (serviceProtoType) { + case Config.SERVICE_PROTO_TYPE_GENERIC -> UsdServiceProtoType.GENERIC; + case Config.SERVICE_PROTO_TYPE_CSA_MATTER -> UsdServiceProtoType.CSA_MATTER; + default -> UsdServiceProtoType.UNKNOWN; + }; + } + + @Override + public boolean startUsdSubscribe(String interfaceName, int cmdId, + SubscribeConfig subscribeConfig) { + if (!isServiceVersionAtLeast(4)) { + return false; + } + String methodStr = "startUsdSubscribe"; + ISupplicantStaIface iface = checkStaIfaceAndLogFailure(interfaceName, methodStr); + if (iface == null) { + return false; + } + try { + iface.startUsdSubscribe(cmdId, frameworkToHalSubscribeConfig(subscribeConfig)); + return true; + } catch (RemoteException e) { + handleRemoteException(e, methodStr); + } catch (ServiceSpecificException e) { + handleServiceSpecificException(e, methodStr); + } + return false; + } + + private UsdSubscribeConfig frameworkToHalSubscribeConfig(SubscribeConfig frameworkConfig) { + UsdSubscribeConfig aidlconfig = new UsdSubscribeConfig(); + aidlconfig.subscribeType = frameworkToHalSubscriberType(frameworkConfig.getSubscribeType()); + aidlconfig.queryPeriodMillis = frameworkConfig.getQueryPeriodMillis(); + aidlconfig.usdBaseConfig.ttlSec = frameworkConfig.getTtlSeconds(); + int[] freqs = frameworkConfig.getOperatingFrequenciesMhz(); + aidlconfig.usdBaseConfig.defaultFreqMhz = (freqs == null) ? 2437 : freqs[0]; + aidlconfig.usdBaseConfig.freqsMhz = + (freqs == null || freqs.length < 2) ? null : Arrays.copyOfRange(freqs, 1, + freqs.length); + aidlconfig.usdBaseConfig.serviceName = Arrays.toString(frameworkConfig.getServiceName()); + aidlconfig.usdBaseConfig.serviceSpecificInfo = frameworkConfig.getServiceSpecificInfo(); + aidlconfig.usdBaseConfig.rxMatchFilter = frameworkConfig.getRxMatchFilterTlv(); + aidlconfig.usdBaseConfig.txMatchFilter = frameworkConfig.getTxMatchFilterTlv(); + aidlconfig.usdBaseConfig.serviceProtoType = frameworkToHalProtoType( + frameworkConfig.getServiceProtoType()); + return aidlconfig; + } + + private byte frameworkToHalSubscriberType(int subscribeType) { + if (subscribeType == Config.SUBSCRIBE_TYPE_ACTIVE) { + return UsdSubscribeConfig.SubscribeType.ACTIVE_MODE; + } + return UsdSubscribeConfig.SubscribeType.PASSIVE_MODE; + } + + @Override + public void updateUsdPublish(String interfaceName, int publishId, byte[] ssi) { + if (!isServiceVersionAtLeast(4)) { + return; + } + String methodStr = "updateUsdPublish"; + ISupplicantStaIface iface = checkStaIfaceAndLogFailure(interfaceName, methodStr); + if (iface == null) { + return; + } + try { + iface.updateUsdPublish(publishId, ssi); + } catch (RemoteException e) { + handleRemoteException(e, methodStr); + } catch (ServiceSpecificException e) { + handleServiceSpecificException(e, methodStr); + } + } + + @Override + public void cancelUsdPublish(String interfaceName, int publishId) { + if (!isServiceVersionAtLeast(4)) { + return; + } + String methodStr = "updateUsdPublish"; + ISupplicantStaIface iface = checkStaIfaceAndLogFailure(interfaceName, methodStr); + if (iface == null) { + return; + } + try { + iface.cancelUsdPublish(publishId); + } catch (RemoteException e) { + handleRemoteException(e, methodStr); + } catch (ServiceSpecificException e) { + handleServiceSpecificException(e, methodStr); + } + } + + @Override + public void cancelUsdSubscribe(String interfaceName, int subscribeId) { + if (!isServiceVersionAtLeast(4)) { + return; + } + String methodStr = "cancelUsdSubscribe"; + ISupplicantStaIface iface = checkStaIfaceAndLogFailure(interfaceName, methodStr); + if (iface == null) { + return; + } + try { + iface.cancelUsdSubscribe(subscribeId); + } catch (RemoteException e) { + handleRemoteException(e, methodStr); + } catch (ServiceSpecificException e) { + handleServiceSpecificException(e, methodStr); + } + } + + @Override + public boolean sendUsdMessage(String interfaceName, int ownId, int peerId, + MacAddress peerMacAddress, byte[] message) { + if (!isServiceVersionAtLeast(4)) { + return false; + } + String methodStr = "sendUsdMessage"; + ISupplicantStaIface iface = checkStaIfaceAndLogFailure(interfaceName, methodStr); + if (iface == null) { + return false; + } + try { + UsdMessageInfo usdMessageInfo = new UsdMessageInfo(); + usdMessageInfo.ownId = ownId; + usdMessageInfo.peerId = peerId; + usdMessageInfo.peerMacAddress = peerMacAddress.toByteArray(); + usdMessageInfo.message = message; + iface.sendUsdMessage(usdMessageInfo); + return true; + } catch (RemoteException e) { + handleRemoteException(e, methodStr); + } catch (ServiceSpecificException e) { + handleServiceSpecificException(e, methodStr); + } + return false; + } + + /** + * Register a framework callback to receive USD events. + */ + public void registerUsdEventsCallback( + UsdRequestManager.UsdNativeEventsCallback usdEventsCallback) { + mUsdEventsCallback = usdEventsCallback; + } + + /** + * Get USD event callback. + */ + @Nullable + public UsdNativeManager.UsdEventsCallback getUsdEventsCallback() { + return mUsdEventsCallback; + } } diff --git a/service/java/com/android/server/wifi/SupplicantStaIfaceHalHidlImpl.java b/service/java/com/android/server/wifi/SupplicantStaIfaceHalHidlImpl.java index c98e23af3a..6b9838ddcb 100644 --- a/service/java/com/android/server/wifi/SupplicantStaIfaceHalHidlImpl.java +++ b/service/java/com/android/server/wifi/SupplicantStaIfaceHalHidlImpl.java @@ -30,8 +30,6 @@ import static android.net.wifi.WifiManager.WIFI_FEATURE_WFD_R2; import static android.net.wifi.WifiManager.WIFI_FEATURE_WPA3_SAE; import static android.net.wifi.WifiManager.WIFI_FEATURE_WPA3_SUITE_B; -import static com.android.server.wifi.util.GeneralUtil.getCapabilityIndex; - import android.annotation.NonNull; import android.content.Context; import android.hardware.wifi.V1_0.WifiChannelWidthInMhz; @@ -2974,7 +2972,7 @@ public class SupplicantStaIfaceHalHidlImpl implements ISupplicantStaIfaceHal { if ((keyMgmtCapabilities & android.hardware.wifi.supplicant.V1_2.ISupplicantStaNetwork .KeyMgmtMask.SAE) != 0) { - advancedCapabilities.set(getCapabilityIndex(WIFI_FEATURE_WPA3_SAE)); + advancedCapabilities.set(WIFI_FEATURE_WPA3_SAE); if (mVerboseLoggingEnabled) { Log.v(TAG, methodStr + ": SAE supported"); @@ -2983,7 +2981,7 @@ public class SupplicantStaIfaceHalHidlImpl implements ISupplicantStaIfaceHal { if ((keyMgmtCapabilities & android.hardware.wifi.supplicant.V1_2.ISupplicantStaNetwork .KeyMgmtMask.SUITE_B_192) != 0) { - advancedCapabilities.set(getCapabilityIndex(WIFI_FEATURE_WPA3_SUITE_B)); + advancedCapabilities.set(WIFI_FEATURE_WPA3_SUITE_B); if (mVerboseLoggingEnabled) { Log.v(TAG, methodStr + ": SUITE_B supported"); @@ -2992,7 +2990,7 @@ public class SupplicantStaIfaceHalHidlImpl implements ISupplicantStaIfaceHal { if ((keyMgmtCapabilities & android.hardware.wifi.supplicant.V1_2.ISupplicantStaNetwork .KeyMgmtMask.OWE) != 0) { - advancedCapabilities.set(getCapabilityIndex(WIFI_FEATURE_OWE)); + advancedCapabilities.set(WIFI_FEATURE_OWE); if (mVerboseLoggingEnabled) { Log.v(TAG, methodStr + ": OWE supported"); @@ -3001,13 +2999,13 @@ public class SupplicantStaIfaceHalHidlImpl implements ISupplicantStaIfaceHal { if ((keyMgmtCapabilities & android.hardware.wifi.supplicant.V1_2.ISupplicantStaNetwork .KeyMgmtMask.DPP) != 0) { - advancedCapabilities.set(getCapabilityIndex(WIFI_FEATURE_DPP)); + advancedCapabilities.set(WIFI_FEATURE_DPP); if (mVerboseLoggingEnabled) { Log.v(TAG, methodStr + ": DPP supported"); } if (isV1_4()) { - advancedCapabilities.set(getCapabilityIndex(WIFI_FEATURE_DPP_ENROLLEE_RESPONDER)); + advancedCapabilities.set(WIFI_FEATURE_DPP_ENROLLEE_RESPONDER); if (mVerboseLoggingEnabled) { Log.v(TAG, methodStr + ": DPP ENROLLEE RESPONDER supported"); } @@ -3015,9 +3013,8 @@ public class SupplicantStaIfaceHalHidlImpl implements ISupplicantStaIfaceHal { } if (isV1_4()) { - advancedCapabilities.set( - getCapabilityIndex(WIFI_FEATURE_PASSPOINT_TERMS_AND_CONDITIONS)); - advancedCapabilities.set(getCapabilityIndex(WIFI_FEATURE_DECORATED_IDENTITY)); + advancedCapabilities.set(WIFI_FEATURE_PASSPOINT_TERMS_AND_CONDITIONS); + advancedCapabilities.set(WIFI_FEATURE_DECORATED_IDENTITY); if (mVerboseLoggingEnabled) { Log.v(TAG, methodStr + ": Passpoint T&C supported"); Log.v(TAG, methodStr + ": RFC 7542 decorated identity supported"); @@ -3026,7 +3023,7 @@ public class SupplicantStaIfaceHalHidlImpl implements ISupplicantStaIfaceHal { if ((keyMgmtCapabilities & android.hardware.wifi.supplicant.V1_3.ISupplicantStaNetwork .KeyMgmtMask.WAPI_PSK) != 0) { - advancedCapabilities.set(getCapabilityIndex(WIFI_FEATURE_WAPI)); + advancedCapabilities.set(WIFI_FEATURE_WAPI); if (mVerboseLoggingEnabled) { Log.v(TAG, methodStr + ": WAPI supported"); @@ -3035,7 +3032,7 @@ public class SupplicantStaIfaceHalHidlImpl implements ISupplicantStaIfaceHal { if ((keyMgmtCapabilities & android.hardware.wifi.supplicant.V1_3.ISupplicantStaNetwork .KeyMgmtMask.FILS_SHA256) != 0) { - advancedCapabilities.set(getCapabilityIndex(WIFI_FEATURE_FILS_SHA256)); + advancedCapabilities.set(WIFI_FEATURE_FILS_SHA256); if (mVerboseLoggingEnabled) { Log.v(TAG, methodStr + ": FILS_SHA256 supported"); @@ -3043,7 +3040,7 @@ public class SupplicantStaIfaceHalHidlImpl implements ISupplicantStaIfaceHal { } if ((keyMgmtCapabilities & android.hardware.wifi.supplicant.V1_3.ISupplicantStaNetwork .KeyMgmtMask.FILS_SHA384) != 0) { - advancedCapabilities.set(getCapabilityIndex(WIFI_FEATURE_FILS_SHA384)); + advancedCapabilities.set(WIFI_FEATURE_FILS_SHA384); if (mVerboseLoggingEnabled) { Log.v(TAG, methodStr + ": FILS_SHA384 supported"); @@ -3214,13 +3211,13 @@ public class SupplicantStaIfaceHalHidlImpl implements ISupplicantStaIfaceHal { } if ((drvCapabilitiesMask.value & WpaDriverCapabilitiesMask.MBO) != 0) { - featureSet.set(getCapabilityIndex(WIFI_FEATURE_MBO)); + featureSet.set(WIFI_FEATURE_MBO); if (mVerboseLoggingEnabled) { Log.v(TAG, methodStr + ": MBO supported"); } if ((drvCapabilitiesMask.value & WpaDriverCapabilitiesMask.OCE) != 0) { - featureSet.set(getCapabilityIndex(WIFI_FEATURE_OCE)); + featureSet.set(WIFI_FEATURE_OCE); if (mVerboseLoggingEnabled) { Log.v(TAG, methodStr + ": OCE supported"); } @@ -3229,7 +3226,7 @@ public class SupplicantStaIfaceHalHidlImpl implements ISupplicantStaIfaceHal { if ((drvCapabilitiesMask.value & android.hardware.wifi.supplicant.V1_4.WpaDriverCapabilitiesMask.SAE_PK) != 0) { - featureSet.set(getCapabilityIndex(WIFI_FEATURE_SAE_PK)); + featureSet.set(WIFI_FEATURE_SAE_PK); if (mVerboseLoggingEnabled) { Log.v(TAG, methodStr + ": SAE-PK supported"); } @@ -3237,7 +3234,7 @@ public class SupplicantStaIfaceHalHidlImpl implements ISupplicantStaIfaceHal { if ((drvCapabilitiesMask.value & android.hardware.wifi.supplicant.V1_4.WpaDriverCapabilitiesMask.WFD_R2) != 0) { - featureSet.set(getCapabilityIndex(WIFI_FEATURE_WFD_R2)); + featureSet.set(WIFI_FEATURE_WFD_R2); if (mVerboseLoggingEnabled) { Log.v(TAG, methodStr + ": WFD-R2 supported"); } diff --git a/service/java/com/android/server/wifi/SupplicantStaNetworkHalAidlImpl.java b/service/java/com/android/server/wifi/SupplicantStaNetworkHalAidlImpl.java index 3a0a810003..4797584cdc 100644 --- a/service/java/com/android/server/wifi/SupplicantStaNetworkHalAidlImpl.java +++ b/service/java/com/android/server/wifi/SupplicantStaNetworkHalAidlImpl.java @@ -19,8 +19,6 @@ package com.android.server.wifi; import static android.net.wifi.WifiManager.WIFI_FEATURE_TLS_V1_3; import static android.net.wifi.WifiManager.WIFI_FEATURE_WPA3_SUITE_B; -import static com.android.server.wifi.util.GeneralUtil.getCapabilityIndex; - import android.annotation.NonNull; import android.content.Context; import android.hardware.wifi.supplicant.AuthAlgMask; @@ -888,7 +886,7 @@ public class SupplicantStaNetworkHalAidlImpl { private int getOptimalMinimumTlsVersion(WifiEnterpriseConfig enterpriseConfig) { int maxTlsVersionSupported = WifiEnterpriseConfig.TLS_V1_2; - if (mWpaDriverFeatures.get(getCapabilityIndex(WIFI_FEATURE_TLS_V1_3))) { + if (mWpaDriverFeatures.get(WIFI_FEATURE_TLS_V1_3)) { maxTlsVersionSupported = WifiEnterpriseConfig.TLS_V1_3; } @@ -1048,8 +1046,7 @@ public class SupplicantStaNetworkHalAidlImpl { mask |= GroupCipherMask.GTK_NOT_USED; break; case WifiConfiguration.GroupCipher.GCMP_256: - if (!mAdvanceKeyMgmtFeatures.get( - getCapabilityIndex(WIFI_FEATURE_WPA3_SUITE_B))) { + if (!mAdvanceKeyMgmtFeatures.get(WIFI_FEATURE_WPA3_SUITE_B)) { Log.d(TAG, "Ignore unsupported GCMP_256 cipher."); break; } @@ -1110,8 +1107,7 @@ public class SupplicantStaNetworkHalAidlImpl { mask |= PairwiseCipherMask.CCMP; break; case WifiConfiguration.PairwiseCipher.GCMP_256: - if (!mAdvanceKeyMgmtFeatures.get( - getCapabilityIndex(WIFI_FEATURE_WPA3_SUITE_B))) { + if (!mAdvanceKeyMgmtFeatures.get(WIFI_FEATURE_WPA3_SUITE_B)) { Log.d(TAG, "Ignore unsupporting GCMP_256 cipher."); break; } diff --git a/service/java/com/android/server/wifi/SupplicantStaNetworkHalHidlImpl.java b/service/java/com/android/server/wifi/SupplicantStaNetworkHalHidlImpl.java index 896019f5ac..4a96230367 100644 --- a/service/java/com/android/server/wifi/SupplicantStaNetworkHalHidlImpl.java +++ b/service/java/com/android/server/wifi/SupplicantStaNetworkHalHidlImpl.java @@ -17,8 +17,6 @@ package com.android.server.wifi; import static android.net.wifi.WifiManager.WIFI_FEATURE_WPA3_SUITE_B; -import static com.android.server.wifi.util.GeneralUtil.getCapabilityIndex; - import android.content.Context; import android.hardware.wifi.supplicant.V1_0.ISupplicantStaNetwork; import android.hardware.wifi.supplicant.V1_0.ISupplicantStaNetworkCallback; @@ -994,8 +992,7 @@ public class SupplicantStaNetworkHalHidlImpl { Log.d(TAG, "Ignore GCMP_256 cipher for the HAL older than 1.2."); break; } - if (!mAdvanceKeyMgmtFeatures.get( - getCapabilityIndex(WIFI_FEATURE_WPA3_SUITE_B))) { + if (!mAdvanceKeyMgmtFeatures.get(WIFI_FEATURE_WPA3_SUITE_B)) { Log.d(TAG, "Ignore unsupporting GCMP_256 cipher."); break; } @@ -1072,8 +1069,7 @@ public class SupplicantStaNetworkHalHidlImpl { Log.d(TAG, "Ignore GCMP_256 cipher for the HAL older than 1.2."); break; } - if (!mAdvanceKeyMgmtFeatures.get( - getCapabilityIndex(WIFI_FEATURE_WPA3_SUITE_B))) { + if (!mAdvanceKeyMgmtFeatures.get(WIFI_FEATURE_WPA3_SUITE_B)) { Log.d(TAG, "Ignore unsupporting GCMP_256 cipher."); break; } diff --git a/service/java/com/android/server/wifi/ThroughputPredictor.java b/service/java/com/android/server/wifi/ThroughputPredictor.java index 8a2063e4e2..b3f23589dd 100644 --- a/service/java/com/android/server/wifi/ThroughputPredictor.java +++ b/service/java/com/android/server/wifi/ThroughputPredictor.java @@ -150,7 +150,7 @@ public class ThroughputPredictor { public int predictTxThroughput(@NonNull WifiNative.ConnectionCapabilities capabilities, int rssiDbm, int frequency, int channelUtilization) { int channelUtilizationFinal = getValidChannelUtilization(frequency, - INVALID, channelUtilization, false); + channelUtilization, false); return predictThroughputInternal(capabilities.wifiStandard, capabilities.is11bMode, capabilities.channelBandwidth, rssiDbm, capabilities.maxNumberTxSpatialStreams, channelUtilizationFinal, frequency, null); @@ -163,7 +163,7 @@ public class ThroughputPredictor { public int predictRxThroughput(@NonNull WifiNative.ConnectionCapabilities capabilities, int rssiDbm, int frequency, int channelUtilization) { int channelUtilizationFinal = getValidChannelUtilization(frequency, - INVALID, channelUtilization, false); + channelUtilization, false); return predictThroughputInternal(capabilities.wifiStandard, capabilities.is11bMode, capabilities.channelBandwidth, rssiDbm, capabilities.maxNumberRxSpatialStreams, channelUtilizationFinal, frequency, null); @@ -277,7 +277,6 @@ public class ThroughputPredictor { } int channelUtilization = getValidChannelUtilization(frequency, - channelUtilizationBssLoad, channelUtilizationLinkLayerStats, isBluetoothConnected); @@ -417,7 +416,7 @@ public class ThroughputPredictor { int phyRateMbps = (int) ((numBitPerSym * MICRO_TO_NANO_RATIO) / (symDurationNs * BIT_PER_TONE_SCALE)); - int airTimeFraction = calculateAirTimeFraction(channelUtilization, channelWidthFactor); + int airTimeFraction = calculateAirTimeFraction(channelUtilization); int throughputMbps = (phyRateMbps * airTimeFraction) / MAX_CHANNEL_UTILIZATION; @@ -458,13 +457,11 @@ public class ThroughputPredictor { return bitPerTone; } - private int getValidChannelUtilization(int frequency, int channelUtilizationBssLoad, + private int getValidChannelUtilization(int frequency, int channelUtilizationLinkLayerStats, boolean isBluetoothConnected) { int channelUtilization; boolean is2G = ScanResult.is24GHz(frequency); - if (isValidUtilizationRatio(channelUtilizationBssLoad)) { - channelUtilization = channelUtilizationBssLoad; - } else if (isValidUtilizationRatio(channelUtilizationLinkLayerStats)) { + if (isValidUtilizationRatio(channelUtilizationLinkLayerStats)) { channelUtilization = channelUtilizationLinkLayerStats; } else { channelUtilization = is2G ? CHANNEL_UTILIZATION_DEFAULT_2G : @@ -477,8 +474,8 @@ public class ThroughputPredictor { } if (mVerboseLoggingEnabled) { StringBuilder sb = new StringBuilder(); - Log.d(TAG, sb.append(" utilization (BssLoad) ").append(channelUtilizationBssLoad) - .append(" utilization (LLStats) ").append(channelUtilizationLinkLayerStats) + Log.d(TAG, sb + .append(" utilization (LLS) ").append(channelUtilizationLinkLayerStats) .append(" isBluetoothConnected: ").append(isBluetoothConnected) .append(" final utilization: ").append(channelUtilization) .toString()); @@ -497,19 +494,7 @@ public class ThroughputPredictor { // Calculate the available airtime fraction value which is multiplied by // MAX_CHANNEL_UTILIZATION for integer representation. It is calculated as // (1 - channelUtilization / MAX_CHANNEL_UTILIZATION) * MAX_CHANNEL_UTILIZATION - private int calculateAirTimeFraction(int channelUtilization, int channelWidthFactor) { - int airTimeFraction20MHz = MAX_CHANNEL_UTILIZATION - channelUtilization; - int airTimeFraction = airTimeFraction20MHz; - // For the cases of 40MHz or above, need to take - // (1 - channelUtilization / MAX_CHANNEL_UTILIZATION) ^ (2 ^ channelWidthFactor) - // because channelUtilization is defined for primary 20MHz channel - for (int i = 1; i <= channelWidthFactor; ++i) { - airTimeFraction *= airTimeFraction; - airTimeFraction /= MAX_CHANNEL_UTILIZATION; - } - if (mVerboseLoggingEnabled) { - Log.d(TAG, " airTime20: " + airTimeFraction20MHz + " airTime: " + airTimeFraction); - } - return airTimeFraction; + private int calculateAirTimeFraction(int channelUtilization) { + return MAX_CHANNEL_UTILIZATION - channelUtilization; } } diff --git a/service/java/com/android/server/wifi/WepNetworkUsageController.java b/service/java/com/android/server/wifi/WepNetworkUsageController.java new file mode 100644 index 0000000000..27817ed37c --- /dev/null +++ b/service/java/com/android/server/wifi/WepNetworkUsageController.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2024 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 com.android.server.wifi.WifiSettingsConfigStore.WIFI_WEP_ALLOWED; + +import android.annotation.NonNull; +import android.net.wifi.WifiInfo; +import android.os.Handler; +import android.os.HandlerThread; +import android.util.LocalLog; +import android.util.Log; + +import com.android.wifi.flags.FeatureFlags; + +import java.io.FileDescriptor; +import java.io.PrintWriter; + +/** + * Class used to control WEP network usage. + * Start to access this class from android 16 and flag: wep_disabled_in_apm is true. + */ +public class WepNetworkUsageController { + private static final String TAG = "WepNetworkUsageController"; + + private final HandlerThread mHandlerThread; + private final WifiDeviceStateChangeManager mWifiDeviceStateChangeManager; + private final WifiSettingsConfigStore mWifiSettingsConfigStore; + private final WifiGlobals mWifiGlobals; + private final ActiveModeWarden mActiveModeWarden; + private final LocalLog mLocalLog; + private final FeatureFlags mFeatureFlags; + + private boolean mVerboseLoggingEnabled; + private boolean mIsWepAllowedSettingEnabled; + private boolean mIsAdvancedProtectionModeEnabled; + + public WepNetworkUsageController( + @NonNull HandlerThread handlerThread, + @NonNull WifiDeviceStateChangeManager wifiDeviceStateChangeManager, + @NonNull WifiSettingsConfigStore wifiSettingsConfigStore, + @NonNull WifiGlobals wifiGlobals, + @NonNull ActiveModeWarden activeModeWarden, + @NonNull FeatureFlags featureFlags) { + mHandlerThread = handlerThread; + mWifiDeviceStateChangeManager = wifiDeviceStateChangeManager; + mWifiSettingsConfigStore = wifiSettingsConfigStore; + mWifiGlobals = wifiGlobals; + mActiveModeWarden = activeModeWarden; + mFeatureFlags = featureFlags; + if (!mFeatureFlags.wepDisabledInApm()) { + Log.wtf(TAG, "WepNetworkUsageController should work only" + + " after feature flag is enabled"); + } + mLocalLog = new LocalLog(32); + } + + /** Handle the boot completed event. Start to monitor WEP network usage */ + public void handleBootCompleted() { + mIsWepAllowedSettingEnabled = mWifiSettingsConfigStore.get(WIFI_WEP_ALLOWED); + mWifiSettingsConfigStore.registerChangeListener(WIFI_WEP_ALLOWED, + (key, value) -> { + mIsWepAllowedSettingEnabled = value; + handleWepAllowedChanged(); + }, + new Handler(mHandlerThread.getLooper())); + mWifiDeviceStateChangeManager.registerStateChangeCallback( + new WifiDeviceStateChangeManager.StateChangeCallback() { + @Override + public void onAdvancedProtectionModeStateChanged(boolean apmOn) { + mIsAdvancedProtectionModeEnabled = apmOn; + handleWepAllowedChanged(); + } + }); + } + + /** + * Enable verbose logging for WifiConnectivityManager. + */ + public void enableVerboseLogging(boolean verbose) { + mVerboseLoggingEnabled = verbose; + } + + /** + * Handles WEP allowed changed either settings: WIFI_WEP_ALLOWED changed or APM changed. + */ + private void handleWepAllowedChanged() { + final boolean isWepAllowed = mIsWepAllowedSettingEnabled + && !mIsAdvancedProtectionModeEnabled; + mLocalLog.log("handleWepAllowedChanged, mIsWepAllowedSettingEnabledByUser = " + + mIsWepAllowedSettingEnabled + + " and isAdvancedProtectionEnabled = " + mIsAdvancedProtectionModeEnabled); + if (isWepAllowed == mWifiGlobals.isWepAllowed()) { + return; // No changed. + } + mWifiGlobals.setWepAllowed(isWepAllowed); + if (!isWepAllowed) { + for (ClientModeManager clientModeManager + : mActiveModeWarden.getClientModeManagers()) { + if (!(clientModeManager instanceof ConcreteClientModeManager)) { + continue; + } + ConcreteClientModeManager cmm = (ConcreteClientModeManager) clientModeManager; + WifiInfo info = cmm.getConnectionInfo(); + if (info != null + && info.getCurrentSecurityType() == WifiInfo.SECURITY_TYPE_WEP) { + clientModeManager.disconnect(); + } + } + } + } + + /** + * Dump output for debugging. + */ + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println("Dump of WepNetworkUsageController:"); + if (mVerboseLoggingEnabled) { + mLocalLog.dump(fd, pw, args); + } + pw.println("mIsAdvancedProtectionModeEnabled=" + mIsAdvancedProtectionModeEnabled); + pw.println("mIsWepAllowedSettingEnabled=" + mIsWepAllowedSettingEnabled); + + } +} diff --git a/service/java/com/android/server/wifi/WifiApConfigStore.java b/service/java/com/android/server/wifi/WifiApConfigStore.java index d5167ac57a..d496bd68ca 100644 --- a/service/java/com/android/server/wifi/WifiApConfigStore.java +++ b/service/java/com/android/server/wifi/WifiApConfigStore.java @@ -487,11 +487,22 @@ public class WifiApConfigStore { /** * Generate a temporary WPA2 based configuration for use by the local only hotspot. * This config is not persisted and will not be stored by the WifiApConfigStore. + * + * @param context the context of wifi. + * @param customConfig customzied softap configuration. + * @param capability current softap capability. + * @param isExclusive whether customConfig is exclusive (set by privledged app). + * @return configuration of local only hotspot. */ public SoftApConfiguration generateLocalOnlyHotspotConfig(@NonNull WifiContext context, - @Nullable SoftApConfiguration customConfig, @NonNull SoftApCapability capability) { + @Nullable SoftApConfiguration customConfig, @NonNull SoftApCapability capability, + boolean isExclusive) { SoftApConfiguration.Builder configBuilder; - if (customConfig != null) { + boolean wasSsidAssigned = false; + if (customConfig != null && isExclusive) { + if (!TextUtils.isEmpty(customConfig.getSsid())) { + wasSsidAssigned = true; + } configBuilder = new SoftApConfiguration.Builder(customConfig); // Make sure that we use available band on old build. if (!SdkLevel.isAtLeastT() @@ -500,8 +511,12 @@ public class WifiApConfigStore { } } else { configBuilder = new SoftApConfiguration.Builder(); - // Make sure the default band configuration is supported. - configBuilder.setBand(generateDefaultBand(context)); + if (customConfig != null && SdkLevel.isAtLeastS()) { + configBuilder.setChannels(customConfig.getChannels()); + } else { + // Make sure the default band configuration is supported. + configBuilder.setBand(generateDefaultBand(context)); + } // Default to disable the auto shutdown configBuilder.setAutoShutdownEnabled(false); try { @@ -543,7 +558,8 @@ public class WifiApConfigStore { } configBuilder.setBand(desiredBand); } - if (customConfig == null || customConfig.getSsid() == null) { + + if (!wasSsidAssigned) { configBuilder.setSsid(generateLohsSsid(context)); } @@ -709,6 +725,13 @@ public class WifiApConfigStore { } } + // Hostapd requires 11AX to configure 11BE + if (SdkLevel.isAtLeastT() && apConfig.isIeee80211beEnabled() + && !apConfig.isIeee80211axEnabledInternal()) { + Log.d(TAG, "11AX is required when configuring 11BE"); + return false; + } + return true; } diff --git a/service/java/com/android/server/wifi/WifiBlocklistMonitor.java b/service/java/com/android/server/wifi/WifiBlocklistMonitor.java index 179f7f32c0..c5e2b5e0ce 100644 --- a/service/java/com/android/server/wifi/WifiBlocklistMonitor.java +++ b/service/java/com/android/server/wifi/WifiBlocklistMonitor.java @@ -34,6 +34,8 @@ import android.util.LocalLog; import android.util.Log; import android.util.SparseArray; +import androidx.annotation.Keep; + import com.android.internal.annotations.VisibleForTesting; import com.android.server.wifi.util.StringUtil; import com.android.server.wifi.util.WifiPermissionsUtil; @@ -89,8 +91,9 @@ public class WifiBlocklistMonitor { public static final int REASON_NONLOCAL_DISCONNECT_CONNECTING = 12; // Connection attempt aborted by the watchdog because the AP didn't respond. public static final int REASON_FAILURE_NO_RESPONSE = 13; + public static final int REASON_APP_DISALLOW = 14; // Constant being used to keep track of how many failure reasons there are. - public static final int NUMBER_REASON_CODES = 14; + public static final int NUMBER_REASON_CODES = 15; public static final int INVALID_REASON = -1; @IntDef(prefix = { "REASON_" }, value = { @@ -107,7 +110,8 @@ public class WifiBlocklistMonitor { REASON_FRAMEWORK_DISCONNECT_FAST_RECONNECT, REASON_FRAMEWORK_DISCONNECT_CONNECTED_SCORE, REASON_NONLOCAL_DISCONNECT_CONNECTING, - REASON_FAILURE_NO_RESPONSE + REASON_FAILURE_NO_RESPONSE, + REASON_APP_DISALLOW }) @Retention(RetentionPolicy.SOURCE) public @interface FailureReason {} @@ -186,6 +190,8 @@ public class WifiBlocklistMonitor { "REASON_NONLOCAL_DISCONNECT_CONNECTING", true, false)); result.put(REASON_FAILURE_NO_RESPONSE, new BssidDisableReason( "REASON_FAILURE_NO_RESPONSE", true, true)); + result.put(REASON_APP_DISALLOW, new BssidDisableReason( + "REASON_APP_DISALLOW", false, false)); return result; } @@ -424,6 +430,14 @@ public class WifiBlocklistMonitor { } } + /** + * Clear the blocklisted bssid entries with a specific block reason. + * @param blockReason block reason from WifiBlocklistMonitor.REASON_* + */ + public void clearBssidBlocklistForReason(@FailureReason int blockReason) { + mBssidStatusMap.entrySet().removeIf(entry -> entry.getValue().blockReason == blockReason); + } + private String getFailureReasonString(@FailureReason int reasonCode) { if (reasonCode == INVALID_REASON) { return "INVALID_REASON"; @@ -729,6 +743,7 @@ public class WifiBlocklistMonitor { * Clears the blocklist for BSSIDs associated with the input SSID only. * @param ssid */ + @Keep public void clearBssidBlocklistForSsid(@NonNull String ssid) { int prevSize = mBssidStatusMap.size(); mBssidStatusMap.entrySet().removeIf(e -> { diff --git a/service/java/com/android/server/wifi/WifiCarrierInfoManager.java b/service/java/com/android/server/wifi/WifiCarrierInfoManager.java index 1e623a6987..1cafbecff1 100644 --- a/service/java/com/android/server/wifi/WifiCarrierInfoManager.java +++ b/service/java/com/android/server/wifi/WifiCarrierInfoManager.java @@ -66,6 +66,7 @@ import android.util.Pair; import android.util.SparseArray; import android.util.SparseBooleanArray; +import androidx.annotation.Keep; import androidx.annotation.RequiresApi; import com.android.internal.annotations.GuardedBy; @@ -883,6 +884,7 @@ public class WifiCarrierInfoManager { * @param subId The subscription ID of SIM card. * @return true if the IMSI encryption is required, otherwise false. */ + @Keep public boolean requiresImsiEncryption(int subId) { PersistableBundle bundle = getCarrierConfigForSubId(subId); if (bundle == null) { @@ -899,6 +901,7 @@ public class WifiCarrierInfoManager { * @param subId The subscription ID of SIM card. * @return true if the IMSI encryption is available, otherwise false. */ + @Keep public boolean isImsiEncryptionInfoAvailable(int subId) { return mImsiEncryptionInfoAvailable.get(subId); } @@ -909,6 +912,7 @@ public class WifiCarrierInfoManager { * @param config the instance of {@link WifiConfiguration} * @return the best match SubscriptionId */ + @Keep public int getBestMatchSubscriptionId(@NonNull WifiConfiguration config) { if (config == null) { Log.wtf(TAG, "getBestMatchSubscriptionId: Config must be NonNull!"); @@ -979,6 +983,7 @@ public class WifiCarrierInfoManager { * @param subId subscription ID of SIM card in the device. * @return true if the SIM is active and all info are available, otherwise false. */ + @Keep public boolean isSimReady(int subId) { if (!SubscriptionManager.isValidSubscriptionId(subId)) { return false; diff --git a/service/java/com/android/server/wifi/WifiConfigManager.java b/service/java/com/android/server/wifi/WifiConfigManager.java index 87d0a5b7dc..3eb3d0ed49 100644 --- a/service/java/com/android/server/wifi/WifiConfigManager.java +++ b/service/java/com/android/server/wifi/WifiConfigManager.java @@ -17,12 +17,16 @@ package com.android.server.wifi; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_PSK; +import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_SAE; import static android.net.wifi.WifiManager.AddNetworkResult.STATUS_INVALID_CONFIGURATION; import static android.net.wifi.WifiManager.AddNetworkResult.STATUS_INVALID_CONFIGURATION_ENTERPRISE; import static android.net.wifi.WifiManager.AddNetworkResult.STATUS_NO_PERMISSION_MODIFY_CONFIG; import static android.net.wifi.WifiManager.AddNetworkResult.STATUS_SUCCESS; import static android.net.wifi.WifiManager.WIFI_FEATURE_TRUST_ON_FIRST_USE; +import static com.android.server.wifi.WifiConfigurationUtil.validatePassword; + import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; @@ -60,6 +64,8 @@ import android.util.LocalLog; import android.util.Log; import android.util.Pair; +import androidx.annotation.Keep; + import com.android.internal.annotations.VisibleForTesting; import com.android.modules.utils.build.SdkLevel; import com.android.net.module.util.MacAddressUtils; @@ -81,6 +87,7 @@ import java.security.cert.CertificateParsingException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; +import java.util.BitSet; import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -224,7 +231,6 @@ public class WifiConfigManager { * Link networks only if the bssid in scan results for the networks match in the first * 16 ASCII chars in the bssid string. For example = "af:de:56;34:15:7" */ - @VisibleForTesting public static final int LINK_CONFIGURATION_BSSID_MATCH_LENGTH = 16; /** * Log tag for this class. @@ -815,6 +821,7 @@ public class WifiConfigManager { * * @return List of WifiConfiguration objects representing the networks. */ + @Keep public List<WifiConfiguration> getSavedNetworks(int targetUid) { return getConfiguredNetworks(true, true, targetUid); } @@ -840,6 +847,7 @@ public class WifiConfigManager { * @param networkId networkId of the requested network. * @return WifiConfiguration object if found, null otherwise. */ + @Keep public @Nullable WifiConfiguration getConfiguredNetwork(int networkId) { WifiConfiguration config = getInternalConfiguredNetwork(networkId); if (config == null) { @@ -1125,6 +1133,12 @@ public class WifiConfigManager { if (internalConfig.isSecurityType(newType)) { internalConfig.setSecurityParamsIsAddedByAutoUpgrade(newType, externalConfig.getDefaultSecurityParams().isAddedByAutoUpgrade()); + // Set to SAE-only in case we're updating a PSK/SAE config with an SAE-only + // passphrase. + if (oldType == SECURITY_TYPE_PSK && newType == SECURITY_TYPE_SAE + && !validatePassword(externalConfig.preSharedKey, false, false, false)) { + internalConfig.setSecurityParams(externalConfig.getSecurityParamsList()); + } } else if (externalConfig.isSecurityType(oldType)) { internalConfig.setSecurityParams(newType); internalConfig.addSecurityParams(oldType); @@ -1439,8 +1453,8 @@ public class WifiConfigManager { } WifiConfiguration newInternalConfig = null; - long supportedFeatures = mWifiInjector.getActiveModeWarden() - .getPrimaryClientModeManager().getSupportedFeatures(); + BitSet supportedFeatures = mWifiInjector.getActiveModeWarden() + .getPrimaryClientModeManager().getSupportedFeaturesBitSet(); // First check if we already have a network with the provided network id or configKey. WifiConfiguration existingInternalConfig = getInternalConfiguredNetwork(config); @@ -1589,7 +1603,7 @@ public class WifiConfigManager { // Validate an Enterprise network with Trust On First Use. if (config.isEnterprise() && config.enterpriseConfig.isTrustOnFirstUseEnabled()) { - if ((supportedFeatures & WIFI_FEATURE_TRUST_ON_FIRST_USE) == 0) { + if (!supportedFeatures.get(WIFI_FEATURE_TRUST_ON_FIRST_USE)) { Log.e(TAG, "Trust On First Use could not be set " + "when Trust On First Use is not supported."); return new Pair<>( @@ -1777,6 +1791,7 @@ public class WifiConfigManager { * @param uid UID of the app requesting the network addition/modification. * @return NetworkUpdateResult object representing status of the update. */ + @Keep public NetworkUpdateResult addOrUpdateNetwork(WifiConfiguration config, int uid) { return addOrUpdateNetwork(config, uid, null, false); } @@ -2183,6 +2198,7 @@ public class WifiConfigManager { * @param reason reason to update the network. * @return true if the input configuration has been updated, false otherwise. */ + @Keep public boolean updateNetworkSelectionStatus(int networkId, int reason) { WifiConfiguration config = getInternalConfiguredNetwork(networkId); if (config == null) { @@ -2731,6 +2747,7 @@ public class WifiConfigManager { * * @return network Id corresponding to the last selected network. */ + @Keep public int getLastSelectedNetwork() { return mLastSelectedNetworkId; } @@ -2757,6 +2774,7 @@ public class WifiConfigManager { * * @return timestamp in milliseconds from boot when this was set. */ + @Keep public long getLastSelectedTimeStamp() { return mLastSelectedTimeStamp; } @@ -2768,6 +2786,7 @@ public class WifiConfigManager { * @param networkId network ID corresponding to the network. * @return existing {@link ScanDetailCache} entry if one exists or null. */ + @Keep public ScanDetailCache getScanDetailCacheForNetwork(int networkId) { return mScanDetailCaches.get(networkId); } @@ -2844,6 +2863,7 @@ public class WifiConfigManager { * @return WifiConfiguration object representing the network corresponding to the scanResult, * null if none exists. */ + @Keep public WifiConfiguration getSavedNetworkForScanResult(@NonNull ScanResult scanResult) { WifiConfiguration config = null; try { @@ -3554,8 +3574,8 @@ public class WifiConfigManager { List<WifiConfiguration> configurations, Map<String, String> macAddressMapping) { - long supportedFeatures = mWifiInjector.getActiveModeWarden() - .getPrimaryClientModeManager().getSupportedFeatures(); + BitSet supportedFeatures = mWifiInjector.getActiveModeWarden() + .getPrimaryClientModeManager().getSupportedFeaturesBitSet(); for (WifiConfiguration configuration : configurations) { if (!WifiConfigurationUtil.validate( @@ -3593,8 +3613,8 @@ public class WifiConfigManager { * @param configurations list of configurations retrieved from store. */ private void loadInternalDataFromUserStore(List<WifiConfiguration> configurations) { - long supportedFeatures = mWifiInjector.getActiveModeWarden() - .getPrimaryClientModeManager().getSupportedFeatures(); + BitSet supportedFeatures = mWifiInjector.getActiveModeWarden() + .getPrimaryClientModeManager().getSupportedFeaturesBitSet(); for (WifiConfiguration configuration : configurations) { if (!WifiConfigurationUtil.validate( diff --git a/service/java/com/android/server/wifi/WifiConfigurationUtil.java b/service/java/com/android/server/wifi/WifiConfigurationUtil.java index 501a071b34..612b7b4e59 100644 --- a/service/java/com/android/server/wifi/WifiConfigurationUtil.java +++ b/service/java/com/android/server/wifi/WifiConfigurationUtil.java @@ -22,11 +22,13 @@ import static android.net.wifi.hotspot2.PasspointConfiguration.MAX_NUMBER_OF_OI; import static android.net.wifi.hotspot2.PasspointConfiguration.MAX_OI_VALUE; import static android.net.wifi.hotspot2.PasspointConfiguration.MAX_URL_BYTES; +import static com.android.server.wifi.util.GeneralUtil.longToBitset; import static com.android.server.wifi.util.NativeUtil.addEnclosingQuotes; import android.annotation.SuppressLint; import android.net.IpConfiguration; import android.net.MacAddress; +import android.net.ProxyInfo; import android.net.StaticIpConfiguration; import android.net.wifi.SecurityParams; import android.net.wifi.WifiConfiguration; @@ -41,6 +43,8 @@ import android.text.TextUtils; import android.util.Log; import android.util.Pair; +import androidx.annotation.Keep; + import com.android.internal.annotations.VisibleForTesting; import com.android.modules.utils.build.SdkLevel; import com.android.server.wifi.util.NativeUtil; @@ -78,6 +82,7 @@ public class WifiConfigurationUtil { private static final int WEP104_KEY_BYTES_LEN = 13; private static final int WEP40_KEY_BYTES_LEN = 5; private static final int MAX_STRING_LENGTH = 512; + private static final int MAX_ENTRY_SIZE = 100; @VisibleForTesting public static final String PASSWORD_MASK = "*"; @@ -103,6 +108,7 @@ public class WifiConfigurationUtil { /** * Helper method to check if the provided |config| corresponds to a PSK network or not. */ + @Keep public static boolean isConfigForPskNetwork(WifiConfiguration config) { return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_PSK); } @@ -124,6 +130,7 @@ public class WifiConfigurationUtil { /** * Helper method to check if the provided |config| corresponds to an SAE network or not. */ + @Keep public static boolean isConfigForSaeNetwork(WifiConfiguration config) { return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_SAE); } @@ -131,6 +138,7 @@ public class WifiConfigurationUtil { /** * Helper method to check if the provided |config| corresponds to an OWE network or not. */ + @Keep public static boolean isConfigForOweNetwork(WifiConfiguration config) { return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_OWE); } @@ -178,6 +186,7 @@ public class WifiConfigurationUtil { /** * Helper method to check if the provided |config| corresponds to a WEP network or not. */ + @Keep public static boolean isConfigForWepNetwork(WifiConfiguration config) { return config.isSecurityType(WifiConfiguration.SECURITY_TYPE_WEP); } @@ -194,6 +203,7 @@ public class WifiConfigurationUtil { * Helper method to check if the provided |config| corresponds to an open or enhanced * open network, or not. */ + @Keep public static boolean isConfigForOpenNetwork(WifiConfiguration config) { return (!(isConfigForWepNetwork(config) || isConfigForPskNetwork(config) || isConfigForWapiPskNetwork(config) || isConfigForWapiCertNetwork(config) @@ -515,7 +525,10 @@ public class WifiConfigurationUtil { return true; } - private static boolean validatePassword(String password, boolean isAdd, boolean isSae, + /** + * Checks that a password is formatted correctly. + */ + public static boolean validatePassword(String password, boolean isAdd, boolean isSae, boolean isWapi) { if (isAdd) { if (password == null) { @@ -721,6 +734,42 @@ public class WifiConfigurationUtil { Log.e(TAG, "validateIpConfiguration failed: null static ip Address"); return false; } + if (staticIpConfig.getDnsServers() != null + && staticIpConfig.getDnsServers().size() > MAX_ENTRY_SIZE) { + Log.e(TAG, "validateIpConfiguration failed: too many DNS server"); + return false; + } + if (staticIpConfig.getDomains() != null + && staticIpConfig.getDomains().length() > MAX_STRING_LENGTH) { + Log.e(TAG, "validateIpConfiguration failed: domain name too long"); + return false; + } + } + ProxyInfo proxyInfo = ipConfig.getHttpProxy(); + if (proxyInfo != null) { + if (!proxyInfo.isValid()) { + Log.e(TAG, "validateIpConfiguration failed: invalid proxy info"); + return false; + } + if (proxyInfo.getHost() != null + && proxyInfo.getHost().length() > MAX_STRING_LENGTH) { + Log.e(TAG, "validateIpConfiguration failed: host name too long"); + return false; + } + if (proxyInfo.getExclusionList() != null) { + if (proxyInfo.getExclusionList().length > MAX_ENTRY_SIZE) { + Log.e(TAG, "validateIpConfiguration failed: too many entry in exclusion list"); + return false; + } + int sum = 0; + for (String s : proxyInfo.getExclusionList()) { + sum += s.length(); + if (sum > MAX_STRING_LENGTH) { + Log.e(TAG, "validateIpConfiguration failed: exclusion list size too large"); + return false; + } + } + } } return true; } @@ -753,7 +802,7 @@ public class WifiConfigurationUtil { * update could contain only the fields that are being changed. * @return true if the parameters are valid, false otherwise. */ - public static boolean validate(WifiConfiguration config, long supportedFeatureSet, + public static boolean validate(WifiConfiguration config, BitSet supportedFeatureSet, boolean isAdd) { if (!validateSsid(config.SSID, isAdd)) { return false; @@ -799,7 +848,7 @@ public class WifiConfigurationUtil { return false; } if (config.isSecurityType(WifiConfiguration.SECURITY_TYPE_DPP) - && (supportedFeatureSet & WifiManager.WIFI_FEATURE_DPP_AKM) == 0) { + && !supportedFeatureSet.get(WifiManager.WIFI_FEATURE_DPP_AKM)) { Log.e(TAG, "DPP AKM is not supported"); return false; } @@ -826,6 +875,15 @@ public class WifiConfigurationUtil { return true; } + /** + * Please check {@link #validate(WifiConfiguration, BitSet, boolean)} + */ + @Keep + public static boolean validate(WifiConfiguration config, long supportedFeatureSet, + boolean isAdd) { + return validate(config, longToBitset(supportedFeatureSet), isAdd); + } + private static boolean validateStringField(String field, int maxLength) { return field == null || field.length() <= maxLength; } @@ -1182,14 +1240,14 @@ public class WifiConfigurationUtil { } private static boolean isSecurityParamsSupported(SecurityParams params) { - final long wifiFeatures = WifiInjector.getInstance() + final BitSet wifiFeatures = WifiInjector.getInstance() .getActiveModeWarden().getPrimaryClientModeManager() - .getSupportedFeatures(); + .getSupportedFeaturesBitSet(); switch (params.getSecurityType()) { case WifiConfiguration.SECURITY_TYPE_SAE: - return 0 != (wifiFeatures & WifiManager.WIFI_FEATURE_WPA3_SAE); + return wifiFeatures.get(WifiManager.WIFI_FEATURE_WPA3_SAE); case WifiConfiguration.SECURITY_TYPE_OWE: - return 0 != (wifiFeatures & WifiManager.WIFI_FEATURE_OWE); + return wifiFeatures.get(WifiManager.WIFI_FEATURE_OWE); } return true; } diff --git a/service/java/com/android/server/wifi/WifiConnectivityHelper.java b/service/java/com/android/server/wifi/WifiConnectivityHelper.java index e1c43ecd86..8beafc6e33 100644 --- a/service/java/com/android/server/wifi/WifiConnectivityHelper.java +++ b/service/java/com/android/server/wifi/WifiConnectivityHelper.java @@ -25,6 +25,7 @@ import com.android.internal.annotations.VisibleForTesting; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.BitSet; /** * This class provides helper functions for Wifi connectivity related modules to @@ -63,10 +64,10 @@ public class WifiConnectivityHelper { ClientModeManager primaryManager = mWifiInjector.getActiveModeWarden().getPrimaryClientModeManager(); - long fwFeatureSet = primaryManager.getSupportedFeatures(); - Log.d(TAG, "Firmware supported feature set: " + Long.toHexString(fwFeatureSet)); + BitSet fwFeatureSet = primaryManager.getSupportedFeaturesBitSet(); + Log.d(TAG, "Firmware supported feature set: " + fwFeatureSet); - if ((fwFeatureSet & WIFI_FEATURE_CONTROL_ROAMING) == 0) { + if (!fwFeatureSet.get(WIFI_FEATURE_CONTROL_ROAMING)) { Log.d(TAG, "Firmware roaming is not supported"); return true; } diff --git a/service/java/com/android/server/wifi/WifiConnectivityManager.java b/service/java/com/android/server/wifi/WifiConnectivityManager.java index b7e12e0686..03e0ac007c 100644 --- a/service/java/com/android/server/wifi/WifiConnectivityManager.java +++ b/service/java/com/android/server/wifi/WifiConnectivityManager.java @@ -69,7 +69,6 @@ import com.android.server.wifi.hotspot2.PasspointManager; import com.android.server.wifi.proto.WifiStatsLog; import com.android.server.wifi.scanner.WifiScannerInternal; import com.android.server.wifi.util.WifiPermissionsUtil; -import com.android.wifi.flags.FeatureFlags; import com.android.wifi.resources.R; import java.io.FileDescriptor; @@ -184,7 +183,6 @@ public class WifiConnectivityManager { private final WifiChannelUtilization mWifiChannelUtilization; private final PowerManager mPowerManager; private final DeviceConfigFacade mDeviceConfigFacade; - private final FeatureFlags mFeatureFlags; private final ActiveModeWarden mActiveModeWarden; private final FrameworkFacade mFrameworkFacade; private final WifiPermissionsUtil mWifiPermissionsUtil; @@ -203,6 +201,7 @@ public class WifiConnectivityManager { private int mInitialScanState = INITIAL_SCAN_STATE_COMPLETE; private boolean mAutoJoinEnabledExternal = true; // enabled by default private boolean mAutoJoinEnabledExternalSetByDeviceAdmin = false; + private int mAutojoinDisallowedSecurityTypes = 0; // restrict none by default private boolean mUntrustedConnectionAllowed = false; private Set<Integer> mRestrictedConnectionAllowedUids = new ArraySet<>(); private boolean mOemPaidConnectionAllowed = false; @@ -663,6 +662,14 @@ public class WifiConnectivityManager { // If cellular is unavailable, re-enable Wi-Fi networks disabled by pinning to cell. mConfigManager.considerStopRestrictingAutoJoinToSubscriptionId(); + if (isFullScan) { + if (mVerboseLoggingEnabled) { + Log.v(TAG, "Clearing blocklist for REASON_FRAMEWORK_DISCONNECT_FAST_RECONNECT"); + } + mWifiBlocklistMonitor.clearBssidBlocklistForReason( + WifiBlocklistMonitor.REASON_FRAMEWORK_DISCONNECT_FAST_RECONNECT); + } + // Check if any blocklisted BSSIDs can be freed. List<ScanDetail> enabledDetails = mWifiBlocklistMonitor.tryEnablingBlockedBssids(scanDetails); @@ -684,13 +691,11 @@ public class WifiConnectivityManager { List<WifiCandidates.Candidate> candidates = mNetworkSelector.getCandidatesFromScan( scanDetails, bssidBlocklist, cmmStates, mUntrustedConnectionAllowed, mOemPaidConnectionAllowed, mOemPrivateConnectionAllowed, - mRestrictedConnectionAllowedUids, skipSufficiencyCheck); - + mRestrictedConnectionAllowedUids, skipSufficiencyCheck, + mAutojoinDisallowedSecurityTypes); // Filter candidates before caching to avoid reconnecting on failure - if (mFeatureFlags.delayedCarrierNetworkSelection()) { - candidates = filterDelayedCarrierSelectionCandidates(candidates, listenerName, - isFullScan); - } + candidates = filterDelayedCarrierSelectionCandidates(candidates, listenerName, + isFullScan); mLatestCandidates = candidates; mLatestCandidatesTimestampMs = mClock.getElapsedSinceBootMillis(); @@ -1507,7 +1512,6 @@ public class WifiConnectivityManager { mPasspointManager = passpointManager; mMultiInternetManager = multiInternetManager; mDeviceConfigFacade = deviceConfigFacade; - mFeatureFlags = mDeviceConfigFacade.getFeatureFlags(); mActiveModeWarden = activeModeWarden; mFrameworkFacade = frameworkFacade; mWifiGlobals = wifiGlobals; @@ -3669,6 +3673,22 @@ public class WifiConnectivityManager { } /** + * Set auto join restriction on select security types + */ + public void setAutojoinDisallowedSecurityTypes(int restrictions) { + localLog("Set auto join restriction on select security types - restrictions: " + + restrictions); + mAutojoinDisallowedSecurityTypes = restrictions; + } + + /** + * Return auto join restriction on select security types + */ + public int getAutojoinDisallowedSecurityTypes() { + return mAutojoinDisallowedSecurityTypes; + } + + /** * Check if multi internet connection exists. * * @return true if multi internet connection exists. diff --git a/service/java/com/android/server/wifi/WifiCountryCode.java b/service/java/com/android/server/wifi/WifiCountryCode.java index c307c010b8..3b041cd25f 100644 --- a/service/java/com/android/server/wifi/WifiCountryCode.java +++ b/service/java/com/android/server/wifi/WifiCountryCode.java @@ -30,6 +30,8 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; +import androidx.annotation.Keep; + import com.android.modules.utils.build.SdkLevel; import com.android.server.wifi.hotspot2.NetworkDetail; import com.android.server.wifi.p2p.WifiP2pMetrics; @@ -514,7 +516,7 @@ public class WifiCountryCode { } private boolean shouldDisconnectWifiToForceUpdate() { - if (!hasCalling() || mWifiCarrierInfoManager.isWifiCallingAvailable()) { + if (hasCalling() && mWifiCarrierInfoManager.isWifiCallingAvailable()) { return false; } @@ -564,6 +566,7 @@ public class WifiCountryCode { * @return The current Wifi Country Code resolved from various sources. Returns null when there * is no Country Code available. */ + @Keep @Nullable public synchronized String getCountryCode() { initializeTelephonyCountryCodeIfNeeded(); @@ -760,7 +763,7 @@ public class WifiCountryCode { /** * Method to check if current driver Country Code is in the world mode */ - private boolean isDriverCountryCodeWorldMode() { + public boolean isDriverCountryCodeWorldMode() { if (mDriverCountryCode == null) { return true; } diff --git a/service/java/com/android/server/wifi/WifiDataStall.java b/service/java/com/android/server/wifi/WifiDataStall.java index 97131f0c96..e7f01c83ad 100644 --- a/service/java/com/android/server/wifi/WifiDataStall.java +++ b/service/java/com/android/server/wifi/WifiDataStall.java @@ -22,6 +22,8 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.net.wifi.ScanResult; +import android.net.wifi.WifiAnnotations; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager.DeviceMobilityState; import android.os.Handler; @@ -84,6 +86,7 @@ public class WifiDataStall { private boolean mPhoneStateListenerEnabled = false; private int mTxTputKbps = INVALID_THROUGHPUT; private int mRxTputKbps = INVALID_THROUGHPUT; + private @WifiAnnotations.ChannelWidth int mChannelBandwidth = ScanResult.UNSPECIFIED; /** @hide */ @IntDef(prefix = { "CELLULAR_DATA_" }, value = { @@ -205,6 +208,7 @@ public class WifiDataStall { mIsThroughputSufficient = true; mTxTputKbps = INVALID_THROUGHPUT; mRxTputKbps = INVALID_THROUGHPUT; + mChannelBandwidth = ScanResult.UNSPECIFIED; } private void createTelephonyManagerForDefaultDataSubIfNeeded() { @@ -344,6 +348,22 @@ public class WifiDataStall { } /** + * Get the number of tx bytes transmitted on current interface since the interface is created + * @return the number of tx bytes transmitted + */ + public long getTxTransmittedBytes() { + return mLastTxBytes; + } + + /** + * Get the number of rx bytes transmitted on current interface since the interface is created + * @return the number of tx bytes transmitted + */ + public long getRxTransmittedBytes() { + return mLastRxBytes; + } + + /** * Update data stall detection, check throughput sufficiency and report wifi health stat * with the latest link layer stats * @param connectionCapabilities Connection capabilities. @@ -466,12 +486,16 @@ public class WifiDataStall { mIsThroughputSufficient = isThroughputSufficientInternal(mTxTputKbps, mRxTputKbps, isTxTrafficHigh, isRxTrafficHigh, timeDeltaLastTwoPollsMs, txBytes, rxBytes); + mChannelBandwidth = connectionCapabilities != null + ? mChannelBandwidth = connectionCapabilities.channelBandwidth + : ScanResult.UNSPECIFIED; + int maxTimeDeltaMs = mWifiGlobals.getPollRssiIntervalMillis() + MAX_TIME_MARGIN_LAST_TWO_POLLS_MS; if (timeDeltaLastTwoPollsMs > 0 && timeDeltaLastTwoPollsMs <= maxTimeDeltaMs) { mWifiMetrics.incrementConnectionDuration(ifaceName, timeDeltaLastTwoPollsMs, mIsThroughputSufficient, mIsCellularDataAvailable, wifiInfo.getRssi(), - mTxTputKbps, mRxTputKbps); + mTxTputKbps, mRxTputKbps, txLinkSpeedMbps, rxLinkSpeedMbps, mChannelBandwidth); } boolean possibleDataStallTx = isTxTputLow diff --git a/service/java/com/android/server/wifi/WifiDeviceStateChangeManager.java b/service/java/com/android/server/wifi/WifiDeviceStateChangeManager.java index 74df0a5324..0b6ffc68d1 100644 --- a/service/java/com/android/server/wifi/WifiDeviceStateChangeManager.java +++ b/service/java/com/android/server/wifi/WifiDeviceStateChangeManager.java @@ -16,15 +16,23 @@ package com.android.server.wifi; +import android.annotation.SuppressLint; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.net.wifi.util.Environment; import android.os.Handler; import android.os.PowerManager; +import android.security.Flags; +import android.security.advancedprotection.AdvancedProtectionManager; import android.text.TextUtils; import android.util.ArraySet; +import com.android.internal.annotations.VisibleForTesting; +import com.android.modules.utils.HandlerExecutor; +import com.android.wifi.flags.FeatureFlags; + import java.util.Set; /** A centralized manager to handle all the device state changes */ @@ -33,6 +41,10 @@ public class WifiDeviceStateChangeManager { private final Context mContext; private final PowerManager mPowerManager; + private final WifiInjector mWifiInjector; + private AdvancedProtectionManager mAdvancedProtectionManager; + private FeatureFlags mFeatureFlags; + private final Set<StateChangeCallback> mChangeCallbackList = new ArraySet<>(); private boolean mIsWifiServiceStarted = false; @@ -47,17 +59,28 @@ public class WifiDeviceStateChangeManager { * @param screenOn true for ON, false otherwise */ default void onScreenStateChanged(boolean screenOn) {} + + /** + * Called when the Advanced protection mode state changes + * + * @param apmOn true for ON, false otherwise + */ + default void onAdvancedProtectionModeStateChanged(boolean apmOn) {} } /** Create the instance of WifiDeviceStateChangeManager. */ - public WifiDeviceStateChangeManager(Context context, Handler handler) { + public WifiDeviceStateChangeManager(Context context, Handler handler, + WifiInjector wifiInjector) { mHandler = handler; mContext = context; + mWifiInjector = wifiInjector; mPowerManager = mContext.getSystemService(PowerManager.class); } /** Handle the boot completed event. Start to register the receiver and callback. */ + @SuppressLint("NewApi") public void handleBootCompleted() { + mFeatureFlags = mWifiInjector.getDeviceConfigFacade().getFeatureFlags(); IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_SCREEN_ON); filter.addAction(Intent.ACTION_SCREEN_OFF); @@ -76,17 +99,46 @@ public class WifiDeviceStateChangeManager { }, filter); handleScreenStateChanged(mPowerManager.isInteractive()); + if (Environment.isSdkAtLeastB() && mFeatureFlags.wepDisabledInApm() + && isAapmApiFlagEnabled()) { + mAdvancedProtectionManager = + mContext.getSystemService(AdvancedProtectionManager.class); + if (mAdvancedProtectionManager != null) { + mAdvancedProtectionManager.registerAdvancedProtectionCallback( + new HandlerExecutor(mHandler), + state -> { + handleAdvancedProtectionModeStateChanged(state); + }); + handleAdvancedProtectionModeStateChanged( + mAdvancedProtectionManager.isAdvancedProtectionEnabled()); + } else { + handleAdvancedProtectionModeStateChanged(false); + } + } else { + handleAdvancedProtectionModeStateChanged(false); + } mIsWifiServiceStarted = true; } + @VisibleForTesting + public boolean isAapmApiFlagEnabled() { + return Flags.aapmApi(); + } /** * Register a state change callback. When the state is changed, caller with receive the callback * event */ + @SuppressLint("NewApi") public void registerStateChangeCallback(StateChangeCallback callback) { mChangeCallbackList.add(callback); if (!mIsWifiServiceStarted) return; callback.onScreenStateChanged(mPowerManager.isInteractive()); + if (Environment.isSdkAtLeastB() && mAdvancedProtectionManager != null) { + callback.onAdvancedProtectionModeStateChanged( + mAdvancedProtectionManager.isAdvancedProtectionEnabled()); + } else { + callback.onAdvancedProtectionModeStateChanged(false); + } } /** @@ -101,4 +153,10 @@ public class WifiDeviceStateChangeManager { callback.onScreenStateChanged(screenOn); } } + + private void handleAdvancedProtectionModeStateChanged(boolean apmOn) { + for (StateChangeCallback callback : mChangeCallbackList) { + callback.onAdvancedProtectionModeStateChanged(apmOn); + } + } } diff --git a/service/java/com/android/server/wifi/WifiDiagnostics.java b/service/java/com/android/server/wifi/WifiDiagnostics.java index 4c488685ec..87fb91d948 100644 --- a/service/java/com/android/server/wifi/WifiDiagnostics.java +++ b/service/java/com/android/server/wifi/WifiDiagnostics.java @@ -1053,6 +1053,13 @@ public class WifiDiagnostics { return; } + if (bugDetail.equals("Subsystem Restart") && !mContext.getResources().getBoolean( + R.bool.config_wifi_subsystem_restart_bugreport_enabled)) { + Log.d(TAG, "config_wifi_subsystem_restart_bugreport_enabled is disabled, skip " + + bugTitle + "(" + bugDetail + ")"); + return; + } + if (!shouldTakeBugreport(bugTitle, bugDetail)) { return; } diff --git a/service/java/com/android/server/wifi/WifiDialogManager.java b/service/java/com/android/server/wifi/WifiDialogManager.java index cc175b61c7..15485e9c80 100644 --- a/service/java/com/android/server/wifi/WifiDialogManager.java +++ b/service/java/com/android/server/wifi/WifiDialogManager.java @@ -232,31 +232,15 @@ public class WifiDialogManager { @AnyThread public void launchDialog() { if (mInternalHandle != null) { - mWifiThreadRunner.post(() -> mInternalHandle.launchDialog(0), + mWifiThreadRunner.post(() -> mInternalHandle.launchDialog(), TAG + "#launchDialog"); } else if (mLegacyHandle != null) { - mWifiThreadRunner.post(() -> mLegacyHandle.launchDialog(0), + mWifiThreadRunner.post(() -> mLegacyHandle.launchDialog(), TAG + "#launchDialog"); } } /** - * Launches the dialog with a timeout before it is auto-cancelled. - * @param timeoutMs timeout in milliseconds before the dialog is auto-cancelled. A value <=0 - * indicates no timeout. - */ - @AnyThread - public void launchDialog(long timeoutMs) { - if (mInternalHandle != null) { - mWifiThreadRunner.post(() -> mInternalHandle.launchDialog(timeoutMs), - TAG + "#launchDialogTimeout"); - } else if (mLegacyHandle != null) { - mWifiThreadRunner.post(() -> mLegacyHandle.launchDialog(timeoutMs), - TAG + "#launchDialogTimeout"); - } - } - - /** * Dismisses the dialog. Dialogs will automatically be dismissed once the user replies, but * this method may be used to dismiss unanswered dialogs that are no longer needed. */ @@ -291,9 +275,9 @@ public class WifiDialogManager { } /** - * @see {@link DialogHandle#launchDialog(long)} + * @see DialogHandle#launchDialog() */ - void launchDialog(long timeoutMs) { + void launchDialog() { if (mIntent == null) { Log.e(TAG, "Cannot launch dialog with null Intent!"); return; @@ -303,7 +287,6 @@ public class WifiDialogManager { return; } registerDialog(); - mIntent.putExtra(WifiManager.EXTRA_DIALOG_TIMEOUT_MS, timeoutMs); mIntent.putExtra(WifiManager.EXTRA_DIALOG_ID, mDialogId); boolean launched = false; // Collapse the QuickSettings since we can't show WifiDialog dialogs over it. @@ -474,10 +457,8 @@ public class WifiDialogManager { final String mNeutralButtonText; @Nullable final SimpleDialogCallback mCallback; @Nullable final WifiThreadRunner mCallbackThreadRunner; - private Runnable mTimeoutRunnable; private AlertDialog mAlertDialog; int mWindowType = WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG; - long mTimeoutMs = 0; LegacySimpleDialogHandle( final String title, @@ -525,18 +506,12 @@ public class WifiDialogManager { mCallbackThreadRunner = callbackThreadRunner; } - void launchDialog(long timeoutMs) { + void launchDialog() { if (mAlertDialog != null && mAlertDialog.isShowing()) { // Dialog is already launched. Dismiss and create a new one. mAlertDialog.setOnDismissListener(null); mAlertDialog.dismiss(); } - if (mTimeoutRunnable != null) { - // Reset the timeout runnable if one has already been created. - mWifiThreadRunner.removeCallbacks(mTimeoutRunnable); - mTimeoutRunnable = null; - } - mTimeoutMs = timeoutMs; mAlertDialog = mFrameworkFacade.makeAlertDialogBuilder( new ContextThemeWrapper(mContext, R.style.wifi_dialog)) .setTitle(mTitle) @@ -579,10 +554,6 @@ public class WifiDialogManager { }) .setOnDismissListener((dialogDismiss) -> { mWifiThreadRunner.post(() -> { - if (mTimeoutRunnable != null) { - mWifiThreadRunner.removeCallbacks(mTimeoutRunnable); - mTimeoutRunnable = null; - } mAlertDialog = null; mActiveLegacySimpleDialogs.remove(this); }, mTitle + "#onDismiss"); @@ -609,11 +580,6 @@ public class WifiDialogManager { if (messageView != null) { messageView.setMovementMethod(LinkMovementMethod.getInstance()); } - if (mTimeoutMs > 0) { - mTimeoutRunnable = mAlertDialog::cancel; - mWifiThreadRunner.postDelayed(mTimeoutRunnable, mTimeoutMs, - TAG + "#cancelDialog"); - } mActiveLegacySimpleDialogs.add(this); } @@ -632,7 +598,7 @@ public class WifiDialogManager { void changeWindowType(int windowType) { mWindowType = windowType; if (mActiveLegacySimpleDialogs.contains(this)) { - launchDialog(mTimeoutMs); + launchDialog(); } } } @@ -657,7 +623,7 @@ public class WifiDialogManager { void onNeutralButtonClicked(); /** - * The dialog was cancelled (back button or home button or timeout). + * The dialog was cancelled (back button or home button). */ void onCancelled(); } @@ -897,6 +863,7 @@ public class WifiDialogManager { final @Nullable String deviceName, final boolean isPinRequested, @Nullable String displayPin, + int timeoutMs, int displayId, @Nullable P2pInvitationReceivedDialogCallback callback, @Nullable WifiThreadRunner callbackThreadRunner) { @@ -904,7 +871,8 @@ public class WifiDialogManager { if (intent != null) { intent.putExtra(WifiManager.EXTRA_P2P_DEVICE_NAME, deviceName) .putExtra(WifiManager.EXTRA_P2P_PIN_REQUESTED, isPinRequested) - .putExtra(WifiManager.EXTRA_P2P_DISPLAY_PIN, displayPin); + .putExtra(WifiManager.EXTRA_P2P_DISPLAY_PIN, displayPin) + .putExtra(WifiManager.EXTRA_DIALOG_TIMEOUT_MS, timeoutMs); setIntent(intent); } setDisplayId(displayId); @@ -952,13 +920,14 @@ public class WifiDialogManager { * @param deviceName Name of the device sending the invitation. * @param isPinRequested True if a PIN was requested and a PIN input UI should be shown. * @param displayPin Display PIN, or {@code null} if no PIN should be displayed + * @param timeoutMs Timeout for the dialog in milliseconds. 0 indicates no timeout. * @param displayId The ID of the Display on which to place the dialog * (Display.DEFAULT_DISPLAY * refers to the default display) * @param callback Callback to receive the dialog response. * @param callbackThreadRunner WifiThreadRunner to run the callback on. * @return DialogHandle Handle for the dialog, or {@code null} if no dialog could - * be created. + * be created. */ @AnyThread @NonNull @@ -966,6 +935,7 @@ public class WifiDialogManager { @Nullable String deviceName, boolean isPinRequested, @Nullable String displayPin, + int timeoutMs, int displayId, @Nullable P2pInvitationReceivedDialogCallback callback, @Nullable WifiThreadRunner callbackThreadRunner) { @@ -974,6 +944,7 @@ public class WifiDialogManager { deviceName, isPinRequested, displayPin, + timeoutMs, displayId, callback, callbackThreadRunner) diff --git a/service/java/com/android/server/wifi/WifiGlobals.java b/service/java/com/android/server/wifi/WifiGlobals.java index 1b37c07f91..1c4e549cf1 100644 --- a/service/java/com/android/server/wifi/WifiGlobals.java +++ b/service/java/com/android/server/wifi/WifiGlobals.java @@ -25,6 +25,8 @@ import android.util.ArraySet; import android.util.Log; import android.util.SparseArray; +import androidx.annotation.Keep; + import com.android.internal.annotations.VisibleForTesting; import com.android.modules.utils.build.SdkLevel; import com.android.server.wifi.WifiBlocklistMonitor.CarrierSpecificEapFailureConfig; @@ -189,6 +191,7 @@ public class WifiGlobals { } /** Get the interval between RSSI polls, in milliseconds. */ + @Keep public int getPollRssiIntervalMillis() { return mPollRssiIntervalMillis.get(); } @@ -221,6 +224,7 @@ public class WifiGlobals { } /** Sets whether CMD_IP_REACHABILITY_LOST events should trigger disconnects. */ + @Keep public void setIpReachabilityDisconnectEnabled(boolean enabled) { mIpReachabilityDisconnectEnabled.set(enabled); } @@ -653,6 +657,15 @@ public class WifiGlobals { .getInteger(R.integer.config_wifiDisableTemporaryMaximumDurationMs); } + /** + * Returns whether device support Wi-Fi 7 multi-link device Soft Ap. + */ + public boolean isMLDApSupported() { + int numberOfMLDSupported = mWifiResourceCache + .getInteger(R.integer.config_wifiSoftApMaxNumberMLDSupported); + return numberOfMLDSupported != 0; + } + /** Dump method for debugging */ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("Dump of WifiGlobals"); diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java index ddb7a0f48a..801b52cebe 100644 --- a/service/java/com/android/server/wifi/WifiInjector.java +++ b/service/java/com/android/server/wifi/WifiInjector.java @@ -51,6 +51,8 @@ import android.telephony.TelephonyManager; import android.util.LocalLog; import android.util.Log; +import androidx.annotation.Keep; + import com.android.internal.annotations.VisibleForTesting; import com.android.modules.utils.BackgroundThread; import com.android.modules.utils.build.SdkLevel; @@ -270,6 +272,9 @@ public class WifiInjector { private final TwtManager mTwtManager; private final WifiVoipDetector mWifiVoipDetector; private final boolean mHasActiveModem; + private final RunnerHandler mWifiHandler; + private boolean mVerboseLoggingEnabled; + @Nullable private final WepNetworkUsageController mWepNetworkUsageController; public WifiInjector(WifiContext context) { if (context == null) { @@ -298,20 +303,21 @@ public class WifiInjector { mWifiMonitor = new WifiMonitor(); mBatteryStats = context.getSystemService(BatteryStatsManager.class); mWifiP2pMetrics = new WifiP2pMetrics(mClock, mContext); - RunnerHandler wifiHandler = new RunnerHandler(wifiLooper, context.getResources().getInteger( + mWifiHandler = new RunnerHandler(wifiLooper, context.getResources().getInteger( R.integer.config_wifiConfigurationWifiRunnerThresholdInMs), mWifiHandlerLocalLog); - mWifiDeviceStateChangeManager = new WifiDeviceStateChangeManager(context, wifiHandler); + mWifiDeviceStateChangeManager = new WifiDeviceStateChangeManager(context, mWifiHandler, + this); + mWifiGlobals = new WifiGlobals(mContext); mWifiMetrics = new WifiMetrics(mContext, mFrameworkFacade, mClock, wifiLooper, awareMetrics, rttMetrics, new WifiPowerMetrics(mBatteryStats), mWifiP2pMetrics, - mDppMetrics, mWifiMonitor, mWifiDeviceStateChangeManager); + mDppMetrics, mWifiMonitor, mWifiDeviceStateChangeManager, mWifiGlobals); mWifiDiagnosticsHandlerThread = new HandlerThread("WifiDiagnostics"); mWifiDiagnosticsHandlerThread.start(); mWifiNotificationManager = new WifiNotificationManager(mContext); - mWifiGlobals = new WifiGlobals(mContext); mScoringParams = new ScoringParams(mContext); mWifiChannelUtilizationScan = new WifiChannelUtilization(mClock, mContext); mSettingsMigrationDataHolder = new SettingsMigrationDataHolder(mContext); @@ -325,35 +331,35 @@ public class WifiInjector { mWifiBackupRestore = new WifiBackupRestore(mWifiPermissionsUtil); mSoftApBackupRestore = new SoftApBackupRestore(mContext, mSettingsMigrationDataHolder); mWifiStateTracker = new WifiStateTracker(mBatteryStats); - mWifiThreadRunner = new WifiThreadRunner(wifiHandler); + mWifiThreadRunner = new WifiThreadRunner(mWifiHandler); mWifiDialogManager = new WifiDialogManager(mContext, mWifiThreadRunner, mFrameworkFacade, this); - mSsidTranslator = new SsidTranslator(mContext, wifiHandler); + mSsidTranslator = new SsidTranslator(mContext, mWifiHandler); mPasspointProvisionerHandlerThread = new HandlerThread("PasspointProvisionerHandlerThread"); mPasspointProvisionerHandlerThread.start(); - mDeviceConfigFacade = new DeviceConfigFacade(mContext, wifiHandler, mWifiMetrics); + mDeviceConfigFacade = new DeviceConfigFacade(mContext, mWifiHandler, mWifiMetrics); mFeatureFlags = mDeviceConfigFacade.getFeatureFlags(); mAdaptiveConnectivityEnabledSettingObserver = - new AdaptiveConnectivityEnabledSettingObserver(wifiHandler, mWifiMetrics, + new AdaptiveConnectivityEnabledSettingObserver(mWifiHandler, mWifiMetrics, mFrameworkFacade, mContext); // Modules interacting with Native. - mHalDeviceManager = new HalDeviceManager(mContext, mClock, this, wifiHandler); + mHalDeviceManager = new HalDeviceManager(mContext, mClock, this, mWifiHandler); mInterfaceConflictManager = new InterfaceConflictManager(this, mContext, mFrameworkFacade, mHalDeviceManager, mWifiThreadRunner, mWifiDialogManager, new LocalLog( mContext.getSystemService(ActivityManager.class).isLowRamDevice() ? 128 : 256)); - mWifiVendorHal = new WifiVendorHal(mContext, mHalDeviceManager, wifiHandler, mWifiGlobals, + mWifiVendorHal = new WifiVendorHal(mContext, mHalDeviceManager, mWifiHandler, mWifiGlobals, mSsidTranslator); mSupplicantStaIfaceHal = new SupplicantStaIfaceHal( - mContext, mWifiMonitor, mFrameworkFacade, wifiHandler, mClock, mWifiMetrics, + mContext, mWifiMonitor, mFrameworkFacade, mWifiHandler, mClock, mWifiMetrics, mWifiGlobals, mSsidTranslator, this); - mHostapdHal = new HostapdHal(mContext, wifiHandler); + mHostapdHal = new HostapdHal(mContext, mWifiHandler); mWifiCondManager = (WifiNl80211Manager) mContext.getSystemService( Context.WIFI_NL80211_SERVICE); mWifiNative = new WifiNative( mWifiVendorHal, mSupplicantStaIfaceHal, mHostapdHal, mWifiCondManager, mWifiMonitor, mPropertyService, mWifiMetrics, - wifiHandler, new Random(), mBuildProperties, this); + mWifiHandler, new Random(), mBuildProperties, this); mWifiP2pMonitor = new WifiP2pMonitor(); mSupplicantP2pIfaceHal = new SupplicantP2pIfaceHal(mWifiP2pMonitor, mWifiGlobals, this); mWifiP2pNative = new WifiP2pNative(mWifiCondManager, mWifiNative, mWifiMetrics, @@ -363,7 +369,7 @@ public class WifiInjector { if (SdkLevel.isAtLeastS()) { mCoexManager = new CoexManager(mContext, mWifiNative, makeTelephonyManager(), subscriptionManager, mContext.getSystemService(CarrierConfigManager.class), - wifiHandler); + mWifiHandler); } else { mCoexManager = null; } @@ -391,7 +397,7 @@ public class WifiInjector { wifiLooper); mWifiCarrierInfoManager = new WifiCarrierInfoManager(makeTelephonyManager(), subscriptionManager, this, mFrameworkFacade, mContext, - mWifiConfigStore, wifiHandler, mWifiMetrics, mClock, mWifiPseudonymManager); + mWifiConfigStore, mWifiHandler, mWifiMetrics, mClock, mWifiPseudonymManager); String l2KeySeed = Secure.getString(mContext.getContentResolver(), Secure.ANDROID_ID); mWifiScoreCard = new WifiScoreCard(mClock, l2KeySeed, mDeviceConfigFacade, mContext, mWifiGlobals); @@ -439,8 +445,8 @@ public class WifiInjector { new RandomizedMacStoreData(), mLruConnectionTracker, this, - wifiHandler); - mSettingsConfigStore = new WifiSettingsConfigStore(context, wifiHandler, + mWifiHandler); + mSettingsConfigStore = new WifiSettingsConfigStore(context, mWifiHandler, mSettingsMigrationDataHolder, mWifiConfigManager, mWifiConfigStore); mWifiSettingsBackupRestore = new WifiSettingsBackupRestore(mSettingsConfigStore); mSettingsStore = new WifiSettingsStore(mContext, mSettingsConfigStore, mWifiThreadRunner, @@ -472,12 +478,12 @@ public class WifiInjector { mThroughputScorer = new ThroughputScorer(mContext, mScoringParams); mWifiNetworkSelector.registerCandidateScorer(mThroughputScorer); mWifiMetrics.setWifiNetworkSelector(mWifiNetworkSelector); - mWifiNetworkSuggestionsManager = new WifiNetworkSuggestionsManager(mContext, wifiHandler, + mWifiNetworkSuggestionsManager = new WifiNetworkSuggestionsManager(mContext, mWifiHandler, this, mWifiPermissionsUtil, mWifiConfigManager, mWifiConfigStore, mWifiMetrics, mWifiCarrierInfoManager, mWifiKeyStore, mLruConnectionTracker, mClock); mPasspointManager = new PasspointManager(mContext, this, - wifiHandler, mWifiNative, mWifiKeyStore, mClock, new PasspointObjectFactory(), + mWifiHandler, mWifiNative, mWifiKeyStore, mClock, new PasspointObjectFactory(), mWifiConfigManager, mWifiConfigStore, mSettingsStore, mWifiMetrics, mWifiCarrierInfoManager, mMacAddressUtil, mWifiPermissionsUtil); mNominateHelper = @@ -499,7 +505,7 @@ public class WifiInjector { mDefaultClientModeManager = new DefaultClientModeManager(); mExternalScoreUpdateObserverProxy = new ExternalScoreUpdateObserverProxy(mWifiThreadRunner); - mDppManager = new DppManager(this, wifiHandler, mWifiNative, + mDppManager = new DppManager(this, mWifiHandler, mWifiNative, mWifiConfigManager, mContext, mDppMetrics, mScanRequestProxy, mWifiPermissionsUtil); mActiveModeWarden = new ActiveModeWarden(this, wifiLooper, mWifiNative, mDefaultClientModeManager, mBatteryStats, mWifiDiagnostics, @@ -507,10 +513,10 @@ public class WifiInjector { mExternalScoreUpdateObserverProxy, mDppManager, mWifiGlobals); mWifiMetrics.setActiveModeWarden(mActiveModeWarden); mWifiHealthMonitor = new WifiHealthMonitor(mContext, this, mClock, mWifiConfigManager, - mWifiScoreCard, wifiHandler, mWifiNative, l2KeySeed, mDeviceConfigFacade, + mWifiScoreCard, mWifiHandler, mWifiNative, l2KeySeed, mDeviceConfigFacade, mActiveModeWarden); mWifiDataStall = new WifiDataStall(mWifiMetrics, mContext, - mDeviceConfigFacade, wifiChannelUtilizationConnected, mClock, wifiHandler, + mDeviceConfigFacade, wifiChannelUtilizationConnected, mClock, mWifiHandler, mThroughputPredictor, mActiveModeWarden, mCmiMonitor, mWifiGlobals); mWifiMetrics.setWifiDataStall(mWifiDataStall); mWifiMetrics.setWifiHealthMonitor(mWifiHealthMonitor); @@ -525,8 +531,8 @@ public class WifiInjector { new ConnectToNetworkNotificationBuilder(mContext, mFrameworkFacade), mMakeBeforeBreakManager, mWifiNotificationManager, mWifiPermissionsUtil); mMultiInternetManager = new MultiInternetManager(mActiveModeWarden, mFrameworkFacade, - mContext, mCmiMonitor, mSettingsStore, wifiHandler, mClock); - mExternalPnoScanRequestManager = new ExternalPnoScanRequestManager(wifiHandler, mContext); + mContext, mCmiMonitor, mSettingsStore, mWifiHandler, mClock); + mExternalPnoScanRequestManager = new ExternalPnoScanRequestManager(mWifiHandler, mContext); mCountryCode = new WifiCountryCode(mContext, mActiveModeWarden, mWifiP2pMetrics, mCmiMonitor, mWifiNative, mSettingsConfigStore, mClock, mWifiPermissionsUtil, mWifiCarrierInfoManager); @@ -534,7 +540,7 @@ public class WifiInjector { mContext, mScoringParams, mWifiConfigManager, mWifiNetworkSuggestionsManager, mWifiNetworkSelector, mWifiConnectivityHelper, mWifiLastResortWatchdog, mOpenNetworkNotifier, - mWifiMetrics, wifiHandler, + mWifiMetrics, mWifiHandler, mClock, mConnectivityLocalLog, mWifiScoreCard, mWifiBlocklistMonitor, mWifiChannelUtilizationScan, mPasspointManager, mMultiInternetManager, mDeviceConfigFacade, mActiveModeWarden, mFrameworkFacade, mWifiGlobals, @@ -545,7 +551,7 @@ public class WifiInjector { mWifiThreadRunner); mConnectionFailureNotifier = new ConnectionFailureNotifier( mContext, mFrameworkFacade, mWifiConfigManager, - mWifiConnectivityManager, wifiHandler, + mWifiConnectivityManager, mWifiHandler, mWifiNotificationManager, mConnectionFailureNotificationBuilder, mWifiDialogManager); mWifiNetworkFactory = new WifiNetworkFactory( @@ -576,27 +582,28 @@ public class WifiInjector { mWifiPermissionsUtil, mMultiInternetManager, mWifiConnectivityManager, mConnectivityLocalLog); mWifiScanAlwaysAvailableSettingsCompatibility = - new WifiScanAlwaysAvailableSettingsCompatibility(mContext, wifiHandler, + new WifiScanAlwaysAvailableSettingsCompatibility(mContext, mWifiHandler, mSettingsStore, mActiveModeWarden, mFrameworkFacade); mWifiApConfigStore = new WifiApConfigStore( - mContext, this, wifiHandler, mBackupManagerProxy, + mContext, this, mWifiHandler, mBackupManagerProxy, mWifiConfigStore, mWifiConfigManager, mActiveModeWarden, mWifiMetrics); WakeupNotificationFactory wakeupNotificationFactory = new WakeupNotificationFactory(mContext, mFrameworkFacade); WakeupOnboarding wakeupOnboarding = new WakeupOnboarding(mContext, mWifiConfigManager, - wifiHandler, mFrameworkFacade, wakeupNotificationFactory, mWifiNotificationManager); - mWakeupController = new WakeupController(mContext, wifiHandler, + mWifiHandler, mFrameworkFacade, wakeupNotificationFactory, + mWifiNotificationManager); + mWakeupController = new WakeupController(mContext, mWifiHandler, new WakeupLock(mWifiConfigManager, mWifiMetrics.getWakeupMetrics(), mClock), new WakeupEvaluator(mScoringParams), wakeupOnboarding, mWifiConfigManager, mWifiConfigStore, mWifiNetworkSuggestionsManager, mWifiMetrics.getWakeupMetrics(), this, mFrameworkFacade, mClock, mActiveModeWarden); mLockManager = new WifiLockManager(mContext, mBatteryStats, mActiveModeWarden, - mFrameworkFacade, wifiHandler, mClock, mWifiMetrics, mDeviceConfigFacade, + mFrameworkFacade, mWifiHandler, mClock, mWifiMetrics, mDeviceConfigFacade, mWifiPermissionsUtil, mWifiDeviceStateChangeManager); mSelfRecovery = new SelfRecovery(mContext, mActiveModeWarden, mClock, mWifiNative, mWifiGlobals); mWifiMulticastLockManager = new WifiMulticastLockManager(mActiveModeWarden, mBatteryStats, - wifiLooper); + wifiLooper, mContext); mApplicationQosPolicyRequestHandler = new ApplicationQosPolicyRequestHandler( mActiveModeWarden, mWifiNative, mWifiHandlerThread, mDeviceConfigFacade, mContext); @@ -607,7 +614,7 @@ public class WifiInjector { mSimRequiredNotifier = new SimRequiredNotifier(mContext, mFrameworkFacade, mWifiNotificationManager); mWifiPulledAtomLogger = new WifiPulledAtomLogger( - mContext.getSystemService(StatsManager.class), wifiHandler, + mContext.getSystemService(StatsManager.class), mWifiHandler, mContext, this); mAfcLocationUtil = new AfcLocationUtil(); mAfcClient = new AfcClient(BackgroundThread.getHandler()); @@ -621,16 +628,23 @@ public class WifiInjector { mActiveModeWarden, new WifiRoamingConfigStore(mWifiConfigManager, mWifiConfigStore)); - mTwtManager = new TwtManager(this, mCmiMonitor, mWifiNative, wifiHandler, mClock, + mTwtManager = new TwtManager(this, mCmiMonitor, mWifiNative, mWifiHandler, mClock, WifiTwtSession.MAX_TWT_SESSIONS, 1); mBackupRestoreController = new BackupRestoreController(mWifiSettingsBackupRestore, mClock); if (mFeatureFlags.voipDetectionBugfix() && SdkLevel.isAtLeastV()) { - mWifiVoipDetector = new WifiVoipDetector(mContext, wifiHandler, this, + mWifiVoipDetector = new WifiVoipDetector(mContext, mWifiHandler, this, mWifiCarrierInfoManager); } else { mWifiVoipDetector = null; } mHasActiveModem = makeTelephonyManager().getActiveModemCount() > 0; + if (mFeatureFlags.wepDisabledInApm()) { + mWepNetworkUsageController = new WepNetworkUsageController(mWifiHandlerThread, + mWifiDeviceStateChangeManager, mSettingsConfigStore, mWifiGlobals, + mActiveModeWarden, mFeatureFlags); + } else { + mWepNetworkUsageController = null; + } } /** @@ -639,6 +653,7 @@ public class WifiInjector { * This is the generic method to get an instance of the class. The first instance should be * retrieved using the getInstanceWithContext method. */ + @Keep public static WifiInjector getInstance() { if (sWifiInjector == null) { throw new IllegalStateException( @@ -653,6 +668,7 @@ public class WifiInjector { */ public void enableVerboseLogging(boolean verboseEnabled, boolean halVerboseEnabled) { Log.i(TAG, "enableVerboseLogging " + verboseEnabled + " hal " + halVerboseEnabled); + mVerboseLoggingEnabled = verboseEnabled; mWifiLastResortWatchdog.enableVerboseLogging(verboseEnabled); mWifiBackupRestore.enableVerboseLogging(verboseEnabled); mHalDeviceManager.enableVerboseLogging(verboseEnabled); @@ -690,6 +706,7 @@ public class WifiInjector { mExternalPnoScanRequestManager.enableVerboseLogging(verboseEnabled); mMultiInternetWifiNetworkFactory.enableVerboseLogging(verboseEnabled); mWifiRoamingModeManager.enableVerboseLogging(verboseEnabled); + mWifiHandler.enableVerboseLogging(verboseEnabled); } public UserManager getUserManager() { @@ -747,7 +764,7 @@ public class WifiInjector { public SarManager getSarManager() { return mSarManager; } - + @Keep public ActiveModeWarden getActiveModeWarden() { return mActiveModeWarden; } @@ -779,7 +796,7 @@ public class WifiInjector { public WifiMulticastLockManager getWifiMulticastLockManager() { return mWifiMulticastLockManager; } - + @Keep public WifiConfigManager getWifiConfigManager() { return mWifiConfigManager; } @@ -811,6 +828,7 @@ public class WifiInjector { return mContext.getSystemService(BatteryManager.class); } + @Keep public WifiCarrierInfoManager getWifiCarrierInfoManager() { return mWifiCarrierInfoManager; } @@ -1013,14 +1031,17 @@ public class WifiInjector { return mHalDeviceManager; } + @Keep public WifiNative getWifiNative() { return mWifiNative; } + @Keep public WifiMonitor getWifiMonitor() { return mWifiMonitor; } + @Keep public WifiP2pNative getWifiP2pNative() { return mWifiP2pNative; } @@ -1041,6 +1062,7 @@ public class WifiInjector { return mSelfRecovery; } + @Keep public ScanRequestProxy getScanRequestProxy() { return mScanRequestProxy; } @@ -1076,6 +1098,7 @@ public class WifiInjector { return mHostapdHal; } + @Keep public WifiThreadRunner getWifiThreadRunner() { return mWifiThreadRunner; } @@ -1121,6 +1144,7 @@ public class WifiInjector { return mConnectHelper; } + @Keep public WifiNetworkFactory getWifiNetworkFactory() { return mWifiNetworkFactory; } @@ -1149,6 +1173,7 @@ public class WifiInjector { return mWifiP2pConnection; } + @Keep public WifiGlobals getWifiGlobals() { return mWifiGlobals; } @@ -1304,4 +1329,16 @@ public class WifiInjector { public boolean hasActiveModem() { return mHasActiveModem; } + + /** + * Check if verbose logging enabled + */ + public boolean isVerboseLoggingEnabled() { + return mVerboseLoggingEnabled; + } + + @Nullable + public WepNetworkUsageController getWepNetworkUsageController() { + return mWepNetworkUsageController; + } } diff --git a/service/java/com/android/server/wifi/WifiLinkLayerStats.java b/service/java/com/android/server/wifi/WifiLinkLayerStats.java index 2db7c24e28..0276514a63 100644 --- a/service/java/com/android/server/wifi/WifiLinkLayerStats.java +++ b/service/java/com/android/server/wifi/WifiLinkLayerStats.java @@ -16,10 +16,13 @@ package com.android.server.wifi; +import android.net.wifi.WifiManager; import android.net.wifi.WifiUsabilityStatsEntry.LinkState; +import android.net.wifi.WifiUsabilityStatsEntry.WifiChannelBandwidth; import android.util.SparseArray; import java.util.Arrays; +import java.util.List; /** * A class representing link layer statistics collected over a Wifi Interface. @@ -155,11 +158,29 @@ public class WifiLinkLayerStats { */ public PeerInfo[] peerInfo; + public List<ScanResultWithSameFreq> scan_results_same_freq; } public LinkSpecificStats[] links; /** + * Scan result who has the same frequency with WiFi Link + */ + public static class ScanResultWithSameFreq { + /** + * timestamp in microseconds (since boot) when + * this result was last seen. + */ + public long scan_result_timestamp_micros; + /** The detected signal level in dBm, also known as the RSSI */ + public int rssi; + /** The center frequency of the primary 20 MHz frequency (in MHz) of the channel */ + public int frequencyMhz; + /** BSSID of access point */ + public String bssid; + } + + /** * The stats below which is already captured in WifiLinkLayerStats#LinkSpecificStats will be * having an aggregated value. The aggregation logic is defined at * wifiNative#setAggregatedLinkLayerStats(). @@ -303,6 +324,18 @@ public class WifiLinkLayerStats { */ public int frequency; /** + * Center frequency in MHz for first segment + */ + public int frequencyFirstSegment; + /** + * Center frequency in MHz for second segment + */ + public int frequencySecondSegment; + /** + * Channel Width as {@link WifiChannelBandwidth} + */ + public @WifiChannelBandwidth int channelWidth; + /** * Cumulative milliseconds radio is awake on this channel */ public int radioOnTimeMs; @@ -454,6 +487,10 @@ public class WifiLinkLayerStats { * Channel stats list */ public final SparseArray<ChannelStats> channelStatsMap = new SparseArray<>(); + /** + * Time for which the radio is in active tranmission per tx level + */ + public int[] tx_time_in_ms_per_level; } /** @@ -461,6 +498,8 @@ public class WifiLinkLayerStats { */ public RadioStat[] radioStats; + public @WifiManager.MloMode int wifiMloMode; + @Override public String toString() { StringBuilder sbuf = new StringBuilder(); diff --git a/service/java/com/android/server/wifi/WifiLockManager.java b/service/java/com/android/server/wifi/WifiLockManager.java index 8ba475923b..d6f6742998 100644 --- a/service/java/com/android/server/wifi/WifiLockManager.java +++ b/service/java/com/android/server/wifi/WifiLockManager.java @@ -43,6 +43,7 @@ import com.android.wifi.resources.R; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.BitSet; import java.util.List; import java.util.NoSuchElementException; import java.util.concurrent.Executor; @@ -59,6 +60,8 @@ public class WifiLockManager { private static final int IGNORE_SCREEN_STATE_MASK = 0x01; private static final int IGNORE_WIFI_STATE_MASK = 0x02; + @VisibleForTesting + public static final long DELAY_LOCK_RELEASE_MS = 1000; private int mLatencyModeSupport = LOW_LATENCY_SUPPORT_UNDEFINED; @@ -103,6 +106,7 @@ public class WifiLockManager { WIFI_CONNECTION_STATE_CHANGED, SCREEN_STATE_CHANGED, }; + private final Object mLock = new Object(); WifiLockManager( Context context, @@ -645,7 +649,7 @@ public class WifiLockManager { // Recalculate the operating mode updateOpMode(); - + mHandler.removeCallbacksAndMessages(mLock); return true; } @@ -708,10 +712,8 @@ public class WifiLockManager { // Do nothing break; } - - // Recalculate the operating mode - updateOpMode(); - + // Delay 1s to release the lock to avoid stress the HAL. + mHandler.postDelayed(this::updateOpMode, mLock, DELAY_LOCK_RELEASE_MS); return true; } @@ -849,13 +851,13 @@ public class WifiLockManager { return mLatencyModeSupport; } - long supportedFeatures = - mActiveModeWarden.getPrimaryClientModeManager().getSupportedFeatures(); - if (supportedFeatures == 0L) { + BitSet supportedFeatures = + mActiveModeWarden.getPrimaryClientModeManager().getSupportedFeaturesBitSet(); + if (supportedFeatures.isEmpty()) { return LOW_LATENCY_SUPPORT_UNDEFINED; } - if ((supportedFeatures & WifiManager.WIFI_FEATURE_LOW_LATENCY) != 0) { + if (supportedFeatures.get(WifiManager.WIFI_FEATURE_LOW_LATENCY)) { mLatencyModeSupport = LOW_LATENCY_SUPPORTED; } else { mLatencyModeSupport = LOW_LATENCY_NOT_SUPPORTED; @@ -966,6 +968,7 @@ public class WifiLockManager { } } mWifiLowLatencyLockListeners.finishBroadcast(); + mWifiMetrics.setLowLatencyState(mIsLowLatencyActivated); } private void notifyLowLatencyOwnershipChanged() { diff --git a/service/java/com/android/server/wifi/WifiMetrics.java b/service/java/com/android/server/wifi/WifiMetrics.java index 45c636e115..00f117a2ef 100644 --- a/service/java/com/android/server/wifi/WifiMetrics.java +++ b/service/java/com/android/server/wifi/WifiMetrics.java @@ -58,6 +58,7 @@ import android.content.pm.ResolveInfo; import android.net.ConnectivityManager; import android.net.Network; import android.net.NetworkCapabilities; +import android.net.wifi.DeauthenticationReasonCode; import android.net.wifi.EAPConstants; import android.net.wifi.IOnWifiUsabilityStatsListener; import android.net.wifi.MloLink; @@ -127,19 +128,24 @@ import com.android.server.wifi.proto.nano.WifiMetricsProto.LinkProbeStats; import com.android.server.wifi.proto.nano.WifiMetricsProto.LinkProbeStats.ExperimentProbeCounts; import com.android.server.wifi.proto.nano.WifiMetricsProto.LinkProbeStats.LinkProbeFailureReasonCount; import com.android.server.wifi.proto.nano.WifiMetricsProto.LinkSpeedCount; +import com.android.server.wifi.proto.nano.WifiMetricsProto.LinkStats; import com.android.server.wifi.proto.nano.WifiMetricsProto.MeteredNetworkStats; import com.android.server.wifi.proto.nano.WifiMetricsProto.NetworkDisableReason; import com.android.server.wifi.proto.nano.WifiMetricsProto.NetworkSelectionExperimentDecisions; +import com.android.server.wifi.proto.nano.WifiMetricsProto.PacketStats; import com.android.server.wifi.proto.nano.WifiMetricsProto.PasspointProfileTypeCount; import com.android.server.wifi.proto.nano.WifiMetricsProto.PasspointProvisionStats; import com.android.server.wifi.proto.nano.WifiMetricsProto.PasspointProvisionStats.ProvisionFailureCount; +import com.android.server.wifi.proto.nano.WifiMetricsProto.PeerInfo; import com.android.server.wifi.proto.nano.WifiMetricsProto.PnoScanMetrics; import com.android.server.wifi.proto.nano.WifiMetricsProto.RadioStats; import com.android.server.wifi.proto.nano.WifiMetricsProto.RateStats; +import com.android.server.wifi.proto.nano.WifiMetricsProto.ScanResultWithSameFreq; import com.android.server.wifi.proto.nano.WifiMetricsProto.SoftApConnectedClientsEvent; import com.android.server.wifi.proto.nano.WifiMetricsProto.StaEvent; import com.android.server.wifi.proto.nano.WifiMetricsProto.StaEvent.ConfigInfo; import com.android.server.wifi.proto.nano.WifiMetricsProto.TargetNetworkInfo; +import com.android.server.wifi.proto.nano.WifiMetricsProto.TrainingData; import com.android.server.wifi.proto.nano.WifiMetricsProto.UserActionEvent; import com.android.server.wifi.proto.nano.WifiMetricsProto.UserReactionToApprovalUiEvent; import com.android.server.wifi.proto.nano.WifiMetricsProto.UserReactionToApprovalUiEvent.UserReaction; @@ -152,8 +158,8 @@ import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiNetworkSuggestion import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiStatus; import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiToWifiSwitchStats; import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiToggleStats; -import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiUsabilityStats; -import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiUsabilityStatsEntry; +import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiUsabilityStatsEntry; // This contains all the stats for a single point in time. +import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiUsabilityStatsTraining; import com.android.server.wifi.rtt.RttMetrics; import com.android.server.wifi.scanner.KnownBandsChannelHelper; import com.android.server.wifi.util.InformationElementUtil; @@ -164,7 +170,6 @@ import com.android.server.wifi.util.IntHistogram; import com.android.server.wifi.util.MetricsUtils; import com.android.server.wifi.util.ObjectCounter; import com.android.server.wifi.util.StringUtil; -import com.android.wifi.flags.Flags; import com.android.wifi.resources.R; import org.json.JSONArray; @@ -174,6 +179,8 @@ import org.json.JSONObject; import java.io.FileDescriptor; import java.io.PrintWriter; import java.time.Duration; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; @@ -239,13 +246,8 @@ public class WifiMetrics { // Minimum time wait before generating next WifiIsUnusableEvent from data stall public static final int MIN_DATA_STALL_WAIT_MS = 120 * 1000; // 2 minutes // Max number of WifiUsabilityStatsEntry elements to store in the ringbuffer. - public static final int MAX_WIFI_USABILITY_STATS_ENTRIES_LIST_SIZE = 40; - // Max number of WifiUsabilityStats elements to store for each type. - public static final int MAX_WIFI_USABILITY_STATS_LIST_SIZE_PER_TYPE = 10; - // Max number of WifiUsabilityStats per labeled type to upload to server - public static final int MAX_WIFI_USABILITY_STATS_PER_TYPE_TO_UPLOAD = 2; - public static final int NUM_WIFI_USABILITY_STATS_ENTRIES_PER_WIFI_GOOD = 100; - public static final int MIN_WIFI_GOOD_USABILITY_STATS_PERIOD_MS = 1000 * 3600; // 1 hour + public static final int MAX_WIFI_USABILITY_STATS_ENTRIES_RING_BUFFER_SIZE = 80; + public static final int MAX_WIFI_USABILITY_STATS_TRAINING_SIZE = 10; public static final int PASSPOINT_DEAUTH_IMMINENT_SCOPE_ESS = 0; public static final int PASSPOINT_DEAUTH_IMMINENT_SCOPE_BSS = 1; public static final int COUNTRY_CODE_CONFLICT_WIFI_SCAN = -1; @@ -273,6 +275,8 @@ public class WifiMetrics { public static final int MIN_DOWNSTREAM_BANDWIDTH_KBPS = 1000; public static final int MIN_UPSTREAM_BANDWIDTH_KBPS = 1000; public static final int INVALID_SPEED = -1; + public static final long MILLIS_IN_A_SECOND = 1000; + public static final long MILLIS_IN_AN_HOUR = 3600 * 1000; private Clock mClock; private boolean mScreenOn; @@ -299,8 +303,8 @@ public class WifiMetrics { private SessionData mPreviousSession; @VisibleForTesting public SessionData mCurrentSession; - private String mLastBssid; - private int mLastFrequency = -1; + private Map<String, String> mLastBssidPerIfaceMap = new ArrayMap<>(); + private Map<String, Integer> mLastFrequencyPerIfaceMap = new ArrayMap<>(); private int mSeqNumInsideFramework = 0; private int mLastWifiUsabilityScore = -1; private int mLastWifiUsabilityScoreNoReset = -1; @@ -312,6 +316,7 @@ public class WifiMetrics { private int mProbeElapsedTimeSinceLastUpdateMs = -1; private int mProbeMcsRateSinceLastUpdate = -1; private long mScoreBreachLowTimeMillis = -1; + private int mAccumulatedLabelBadCount = 0; public static final int MAX_STA_EVENTS = 768; @VisibleForTesting static final int MAX_USER_ACTION_EVENTS = 200; @@ -334,6 +339,13 @@ public class WifiMetrics { private int mScorerUid = Process.WIFI_UID; @VisibleForTesting int mUnusableEventType = WifiIsUnusableEvent.TYPE_UNKNOWN; + private int mWifiFrameworkState = 0; + private SpeedSufficient mSpeedSufficientNetworkCapabilities = new SpeedSufficient(); + private SpeedSufficient mSpeedSufficientThroughputPredictor = new SpeedSufficient(); + private int mLastUwbState = -1; + private boolean mIsLowLatencyActivated = false; + private int mVoipMode = -1; + private int mLastThreadDeviceRole = -1; /** * Wi-Fi usability state per interface as predicted by the network scorer. @@ -479,11 +491,18 @@ public class WifiMetrics { private int mLinkProbeStaEventCount = 0; @VisibleForTesting static final int MAX_LINK_PROBE_STA_EVENTS = MAX_STA_EVENTS / 4; - private final LinkedList<WifiUsabilityStatsEntry> mWifiUsabilityStatsEntriesList = + // Each WifiUsabilityStatsEntry contains the stats for one instant in time. This LinkedList + // is used as a ring buffer and contains the history of the most recent + // MAX_WIFI_USABILITY_STATS_ENTRIES_RING_BUFFER_SIZE WifiUsabilityStatsEntry values. + @VisibleForTesting + public final LinkedList<WifiUsabilityStatsEntry> mWifiUsabilityStatsEntriesRingBuffer = new LinkedList<>(); - private final LinkedList<WifiUsabilityStats> mWifiUsabilityStatsListBad = new LinkedList<>(); - private final LinkedList<WifiUsabilityStats> mWifiUsabilityStatsListGood = new LinkedList<>(); - private int mWifiUsabilityStatsCounter = 0; + // Each WifiUsabilityStatsTraining instance contains a list of WifiUsabilityStatsEntry objects, + // representing a time series of WiFi usability statistics recorded within a specific data + // capture period. It also includes information about the type of data capture and the duration + // of the capture period. + public final List<WifiUsabilityStatsTraining> mWifiUsabilityStatsTrainingExamples = + new ArrayList<>(); private final Random mRand = new Random(); private final RemoteCallbackList<IOnWifiUsabilityStatsListener> mOnWifiUsabilityListeners; @@ -513,6 +532,7 @@ public class WifiMetrics { private final WifiMonitor mWifiMonitor; private ActiveModeWarden mActiveModeWarden; + private WifiGlobals mWifiGlobals; private final Map<String, ActiveModeManager.ClientRole> mIfaceToRoleMap = new ArrayMap<>(); /** WifiConfigStore read duration histogram. */ @@ -1660,7 +1680,8 @@ public class WifiMetrics { WifiP2pMetrics wifiP2pMetrics, DppMetrics dppMetrics, WifiMonitor wifiMonitor, - WifiDeviceStateChangeManager wifiDeviceStateChangeManager) { + WifiDeviceStateChangeManager wifiDeviceStateChangeManager, + WifiGlobals wifiGlobals) { mContext = context; mFacade = facade; mClock = clock; @@ -1695,6 +1716,7 @@ public class WifiMetrics { handleScreenStateChanged(screenOn); } }); + mWifiGlobals = wifiGlobals; } /** Sets internal ScoringParams member */ @@ -2309,7 +2331,8 @@ public class WifiMetrics { currentConnectionEvent.mPasspointRoamingType, currentConnectionEvent.mCarrierId, currentConnectionEvent.mTofuConnectionState, - currentConnectionEvent.mUid); + currentConnectionEvent.mUid, + frequency); if (connectionSucceeded) { reportRouterCapabilities(currentConnectionEvent.mRouterFingerPrint); @@ -4397,8 +4420,8 @@ public class WifiMetrics { public void logFirmwareAlert(String ifaceName, int errorCode) { incrementAlertReasonCount(errorCode); logWifiIsUnusableEvent(ifaceName, WifiIsUnusableEvent.TYPE_FIRMWARE_ALERT, errorCode); - addToWifiUsabilityStatsList(ifaceName, WifiUsabilityStats.LABEL_BAD, - WifiUsabilityStats.TYPE_FIRMWARE_ALERT, errorCode); + logAsynchronousEvent(ifaceName, + WifiUsabilityStatsEntry.CAPTURE_EVENT_TYPE_FIRMWARE_ALERT, errorCode); } public static final String PROTO_DUMP_ARG = "wifiMetricsProto"; @@ -4969,25 +4992,19 @@ public class WifiMetrics { } pw.println("Hardware Version: " + SystemProperties.get("ro.boot.revision", "")); - pw.println("mWifiUsabilityStatsEntriesList:"); - for (WifiUsabilityStatsEntry stats : mWifiUsabilityStatsEntriesList) { + pw.println("mWifiUsabilityStatsEntriesRingBuffer:"); + for (WifiUsabilityStatsEntry stats : mWifiUsabilityStatsEntriesRingBuffer) { printWifiUsabilityStatsEntry(pw, stats); } - pw.println("mWifiUsabilityStatsList:"); - for (WifiUsabilityStats stats : mWifiUsabilityStatsListGood) { - pw.println("\nlabel=" + stats.label); - pw.println("\ntrigger_type=" + stats.triggerType); - pw.println("\ntime_stamp_ms=" + stats.timeStampMs); - for (WifiUsabilityStatsEntry entry : stats.stats) { - printWifiUsabilityStatsEntry(pw, entry); - } - } - for (WifiUsabilityStats stats : mWifiUsabilityStatsListBad) { - pw.println("\nlabel=" + stats.label); - pw.println("\ntrigger_type=" + stats.triggerType); - pw.println("\ntime_stamp_ms=" + stats.timeStampMs); - for (WifiUsabilityStatsEntry entry : stats.stats) { - printWifiUsabilityStatsEntry(pw, entry); + + pw.println("mWifiUsabilityStatsTrainingExamples:"); + for (WifiUsabilityStatsTraining statsTraining + : mWifiUsabilityStatsTrainingExamples) { + pw.println("\ndata_capture_type=" + statsTraining.dataCaptureType); + pw.println("\ncapture_start_timestamp_secs=" + + statsTraining.captureStartTimestampSecs); + for (WifiUsabilityStatsEntry stats : statsTraining.trainingData.stats) { + printWifiUsabilityStatsEntry(pw, stats); } } @@ -5135,6 +5152,11 @@ public class WifiMetrics { line.append(",roam_scan_time_ms=" + radioStat.totalRoamScanTimeMs); line.append(",pno_scan_time_ms=" + radioStat.totalPnoScanTimeMs); line.append(",hotspot_2_scan_time_ms=" + radioStat.totalHotspot2ScanTimeMs); + if (radioStat.txTimeMsPerLevel != null && radioStat.txTimeMsPerLevel.length > 0) { + for (int i = 0; i < radioStat.txTimeMsPerLevel.length; ++i) { + line.append(",tx_time_ms_per_level=" + radioStat.txTimeMsPerLevel[i]); + } + } } } line.append(",total_radio_on_time_ms=" + entry.totalRadioOnTimeMs); @@ -5190,6 +5212,98 @@ public class WifiMetrics { line.append(",retries=" + rateStat.retries); } } + line.append(",wifi_link_count=" + entry.wifiLinkCount); + for (LinkStats linkStat : entry.linkStats) { + line.append(",Link Stats from link_id=" + linkStat.linkId); + line.append(",state=" + linkStat.state); + line.append(",radio_id=" + linkStat.radioId); + line.append(",frequency_mhz=" + linkStat.frequencyMhz); + line.append(",beacon_rx=" + linkStat.beaconRx); + line.append(",rssi_mgmt=" + linkStat.rssiMgmt); + line.append(",time_slice_duty_cycle_in_percent=" + + linkStat.timeSliceDutyCycleInPercent); + line.append(",rssi=" + linkStat.rssi); + line.append(",channel_width=" + linkStat.channelWidth); + line.append(",center_freq_first_seg=" + linkStat.centerFreqFirstSeg); + line.append(",center_freq_second_seg=" + linkStat.centerFreqSecondSeg); + line.append(",on_time_in_ms=" + linkStat.onTimeInMs); + line.append(",cca_busy_time_in_ms=" + linkStat.ccaBusyTimeInMs); + if (linkStat.contentionTimeStats != null) { + for (ContentionTimeStats contentionTimeStat : linkStat.contentionTimeStats) { + line.append(",access_category=" + contentionTimeStat.accessCategory); + line.append(",contention_time_min_micros=" + + contentionTimeStat.contentionTimeMinMicros); + line.append(",contention_time_max_micros=" + + contentionTimeStat.contentionTimeMaxMicros); + line.append(",contention_time_avg_micros=" + + contentionTimeStat.contentionTimeAvgMicros); + line.append(",contention_num_samples=" + + contentionTimeStat.contentionNumSamples); + } + } + if (linkStat.packetStats != null) { + for (PacketStats packetStats : linkStat.packetStats) { + line.append(",access_category=" + packetStats.accessCategory); + line.append(",tx_success=" + packetStats.txSuccess); + line.append(",tx_retries=" + packetStats.txRetries); + line.append(",tx_bad=" + packetStats.txBad); + line.append(",rx_success=" + packetStats.rxSuccess); + } + } + if (linkStat.peerInfo != null) { + for (PeerInfo peerInfo : linkStat.peerInfo) { + line.append(",sta_count=" + peerInfo.staCount); + line.append(",chan_util=" + peerInfo.chanUtil); + if (peerInfo.rateStats != null) { + for (RateStats rateStat : peerInfo.rateStats) { + line.append(",preamble=" + rateStat.preamble); + line.append(",nss=" + rateStat.nss); + line.append(",bw=" + rateStat.bw); + line.append(",rate_mcs_idx=" + rateStat.rateMcsIdx); + line.append(",bit_rate_in_kbps=" + rateStat.bitRateInKbps); + line.append(",tx_mpdu=" + rateStat.txMpdu); + line.append(",rx_mpdu=" + rateStat.rxMpdu); + line.append(",mpdu_lost=" + rateStat.mpduLost); + line.append(",retries=" + rateStat.retries); + } + } + } + } + if (linkStat.scanResultWithSameFreq != null) { + for (ScanResultWithSameFreq scanResultWithSameFreq + : linkStat.scanResultWithSameFreq) { + line.append(",scan_result_timestamp_micros=" + + scanResultWithSameFreq.scanResultTimestampMicros); + line.append(",rssi=" + scanResultWithSameFreq.rssi); + line.append(",frequencyMhz=" + scanResultWithSameFreq.frequencyMhz); + } + } + line.append(",tx_linkspeed=" + linkStat.txLinkspeed); + line.append(",rx_linkspeed=" + linkStat.rxLinkspeed); + } + line.append(",mlo_mode=" + entry.mloMode); + line.append(",tx_transmitted_bytes=" + entry.txTransmittedBytes); + line.append(",rx_transmitted_bytes=" + entry.rxTransmittedBytes); + line.append(",label_bad_event_count=" + entry.labelBadEventCount); + line.append(",wifi_framework_state=" + entry.wifiFrameworkState); + line.append(",is_network_capabilities_downstream_sufficient=" + + entry.isNetworkCapabilitiesDownstreamSufficient); + line.append(",is_network_capabilities_upstream_sufficient=" + + entry.isNetworkCapabilitiesUpstreamSufficient); + line.append(",is_throughput_predictor_downstream_sufficient=" + + entry.isThroughputPredictorDownstreamSufficient); + line.append(",is_throughput_predictor_upstream_sufficient=" + + entry.isThroughputPredictorUpstreamSufficient); + line.append(",is_bluetooth_connected=" + entry.isBluetoothConnected); + line.append(",uwb_adapter_state=" + entry.uwbAdapterState); + line.append(",is_low_latency_activated=" + entry.isLowLatencyActivated); + line.append(",max_supported_tx_linkspeed=" + entry.maxSupportedTxLinkspeed); + line.append(",max_supported_rx_linkspeed=" + entry.maxSupportedRxLinkspeed); + line.append(",voip_mode=" + entry.voipMode); + line.append(",thread_device_role=" + entry.threadDeviceRole); + line.append(",capture_event_type=" + entry.captureEventType); + line.append(",capture_event_type_subcode=" + entry.captureEventTypeSubcode); + line.append(",status_data_stall=" + entry.statusDataStall); pw.println(line.toString()); } @@ -5661,23 +5775,13 @@ public class WifiMetrics { } mWifiLogProto.hardwareRevision = SystemProperties.get("ro.boot.revision", ""); - // Postprocessing on WifiUsabilityStats to upload an equal number of LABEL_GOOD and - // LABEL_BAD WifiUsabilityStats - final int numUsabilityStats = Math.min( - Math.min(mWifiUsabilityStatsListBad.size(), - mWifiUsabilityStatsListGood.size()), - MAX_WIFI_USABILITY_STATS_PER_TYPE_TO_UPLOAD); - LinkedList<WifiUsabilityStats> usabilityStatsGoodCopy = - new LinkedList<>(mWifiUsabilityStatsListGood); - LinkedList<WifiUsabilityStats> usabilityStatsBadCopy = - new LinkedList<>(mWifiUsabilityStatsListBad); - mWifiLogProto.wifiUsabilityStatsList = new WifiUsabilityStats[numUsabilityStats * 2]; - for (int i = 0; i < numUsabilityStats; i++) { - mWifiLogProto.wifiUsabilityStatsList[2 * i] = usabilityStatsGoodCopy.remove( - mRand.nextInt(usabilityStatsGoodCopy.size())); - mWifiLogProto.wifiUsabilityStatsList[2 * i + 1] = usabilityStatsBadCopy.remove( - mRand.nextInt(usabilityStatsBadCopy.size())); + mWifiLogProto.wifiUsabilityStatsTraining = + new WifiUsabilityStatsTraining[mWifiUsabilityStatsTrainingExamples.size()]; + for (int i = 0; i < mWifiUsabilityStatsTrainingExamples.size(); i++) { + mWifiLogProto.wifiUsabilityStatsTraining[i] = + mWifiUsabilityStatsTrainingExamples.get(i); } + mWifiUsabilityStatsTrainingExamples.clear(); mWifiLogProto.mobilityStatePnoStatsList = new DeviceMobilityStatePnoScanStats[mMobilityStatePnoStatsMap.size()]; for (int i = 0; i < mMobilityStatePnoStatsMap.size(); i++) { @@ -6013,15 +6117,11 @@ public class WifiMetrics { mWifiIsUnusableList.clear(); mInstalledPasspointProfileTypeForR1.clear(); mInstalledPasspointProfileTypeForR2.clear(); - mWifiUsabilityStatsListGood.clear(); - mWifiUsabilityStatsListBad.clear(); - mWifiUsabilityStatsEntriesList.clear(); mMobilityStatePnoStatsMap.clear(); mWifiP2pMetrics.clear(); mDppMetrics.clear(); - mWifiUsabilityStatsCounter = 0; - mLastBssid = null; - mLastFrequency = -1; + mLastBssidPerIfaceMap.clear(); + mLastFrequencyPerIfaceMap.clear(); mSeqNumInsideFramework = 0; mLastWifiUsabilityScore = -1; mLastWifiUsabilityScoreNoReset = -1; @@ -6033,6 +6133,7 @@ public class WifiMetrics { mProbeElapsedTimeSinceLastUpdateMs = -1; mProbeMcsRateSinceLastUpdate = -1; mScoreBreachLowTimeMillis = -1; + mAccumulatedLabelBadCount = 0; mMeteredNetworkStatsBuilder.clear(); mWifiConfigStoreReadDurationHistogram.clear(); mWifiConfigStoreWriteDurationHistogram.clear(); @@ -6621,6 +6722,16 @@ public class WifiMetrics { return "DISCONNECT_VCN_REQUEST"; case StaEvent.DISCONNECT_UNKNOWN_NETWORK: return "DISCONNECT_UNKNOWN_NETWORK"; + case StaEvent.DISCONNECT_NETWORK_UNTRUSTED: + return "DISCONNECT_NETWORK_UNTRUSTED"; + case StaEvent.DISCONNECT_NETWORK_WIFI7_TOGGLED: + return "DISCONNECT_NETWORK_WIFI7_TOGGLED"; + case StaEvent.DISCONNECT_IP_CONFIGURATION_LOST: + return "DISCONNECT_IP_CONFIGURATION_LOST"; + case StaEvent.DISCONNECT_IP_REACHABILITY_LOST: + return "DISCONNECT_IP_REACHABILITY_LOST"; + case StaEvent.DISCONNECT_NO_CREDENTIALS: + return "DISCONNECT_NO_CREDENTIALS"; default: return "DISCONNECT_UNKNOWN=" + frameworkDisconnectReason; } @@ -7043,22 +7154,102 @@ public class WifiMetrics { } } - public boolean isWiFiScorerNewStatsCollected() { - return Flags.wifiScorerNewStatsCollection(); + /** + * If isFullCapture is true, capture everything in ring buffer + * + * If isFullCapture is false, extract WifiUsabilityStatsEntries from ring buffer whose + * timestamps are within [triggerStartTimeMillis, triggerStopTimeMillis) and store them as + * upload candidates. + * + * @param triggerType data capture trigger type + * @param isFullCapture if we do full capture on ring buffer or not + * @param triggerStartTimeMillis data capture start timestamp, elapsed time since boot + * @param triggerStopTimeMillis data capture stop timestamp, elapsed time since boot + * @return error code, 0 is success + */ + public int storeCapturedData(int triggerType, boolean isFullCapture, + long triggerStartTimeMillis, long triggerStopTimeMillis) { + synchronized (mLock) { + Instant bootTime = Instant.now() + .minus(Duration.ofMillis(mClock.getElapsedSinceBootMillis())); + Log.d(TAG, "storeCapturedData: triggerType=" + triggerType + + ", isFullCapture=" + isFullCapture + + ", triggerStartTimeMillis=" + triggerStartTimeMillis + + ", triggerStartTime=" + + bootTime.plus(Duration.ofMillis(triggerStartTimeMillis)) + + ", triggerStopTimeMillis=" + triggerStopTimeMillis + + ", triggerStopTime=" + + bootTime.plus(Duration.ofMillis(triggerStopTimeMillis))); + + // Validate triggerStartTimeMillis and triggerStopTimeMillis in non full-capture case + if (!isFullCapture && ((triggerStartTimeMillis < 0 || triggerStopTimeMillis < 0 + || triggerStopTimeMillis <= triggerStartTimeMillis))) { + return 1; + } + + Instant now = mClock.getCurrentInstant(); + Duration durationSinceBoot = Duration.ofMillis(mClock.getElapsedSinceBootMillis()); + + WifiUsabilityStatsTraining wifiUsabilityStatsTraining = + new WifiUsabilityStatsTraining(); + while (mWifiUsabilityStatsTrainingExamples.size() + >= MAX_WIFI_USABILITY_STATS_TRAINING_SIZE) { + mWifiUsabilityStatsTrainingExamples.remove(0); + } + wifiUsabilityStatsTraining.dataCaptureType = triggerType; + + long capturePeriodStartTime = triggerStartTimeMillis; + long capturePeriodStopTime = triggerStopTimeMillis; + + if (isFullCapture) { + capturePeriodStartTime = mWifiUsabilityStatsEntriesRingBuffer.size() > 0 + ? mWifiUsabilityStatsEntriesRingBuffer.get(0).timeStampMs : + 0; + capturePeriodStopTime = mWifiUsabilityStatsEntriesRingBuffer.size() > 0 + ? mWifiUsabilityStatsEntriesRingBuffer.get( + mWifiUsabilityStatsEntriesRingBuffer.size() - 1).timeStampMs : + durationSinceBoot.toMillis(); + } + + wifiUsabilityStatsTraining.captureStartTimestampSecs = + now.minus(durationSinceBoot) + .plus(Duration.ofMillis(capturePeriodStartTime)) + .truncatedTo(ChronoUnit.HOURS) + .getEpochSecond(); + wifiUsabilityStatsTraining.storeTimeOffsetMs = + durationSinceBoot.toMillis() - capturePeriodStopTime; + + // If isFullCapture is true, store everything in ring buffer + // If isFullCapture is false, Store WifiUsabilityStatsEntries within capture period + TrainingData trainingData = new TrainingData(); + List<WifiUsabilityStatsEntry> trainingDataList = new ArrayList<>(); + for (WifiUsabilityStatsEntry currStats : mWifiUsabilityStatsEntriesRingBuffer) { + if (isFullCapture || (currStats.timeStampMs >= triggerStartTimeMillis + && currStats.timeStampMs < triggerStopTimeMillis)) { + WifiUsabilityStatsEntry trainingStats = + createNewWifiUsabilityStatsEntry(currStats, capturePeriodStartTime); + trainingDataList.add(trainingStats); + } + } + trainingData.stats = trainingDataList.toArray(new WifiUsabilityStatsEntry[0]); + wifiUsabilityStatsTraining.trainingData = trainingData; + + mWifiUsabilityStatsTrainingExamples.add(wifiUsabilityStatsTraining); + return 0; + } } /** * Extract data from |info| and |stats| to build a WifiUsabilityStatsEntry and then adds it * into an internal ring buffer. - * @param info - * @param stats - * @param ifaceName + * + * oneshot is used to indicate that this call came from CMD_ONESHOT_RSSI_POLL. */ public void updateWifiUsabilityStatsEntries(String ifaceName, WifiInfo info, - WifiLinkLayerStats stats) { - // This is only collected for primary STA currently because RSSI polling is disabled for - // non-primary STAs. + WifiLinkLayerStats stats, boolean oneshot, int statusDataStall) { synchronized (mLock) { + // This is only collected for primary STA currently because RSSI polling is disabled for + // non-primary STAs. if (info == null) { return; } @@ -7072,9 +7263,211 @@ public class WifiMetrics { stats.rxmpdu_be = info.rxSuccess; } WifiUsabilityStatsEntry wifiUsabilityStatsEntry = - mWifiUsabilityStatsEntriesList.size() - < MAX_WIFI_USABILITY_STATS_ENTRIES_LIST_SIZE - ? new WifiUsabilityStatsEntry() : mWifiUsabilityStatsEntriesList.remove(); + mWifiUsabilityStatsEntriesRingBuffer.size() + < MAX_WIFI_USABILITY_STATS_ENTRIES_RING_BUFFER_SIZE + ? new WifiUsabilityStatsEntry() : mWifiUsabilityStatsEntriesRingBuffer.remove() + .clear(); + SparseArray<MloLink> mloLinks = new SparseArray<>(); + for (MloLink link: info.getAffiliatedMloLinks()) { + mloLinks.put(link.getLinkId(), link); + } + if (stats.links != null && stats.links.length > 0) { + int numLinks = stats.links.length; + wifiUsabilityStatsEntry.wifiLinkCount = numLinks; + wifiUsabilityStatsEntry.linkStats = new LinkStats[numLinks]; + for (int i = 0; i < numLinks; ++i) { + LinkStats linkStats = new LinkStats(); + WifiLinkLayerStats.LinkSpecificStats link = stats.links[i]; + linkStats.linkId = link.link_id; + linkStats.state = link.state; + linkStats.radioId = link.radio_id; + linkStats.frequencyMhz = link.frequencyMhz; + linkStats.beaconRx = link.beacon_rx; + linkStats.rssiMgmt = link.rssi_mgmt; + linkStats.timeSliceDutyCycleInPercent = link.timeSliceDutyCycleInPercent; + linkStats.rssi = (mloLinks.size() > 0) ? mloLinks.get(link.link_id, + new MloLink()).getRssi() : info.getRssi(); + linkStats.txLinkspeed = (mloLinks.size() > 0) ? mloLinks.get(link.link_id, + new MloLink()).getTxLinkSpeedMbps() : info.getTxLinkSpeedMbps(); + linkStats.rxLinkspeed = (mloLinks.size() > 0) ? mloLinks.get(link.link_id, + new MloLink()).getRxLinkSpeedMbps() : info.getRxLinkSpeedMbps(); + WifiLinkLayerStats.ChannelStats channlStatsEntryOnFreq = + stats.channelStatsMap.get(link.frequencyMhz); + if (channlStatsEntryOnFreq != null) { + linkStats.channelWidth = channlStatsEntryOnFreq.channelWidth; + linkStats.centerFreqFirstSeg = + channlStatsEntryOnFreq.frequencyFirstSegment; + linkStats.centerFreqSecondSeg = + channlStatsEntryOnFreq.frequencySecondSegment; + linkStats.onTimeInMs = channlStatsEntryOnFreq.radioOnTimeMs; + linkStats.ccaBusyTimeInMs = channlStatsEntryOnFreq.ccaBusyTimeMs; + } + linkStats.contentionTimeStats = + new ContentionTimeStats[NUM_WME_ACCESS_CATEGORIES]; + linkStats.packetStats = new PacketStats[NUM_WME_ACCESS_CATEGORIES]; + for (int ac = 0; ac < NUM_WME_ACCESS_CATEGORIES; ac++) { + ContentionTimeStats contentionTimeStats = new ContentionTimeStats(); + PacketStats packetStats = new PacketStats(); + switch (ac) { + case ContentionTimeStats.WME_ACCESS_CATEGORY_BE: + contentionTimeStats.accessCategory = + ContentionTimeStats.WME_ACCESS_CATEGORY_BE; + contentionTimeStats.contentionTimeMinMicros = + stats.contentionTimeMinBeInUsec; + contentionTimeStats.contentionTimeMaxMicros = + stats.contentionTimeMaxBeInUsec; + contentionTimeStats.contentionTimeAvgMicros = + stats.contentionTimeAvgBeInUsec; + contentionTimeStats.contentionNumSamples = + stats.contentionNumSamplesBe; + packetStats.accessCategory = + ContentionTimeStats.WME_ACCESS_CATEGORY_BE; + packetStats.txSuccess = link.txmpdu_be; + packetStats.txRetries = link.retries_be; + packetStats.txBad = link.lostmpdu_be; + packetStats.rxSuccess = link.rxmpdu_be; + break; + case ContentionTimeStats.WME_ACCESS_CATEGORY_BK: + contentionTimeStats.accessCategory = + ContentionTimeStats.WME_ACCESS_CATEGORY_BK; + contentionTimeStats.contentionTimeMinMicros = + stats.contentionTimeMinBkInUsec; + contentionTimeStats.contentionTimeMaxMicros = + stats.contentionTimeMaxBkInUsec; + contentionTimeStats.contentionTimeAvgMicros = + stats.contentionTimeAvgBkInUsec; + contentionTimeStats.contentionNumSamples = + stats.contentionNumSamplesBk; + packetStats.accessCategory = + ContentionTimeStats.WME_ACCESS_CATEGORY_BK; + packetStats.txSuccess = link.txmpdu_bk; + packetStats.txRetries = link.retries_bk; + packetStats.txBad = link.lostmpdu_bk; + packetStats.rxSuccess = link.rxmpdu_bk; + break; + case ContentionTimeStats.WME_ACCESS_CATEGORY_VI: + contentionTimeStats.accessCategory = + ContentionTimeStats.WME_ACCESS_CATEGORY_VI; + contentionTimeStats.contentionTimeMinMicros = + stats.contentionTimeMinViInUsec; + contentionTimeStats.contentionTimeMaxMicros = + stats.contentionTimeMaxViInUsec; + contentionTimeStats.contentionTimeAvgMicros = + stats.contentionTimeAvgViInUsec; + contentionTimeStats.contentionNumSamples = + stats.contentionNumSamplesVi; + packetStats.accessCategory = + ContentionTimeStats.WME_ACCESS_CATEGORY_VI; + packetStats.txSuccess = link.txmpdu_vi; + packetStats.txRetries = link.retries_vi; + packetStats.txBad = link.lostmpdu_vi; + packetStats.rxSuccess = link.rxmpdu_vi; + break; + case ContentionTimeStats.WME_ACCESS_CATEGORY_VO: + contentionTimeStats.accessCategory = + ContentionTimeStats.WME_ACCESS_CATEGORY_VO; + contentionTimeStats.contentionTimeMinMicros = + stats.contentionTimeMinVoInUsec; + contentionTimeStats.contentionTimeMaxMicros = + stats.contentionTimeMaxVoInUsec; + contentionTimeStats.contentionTimeAvgMicros = + stats.contentionTimeAvgVoInUsec; + contentionTimeStats.contentionNumSamples = + stats.contentionNumSamplesVo; + packetStats.accessCategory = + ContentionTimeStats.WME_ACCESS_CATEGORY_VO; + packetStats.txSuccess = link.txmpdu_vo; + packetStats.txRetries = link.retries_vo; + packetStats.txBad = link.lostmpdu_vo; + packetStats.rxSuccess = link.rxmpdu_vo; + break; + default: + Log.e(TAG, "Unknown WME Access Category: " + ac); + } + linkStats.contentionTimeStats[ac] = contentionTimeStats; + linkStats.packetStats[ac] = packetStats; + } + if (link.peerInfo != null && link.peerInfo.length > 0) { + int numPeers = link.peerInfo.length; + linkStats.peerInfo = new PeerInfo[numPeers]; + for (int peerIndex = 0; peerIndex < numPeers; ++peerIndex) { + PeerInfo peerInfo = new PeerInfo(); + WifiLinkLayerStats.PeerInfo curPeer = link.peerInfo[peerIndex]; + peerInfo.staCount = curPeer.staCount; + peerInfo.chanUtil = curPeer.chanUtil; + if (curPeer.rateStats != null && curPeer.rateStats.length > 0) { + int numRates = curPeer.rateStats.length; + peerInfo.rateStats = new RateStats[numRates]; + for (int rateIndex = 0; rateIndex < numRates; rateIndex++) { + RateStats rateStats = new RateStats(); + WifiLinkLayerStats.RateStat curRate = + curPeer.rateStats[rateIndex]; + rateStats.preamble = curRate.preamble; + rateStats.nss = curRate.nss; + rateStats.bw = curRate.bw; + rateStats.rateMcsIdx = curRate.rateMcsIdx; + rateStats.bitRateInKbps = curRate.bitRateInKbps; + rateStats.txMpdu = curRate.txMpdu; + rateStats.rxMpdu = curRate.rxMpdu; + rateStats.mpduLost = curRate.mpduLost; + rateStats.retries = curRate.retries; + peerInfo.rateStats[rateIndex] = rateStats; + } + } + linkStats.peerInfo[peerIndex] = peerInfo; + } + } + List<ScanResultWithSameFreq> scanResultsWithSameFreq = new ArrayList<>(); + if (link.scan_results_same_freq != null + && link.scan_results_same_freq.size() > 0) { + for (int scanResultsIndex = 0; scanResultsIndex + < link.scan_results_same_freq.size(); ++scanResultsIndex) { + WifiLinkLayerStats.ScanResultWithSameFreq linkLayerScanResult = + link.scan_results_same_freq.get(scanResultsIndex); + if (linkLayerScanResult != null) { + String wifiLinkBssid = (mloLinks.size() > 0) + ? mloLinks.get(link.link_id, new MloLink()) + .getApMacAddress().toString() : info.getBSSID(); + if (!linkLayerScanResult.bssid.equals(wifiLinkBssid)) { + ScanResultWithSameFreq scanResultWithSameFreq = + new ScanResultWithSameFreq(); + scanResultWithSameFreq.scanResultTimestampMicros = + linkLayerScanResult.scan_result_timestamp_micros; + scanResultWithSameFreq.rssi = linkLayerScanResult.rssi; + scanResultWithSameFreq.frequencyMhz = + linkLayerScanResult.frequencyMhz; + scanResultsWithSameFreq.add(scanResultWithSameFreq); + } + } + } + } + linkStats.scanResultWithSameFreq = + scanResultsWithSameFreq.toArray(new ScanResultWithSameFreq[0]); + wifiUsabilityStatsEntry.linkStats[i] = linkStats; + } + } + wifiUsabilityStatsEntry.mloMode = stats.wifiMloMode; + wifiUsabilityStatsEntry.labelBadEventCount = mAccumulatedLabelBadCount; + wifiUsabilityStatsEntry.wifiFrameworkState = mWifiFrameworkState; + wifiUsabilityStatsEntry.isNetworkCapabilitiesDownstreamSufficient = + mSpeedSufficientNetworkCapabilities.Downstream; + wifiUsabilityStatsEntry.isNetworkCapabilitiesUpstreamSufficient = + mSpeedSufficientNetworkCapabilities.Upstream; + wifiUsabilityStatsEntry.isThroughputPredictorDownstreamSufficient = + mSpeedSufficientThroughputPredictor.Downstream; + wifiUsabilityStatsEntry.isThroughputPredictorUpstreamSufficient = + mSpeedSufficientThroughputPredictor.Upstream; + wifiUsabilityStatsEntry.isBluetoothConnected = + mWifiGlobals.isBluetoothConnected(); + wifiUsabilityStatsEntry.uwbAdapterState = getLastUwbState(); + wifiUsabilityStatsEntry.isLowLatencyActivated = getLowLatencyState(); + wifiUsabilityStatsEntry.maxSupportedTxLinkspeed = + info.getMaxSupportedTxLinkSpeedMbps(); + wifiUsabilityStatsEntry.maxSupportedRxLinkspeed = + info.getMaxSupportedRxLinkSpeedMbps(); + wifiUsabilityStatsEntry.voipMode = getVoipMode(); + wifiUsabilityStatsEntry.threadDeviceRole = getLastThreadDeviceRole(); + wifiUsabilityStatsEntry.timeStampMs = stats.timeStampInMs; wifiUsabilityStatsEntry.totalTxSuccess = stats.txmpdu_be + stats.txmpdu_bk + stats.txmpdu_vi + stats.txmpdu_vo; @@ -7102,6 +7495,17 @@ public class WifiMetrics { radioStats.totalRoamScanTimeMs = radio.on_time_roam_scan; radioStats.totalPnoScanTimeMs = radio.on_time_pno_scan; radioStats.totalHotspot2ScanTimeMs = radio.on_time_hs20_scan; + if (radio.tx_time_in_ms_per_level != null + && radio.tx_time_in_ms_per_level.length > 0) { + int txTimePerLevelLength = radio.tx_time_in_ms_per_level.length; + radioStats.txTimeMsPerLevel = new int[txTimePerLevelLength]; + for (int txTimePerLevelIndex = 0; + txTimePerLevelIndex < txTimePerLevelLength; + ++txTimePerLevelIndex) { + radioStats.txTimeMsPerLevel[txTimePerLevelIndex] = + radio.tx_time_in_ms_per_level[txTimePerLevelIndex]; + } + } wifiUsabilityStatsEntry.radioStats[i] = radioStats; } } @@ -7126,11 +7530,12 @@ public class WifiMetrics { mLastTotalBeaconRx = stats.beacon_rx; wifiUsabilityStatsEntry.timeSliceDutyCycleInPercent = stats.timeSliceDutyCycleInPercent; - boolean isSameBssidAndFreq = mLastBssid == null || mLastFrequency == -1 - || (mLastBssid.equals(info.getBSSID()) - && mLastFrequency == info.getFrequency()); - mLastBssid = info.getBSSID(); - mLastFrequency = info.getFrequency(); + String lastBssid = mLastBssidPerIfaceMap.get(ifaceName); + int lastFrequency = mLastFrequencyPerIfaceMap.getOrDefault(ifaceName, -1); + boolean isSameBssidAndFreq = lastBssid == null || lastFrequency == -1 + || (lastBssid.equals(info.getBSSID()) && lastFrequency == info.getFrequency()); + mLastBssidPerIfaceMap.put(ifaceName, info.getBSSID()); + mLastFrequencyPerIfaceMap.put(ifaceName, info.getFrequency()); wifiUsabilityStatsEntry.wifiScore = mLastScoreNoReset; wifiUsabilityStatsEntry.wifiUsabilityScore = mLastWifiUsabilityScoreNoReset; wifiUsabilityStatsEntry.seqNumToFramework = mSeqNumToFramework; @@ -7220,13 +7625,18 @@ public class WifiMetrics { } if (mWifiChannelUtilization != null) { wifiUsabilityStatsEntry.channelUtilizationRatio = - mWifiChannelUtilization.getUtilizationRatio(mLastFrequency); + mWifiChannelUtilization.getUtilizationRatio(lastFrequency); } if (mWifiDataStall != null) { wifiUsabilityStatsEntry.isThroughputSufficient = mWifiDataStall.isThroughputSufficient(); wifiUsabilityStatsEntry.isCellularDataAvailable = mWifiDataStall.isCellularDataAvailable(); + wifiUsabilityStatsEntry.txTransmittedBytes = + mWifiDataStall.getTxTransmittedBytes(); + wifiUsabilityStatsEntry.rxTransmittedBytes = + mWifiDataStall.getRxTransmittedBytes(); + wifiUsabilityStatsEntry.statusDataStall = statusDataStall; } if (mWifiSettingsStore != null) { wifiUsabilityStatsEntry.isWifiScoringEnabled = @@ -7256,21 +7666,14 @@ public class WifiMetrics { wifiUsabilityStatsEntry.rateStats[i] = rate; } } + wifiUsabilityStatsEntry.captureEventType = oneshot + ? WifiUsabilityStatsEntry.CAPTURE_EVENT_TYPE_ONESHOT_RSSI_POLL + : WifiUsabilityStatsEntry.CAPTURE_EVENT_TYPE_SYNCHRONOUS; - mWifiUsabilityStatsEntriesList.add(wifiUsabilityStatsEntry); - mWifiUsabilityStatsCounter++; - if (mWifiUsabilityStatsCounter >= NUM_WIFI_USABILITY_STATS_ENTRIES_PER_WIFI_GOOD) { - addToWifiUsabilityStatsList(ifaceName, WifiUsabilityStats.LABEL_GOOD, - WifiUsabilityStats.TYPE_UNKNOWN, -1); - } if (mScoreBreachLowTimeMillis != -1) { long elapsedTime = mClock.getElapsedSinceBootMillis() - mScoreBreachLowTimeMillis; if (elapsedTime >= MIN_SCORE_BREACH_TO_GOOD_STATS_WAIT_TIME_MS) { mScoreBreachLowTimeMillis = -1; - if (elapsedTime <= VALIDITY_PERIOD_OF_SCORE_BREACH_LOW_MS) { - addToWifiUsabilityStatsList(ifaceName, WifiUsabilityStats.LABEL_GOOD, - WifiUsabilityStats.TYPE_UNKNOWN, -1); - } } } @@ -7283,6 +7686,17 @@ public class WifiMetrics { info)); } + // We need the records in the ring buffer to all have the same timebase. The records + // created here are timestamped by the WiFi driver and the timestamps have been found to + // drift relative to the Android clock. Historically, these records have been forwarded + // to external WiFi scorers with the drifting clock. In order to maintain historical + // behavior while ensuring that records in the ring buffer have the same timebase, we + // will send the record created in this function unmodified to any external WiFi Scorer, + // but we will modify the timestamp before storing in the ring buffer. Thus, the + // following statement, which also modifies the timestamp, must be executed AFTER the + // record is deep copied and sent to the external WiFi Scorer. + addToRingBuffer(wifiUsabilityStatsEntry); + mSeqNumInsideFramework++; mProbeStatusSinceLastUpdate = android.net.wifi.WifiUsabilityStatsEntry.PROBE_STATUS_NO_PROBE; @@ -7365,6 +7779,60 @@ public class WifiMetrics { return contentionTimeStatsArray; } + private android.net.wifi.WifiUsabilityStatsEntry.PacketStats[] + convertPacketStats(WifiLinkLayerStats.LinkSpecificStats stats) { + android.net.wifi.WifiUsabilityStatsEntry.PacketStats[] packetStatsArray = + new android.net.wifi.WifiUsabilityStatsEntry.PacketStats[ + android.net.wifi.WifiUsabilityStatsEntry.NUM_WME_ACCESS_CATEGORIES]; + for (int ac = 0; ac < android.net.wifi.WifiUsabilityStatsEntry.NUM_WME_ACCESS_CATEGORIES; + ac++) { + android.net.wifi.WifiUsabilityStatsEntry.PacketStats packetStats = null; + switch (ac) { + case android.net.wifi.WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_BE: + packetStats = + new android.net.wifi.WifiUsabilityStatsEntry.PacketStats( + stats.txmpdu_be, + stats.retries_be, + stats.lostmpdu_be, + stats.rxmpdu_be + ); + break; + case android.net.wifi.WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_BK: + packetStats = + new android.net.wifi.WifiUsabilityStatsEntry.PacketStats( + stats.txmpdu_bk, + stats.retries_bk, + stats.lostmpdu_bk, + stats.rxmpdu_bk + ); + break; + case android.net.wifi.WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_VO: + packetStats = + new android.net.wifi.WifiUsabilityStatsEntry.PacketStats( + stats.txmpdu_vo, + stats.retries_vo, + stats.lostmpdu_vo, + stats.rxmpdu_vo + ); + break; + case android.net.wifi.WifiUsabilityStatsEntry.WME_ACCESS_CATEGORY_VI: + packetStats = + new android.net.wifi.WifiUsabilityStatsEntry.PacketStats( + stats.txmpdu_vi, + stats.retries_vi, + stats.lostmpdu_vi, + stats.rxmpdu_vi + ); + break; + default: + Log.d(TAG, "Unknown WME Access Category: " + ac); + packetStats = null; + } + packetStatsArray[ac] = packetStats; + } + return packetStatsArray; + } + private android.net.wifi.WifiUsabilityStatsEntry.RateStats[] convertRateStats( WifiLinkLayerStats.LinkSpecificStats stats) { android.net.wifi.WifiUsabilityStatsEntry.RateStats[] rateStats = null; @@ -7388,6 +7856,43 @@ public class WifiMetrics { return rateStats; } + private android.net.wifi.WifiUsabilityStatsEntry.PeerInfo[] convertPeerInfo( + WifiLinkLayerStats.LinkSpecificStats stats) { + android.net.wifi.WifiUsabilityStatsEntry.PeerInfo[] peerInfos = null; + if (stats.peerInfo != null && stats.peerInfo.length > 0) { + int numPeers = stats.peerInfo.length; + peerInfos = new android.net.wifi.WifiUsabilityStatsEntry.PeerInfo[numPeers]; + for (int i = 0; i < numPeers; i++) { + WifiLinkLayerStats.PeerInfo curPeer = stats.peerInfo[i]; + android.net.wifi.WifiUsabilityStatsEntry.RateStats[] rateStats = null; + if (curPeer.rateStats != null && curPeer.rateStats.length > 0) { + int numRates = curPeer.rateStats.length; + rateStats = new android.net.wifi.WifiUsabilityStatsEntry.RateStats[numRates]; + for (int rateIndex = 0; rateIndex < numRates; ++rateIndex) { + WifiLinkLayerStats.RateStat curRate = curPeer.rateStats[rateIndex]; + rateStats[rateIndex] = + new android.net.wifi.WifiUsabilityStatsEntry.RateStats( + convertPreambleTypeEnumToUsabilityStatsType( + curRate.preamble), + convertSpatialStreamEnumToUsabilityStatsType(curRate.nss), + convertBandwidthEnumToUsabilityStatsType(curRate.bw), + curRate.rateMcsIdx, + curRate.bitRateInKbps, + curRate.txMpdu, + curRate.rxMpdu, + curRate.mpduLost, + curRate.retries); + } + } + android.net.wifi.WifiUsabilityStatsEntry.PeerInfo peerInfo = + new android.net.wifi.WifiUsabilityStatsEntry.PeerInfo( + curPeer.staCount, curPeer.chanUtil, rateStats); + peerInfos[i] = peerInfo; + } + } + return peerInfos; + } + private SparseArray<android.net.wifi.WifiUsabilityStatsEntry.LinkStats> convertLinkStats( WifiLinkLayerStats stats, WifiInfo info) { SparseArray<android.net.wifi.WifiUsabilityStatsEntry.LinkStats> linkStats = @@ -7408,6 +7913,30 @@ public class WifiMetrics { mLastLinkMetrics.put(inStat.link_id, linkMetrics); WifiLinkLayerStats.ChannelStats channelStatsMap = stats.channelStatsMap.get( inStat.frequencyMhz); + List<android.net.wifi.WifiUsabilityStatsEntry.ScanResultWithSameFreq> + scanResultsWithSameFreq = new ArrayList<>(); + + if (inStat.scan_results_same_freq != null + && inStat.scan_results_same_freq.size() > 0) { + for (int scanResultsIndex = 0; scanResultsIndex + < inStat.scan_results_same_freq.size(); ++scanResultsIndex) { + WifiLinkLayerStats.ScanResultWithSameFreq linkLayerScanResult = + inStat.scan_results_same_freq.get(scanResultsIndex); + if (linkLayerScanResult != null) { + if (!linkLayerScanResult.bssid.equals(info.getBSSID())) { + android.net.wifi.WifiUsabilityStatsEntry.ScanResultWithSameFreq + scanResultWithSameFreq = + new android.net.wifi.WifiUsabilityStatsEntry + .ScanResultWithSameFreq( + linkLayerScanResult.scan_result_timestamp_micros, + linkLayerScanResult.rssi, + linkLayerScanResult.frequencyMhz + ); + scanResultsWithSameFreq.add(scanResultWithSameFreq); + } + } + } + } // Note: RSSI, Tx & Rx link speed are derived from signal poll stats which is updated in // Mlolink or WifiInfo (non-MLO case). android.net.wifi.WifiUsabilityStatsEntry.LinkStats outStat = @@ -7415,6 +7944,10 @@ public class WifiMetrics { inStat.state, inStat.radio_id, (mloLinks.size() > 0) ? mloLinks.get(inStat.link_id, new MloLink()).getRssi() : info.getRssi(), + inStat.frequencyMhz, inStat.rssi_mgmt, + (channelStatsMap != null) ? channelStatsMap.channelWidth : 0, + (channelStatsMap != null) ? channelStatsMap.frequencyFirstSegment : 0, + (channelStatsMap != null) ? channelStatsMap.frequencySecondSegment : 0, (mloLinks.size() > 0) ? mloLinks.get(inStat.link_id, new MloLink()).getTxLinkSpeedMbps() : info.getTxLinkSpeedMbps(), (mloLinks.size() > 0) ? mloLinks.get(inStat.link_id, @@ -7430,14 +7963,23 @@ public class WifiMetrics { inStat.beacon_rx, inStat.timeSliceDutyCycleInPercent, (channelStatsMap != null) ? channelStatsMap.ccaBusyTimeMs : 0 , (channelStatsMap != null) ? channelStatsMap.radioOnTimeMs : 0, - convertContentionTimeStats(inStat), - convertRateStats(inStat)); + convertContentionTimeStats(inStat), convertRateStats(inStat), + convertPacketStats(inStat), convertPeerInfo(inStat), + scanResultsWithSameFreq.toArray( + new android.net.wifi.WifiUsabilityStatsEntry + .ScanResultWithSameFreq[0])); linkStats.put(inStat.link_id, outStat); } return linkStats; } + /** + * Converts from the WifiUsabilityStatsEntry proto used internally to the + * WifiUsabilityStatsEntry structure sent on the SDK API. + * + * These are two different types. + */ private android.net.wifi.WifiUsabilityStatsEntry createNewWifiUsabilityStatsEntryParcelable( WifiUsabilityStatsEntry s, WifiLinkLayerStats stats, WifiInfo info) { int probeStatus; @@ -7479,7 +8021,14 @@ public class WifiMetrics { s.rxLinkSpeedMbps, s.timeSliceDutyCycleInPercent, contentionTimeStats, rateStats, radioStats, s.channelUtilizationRatio, s.isThroughputSufficient, s.isWifiScoringEnabled, s.isCellularDataAvailable, 0, 0, 0, false, - convertLinkStats(stats, info) + convertLinkStats(stats, info), s.wifiLinkCount, s.mloMode, + s.txTransmittedBytes, s.rxTransmittedBytes, s.labelBadEventCount, + s.wifiFrameworkState, s.isNetworkCapabilitiesDownstreamSufficient, + s.isNetworkCapabilitiesUpstreamSufficient, + s.isThroughputPredictorDownstreamSufficient, + s.isThroughputPredictorUpstreamSufficient, s.isBluetoothConnected, + s.uwbAdapterState, s.isLowLatencyActivated, s.maxSupportedTxLinkspeed, + s.maxSupportedRxLinkspeed, s.voipMode, s.threadDeviceRole, s.statusDataStall ); } @@ -7611,6 +8160,14 @@ public class WifiMetrics { return; } for (int i = 0; i < stats.length; i++) { + int[] txTimeMsPerLevel = null; + if (stats[i].txTimeMsPerLevel != null && stats[i].txTimeMsPerLevel.length > 0) { + int txTimeMsPerLevelLength = stats[i].txTimeMsPerLevel.length; + txTimeMsPerLevel = new int[txTimeMsPerLevelLength]; + for (int j = 0; j < txTimeMsPerLevelLength; ++j) { + txTimeMsPerLevel[j] = stats[i].txTimeMsPerLevel[j]; + } + } statsParcelable[i] = new android.net.wifi.WifiUsabilityStatsEntry.RadioStats( stats[i].radioId, @@ -7622,13 +8179,20 @@ public class WifiMetrics { stats[i].totalBackgroundScanTimeMs, stats[i].totalRoamScanTimeMs, stats[i].totalPnoScanTimeMs, - stats[i].totalHotspot2ScanTimeMs); + stats[i].totalHotspot2ScanTimeMs, + txTimeMsPerLevel); } } - private WifiUsabilityStatsEntry createNewWifiUsabilityStatsEntry(WifiUsabilityStatsEntry s) { + private WifiUsabilityStatsEntry createNewWifiUsabilityStatsEntry(WifiUsabilityStatsEntry s, + long referenceTimestampMs) { WifiUsabilityStatsEntry out = new WifiUsabilityStatsEntry(); - out.timeStampMs = s.timeStampMs; + // Order the fields here according to the ID in + // packages/modules/Wifi/service/proto/src/metrics.proto + // Privacy review suggests not to upload real timestamp + out.timeStampMs = 0; + out.rssi = s.rssi; + out.linkSpeedMbps = s.linkSpeedMbps; out.totalTxSuccess = s.totalTxSuccess; out.totalTxRetries = s.totalTxRetries; out.totalTxBad = s.totalTxBad; @@ -7642,21 +8206,23 @@ public class WifiMetrics { out.totalRoamScanTimeMs = s.totalRoamScanTimeMs; out.totalPnoScanTimeMs = s.totalPnoScanTimeMs; out.totalHotspot2ScanTimeMs = s.totalHotspot2ScanTimeMs; - out.rssi = s.rssi; - out.linkSpeedMbps = s.linkSpeedMbps; - out.totalCcaBusyFreqTimeMs = s.totalCcaBusyFreqTimeMs; - out.totalRadioOnFreqTimeMs = s.totalRadioOnFreqTimeMs; - out.totalBeaconRx = s.totalBeaconRx; out.wifiScore = s.wifiScore; out.wifiUsabilityScore = s.wifiUsabilityScore; out.seqNumToFramework = s.seqNumToFramework; + out.totalCcaBusyFreqTimeMs = s.totalCcaBusyFreqTimeMs; + out.totalRadioOnFreqTimeMs = s.totalRadioOnFreqTimeMs; + out.totalBeaconRx = s.totalBeaconRx; out.predictionHorizonSec = s.predictionHorizonSec; out.probeStatusSinceLastUpdate = s.probeStatusSinceLastUpdate; out.probeElapsedTimeSinceLastUpdateMs = s.probeElapsedTimeSinceLastUpdateMs; out.probeMcsRateSinceLastUpdate = s.probeMcsRateSinceLastUpdate; out.rxLinkSpeedMbps = s.rxLinkSpeedMbps; - out.isSameBssidAndFreq = s.isSameBssidAndFreq; out.seqNumInsideFramework = s.seqNumInsideFramework; + out.isSameBssidAndFreq = s.isSameBssidAndFreq; + // WifiUsabilityStatsEntry.cellularDataNetworkType (ID: 30) is not implemented + // WifiUsabilityStatsEntry.cellularSignalStrengthDbm (ID: 31) is not implemented + // WifiUsabilityStatsEntry.cellularSignalStrengthDb (ID: 32) is not implemented + // WifiUsabilityStatsEntry.isSameRegisteredCell (ID: 33) is not implemented out.deviceMobilityState = s.deviceMobilityState; out.timeSliceDutyCycleInPercent = s.timeSliceDutyCycleInPercent; out.contentionTimeStats = s.contentionTimeStats; @@ -7668,81 +8234,63 @@ public class WifiMetrics { out.staCount = s.staCount; out.channelUtilization = s.channelUtilization; out.radioStats = s.radioStats; + out.wifiLinkCount = s.wifiLinkCount; + out.linkStats = s.linkStats; + out.mloMode = s.mloMode; + out.txTransmittedBytes = s.txTransmittedBytes; + out.rxTransmittedBytes = s.rxTransmittedBytes; + out.labelBadEventCount = s.labelBadEventCount; + out.wifiFrameworkState = s.wifiFrameworkState; + out.isNetworkCapabilitiesDownstreamSufficient = s.isNetworkCapabilitiesDownstreamSufficient; + out.isNetworkCapabilitiesUpstreamSufficient = s.isNetworkCapabilitiesUpstreamSufficient; + out.isThroughputPredictorDownstreamSufficient = s.isThroughputPredictorDownstreamSufficient; + out.isThroughputPredictorUpstreamSufficient = s.isThroughputPredictorUpstreamSufficient; + out.isBluetoothConnected = s.isBluetoothConnected; + out.uwbAdapterState = s.uwbAdapterState; + out.isLowLatencyActivated = s.isLowLatencyActivated; + out.maxSupportedTxLinkspeed = s.maxSupportedTxLinkspeed; + out.maxSupportedRxLinkspeed = s.maxSupportedRxLinkspeed; + out.voipMode = s.voipMode; + out.threadDeviceRole = s.threadDeviceRole; + out.captureEventType = s.captureEventType; + out.captureEventTypeSubcode = s.captureEventTypeSubcode; + out.statusDataStall = s.statusDataStall; + out.timestampOffsetMs = s.timeStampMs - referenceTimestampMs; return out; } - private WifiUsabilityStats createWifiUsabilityStatsWithLabel(int label, int triggerType, - int firmwareAlertCode) { - WifiUsabilityStats wifiUsabilityStats = new WifiUsabilityStats(); - wifiUsabilityStats.label = label; - wifiUsabilityStats.triggerType = triggerType; - wifiUsabilityStats.firmwareAlertCode = firmwareAlertCode; - wifiUsabilityStats.timeStampMs = mClock.getElapsedSinceBootMillis(); - wifiUsabilityStats.stats = - new WifiUsabilityStatsEntry[mWifiUsabilityStatsEntriesList.size()]; - for (int i = 0; i < mWifiUsabilityStatsEntriesList.size(); i++) { - wifiUsabilityStats.stats[i] = - createNewWifiUsabilityStatsEntry(mWifiUsabilityStatsEntriesList.get(i)); - } - return wifiUsabilityStats; + private void addToRingBuffer(WifiUsabilityStatsEntry wifiUsabilityStatsEntry) { + // We override the timestamp here so that all records have the same time base. + wifiUsabilityStatsEntry.timeStampMs = mClock.getElapsedSinceBootMillis(); + mWifiUsabilityStatsEntriesRingBuffer.add(wifiUsabilityStatsEntry); } /** - * Label the current snapshot of WifiUsabilityStatsEntrys and save the labeled data in memory. - * @param label WifiUsabilityStats.LABEL_GOOD or WifiUsabilityStats.LABEL_BAD - * @param triggerType what event triggers WifiUsabilityStats - * @param firmwareAlertCode the firmware alert code when the stats was triggered by a - * firmware alert + * Used to log an asynchronous event (such as WiFi disconnect) into the ring buffer. */ - public void addToWifiUsabilityStatsList(String ifaceName, int label, int triggerType, - int firmwareAlertCode) { + public void logAsynchronousEvent(String ifaceName, int e, int c) { + if (!isPrimary(ifaceName)) { + return; + } synchronized (mLock) { - if (!isPrimary(ifaceName)) { - return; - } - if (mWifiUsabilityStatsEntriesList.isEmpty() || !mScreenOn) { - return; - } - if (label == WifiUsabilityStats.LABEL_GOOD) { - // Only add a good event if at least |MIN_WIFI_GOOD_USABILITY_STATS_PERIOD_MS| - // has passed. - if (mWifiUsabilityStatsListGood.isEmpty() - || mWifiUsabilityStatsListGood.getLast().stats[mWifiUsabilityStatsListGood - .getLast().stats.length - 1].timeStampMs - + MIN_WIFI_GOOD_USABILITY_STATS_PERIOD_MS - < mWifiUsabilityStatsEntriesList.getLast().timeStampMs) { - while (mWifiUsabilityStatsListGood.size() - >= MAX_WIFI_USABILITY_STATS_LIST_SIZE_PER_TYPE) { - mWifiUsabilityStatsListGood.remove( - mRand.nextInt(mWifiUsabilityStatsListGood.size())); - } - mWifiUsabilityStatsListGood.add( - createWifiUsabilityStatsWithLabel(label, triggerType, - firmwareAlertCode)); - } - } else { - // Only add a bad event if at least |MIN_DATA_STALL_WAIT_MS| - // has passed. - mScoreBreachLowTimeMillis = -1; - if (mWifiUsabilityStatsListBad.isEmpty() - || (mWifiUsabilityStatsListBad.getLast().stats[mWifiUsabilityStatsListBad - .getLast().stats.length - 1].timeStampMs - + MIN_DATA_STALL_WAIT_MS - < mWifiUsabilityStatsEntriesList.getLast().timeStampMs)) { - while (mWifiUsabilityStatsListBad.size() - >= MAX_WIFI_USABILITY_STATS_LIST_SIZE_PER_TYPE) { - mWifiUsabilityStatsListBad.remove( - mRand.nextInt(mWifiUsabilityStatsListBad.size())); - } - mWifiUsabilityStatsListBad.add( - createWifiUsabilityStatsWithLabel(label, triggerType, - firmwareAlertCode)); - } - } - mWifiUsabilityStatsCounter = 0; - mWifiUsabilityStatsEntriesList.clear(); + WifiUsabilityStatsEntry wifiUsabilityStatsEntry = + mWifiUsabilityStatsEntriesRingBuffer.size() + < MAX_WIFI_USABILITY_STATS_ENTRIES_RING_BUFFER_SIZE + ? new WifiUsabilityStatsEntry() : mWifiUsabilityStatsEntriesRingBuffer.remove() + .clear(); + wifiUsabilityStatsEntry.captureEventType = e; + wifiUsabilityStatsEntry.captureEventTypeSubcode = c; + addToRingBuffer(wifiUsabilityStatsEntry); } } + /** + * Used to log an asynchronous event (such as WiFi disconnect) into the ring buffer. + * + * Helper function when the subcode is not needed. + */ + public void logAsynchronousEvent(String ifaceName, int e) { + logAsynchronousEvent(ifaceName, e, -1); + } private DeviceMobilityStatePnoScanStats getOrCreateDeviceMobilityStatePnoScanStats( @DeviceMobilityState int deviceMobilityState) { @@ -8640,7 +9188,8 @@ public class WifiMetrics { */ public void incrementConnectionDuration(String ifaceName, int timeDeltaLastTwoPollsMs, boolean isThroughputSufficient, boolean isCellularDataAvailable, int rssi, int txKbps, - int rxKbps) { + int rxKbps, int txLinkSpeedMbps, int rxLinkSpeedMbps, + @WifiAnnotations.ChannelWidth int channelBandwidth) { synchronized (mLock) { if (!isPrimary(ifaceName)) { return; @@ -8653,7 +9202,8 @@ public class WifiMetrics { WifiStatsLog.write(WifiStatsLog.WIFI_HEALTH_STAT_REPORTED, timeDeltaLastTwoPollsMs, isThroughputSufficient || !mWifiWins, isCellularDataAvailable, band, rssi, txKbps, rxKbps, mScorerUid, (wifiUsabilityState == WifiUsabilityState.USABLE), - convertWifiUsabilityState(wifiUsabilityState)); + convertWifiUsabilityState(wifiUsabilityState), + txLinkSpeedMbps, rxLinkSpeedMbps, convertChannelWidthToProto(channelBandwidth)); } } @@ -8996,6 +9546,18 @@ public class WifiMetrics { return speedSufficient; } + public void updateWiFiEvaluationAndScorerStats(boolean lingering, WifiInfo wifiInfo, + ConnectionCapabilities connectionCapabilities) { + mWifiFrameworkState = getFrameworkStateForScorer(lingering); + Speeds speedsNetworkCapabilities = getNetworkCapabilitiesSpeeds(); + mSpeedSufficientNetworkCapabilities = + calcSpeedSufficientNetworkCapabilities(speedsNetworkCapabilities); + WifiDataStall.Speeds speedsThroughputPredictor = + mWifiDataStall.getThrouhgputPredictorSpeeds(wifiInfo, connectionCapabilities); + mSpeedSufficientThroughputPredictor = + calcSpeedSufficientThroughputPredictor(speedsThroughputPredictor); + } + /** * Log a ScorerPredictionResultReported atom. */ @@ -9004,10 +9566,7 @@ public class WifiMetrics { boolean isMobileDataEnabled, int pollingIntervalMs, int aospScorerPrediction, - int externalScorerPrediction, - boolean lingering, - WifiInfo wifiInfo, - ConnectionCapabilities connectionCapabilities + int externalScorerPrediction ) { boolean isCellularDataAvailable = mWifiDataStall.isCellularDataAvailable(); boolean isThroughputSufficient = mWifiDataStall.isThroughputSufficient(); @@ -9016,14 +9575,6 @@ public class WifiMetrics { hasActiveSubInfo, isMobileDataEnabled, isCellularDataAvailable, mAdaptiveConnectivityEnabled); int scorerUnusableEvent = convertWifiUnusableTypeForScorer(mUnusableEventType); - int wifiFrameworkState = getFrameworkStateForScorer(lingering); - Speeds speedsNetworkCapabilities = getNetworkCapabilitiesSpeeds(); - SpeedSufficient speedSufficientNetworkCapabilities = - calcSpeedSufficientNetworkCapabilities(speedsNetworkCapabilities); - WifiDataStall.Speeds speedsThroughputPredictor = mWifiDataStall.getThrouhgputPredictorSpeeds( - wifiInfo, connectionCapabilities); - SpeedSufficient speedSufficientThroughputPredictor = - calcSpeedSufficientThroughputPredictor(speedsThroughputPredictor); WifiStatsLog.write_non_chained(SCORER_PREDICTION_RESULT_REPORTED, Process.WIFI_UID, @@ -9031,10 +9582,10 @@ public class WifiMetrics { aospScorerPrediction, scorerUnusableEvent, isThroughputSufficient, deviceState, pollingIntervalMs, - wifiFrameworkState, speedSufficientNetworkCapabilities.Downstream, - speedSufficientNetworkCapabilities.Upstream, - speedSufficientThroughputPredictor.Downstream, - speedSufficientThroughputPredictor.Upstream); + mWifiFrameworkState, mSpeedSufficientNetworkCapabilities.Downstream, + mSpeedSufficientNetworkCapabilities.Upstream, + mSpeedSufficientThroughputPredictor.Downstream, + mSpeedSufficientThroughputPredictor.Upstream); if (mScorerUid != Process.WIFI_UID) { WifiStatsLog.write_non_chained(SCORER_PREDICTION_RESULT_REPORTED, mScorerUid, @@ -9042,10 +9593,10 @@ public class WifiMetrics { externalScorerPrediction, scorerUnusableEvent, isThroughputSufficient, deviceState, pollingIntervalMs, - wifiFrameworkState, speedSufficientNetworkCapabilities.Downstream, - speedSufficientNetworkCapabilities.Upstream, - speedSufficientThroughputPredictor.Downstream, - speedSufficientThroughputPredictor.Upstream); + mWifiFrameworkState, mSpeedSufficientNetworkCapabilities.Downstream, + mSpeedSufficientNetworkCapabilities.Upstream, + mSpeedSufficientThroughputPredictor.Downstream, + mSpeedSufficientThroughputPredictor.Upstream); } // We'd better reset to TYPE_NONE if it is defined in the future. @@ -9940,4 +10491,185 @@ public class WifiMetrics { WifiStatsLog.write(WifiStatsLog.SOFT_AP_STATE_CHANGED, WifiStatsLog.SOFT_AP_STATE_CHANGED__HOTSPOT_ON__STATE_OFF); } + + /** + * Report that a client has disconnected from a soft ap session. + * + * @param disconnectReason reason for disconnection. + * @param source calling WorkSource that identifies the creator of the SoftAp. + */ + public void reportOnClientsDisconnected( + @WifiAnnotations.SoftApDisconnectReason int disconnectReason, + WorkSource source) { + WifiStatsLog.write(WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED, + convertDisconnectReasonToProto(disconnectReason), + source.getUid(0) + ); + } + + private static int convertDisconnectReasonToProto( + @WifiAnnotations.SoftApDisconnectReason int disconnectReason) { + return switch (disconnectReason) { + case DeauthenticationReasonCode.REASON_UNKNOWN -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__UNKNOWN; + case DeauthenticationReasonCode.REASON_UNSPECIFIED -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__UNSPECIFIED; + case DeauthenticationReasonCode.REASON_PREV_AUTH_NOT_VALID -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__PREV_AUTH_NOT_VALID; + case DeauthenticationReasonCode.REASON_DEAUTH_LEAVING -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__DEAUTH_LEAVING; + case DeauthenticationReasonCode.REASON_DISASSOC_DUE_TO_INACTIVITY -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__DISASSOC_DUE_TO_INACTIVITY; + case DeauthenticationReasonCode.REASON_DISASSOC_AP_BUSY -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__DISASSOC_AP_BUSY; + case DeauthenticationReasonCode.REASON_CLASS2_FRAME_FROM_NONAUTH_STA -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__CLASS2_FRAME_FROM_NONAUTH_STA; + case DeauthenticationReasonCode.REASON_CLASS3_FRAME_FROM_NONASSOC_STA -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__CLASS3_FRAME_FROM_NONASSOC_STA; + case DeauthenticationReasonCode.REASON_DISASSOC_STA_HAS_LEFT -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__DISASSOC_STA_HAS_LEFT; + case DeauthenticationReasonCode.REASON_STA_REQ_ASSOC_WITHOUT_AUTH -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__STA_REQ_ASSOC_WITHOUT_AUTH; + case DeauthenticationReasonCode.REASON_PWR_CAPABILITY_NOT_VALID -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__PWR_CAPABILITY_NOT_VALID; + case DeauthenticationReasonCode.REASON_SUPPORTED_CHANNEL_NOT_VALID -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__SUPPORTED_CHANNEL_NOT_VALID; + case DeauthenticationReasonCode.REASON_BSS_TRANSITION_DISASSOC -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__BSS_TRANSITION_DISASSOC; + case DeauthenticationReasonCode.REASON_INVALID_IE -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__INVALID_IE; + case DeauthenticationReasonCode.REASON_MICHAEL_MIC_FAILURE -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__MICHAEL_MIC_FAILURE; + case DeauthenticationReasonCode.REASON_FOURWAY_HANDSHAKE_TIMEOUT -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__FOURWAY_HANDSHAKE_TIMEOUT; + case DeauthenticationReasonCode.REASON_GROUP_KEY_UPDATE_TIMEOUT -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__GROUP_KEY_UPDATE_TIMEOUT; + case DeauthenticationReasonCode.REASON_IE_IN_4WAY_DIFFERS -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__IE_IN_4WAY_DIFFERS; + case DeauthenticationReasonCode.REASON_GROUP_CIPHER_NOT_VALID -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__GROUP_CIPHER_NOT_VALID; + case DeauthenticationReasonCode.REASON_PAIRWISE_CIPHER_NOT_VALID -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__PAIRWISE_CIPHER_NOT_VALID; + case DeauthenticationReasonCode.REASON_AKMP_NOT_VALID -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__AKMP_NOT_VALID; + case DeauthenticationReasonCode.REASON_UNSUPPORTED_RSN_IE_VERSION -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__UNSUPPORTED_RSN_IE_VERSION; + case DeauthenticationReasonCode.REASON_INVALID_RSN_IE_CAPAB -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__INVALID_RSN_IE_CAPAB; + case DeauthenticationReasonCode.REASON_IEEE_802_1X_AUTH_FAILED -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__IEEE_802_1X_AUTH_FAILED; + case DeauthenticationReasonCode.REASON_CIPHER_SUITE_REJECTED -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__CIPHER_SUITE_REJECTED; + case DeauthenticationReasonCode.REASON_TDLS_TEARDOWN_UNREACHABLE -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__TDLS_TEARDOWN_UNREACHABLE; + case DeauthenticationReasonCode.REASON_TDLS_TEARDOWN_UNSPECIFIED -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__TDLS_TEARDOWN_UNSPECIFIED; + case DeauthenticationReasonCode.REASON_SSP_REQUESTED_DISASSOC -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__SSP_REQUESTED_DISASSOC; + case DeauthenticationReasonCode.REASON_NO_SSP_ROAMING_AGREEMENT -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__NO_SSP_ROAMING_AGREEMENT; + case DeauthenticationReasonCode.REASON_BAD_CIPHER_OR_AKM -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__BAD_CIPHER_OR_AKM; + case DeauthenticationReasonCode.REASON_NOT_AUTHORIZED_THIS_LOCATION -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__NOT_AUTHORIZED_THIS_LOCATION; + case DeauthenticationReasonCode.REASON_SERVICE_CHANGE_PRECLUDES_TS -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__SERVICE_CHANGE_PRECLUDES_TS; + case DeauthenticationReasonCode.REASON_UNSPECIFIED_QOS_REASON -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__UNSPECIFIED_QOS_REASON; + case DeauthenticationReasonCode.REASON_NOT_ENOUGH_BANDWIDTH -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__NOT_ENOUGH_BANDWIDTH; + case DeauthenticationReasonCode.REASON_DISASSOC_LOW_ACK -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__DISASSOC_LOW_ACK; + case DeauthenticationReasonCode.REASON_EXCEEDED_TXOP -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__EXCEEDED_TXOP; + case DeauthenticationReasonCode.REASON_STA_LEAVING -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__STA_LEAVING; + case DeauthenticationReasonCode.REASON_END_TS_BA_DLS -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__END_TS_BA_DLS; + case DeauthenticationReasonCode.REASON_UNKNOWN_TS_BA -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__UNKNOWN_TS_BA; + case DeauthenticationReasonCode.REASON_TIMEOUT -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__TIMEOUT; + case DeauthenticationReasonCode.REASON_PEERKEY_MISMATCH -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__PEERKEY_MISMATCH; + case DeauthenticationReasonCode.REASON_AUTHORIZED_ACCESS_LIMIT_REACHED -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__AUTHORIZED_ACCESS_LIMIT_REACHED; + case DeauthenticationReasonCode.REASON_EXTERNAL_SERVICE_REQUIREMENTS -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__EXTERNAL_SERVICE_REQUIREMENTS; + case DeauthenticationReasonCode.REASON_INVALID_FT_ACTION_FRAME_COUNT -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__INVALID_FT_ACTION_FRAME_COUNT; + case DeauthenticationReasonCode.REASON_INVALID_PMKID -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__INVALID_PMKID; + case DeauthenticationReasonCode.REASON_INVALID_MDE -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__INVALID_MDE; + case DeauthenticationReasonCode.REASON_INVALID_FTE -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__INVALID_FTE; + case DeauthenticationReasonCode.REASON_MESH_PEERING_CANCELLED -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__MESH_PEERING_CANCELLED; + case DeauthenticationReasonCode.REASON_MESH_MAX_PEERS -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__MESH_MAX_PEERS; + case DeauthenticationReasonCode.REASON_MESH_CONFIG_POLICY_VIOLATION -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__MESH_CONFIG_POLICY_VIOLATION; + case DeauthenticationReasonCode.REASON_MESH_CLOSE_RCVD -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__MESH_CLOSE_RCVD; + case DeauthenticationReasonCode.REASON_MESH_MAX_RETRIES -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__MESH_MAX_RETRIES; + case DeauthenticationReasonCode.REASON_MESH_CONFIRM_TIMEOUT -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__MESH_CONFIRM_TIMEOUT; + case DeauthenticationReasonCode.REASON_MESH_INVALID_GTK -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__MESH_INVALID_GTK; + case DeauthenticationReasonCode.REASON_MESH_INCONSISTENT_PARAMS -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__MESH_INCONSISTENT_PARAMS; + case DeauthenticationReasonCode.REASON_MESH_INVALID_SECURITY_CAP -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__MESH_INVALID_SECURITY_CAP; + case DeauthenticationReasonCode.REASON_MESH_PATH_ERROR_NO_PROXY_INFO -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__MESH_PATH_ERROR_NO_PROXY_INFO; + case DeauthenticationReasonCode.REASON_MESH_PATH_ERROR_NO_FORWARDING_INFO -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__MESH_PATH_ERROR_NO_FORWARDING_INFO; + case DeauthenticationReasonCode.REASON_MESH_PATH_ERROR_DEST_UNREACHABLE -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__MESH_PATH_ERROR_DEST_UNREACHABLE; + case DeauthenticationReasonCode.REASON_MAC_ADDRESS_ALREADY_EXISTS_IN_MBSS -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__MAC_ADDRESS_ALREADY_EXISTS_IN_MBSS; + case DeauthenticationReasonCode.REASON_MESH_CHANNEL_SWITCH_REGULATORY_REQ -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__MESH_CHANNEL_SWITCH_REGULATORY_REQ; + case DeauthenticationReasonCode.REASON_MESH_CHANNEL_SWITCH_UNSPECIFIED -> + WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__MESH_CHANNEL_SWITCH_UNSPECIFIED; + default -> { + Log.e(TAG, "Invalid disconnectReason: " + disconnectReason); + yield WifiStatsLog.WIFI_SOFT_AP_CALLBACK_ON_CLIENTS_DISCONNECTED__DISCONNECT_REASON__UNKNOWN; + } + }; + } + + public int getLastUwbState() { + return mLastUwbState; + } + + public void setLastUwbState(int state) { + mLastUwbState = state; + } + + public boolean getLowLatencyState() { + return mIsLowLatencyActivated; + } + + public void setLowLatencyState(boolean state) { + mIsLowLatencyActivated = state; + } + + public int getVoipMode() { + return mVoipMode; + } + + public void setVoipMode(int mode) { + mVoipMode = mode; + } + + public int getLastThreadDeviceRole() { + return mLastThreadDeviceRole; + } + + public void setLastThreadDeviceRole(int deviceRole) { + mLastThreadDeviceRole = deviceRole; + } } diff --git a/service/java/com/android/server/wifi/WifiMonitor.java b/service/java/com/android/server/wifi/WifiMonitor.java index 7e2e503fe1..eaee9642c9 100644 --- a/service/java/com/android/server/wifi/WifiMonitor.java +++ b/service/java/com/android/server/wifi/WifiMonitor.java @@ -28,6 +28,8 @@ import android.util.ArraySet; import android.util.Log; import android.util.SparseArray; +import androidx.annotation.Keep; + import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Protocol; import com.android.server.wifi.MboOceController.BtmFrameData; @@ -178,6 +180,7 @@ public class WifiMonitor { } private final Map<String, SparseArray<Set<Handler>>> mHandlerMap = new HashMap<>(); + @Keep public synchronized void registerHandler(String iface, int what, Handler handler) { SparseArray<Set<Handler>> ifaceHandlers = mHandlerMap.get(iface); if (ifaceHandlers == null) { @@ -198,6 +201,7 @@ public class WifiMonitor { * @param what * @param handler */ + @Keep public synchronized void deregisterHandler(String iface, int what, Handler handler) { SparseArray<Set<Handler>> ifaceHandlers = mHandlerMap.get(iface); if (ifaceHandlers == null) { diff --git a/service/java/com/android/server/wifi/WifiMulticastLockManager.java b/service/java/com/android/server/wifi/WifiMulticastLockManager.java index 07de76a46e..fa40fd7d3b 100644 --- a/service/java/com/android/server/wifi/WifiMulticastLockManager.java +++ b/service/java/com/android/server/wifi/WifiMulticastLockManager.java @@ -17,6 +17,8 @@ package com.android.server.wifi; import android.annotation.Nullable; +import android.app.ActivityManager; +import android.content.Context; import android.os.BatteryStatsManager; import android.os.Binder; import android.os.Handler; @@ -30,7 +32,9 @@ import com.android.server.wifi.proto.WifiStatsLog; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * WifiMulticastLockManager tracks holders of multicast locks and @@ -38,10 +42,15 @@ import java.util.List; */ public class WifiMulticastLockManager { private static final String TAG = "WifiMulticastLockManager"; + private static final int IMPORTANCE_THRESHOLD = + ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED; private final List<Multicaster> mMulticasters = new ArrayList<>(); + private final Map<Integer, Integer> mNumLocksPerActiveOwner = new HashMap<>(); + private final Map<Integer, Integer> mNumLocksPerInactiveOwner = new HashMap<>(); private int mMulticastEnabled = 0; private int mMulticastDisabled = 0; private final Handler mHandler; + private final Object mLock = new Object(); private boolean mVerboseLoggingEnabled = false; private final BatteryStatsManager mBatteryStats; private final ActiveModeWarden mActiveModeWarden; @@ -58,13 +67,22 @@ public class WifiMulticastLockManager { public WifiMulticastLockManager( ActiveModeWarden activeModeWarden, BatteryStatsManager batteryStats, - Looper looper) { + Looper looper, + Context context) { mBatteryStats = batteryStats; mActiveModeWarden = activeModeWarden; mHandler = new Handler(looper); mActiveModeWarden.registerPrimaryClientModeManagerChangedCallback( new PrimaryClientModeManagerChangedCallback()); + + ActivityManager activityManager = context.getSystemService(ActivityManager.class); + activityManager.addOnUidImportanceListener(new ActivityManager.OnUidImportanceListener() { + @Override + public void onUidImportance(final int uid, final int importance) { + handleImportanceChanged(uid, importance); + } + }, IMPORTANCE_THRESHOLD); } private class Multicaster implements IBinder.DeathRecipient { @@ -72,9 +90,9 @@ public class WifiMulticastLockManager { int mUid; IBinder mBinder; - Multicaster(String tag, IBinder binder) { + Multicaster(int uid, IBinder binder, String tag) { mTag = tag; - mUid = Binder.getCallingUid(); + mUid = uid; mBinder = binder; try { mBinder.linkToDeath(this, 0); @@ -87,7 +105,7 @@ public class WifiMulticastLockManager { public void binderDied() { mHandler.post(() -> { Log.e(TAG, "Multicaster binderDied"); - synchronized (mMulticasters) { + synchronized (mLock) { int i = mMulticasters.indexOf(this); if (i != -1) { removeMulticasterLocked(i, mUid, mTag); @@ -108,16 +126,71 @@ public class WifiMulticastLockManager { return mTag; } + public IBinder getBinder() { + return mBinder; + } + public String toString() { return "Multicaster{" + mTag + " uid=" + mUid + "}"; } } + private boolean uidIsLockOwner(int uid) { + return mNumLocksPerActiveOwner.containsKey(uid) + || mNumLocksPerInactiveOwner.containsKey(uid); + } + + private void transitionUidToActive(int uid) { + if (mNumLocksPerInactiveOwner.containsKey(uid)) { + mNumLocksPerActiveOwner.put(uid, mNumLocksPerInactiveOwner.get(uid)); + mNumLocksPerInactiveOwner.remove(uid); + } + } + + private void transitionUidToInactive(int uid) { + if (mNumLocksPerActiveOwner.containsKey(uid)) { + mNumLocksPerInactiveOwner.put(uid, mNumLocksPerActiveOwner.get(uid)); + mNumLocksPerActiveOwner.remove(uid); + } + } + + private void handleImportanceChanged(int uid, int importance) { + mHandler.post(() -> { + synchronized (mLock) { + if (!uidIsLockOwner(uid)) { + return; + } + + boolean uidIsNowActive = importance < IMPORTANCE_THRESHOLD; + boolean prevIsMulticastEnabled = isMulticastEnabled(); + Log.i(TAG, "Handling importance changed for uid=" + uid + + ", isNowActive=" + uidIsNowActive + ", importance=" + importance); + if (uidIsNowActive) { + transitionUidToActive(uid); + } else { + transitionUidToInactive(uid); + } + + boolean currentIsMulticastEnabled = isMulticastEnabled(); + if (prevIsMulticastEnabled != currentIsMulticastEnabled) { + if (currentIsMulticastEnabled) { + // Filtering should be stopped if multicast is enabled + stopFilteringMulticastPackets(); + } else { + startFilteringMulticastPackets(); + } + } + } + }); + } + protected void dump(PrintWriter pw) { pw.println("mMulticastEnabled " + mMulticastEnabled); pw.println("mMulticastDisabled " + mMulticastDisabled); - pw.println("Multicast Locks held:"); - synchronized (mMulticasters) { + synchronized (mLock) { + pw.println("Active lock owners: " + mNumLocksPerActiveOwner); + pw.println("Inactive lock owners: " + mNumLocksPerInactiveOwner); + pw.println("Multicast Locks held:"); for (Multicaster l : mMulticasters) { pw.print(" "); pw.println(l); @@ -129,11 +202,10 @@ public class WifiMulticastLockManager { mVerboseLoggingEnabled = verboseEnabled; } - /** Start filtering if no multicasters exist. */ - public void initializeFiltering() { - synchronized (mMulticasters) { - // if anybody had requested filters be off, leave off - if (mMulticasters.size() == 0) { + /** Start filtering multicast packets if no locks are actively held */ + public void startFilteringMulticastPackets() { + synchronized (mLock) { + if (!isMulticastEnabled()) { mActiveModeWarden.getPrimaryClientModeManager() .getMcastLockManagerFilterController() .startFilteringMulticastPackets(); @@ -141,15 +213,29 @@ public class WifiMulticastLockManager { } } + private void stopFilteringMulticastPackets() { + mActiveModeWarden.getPrimaryClientModeManager() + .getMcastLockManagerFilterController() + .stopFilteringMulticastPackets(); + } + /** * Acquire a multicast lock. * @param binder a binder used to ensure caller is still alive * @param tag string name of the caller. */ - public void acquireLock(IBinder binder, String tag) { - synchronized (mMulticasters) { + public void acquireLock(int uid, IBinder binder, String tag) { + synchronized (mLock) { mMulticastEnabled++; - mMulticasters.add(new Multicaster(tag, binder)); + + // Assume that the application is active if it is requesting a lock + if (mNumLocksPerInactiveOwner.containsKey(uid)) { + transitionUidToActive(uid); + } + int numLocksHeldByUid = mNumLocksPerActiveOwner.getOrDefault(uid, 0); + mNumLocksPerActiveOwner.put(uid, numLocksHeldByUid + 1); + mMulticasters.add(new Multicaster(uid, binder, tag)); + // Note that we could call stopFilteringMulticastPackets only when // our new size == 1 (first call), but this function won't // be called often and by making the stopPacket call each @@ -159,7 +245,6 @@ public class WifiMulticastLockManager { .stopFilteringMulticastPackets(); } - int uid = Binder.getCallingUid(); final long ident = Binder.clearCallingIdentity(); mBatteryStats.reportWifiMulticastEnabled(new WorkSource(uid)); WifiStatsLog.write_non_chained( @@ -169,14 +254,14 @@ public class WifiMulticastLockManager { } /** Releases a multicast lock */ - public void releaseLock(String tag) { - int uid = Binder.getCallingUid(); - synchronized (mMulticasters) { + public void releaseLock(int uid, IBinder binder, String tag) { + synchronized (mLock) { mMulticastDisabled++; int size = mMulticasters.size(); for (int i = size - 1; i >= 0; i--) { Multicaster m = mMulticasters.get(i); - if ((m != null) && (m.getUid() == uid) && (m.getTag().equals(tag))) { + if ((m != null) && (m.getUid() == uid) && (m.getTag().equals(tag)) + && (m.getBinder() == binder)) { removeMulticasterLocked(i, uid, tag); break; } @@ -184,13 +269,28 @@ public class WifiMulticastLockManager { } } + private void decrementNumLocksForUid(int uid, Map<Integer, Integer> map) { + int numLocksHeldByUid = map.get(uid) - 1; + if (numLocksHeldByUid == 0) { + map.remove(uid); + } else { + map.put(uid, numLocksHeldByUid); + } + } + private void removeMulticasterLocked(int i, int uid, String tag) { Multicaster removed = mMulticasters.remove(i); - if (removed != null) { removed.unlinkDeathRecipient(); } - if (mMulticasters.size() == 0) { + + if (mNumLocksPerActiveOwner.containsKey(uid)) { + decrementNumLocksForUid(uid, mNumLocksPerActiveOwner); + } else if (mNumLocksPerInactiveOwner.containsKey(uid)) { + decrementNumLocksForUid(uid, mNumLocksPerInactiveOwner); + } + + if (!isMulticastEnabled()) { mActiveModeWarden.getPrimaryClientModeManager() .getMcastLockManagerFilterController() .startFilteringMulticastPackets(); @@ -206,8 +306,9 @@ public class WifiMulticastLockManager { /** Returns whether multicast should be allowed (filtering disabled). */ public boolean isMulticastEnabled() { - synchronized (mMulticasters) { - return mMulticasters.size() > 0; + synchronized (mLock) { + // Multicast is enabled if any active lock owners exist + return !mNumLocksPerActiveOwner.isEmpty(); } } diff --git a/service/java/com/android/server/wifi/WifiNative.java b/service/java/com/android/server/wifi/WifiNative.java index aa1d3398e9..3bef19e367 100644 --- a/service/java/com/android/server/wifi/WifiNative.java +++ b/service/java/com/android/server/wifi/WifiNative.java @@ -27,7 +27,6 @@ import static com.android.server.wifi.WifiSettingsConfigStore.WIFI_NATIVE_EXTEND import static com.android.server.wifi.WifiSettingsConfigStore.WIFI_NATIVE_SUPPORTED_FEATURES; import static com.android.server.wifi.p2p.WifiP2pNative.P2P_IFACE_NAME; import static com.android.server.wifi.p2p.WifiP2pNative.P2P_INTERFACE_PROPERTY; -import static com.android.server.wifi.util.GeneralUtil.bitsetToLong; import static com.android.server.wifi.util.GeneralUtil.longToBitset; import static com.android.wifi.flags.Flags.rsnOverriding; @@ -40,6 +39,7 @@ import android.net.MacAddress; import android.net.TrafficStats; import android.net.apf.ApfCapabilities; import android.net.wifi.CoexUnsafeChannel; +import android.net.wifi.DeauthenticationReasonCode; import android.net.wifi.MscsParams; import android.net.wifi.OuiKeyedData; import android.net.wifi.QosPolicyParams; @@ -62,6 +62,8 @@ import android.net.wifi.nl80211.RadioChainInfo; import android.net.wifi.nl80211.WifiNl80211Manager; import android.net.wifi.twt.TwtRequest; import android.net.wifi.twt.TwtSessionCallback; +import android.net.wifi.usd.PublishConfig; +import android.net.wifi.usd.SubscribeConfig; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -74,22 +76,27 @@ import android.util.Log; import android.util.SparseArray; import android.util.SparseIntArray; +import androidx.annotation.Keep; + import com.android.internal.annotations.Immutable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.HexDump; import com.android.modules.utils.build.SdkLevel; import com.android.server.wifi.SupplicantStaIfaceHal.QosPolicyStatus; +import com.android.server.wifi.WifiLinkLayerStats.ScanResultWithSameFreq; import com.android.server.wifi.hal.WifiChip; import com.android.server.wifi.hal.WifiHal; import com.android.server.wifi.hal.WifiNanIface; import com.android.server.wifi.hotspot2.NetworkDetail; import com.android.server.wifi.mockwifi.MockWifiServiceUtil; import com.android.server.wifi.proto.WifiStatsLog; +import com.android.server.wifi.usd.UsdRequestManager; import com.android.server.wifi.util.FrameParser; import com.android.server.wifi.util.InformationElementUtil; import com.android.server.wifi.util.NativeUtil; import com.android.server.wifi.util.NetdWrapper; import com.android.server.wifi.util.NetdWrapper.NetdEventObserver; +import com.android.wifi.flags.Flags; import com.android.wifi.resources.R; import java.io.PrintWriter; @@ -160,6 +167,7 @@ public class WifiNative { * (ScanResult.KEY_MGMT_XX) */ @VisibleForTesting @Nullable SparseIntArray mUnknownAkmMap; + private SupplicantStaIfaceHal.UsdCapabilitiesInternal mCachedUsdCapabilities = null; public WifiNative(WifiVendorHal vendorHal, SupplicantStaIfaceHal staIfaceHal, HostapdHal hostapdHal, @@ -266,6 +274,80 @@ public class WifiNative { } /** + * Whether USD subscriber is supported in USD capability or not. + */ + public boolean isUsdSubscriberSupported() { + return mCachedUsdCapabilities != null && mCachedUsdCapabilities.isUsdSubscriberSupported; + } + + /** + * Whether USD publisher is supported in USD capability or not. + */ + public boolean isUsdPublisherSupported() { + return mCachedUsdCapabilities != null && mCachedUsdCapabilities.isUsdPublisherSupported; + } + + /** + * Gets USD capabilities. + */ + public SupplicantStaIfaceHal.UsdCapabilitiesInternal getUsdCapabilities() { + return mCachedUsdCapabilities; + } + + /** + * Start USD publish. + */ + public boolean startUsdPublish(String interfaceName, int cmdId, PublishConfig publishConfig) { + return mSupplicantStaIfaceHal.startUsdPublish(interfaceName, cmdId, publishConfig); + } + + /** + * Register a framework callback to receive USD events from HAL. + */ + public void registerUsdEventsCallback( + UsdRequestManager.UsdNativeEventsCallback usdNativeEventsCallback) { + mSupplicantStaIfaceHal.registerUsdEventsCallback(usdNativeEventsCallback); + } + + /** + * Start USD subscribe. + */ + public boolean startUsdSubscribe(String interfaceName, int cmdId, + SubscribeConfig subscribeConfig) { + return mSupplicantStaIfaceHal.startUsdSubscribe(interfaceName, cmdId, subscribeConfig); + } + + /** + * Update USD publish. + */ + public void updateUsdPublish(String interfaceName, int publishId, byte[] ssi) { + mSupplicantStaIfaceHal.updateUsdPublish(interfaceName, publishId, ssi); + } + + /** + * Cancel USD publish. + */ + public void cancelUsdPublish(String interfaceName, int publishId) { + mSupplicantStaIfaceHal.cancelUsdPublish(interfaceName, publishId); + } + + /** + * Cancel USD subscribe. + */ + public void cancelUsdSubscribe(String interfaceName, int subscribeId) { + mSupplicantStaIfaceHal.cancelUsdSubscribe(interfaceName, subscribeId); + } + + /** + * Send USD message to the peer identified by the peerId and the peerMacAddress. + */ + public boolean sendUsdMessage(String interfaceName, int ownId, int peerId, + MacAddress peerMacAddress, byte[] message) { + return mSupplicantStaIfaceHal.sendUsdMessage(interfaceName, ownId, peerId, peerMacAddress, + message); + } + + /** * Callbacks for SoftAp interface. */ public class SoftApHalCallbackFromWificond implements WifiNl80211Manager.SoftApCallback { @@ -288,13 +370,14 @@ public class WifiNative { @Override public void onSoftApChannelSwitched(int frequency, int bandwidth) { mSoftApHalCallback.onInfoChanged(mIfaceName, frequency, bandwidth, - ScanResult.WIFI_STANDARD_UNKNOWN, null, Collections.emptyList()); + ScanResult.WIFI_STANDARD_UNKNOWN, null, null, Collections.emptyList()); } @Override public void onConnectedClientsChanged(NativeWifiClient client, boolean isConnected) { mSoftApHalCallback.onConnectedClientsChanged(mIfaceName, - client.getMacAddress(), isConnected); + client.getMacAddress(), isConnected, + DeauthenticationReasonCode.REASON_UNKNOWN); } } @@ -346,11 +429,15 @@ public class WifiNative { * indication that the SoftAp is not enabled. * @param bandwidth The new bandwidth of the SoftAp. * @param generation The new generation of the SoftAp. + * @param apIfaceInstanceMacAddress MAC Address of the apIfaceInstance. + * @param mldMacAddress MAC Address of the multiple link device (MLD) which apIfaceInstance + * is associated with. * @param vendorData List of {@link OuiKeyedData} containing vendor-specific configuration * data, or empty list if not provided. */ void onInfoChanged(String apIfaceInstance, int frequency, int bandwidth, - int generation, MacAddress apIfaceInstanceMacAddress, + int generation, @Nullable MacAddress apIfaceInstanceMacAddress, + @Nullable MacAddress mldMacAddress, @NonNull List<OuiKeyedData> vendorData); /** * Invoked when there is a change in the associated station (STA). @@ -359,9 +446,11 @@ public class WifiNative { * @param clientAddress Macaddress of the client. * @param isConnected Indication as to whether the client is connected (true), or * disconnected (false). + * @param disconnectReason The reason for disconnection, if applicable. This + * parameter is only meaningful when {@code isConnected} is false. */ void onConnectedClientsChanged(String apIfaceInstance, MacAddress clientAddress, - boolean isConnected); + boolean isConnected, @WifiAnnotations.SoftApDisconnectReason int disconnectReason); } /******************************************************** @@ -396,7 +485,7 @@ public class WifiNative { /** Network observer registered for this interface */ public NetworkObserverInternal networkObserver; /** Interface feature set / capabilities */ - public long featureSet; + public BitSet featureSet = new BitSet(); public int bandsSupported; public DeviceWiphyCapabilities phyCapabilities; public WifiHal.WifiInterface iface; @@ -420,6 +509,12 @@ public class WifiNative { case IFACE_TYPE_AP: typeString = "AP"; break; + case IFACE_TYPE_P2P: + typeString = "P2P"; + break; + case IFACE_TYPE_NAN: + typeString = "NAN"; + break; default: typeString = "<UNKNOWN>"; break; @@ -1316,8 +1411,12 @@ public class WifiNative { nanInterfaceDestroyedListener, handler, requestorWs); if (nanIface != null) { iface.iface = nanIface; - return iface; + iface.name = nanIface.getName(); + if (!TextUtils.isEmpty(iface.name)) { + return iface; + } } + mIfaceMgr.removeIface(iface.id); } Log.e(TAG, "Failed to allocate new Nan iface"); stopHalAndWificondIfNecessary(); @@ -1402,11 +1501,16 @@ public class WifiNative { * * @param ifaceName Name of the iface. * @param apIfaceInstance The identity of the ap instance. + * @param isMloAp true when current access point is using multiple link operation. * @return true if the operation succeeded, false if there is an error in Hal. */ public boolean removeIfaceInstanceFromBridgedApIface(@NonNull String ifaceName, - @NonNull String apIfaceInstance) { + @NonNull String apIfaceInstance, boolean isMloAp) { synchronized (mLock) { + if (isMloAp && mHostapdHal != null && Flags.mloSap()) { + mHostapdHal.removeLinkFromMultipleLinkBridgedApIface(ifaceName, + apIfaceInstance); + } if (mWifiVendorHal.isVendorHalSupported()) { return mWifiVendorHal.removeIfaceInstanceFromBridgedApIface(ifaceName, apIfaceInstance); @@ -1562,8 +1666,6 @@ public class WifiNative { iface.featureSet = getSupportedFeatureSetInternal(iface.name); updateSupportedBandForStaInternal(iface); - mIsRsnOverridingSupported = mContext.getResources().getBoolean( - R.bool.config_wifiRsnOverridingEnabled) && rsnOverriding(); mWifiVendorHal.enableStaChannelForPeerNetwork(mContext.getResources().getBoolean( R.bool.config_wifiEnableStaIndoorChannelForPeerNetwork), @@ -1574,6 +1676,29 @@ public class WifiNative { } /** + * Return true when the device supports Wi-Fi 7 MLD AP and multiple links operation (MLO). + */ + public boolean isMLDApSupportMLO() { + if (!Flags.mloSap()) { + return false; + } + BitSet cachedFeatureSet = getCompleteFeatureSetFromConfigStore(); + return mWifiInjector.getWifiGlobals().isMLDApSupported() + && cachedFeatureSet.get(WifiManager.WIFI_FEATURE_SOFTAP_MLO); + } + + /** + * Return true when the device supports multiple Wi-Fi 7 multi-link devices (MLD) on SoftAp. + */ + public boolean isMultipleMLDSupportedOnSap() { + if (!Flags.multipleMldOnSapSupported()) { + return false; + } + BitSet cachedFeatureSet = getCompleteFeatureSetFromConfigStore(); + return cachedFeatureSet.get(WifiManager.WIFI_FEATURE_MULTIPLE_MLD_ON_SAP); + } + + /** * Setup an interface for Soft AP mode operations. * * This method configures an interface in AP mode in all the native daemons @@ -1590,7 +1715,8 @@ public class WifiNative { public String setupInterfaceForSoftApMode( @NonNull InterfaceCallback interfaceCallback, @NonNull WorkSource requestorWs, @SoftApConfiguration.BandType int band, boolean isBridged, - @NonNull SoftApManager softApManager, @NonNull List<OuiKeyedData> vendorData) { + @NonNull SoftApManager softApManager, @NonNull List<OuiKeyedData> vendorData, + boolean isUsingMlo) { synchronized (mLock) { String bugTitle = "Wi-Fi BugReport (softAp interface failure)"; String errorMsg = ""; @@ -1628,7 +1754,7 @@ public class WifiNative { return null; } String ifaceInstanceName = iface.name; - if (isBridged) { + if (isBridged && !isUsingMlo) { List<String> instances = getBridgedApInstances(iface.name); if (instances == null || instances.size() == 0) { errorMsg = "Failed to get bridged AP instances" + iface.name; @@ -1764,7 +1890,13 @@ public class WifiNative { iface.featureSet = getSupportedFeatureSetInternal(iface.name); saveCompleteFeatureSetInConfigStoreIfNecessary(iface.featureSet); updateSupportedBandForStaInternal(iface); - mIsEnhancedOpenSupported = (iface.featureSet & WIFI_FEATURE_OWE) != 0; + mIsEnhancedOpenSupported = iface.featureSet.get(WIFI_FEATURE_OWE); + if (rsnOverriding()) { + mIsRsnOverridingSupported = isSupplicantAidlServiceVersionAtLeast(4) + ? mSupplicantStaIfaceHal.isRsnOverridingSupported(iface.name) + : mContext.getResources().getBoolean( + R.bool.config_wifiRsnOverridingEnabled); + } Log.i(TAG, "Successfully switched to connectivity mode on iface=" + iface); return true; } @@ -1909,6 +2041,7 @@ public class WifiNative { * @return frequencies vector of valid frequencies (MHz), or null for error. * @throws IllegalArgumentException if band is not recognized. */ + @Keep public int [] getChannelsForBand(@WifiAnnotations.WifiBandBasic int band) { if (!SdkLevel.isAtLeastS() && band == WifiScanner.WIFI_BAND_60_GHZ) { // 60 GHz band is new in Android S, return empty array on older SDK versions @@ -2368,7 +2501,7 @@ public class WifiNative { */ public @SoftApManager.StartResult int startSoftAp( @NonNull String ifaceName, SoftApConfiguration config, boolean isMetered, - SoftApHalCallback callback) { + SoftApHalCallback callback, boolean isUsingMlo) { if (mHostapdHal.isApInfoCallbackSupported()) { if (!mHostapdHal.registerApCallback(ifaceName, callback)) { Log.e(TAG, "Failed to register ap hal event callback"); @@ -2383,8 +2516,10 @@ public class WifiNative { return SoftApManager.START_RESULT_FAILURE_REGISTER_AP_CALLBACK_WIFICOND; } } - - if (!mHostapdHal.addAccessPoint(ifaceName, config, isMetered, callback::onFailure)) { + if (!mHostapdHal.addAccessPoint(ifaceName, config, isMetered, + isUsingMlo, + getBridgedApInstances(ifaceName), + callback::onFailure)) { String errorMsg = "Failed to add softAp"; Log.e(TAG, errorMsg); mWifiMetrics.incrementNumSetupSoftApInterfaceFailureDueToHostapd(); @@ -2404,6 +2539,7 @@ public class WifiNative { * @param reasonCode One of disconnect reason code which defined in {@link ApConfigUtil}. * @return true on success, false otherwise. */ + @Keep public boolean forceClientDisconnect(@NonNull String ifaceName, @NonNull MacAddress client, int reasonCode) { return mHostapdHal.forceClientDisconnect(ifaceName, client, reasonCode); @@ -3731,12 +3867,13 @@ public class WifiNative { @Nullable ScanData getCachedScanResults(String ifaceName) { ScanData scanData = mWifiVendorHal.getCachedScanData(ifaceName); - if (scanData == null || scanData.getResults() == null) { + ScanResult[] scanResults = scanData != null ? scanData.getResults() : null; + if (scanResults == null) { return null; } - ScanResult[] results = getCachedScanResultsFilteredByLocationModeEnabled( - scanData.getResults()); - return new ScanData(0, 0, 0, scanData.getScannedBands(), results); + ScanResult[] filteredResults = getCachedScanResultsFilteredByLocationModeEnabled( + scanResults); + return new ScanData(0, 0, 0, scanData.getScannedBands(), filteredResults); } /** @@ -3760,10 +3897,35 @@ public class WifiNative { * Gets the latest link layer stats * @param ifaceName Name of the interface. */ + @Keep public WifiLinkLayerStats getWifiLinkLayerStats(@NonNull String ifaceName) { WifiLinkLayerStats stats = mWifiVendorHal.getWifiLinkLayerStats(ifaceName); if (stats != null) { stats.aggregateLinkLayerStats(); + stats.wifiMloMode = getMloMode(); + ScanData scanData = getCachedScanResults(ifaceName); + ScanResult[] scanResults = scanData != null ? scanData.getResults() : null; + if (scanResults != null && scanResults.length > 0) { + for (int linkIndex = 0; linkIndex < stats.links.length; ++linkIndex) { + List<ScanResultWithSameFreq> ScanResultsSameFreq = new ArrayList<>(); + for (int scanResultsIndex = 0; scanResultsIndex < scanResults.length; + ++scanResultsIndex) { + if (scanResults[scanResultsIndex].frequency + != stats.links[linkIndex].frequencyMhz) { + continue; + } + ScanResultWithSameFreq ScanResultSameFreq = new ScanResultWithSameFreq(); + ScanResultSameFreq.scan_result_timestamp_micros = + scanResults[scanResultsIndex].timestamp; + ScanResultSameFreq.rssi = scanResults[scanResultsIndex].level; + ScanResultSameFreq.frequencyMhz = + scanResults[scanResultsIndex].frequency; + ScanResultSameFreq.bssid = scanResults[scanResultsIndex].BSSID; + ScanResultsSameFreq.add(ScanResultSameFreq); + } + stats.links[linkIndex].scan_results_same_freq = ScanResultsSameFreq; + } + } } return stats; } @@ -3944,19 +4106,18 @@ public class WifiNative { * @param ifaceName Name of the interface. * @return bitmask defined by WifiManager.WIFI_FEATURE_* */ - public long getSupportedFeatureSet(String ifaceName) { + public @NonNull BitSet getSupportedFeatureSet(String ifaceName) { synchronized (mLock) { - long featureSet = 0; // First get the complete feature set stored in config store when supplicant was // started - featureSet = bitsetToLong(getCompleteFeatureSetFromConfigStore()); + BitSet featureSet = getCompleteFeatureSetFromConfigStore(); // Include the feature set saved in interface class. This is to make sure that // framework is returning the feature set for SoftAp only products and multi-chip // products. if (ifaceName != null) { Iface iface = mIfaceMgr.getIface(ifaceName); if (iface != null) { - featureSet |= iface.featureSet; + featureSet.or(iface.featureSet); } } return featureSet; @@ -3985,21 +4146,21 @@ public class WifiNative { * @param ifaceName Name of the interface. * @return bitmask defined by WifiManager.WIFI_FEATURE_* */ - private long getSupportedFeatureSetInternal(@NonNull String ifaceName) { - BitSet featureBitset = mSupplicantStaIfaceHal.getAdvancedCapabilities(ifaceName); - featureBitset.or(mSupplicantStaIfaceHal.getWpaDriverFeatureSet(ifaceName)); - featureBitset.or(mWifiVendorHal.getSupportedFeatureSet(ifaceName)); - long featureSet = bitsetToLong(featureBitset); + private BitSet getSupportedFeatureSetInternal(@NonNull String ifaceName) { + BitSet featureSet = mSupplicantStaIfaceHal.getAdvancedCapabilities(ifaceName); + featureSet.or(mSupplicantStaIfaceHal.getWpaDriverFeatureSet(ifaceName)); + featureSet.or(mWifiVendorHal.getSupportedFeatureSet(ifaceName)); if (SdkLevel.isAtLeastT()) { - if (((featureSet & WifiManager.WIFI_FEATURE_DPP) != 0) + if (featureSet.get(WifiManager.WIFI_FEATURE_DPP) && mContext.getResources().getBoolean(R.bool.config_wifiDppAkmSupported)) { // Set if DPP is filled by supplicant and DPP AKM is enabled by overlay. - featureSet |= WifiManager.WIFI_FEATURE_DPP_AKM; + featureSet.set(WifiManager.WIFI_FEATURE_DPP_AKM); Log.v(TAG, ": DPP AKM supported"); } } Bundle twtCapabilities = mWifiVendorHal.getTwtCapabilities(ifaceName); if (twtCapabilities != null) mCachedTwtCapabilities.put(ifaceName, twtCapabilities); + mCachedUsdCapabilities = mSupplicantStaIfaceHal.getUsdCapabilities(ifaceName); return featureSet; } @@ -4014,7 +4175,8 @@ public class WifiNative { if (getChannelsForBand(WifiScanner.WIFI_BAND_24_GHZ).length > 0) { bands |= WifiScanner.WIFI_BAND_24_GHZ; } - if (getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ).length > 0) { + if ((getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ).length > 0) + || (getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY).length > 0)) { bands |= WifiScanner.WIFI_BAND_5_GHZ; } if (getChannelsForBand(WifiScanner.WIFI_BAND_6_GHZ).length > 0) { @@ -4083,6 +4245,7 @@ public class WifiNative { * Returns an array of SignalPollResult objects. * Returns null on failure. */ + @Keep @Nullable public WifiSignalPollResults signalPoll(@NonNull String ifaceName) { if (mMockWifiModem != null @@ -4914,6 +5077,7 @@ public class WifiNative { * @param ifaceName name of the interface * @return the device capabilities for this interface */ + @Keep public DeviceWiphyCapabilities getDeviceWiphyCapabilities(@NonNull String ifaceName) { return getDeviceWiphyCapabilities(ifaceName, false); } @@ -4965,6 +5129,7 @@ public class WifiNative { * @param ifaceName name of the interface * @param capabilities the wiphy capabilities to set for this interface */ + @Keep public void setDeviceWiphyCapabilities(@NonNull String ifaceName, DeviceWiphyCapabilities capabilities) { synchronized (mLock) { @@ -5173,8 +5338,7 @@ public class WifiNative { * Save the complete list of features retrieved from WiFi HAL and Supplicant HAL in * config store. */ - private void saveCompleteFeatureSetInConfigStoreIfNecessary(long featureSetLong) { - BitSet featureSet = longToBitset(featureSetLong); + private void saveCompleteFeatureSetInConfigStoreIfNecessary(BitSet featureSet) { BitSet cachedFeatureSet = getCompleteFeatureSetFromConfigStore(); if (!cachedFeatureSet.equals(featureSet)) { mCachedFeatureSet = featureSet; diff --git a/service/java/com/android/server/wifi/WifiNetworkFactory.java b/service/java/com/android/server/wifi/WifiNetworkFactory.java index bdb3309656..a4b876dfb0 100644 --- a/service/java/com/android/server/wifi/WifiNetworkFactory.java +++ b/service/java/com/android/server/wifi/WifiNetworkFactory.java @@ -206,6 +206,7 @@ public class WifiNetworkFactory extends NetworkFactory { private final HashMap<String, RemoteCallbackList<ILocalOnlyConnectionStatusListener>> mLocalOnlyStatusListenerPerApp = new HashMap<>(); private final HashMap<String, String> mFeatureIdPerApp = new HashMap<>(); + private boolean mShouldTriggerScanImmediately = false; /** * Helper class to store an access point that the user previously approved for a specific app. @@ -908,7 +909,8 @@ public class WifiNetworkFactory extends NetworkFactory { WifiNetworkSpecifier wns = (WifiNetworkSpecifier) ns; mActiveSpecificNetworkRequestSpecifier = new WifiNetworkSpecifier( wns.ssidPatternMatcher, wns.bssidPatternMatcher, wns.getBand(), - wns.wifiConfiguration, wns.getPreferredChannelFrequenciesMhz()); + wns.wifiConfiguration, wns.getPreferredChannelFrequenciesMhz(), + wns.isPreferSecondarySta()); mSkipUserDialogue = false; mWifiMetrics.incrementNetworkRequestApiNumRequest(); @@ -1155,7 +1157,7 @@ public class WifiNetworkFactory extends NetworkFactory { } private void handleConnectToNetworkUserSelectionInternal(WifiConfiguration network, - boolean didUserSeeUi) { + boolean didUserSeeUi, boolean preferSecondarySta) { // Copy over the credentials from the app's request and then copy the ssid from user // selection. WifiConfiguration networkToConnect = @@ -1201,8 +1203,10 @@ public class WifiNetworkFactory extends NetworkFactory { } WorkSource ws = new WorkSource(mActiveSpecificNetworkRequest.getRequestorUid(), mActiveSpecificNetworkRequest.getRequestorPackageName()); + // mPreferSecondarySta mActiveModeWarden.requestLocalOnlyClientModeManager(new ClientModeManagerRequestListener(), - ws, networkToConnect.SSID, networkToConnect.BSSID, didUserSeeUi); + ws, networkToConnect.SSID, networkToConnect.BSSID, didUserSeeUi, + preferSecondarySta); } private boolean hasNetworkForInternet(WifiConfiguration network) { @@ -1221,9 +1225,11 @@ public class WifiNetworkFactory extends NetworkFactory { // Cancel the ongoing scans after user selection. cancelPeriodicScans(); mIsPeriodicScanEnabled = false; + boolean preferSecondarySta = mActiveSpecificNetworkRequestSpecifier == null + ? false : mActiveSpecificNetworkRequestSpecifier.isPreferSecondarySta(); // Trigger connection attempts. - handleConnectToNetworkUserSelectionInternal(network, didUserSeeUi); + handleConnectToNetworkUserSelectionInternal(network, didUserSeeUi, preferSecondarySta); // Add the network to the approved access point map for the app. addNetworkToUserApprovedAccessPointMap(mUserSelectedNetwork); @@ -1606,6 +1612,7 @@ public class WifiNetworkFactory extends NetworkFactory { mScanSettings.channels[index++] = new WifiScanner.ChannelSpec(freq); } mScanSettings.band = WIFI_BAND_UNSPECIFIED; + mShouldTriggerScanImmediately = true; } mIsPeriodicScanEnabled = true; startScan(); @@ -1615,6 +1622,7 @@ public class WifiNetworkFactory extends NetworkFactory { } private void cancelPeriodicScans() { + mShouldTriggerScanImmediately = false; if (mPeriodicScanTimerSet) { mAlarmManager.cancel(mPeriodicScanTimerListener); mPeriodicScanTimerSet = false; @@ -1624,6 +1632,8 @@ public class WifiNetworkFactory extends NetworkFactory { } private void scheduleNextPeriodicScan() { + boolean triggerScanImmediately = mShouldTriggerScanImmediately; + mShouldTriggerScanImmediately = false; if (mIsPeriodicScanPaused) { Log.e(TAG, "Scan triggered when periodic scanning paused. Ignoring..."); return; @@ -1635,6 +1645,10 @@ public class WifiNetworkFactory extends NetworkFactory { cleanupActiveRequest(); return; } + if (triggerScanImmediately) { + startScan(); + return; + } mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, mClock.getElapsedSinceBootMillis() + PERIODIC_SCAN_INTERVAL_MS, TAG, mPeriodicScanTimerListener, mHandler); @@ -1987,7 +2001,8 @@ public class WifiNetworkFactory extends NetworkFactory { WifiConfiguration config = mActiveSpecificNetworkRequestSpecifier.wifiConfiguration; config.SSID = "\"" + ssid + "\""; config.BSSID = bssid.toString(); - handleConnectToNetworkUserSelectionInternal(config, false); + handleConnectToNetworkUserSelectionInternal(config, false, + mActiveSpecificNetworkRequestSpecifier.isPreferSecondarySta()); mWifiMetrics.incrementNetworkRequestApiNumUserApprovalBypass(); return true; } diff --git a/service/java/com/android/server/wifi/WifiNetworkSelector.java b/service/java/com/android/server/wifi/WifiNetworkSelector.java index 7686e226f7..f1ba972ddd 100644 --- a/service/java/com/android/server/wifi/WifiNetworkSelector.java +++ b/service/java/com/android/server/wifi/WifiNetworkSelector.java @@ -467,7 +467,8 @@ public class WifiNetworkSelector { @SuppressLint("NewApi") private List<ScanDetail> filterScanResults(List<ScanDetail> scanDetails, - Set<String> bssidBlocklist, List<ClientModeManagerState> cmmStates) { + Set<String> bssidBlocklist, List<ClientModeManagerState> cmmStates, + int autojoinRestrictionSecurityTypes) { List<ScanDetail> validScanDetails = new ArrayList<>(); StringBuffer noValidSsid = new StringBuffer(); StringBuffer blockedBssid = new StringBuffer(); @@ -475,6 +476,7 @@ public class WifiNetworkSelector { StringBuffer mboAssociationDisallowedBssid = new StringBuffer(); StringBuffer adminRestrictedSsid = new StringBuffer(); StringJoiner deprecatedSecurityTypeSsid = new StringJoiner(" / "); + StringJoiner autojoinRestrictionSecurityTypesBssid = new StringJoiner(" / "); List<String> currentBssids = cmmStates.stream() .map(cmmState -> cmmState.wifiInfo.getBSSID()) .collect(Collectors.toList()); @@ -611,6 +613,22 @@ public class WifiNetworkSelector { } } + // Skip network with security type that is restricted to auto join + if (autojoinRestrictionSecurityTypes != 0/*restrict none*/) { + @WifiAnnotations.SecurityType int[] securityTypes = scanResult.getSecurityTypes(); + boolean securityTypeRestricted = true; + for (int type : securityTypes) { + if (((0x1 << type) & autojoinRestrictionSecurityTypes) == 0) { + securityTypeRestricted = false; + break; + } + } + if (securityTypeRestricted) { + autojoinRestrictionSecurityTypesBssid.add(scanId); + continue; + } + } + validScanDetails.add(scanDetail); } mWifiMetrics.incrementNetworkSelectionFilteredBssidCount(numBssidFiltered); @@ -663,6 +681,11 @@ public class WifiNetworkSelector { + deprecatedSecurityTypeSsid); } + if (autojoinRestrictionSecurityTypesBssid.length() != 0) { + localLog("Networks filtered out due to auto join restriction on the security type: " + + autojoinRestrictionSecurityTypesBssid); + } + return validScanDetails; } @@ -685,7 +708,8 @@ public class WifiNetworkSelector { mIsEnhancedOpenSupportedInitialized = true; ClientModeManager primaryManager = mWifiInjector.getActiveModeWarden().getPrimaryClientModeManager(); - mIsEnhancedOpenSupported = (primaryManager.getSupportedFeatures() & WIFI_FEATURE_OWE) != 0; + mIsEnhancedOpenSupported = primaryManager + .getSupportedFeaturesBitSet().get(WIFI_FEATURE_OWE); return mIsEnhancedOpenSupported; } @@ -1075,7 +1099,8 @@ public class WifiNetworkSelector { @NonNull List<ScanDetail> scanDetails, @NonNull Set<String> bssidBlocklist, @NonNull List<ClientModeManagerState> cmmStates, boolean untrustedNetworkAllowed, boolean oemPaidNetworkAllowed, boolean oemPrivateNetworkAllowed, - Set<Integer> restrictedNetworkAllowedUids, boolean skipSufficiencyCheck) { + Set<Integer> restrictedNetworkAllowedUids, boolean skipSufficiencyCheck, + int autojoinRestrictionSecurityTypes) { mFilteredNetworks.clear(); mConnectableNetworks.clear(); if (scanDetails.size() == 0) { @@ -1092,7 +1117,8 @@ public class WifiNetworkSelector { } // Filter out unwanted networks. - mFilteredNetworks = filterScanResults(scanDetails, bssidBlocklist, cmmStates); + mFilteredNetworks = filterScanResults(scanDetails, bssidBlocklist, cmmStates, + autojoinRestrictionSecurityTypes); if (mFilteredNetworks.size() == 0) { return null; } diff --git a/service/java/com/android/server/wifi/WifiNetworkSuggestionsManager.java b/service/java/com/android/server/wifi/WifiNetworkSuggestionsManager.java index 9fa6113b1f..be384213a3 100644 --- a/service/java/com/android/server/wifi/WifiNetworkSuggestionsManager.java +++ b/service/java/com/android/server/wifi/WifiNetworkSuggestionsManager.java @@ -74,6 +74,7 @@ import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import java.util.BitSet; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; @@ -1115,8 +1116,8 @@ public class WifiNetworkSuggestionsManager { return false; } - long supportedFeatures = mWifiInjector.getActiveModeWarden() - .getPrimaryClientModeManager().getSupportedFeatures(); + BitSet supportedFeatures = mWifiInjector.getActiveModeWarden() + .getPrimaryClientModeManager().getSupportedFeaturesBitSet(); for (WifiNetworkSuggestion wns : networkSuggestions) { if (wns.passpointConfiguration == null) { diff --git a/service/java/com/android/server/wifi/WifiPulledAtomLogger.java b/service/java/com/android/server/wifi/WifiPulledAtomLogger.java index 44bc126557..36a54e68e6 100644 --- a/service/java/com/android/server/wifi/WifiPulledAtomLogger.java +++ b/service/java/com/android/server/wifi/WifiPulledAtomLogger.java @@ -26,6 +26,7 @@ import android.net.wifi.WifiNetworkSuggestion; import android.net.wifi.WifiSsid; import android.os.Handler; import android.os.Process; +import android.text.TextUtils; import android.util.Log; import android.util.StatsEvent; @@ -149,6 +150,10 @@ public class WifiPulledAtomLogger { data.add(WifiStatsLog.buildStatsEvent(atomTag, WifiStatsLog.WIFI_SETTING_INFO__SETTING_NAME__LOCATION_MODE, mWifiInjector.getWifiPermissionsUtil().isLocationModeEnabled())); + data.add(WifiStatsLog.buildStatsEvent(atomTag, + WifiStatsLog.WIFI_SETTING_INFO__SETTING_NAME__EXTERNAL_SCORER_DRY_RUN, + !TextUtils.isEmpty( + mWifiInjector.getDeviceConfigFacade().getDryRunScorerPkgName()))); return StatsManager.PULL_SUCCESS; } diff --git a/service/java/com/android/server/wifi/WifiScoreReport.java b/service/java/com/android/server/wifi/WifiScoreReport.java index c3c4a0fc34..a65fe8a862 100644 --- a/service/java/com/android/server/wifi/WifiScoreReport.java +++ b/service/java/com/android/server/wifi/WifiScoreReport.java @@ -138,6 +138,8 @@ public class WifiScoreReport { /** * Callback from {@link ExternalScoreUpdateObserverProxy} + * + * Wifi Scorer calls these callbacks when it needs to send information to us. */ private class ScoreUpdateObserverProxy implements WifiManager.ScoreUpdateObserver { @Override @@ -255,7 +257,8 @@ public class WifiScoreReport { } // TODO(b/153075963): This should not be plumbed through WifiMetrics - mWifiMetrics.updateWifiUsabilityStatsEntries(mInterfaceName, mWifiInfo, stats); + mWifiMetrics.updateWifiUsabilityStatsEntries(mInterfaceName, mWifiInfo, stats, false, + 0); } @Override @@ -652,12 +655,13 @@ public class WifiScoreReport { } /** - * Calculate wifi network score based on updated link layer stats and send the score to - * the WifiNetworkAgent. - * - * If the score has changed from the previous value, update the WifiNetworkAgent. + * Calculate the new wifi network score based on updated link layer stats. * * Called periodically (POLL_RSSI_INTERVAL_MSECS) about every 3 seconds. + * + * Note: This function will only notify connectivity services of the updated route if we are NOT + * using a connected external WiFi scorer. + * */ public void calculateAndReportScore() { if (mWifiInfo.getRssi() == mWifiInfo.INVALID_RSSI) { @@ -1122,11 +1126,14 @@ public class WifiScoreReport { || netId != getCurrentNetId() || isLocalOnlyOrRestrictedConnection() || sessionId == INVALID_SESSION_ID) { - Log.w(TAG, "Cannot start external scoring" - + " netId=" + netId - + " currentNetId=" + getCurrentNetId() - + " currentNetCapabilities=" + getCurrentNetCapabilities() - + " sessionId=" + sessionId); + StringBuilder sb = new StringBuilder(); + sb.append("Cannot start external scoring netId=").append(netId) + .append(" currentNetId=").append(getCurrentNetId()); + if (mVerboseLoggingEnabled) { + sb.append(" currentNetCapabilities=").append(getCurrentNetCapabilities()); + } + sb.append(" sessionId=").append(sessionId); + Log.w(TAG, sb.toString()); return; } mCurrentWifiConfiguration = mWifiConfigManager.getConfiguredNetwork( diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java index 4347040fca..ec20ffbd66 100644 --- a/service/java/com/android/server/wifi/WifiServiceImpl.java +++ b/service/java/com/android/server/wifi/WifiServiceImpl.java @@ -38,12 +38,12 @@ import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING; import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED; import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING; import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED; -import static android.net.wifi.WifiManager.WIFI_FEATURE_TRUST_ON_FIRST_USE; import static android.net.wifi.WifiManager.WIFI_INTERFACE_TYPE_AP; import static android.net.wifi.WifiManager.WIFI_INTERFACE_TYPE_AWARE; import static android.net.wifi.WifiManager.WIFI_INTERFACE_TYPE_DIRECT; import static android.net.wifi.WifiManager.WIFI_INTERFACE_TYPE_STA; import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED; +import static android.net.wifi.WifiManager.WifiStateChangedListener; import static android.os.Process.WIFI_UID; import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_LOCAL_ONLY; @@ -70,6 +70,7 @@ import static com.android.server.wifi.WifiSettingsConfigStore.WIFI_WEP_ALLOWED; import android.Manifest; import android.annotation.AnyThread; import android.annotation.CheckResult; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppOpsManager; @@ -83,6 +84,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.ServiceConnection; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -101,7 +103,9 @@ import android.net.NetworkStack; import android.net.TetheringManager; import android.net.Uri; import android.net.ip.IpClientUtil; -import android.net.wifi.BaseWifiService; +import android.net.thread.ThreadNetworkController; +import android.net.thread.ThreadNetworkManager; +import android.net.wifi.BlockingOption; import android.net.wifi.CoexUnsafeChannel; import android.net.wifi.IActionListener; import android.net.wifi.IBooleanListener; @@ -134,8 +138,10 @@ import android.net.wifi.ITwtStatsListener; import android.net.wifi.IWifiBandsListener; import android.net.wifi.IWifiConnectedNetworkScorer; import android.net.wifi.IWifiLowLatencyLockListener; +import android.net.wifi.IWifiManager; import android.net.wifi.IWifiNetworkSelectionConfigListener; import android.net.wifi.IWifiNetworkStateChangedListener; +import android.net.wifi.IWifiStateChangedListener; import android.net.wifi.IWifiVerboseLoggingStatusChangedListener; import android.net.wifi.MscsParams; import android.net.wifi.QosPolicyParams; @@ -169,6 +175,7 @@ import android.net.wifi.hotspot2.PasspointConfiguration; import android.net.wifi.twt.TwtRequest; import android.net.wifi.twt.TwtSession; import android.net.wifi.twt.TwtSessionCallback; +import android.net.wifi.util.Environment; import android.net.wifi.util.ScanResultUtil; import android.net.wifi.util.WifiResourceCache; import android.os.AsyncTask; @@ -201,6 +208,7 @@ import android.util.Log; import android.util.Pair; import android.util.SparseArray; import android.util.SparseIntArray; +import android.uwb.UwbManager; import androidx.annotation.RequiresApi; @@ -219,10 +227,13 @@ import com.android.server.wifi.proto.WifiStatsLog; import com.android.server.wifi.proto.nano.WifiMetricsProto.UserActionEvent; import com.android.server.wifi.util.ActionListenerWrapper; import com.android.server.wifi.util.ApConfigUtil; +import com.android.server.wifi.util.FeatureBitsetUtils; import com.android.server.wifi.util.GeneralUtil.Mutable; import com.android.server.wifi.util.LastCallerInfoManager; import com.android.server.wifi.util.RssiUtil; import com.android.server.wifi.util.WifiPermissionsUtil; +import com.android.server.wifi.util.WorkSourceHelper; +import com.android.wifi.flags.FeatureFlags; import com.android.wifi.resources.R; import org.json.JSONArray; @@ -234,6 +245,8 @@ import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.net.Inet4Address; import java.net.InetAddress; import java.security.GeneralSecurityException; @@ -245,6 +258,7 @@ import java.security.cert.PKIXParameters; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; +import java.util.BitSet; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -264,7 +278,7 @@ import java.util.function.IntConsumer; * WifiService handles remote WiFi operation requests by implementing * the IWifiManager interface. */ -public class WifiServiceImpl extends BaseWifiService { +public class WifiServiceImpl extends IWifiManager.Stub { private static final String TAG = "WifiService"; private static final boolean VDBG = false; @@ -274,6 +288,40 @@ public class WifiServiceImpl extends BaseWifiService { static final int AUTO_DISABLE_SHOW_KEY_COUNTDOWN_MILLIS = 24 * 60 * 60 * 1000; private static final int CHANNEL_USAGE_WEAK_SCAN_RSSI_DBM = -80; + private static final int SCORER_BINDING_STATE_INVALID = -1; + // The system is brining up the scorer service. + private static final int SCORER_BINDING_STATE_BRINGING_UP = 0; + private static final int SCORER_BINDING_STATE_CONNECTED = 1; + private static final int SCORER_BINDING_STATE_DISCONNECTED = 2; + private static final int SCORER_BINDING_STATE_BINDING_DIED = 3; + // A null binder is received. + private static final int SCORER_BINDING_STATE_NULL_BINDING = 4; + // The system couldn't find the service to bind to. + private static final int SCORER_BINDING_STATE_NO_SERVICE = 5; + // The app has cleared the Wi-Fi scorer. So the service is unbound. + private static final int SCORER_BINDING_STATE_CLEARED = 6; + // The app has set a new Wi-Fi scorer. Rebind to the service. + public static final int SCORER_BINDING_STATE_REBINDING = 7; + + @IntDef(prefix = { "SCORER_BINDING_STATE_" }, value = { + SCORER_BINDING_STATE_INVALID, + SCORER_BINDING_STATE_BRINGING_UP, + SCORER_BINDING_STATE_CONNECTED, + SCORER_BINDING_STATE_DISCONNECTED, + SCORER_BINDING_STATE_BINDING_DIED, + SCORER_BINDING_STATE_NULL_BINDING, + SCORER_BINDING_STATE_NO_SERVICE, + SCORER_BINDING_STATE_CLEARED, + SCORER_BINDING_STATE_REBINDING + + }) + @Retention(RetentionPolicy.SOURCE) + private @interface ScorerBindingState {} + + private @ScorerBindingState int mLastScorerBindingState = SCORER_BINDING_STATE_INVALID; + @VisibleForTesting + ScorerServiceConnection mScorerServiceConnection; + private final ActiveModeWarden mActiveModeWarden; private final ScanRequestProxy mScanRequestProxy; @@ -329,6 +377,9 @@ public class WifiServiceImpl extends BaseWifiService { private final DefaultClientModeManager mDefaultClientModeManager; + private final WepNetworkUsageController mWepNetworkUsageController; + + private final FeatureFlags mFeatureFlags; @VisibleForTesting public final CountryCodeTracker mCountryCodeTracker; private final MultiInternetManager mMultiInternetManager; @@ -339,6 +390,7 @@ public class WifiServiceImpl extends BaseWifiService { private final WifiSettingsConfigStore mSettingsConfigStore; private final WifiResourceCache mResourceCache; + private boolean mIsUsdSupported = false; /** * Callback for use with LocalOnlyHotspot to unregister requesting applications upon death. @@ -377,6 +429,112 @@ public class WifiServiceImpl extends BaseWifiService { } } + private class ScorerServiceConnection implements ServiceConnection { + @Override + public void onServiceConnected(ComponentName name, IBinder binder) { + Log.i(TAG, "ScorerServiceConnection.onServiceConnected(" + name + ", " + binder + ")"); + if (binder == null) { + mWifiThreadRunner.post(() -> unbindScorerService(SCORER_BINDING_STATE_NULL_BINDING), + "ScorerServiceConnection#onServiceConnected"); + } else { + mWifiThreadRunner.post(() -> { + mLastScorerBindingState = SCORER_BINDING_STATE_CONNECTED; + }, "ScorerServiceConnection#onServiceConnected"); + } + } + @Override + public void onServiceDisconnected(ComponentName name) { + Log.w(TAG, "ScorerServiceConnection.onServiceDisconnected(" + name + ")"); + mWifiThreadRunner.post(() -> unbindScorerService(SCORER_BINDING_STATE_DISCONNECTED), + "ScorerServiceConnection#onServiceDisconnected"); + } + + @Override + public void onBindingDied(ComponentName name) { + Log.w(TAG, "ScorerServiceConnection.onBindingDied(" + name + ")"); + mWifiThreadRunner.post(() -> unbindScorerService(SCORER_BINDING_STATE_BINDING_DIED), + "ScorerServiceConnection#onBindingDied"); + } + + @Override + public void onNullBinding(ComponentName name) { + Log.i(TAG, "Wifi scorer service " + name + " - onNullBinding"); + mWifiThreadRunner.post(() -> unbindScorerService(SCORER_BINDING_STATE_NULL_BINDING), + "ScorerServiceConnection#onNullBinding"); + } + } + + /** + * Bind to the scorer service to prevent the process of the external scorer from freezing. + */ + private void bindScorerService(int scorerUid) { + if (mScorerServiceConnection != null) { + Log.i(TAG, "bindScorerService() service is already bound. Unbind it now."); + unbindScorerService(SCORER_BINDING_STATE_REBINDING); + } + + mLastScorerBindingState = SCORER_BINDING_STATE_NO_SERVICE; + PackageManager pm = mContext.getPackageManager(); + if (pm == null) { + Log.e(TAG, "PackageManager is null!"); + return; + } else { + String[] packageNames = pm.getPackagesForUid(scorerUid); + for (String packageName : packageNames) { + if (!TextUtils.isEmpty(packageName)) { + mScorerServiceConnection = new ScorerServiceConnection(); + if (bindScorerServiceAsSystemUser(packageName, mScorerServiceConnection)) { + mLastScorerBindingState = SCORER_BINDING_STATE_BRINGING_UP; + return; + } else { + unbindScorerService(SCORER_BINDING_STATE_NO_SERVICE); + } + } else { + Log.d(TAG, "bindScorerService() packageName is empty"); + } + } + } + } + + private boolean bindScorerServiceAsSystemUser(@NonNull String packageName, + @NonNull ServiceConnection serviceConnection) { + Intent intent = new Intent("android.wifi.ScorerService"); + intent.setPackage(packageName); + try { + if (mContext.bindServiceAsUser(intent, serviceConnection, + Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, + UserHandle.SYSTEM)) { + Log.i(TAG, "bindScorerServiceAsSystemUser(): Bringing up service: " + packageName); + return true; + } + } catch (SecurityException ex) { + Log.e(TAG, "Fail to bindServiceAsSystemUser(): " + ex); + } catch (Exception ex) { + // Just make sure that it won't crash WiFi process + Log.e(TAG, "Fail to bindServiceAsSystemUser(): " + ex); + } + return false; + } + + /** + * Unbind scorer service to let the system freeze the process of the external scorer freely. + */ + private void unbindScorerService(@ScorerBindingState int state) { + mLastScorerBindingState = state; + if (mScorerServiceConnection != null) { + Log.i(TAG, "unbindScorerService(" + state + ")"); + try { + mContext.unbindService(mScorerServiceConnection); + } catch (Exception e) { + Log.e(TAG, "Fail to unbindService! " + e); + } + mScorerServiceConnection = null; + } else { + Log.i(TAG, "unbindScorerService(" + state + + ") mScorerServiceConnection is already null."); + } + } + private final WifiLockManager mWifiLockManager; private final WifiMulticastLockManager mWifiMulticastLockManager; private final DppManager mDppManager; @@ -428,6 +586,14 @@ public class WifiServiceImpl extends BaseWifiService { Map<String, List<WifiClient>> clients, boolean isBridged) {} /** + * see: + * {@code WifiManager.SoftApCallback#onClientsDisconnected(SoftApInfo, + * List<WifiClient>)} + */ + void onClientsDisconnected(@NonNull SoftApInfo info, + @NonNull List<WifiClient> clients) {} + + /** * see: {@code WifiManager.SoftApCallback#onCapabilityChanged(SoftApCapability)} */ void onCapabilityChanged(@NonNull SoftApCapability softApCapability) {} @@ -439,6 +605,14 @@ public class WifiServiceImpl extends BaseWifiService { @SapClientBlockedReason int blockedReason) {} /** + * Checks the AttributionSource of a callback and returns true if the AttributionSource + * has the correct permissions. This should be extended by child classes. + */ + boolean checkCallbackPermission(@Nullable Object broadcastCookie) { + return true; + } + + /** * Notify register the state of soft AP changed. */ public void notifyRegisterOnStateChanged(RemoteCallbackList<ISoftApCallback> callbacks, @@ -446,6 +620,9 @@ public class WifiServiceImpl extends BaseWifiService { int itemCount = callbacks.beginBroadcast(); for (int i = 0; i < itemCount; i++) { try { + if (!checkCallbackPermission(callbacks.getBroadcastCookie(i))) { + continue; + } callbacks.getBroadcastItem(i).onStateChanged(state); } catch (RemoteException e) { Log.e(TAG, "onStateChanged: remote exception -- " + e); @@ -465,6 +642,9 @@ public class WifiServiceImpl extends BaseWifiService { int itemCount = callbacks.beginBroadcast(); for (int i = 0; i < itemCount; i++) { try { + if (!checkCallbackPermission(callbacks.getBroadcastCookie(i))) { + continue; + } callbacks.getBroadcastItem(i).onConnectedClientsOrInfoChanged( ApConfigUtil.deepCopyForSoftApInfoMap(infos), ApConfigUtil.deepCopyForWifiClientListMap( @@ -486,6 +666,9 @@ public class WifiServiceImpl extends BaseWifiService { int itemCount = callbacks.beginBroadcast(); for (int i = 0; i < itemCount; i++) { try { + if (!checkCallbackPermission(callbacks.getBroadcastCookie(i))) { + continue; + } callbacks.getBroadcastItem(i).onCapabilityChanged(capability); } catch (RemoteException e) { Log.e(TAG, "onCapabilityChanged: remote exception -- " + e); @@ -508,6 +691,9 @@ public class WifiServiceImpl extends BaseWifiService { int itemCount = callbacks.beginBroadcast(); for (int i = 0; i < itemCount; i++) { try { + if (!checkCallbackPermission(callbacks.getBroadcastCookie(i))) { + continue; + } callbacks.getBroadcastItem(i).onBlockedClientConnecting(client, blockedReason); } catch (RemoteException e) { Log.e(TAG, "onBlockedClientConnecting: remote exception -- " + e); @@ -515,6 +701,31 @@ public class WifiServiceImpl extends BaseWifiService { } callbacks.finishBroadcast(); } + + /** + * Notify register that clients have disconnected from a soft AP instance. + * + * @param info The {@link SoftApInfo} of the AP. + * @param clients The clients that have disconnected from the AP instance specified by + * {@code info}. + */ + public void notifyRegisterOnClientsDisconnected( + RemoteCallbackList<ISoftApCallback> callbacks, SoftApInfo info, + List<WifiClient> clients) { + int itemCount = callbacks.beginBroadcast(); + for (int i = 0; i < itemCount; i++) { + try { + if (!checkCallbackPermission(callbacks.getBroadcastCookie(i))) { + continue; + } + callbacks.getBroadcastItem(i).onClientsDisconnected(new SoftApInfo(info), + clients); + } catch (RemoteException e) { + Log.e(TAG, "onClientsDisconnected: remote exception -- " + e); + } + } + callbacks.finishBroadcast(); + } } public WifiServiceImpl(WifiContext context, WifiInjector wifiInjector) { @@ -579,10 +790,17 @@ public class WifiServiceImpl extends BaseWifiService { mWifiTetheringDisallowed = false; mMultiInternetManager = mWifiInjector.getMultiInternetManager(); mDeviceConfigFacade = mWifiInjector.getDeviceConfigFacade(); + mFeatureFlags = mDeviceConfigFacade.getFeatureFlags(); mApplicationQosPolicyRequestHandler = mWifiInjector.getApplicationQosPolicyRequestHandler(); mWifiPulledAtomLogger = mWifiInjector.getWifiPulledAtomLogger(); mAfcManager = mWifiInjector.getAfcManager(); mTwtManager = mWifiInjector.getTwtManager(); + mWepNetworkUsageController = mWifiInjector.getWepNetworkUsageController(); + if (Environment.isSdkAtLeastB()) { + mIsUsdSupported = mContext.getResources().getBoolean( + mContext.getResources().getIdentifier("config_deviceSupportsWifiUsd", "bool", + "android")); + } } /** @@ -608,17 +826,20 @@ public class WifiServiceImpl extends BaseWifiService { mWifiInjector.getWifiScanAlwaysAvailableSettingsCompatibility().initialize(); mWifiInjector.getWifiNotificationManager().createNotificationChannels(); - // Align the value between config stroe (i.e.WifiConfigStore.xml) and WifiGlobals. - mSettingsConfigStore.registerChangeListener(WIFI_WEP_ALLOWED, - (key, value) -> { - if (mWifiGlobals.isWepAllowed() != value) { - // It should only happen when settings is restored from cloud. - handleWepAllowedChanged(value); - Log.i(TAG, "(Cloud Restoration) Wep allowed is changed to " + value); - } - }, - new Handler(mWifiHandlerThread.getLooper())); - mWifiGlobals.setWepAllowed(mSettingsConfigStore.get(WIFI_WEP_ALLOWED)); + // Old design, flag is disabled. + if (!mFeatureFlags.wepDisabledInApm()) { + mWifiGlobals.setWepAllowed(mSettingsConfigStore.get(WIFI_WEP_ALLOWED)); + // Align the value between config store (i.e.WifiConfigStore.xml) and WifiGlobals. + mSettingsConfigStore.registerChangeListener(WIFI_WEP_ALLOWED, + (key, value) -> { + if (mWifiGlobals.isWepAllowed() != value) { + handleWepAllowedChanged(value); + Log.i(TAG, "Wep allowed is changed to " + + value); + } + }, + new Handler(mWifiHandlerThread.getLooper())); + } mContext.registerReceiver( new BroadcastReceiver() { @Override @@ -690,6 +911,7 @@ public class WifiServiceImpl extends BaseWifiService { public void onReceive(Context context, Intent intent) { Log.d(TAG, "locale changed"); resetNotificationManager(); + mResourceCache.handleLocaleChange(); } }, new IntentFilter(Intent.ACTION_LOCALE_CHANGED), @@ -757,6 +979,7 @@ public class WifiServiceImpl extends BaseWifiService { } private void resetCarrierNetworks(@ClientModeImpl.ResetSimReason int resetReason) { + mResourceCache.reset(); Log.d(TAG, "resetting carrier networks since SIM was changed"); if (resetReason == RESET_SIM_REASON_SIM_INSERTED) { // clear all SIM related notifications since some action was taken to address @@ -872,9 +1095,13 @@ public class WifiServiceImpl extends BaseWifiService { } updateVerboseLoggingEnabled(); mWifiInjector.getWifiDeviceStateChangeManager().handleBootCompleted(); + if (mFeatureFlags.wepDisabledInApm() + && mWepNetworkUsageController != null) { + mWepNetworkUsageController.handleBootCompleted(); + } setPulledAtomCallbacks(); mTwtManager.registerWifiNativeTwtEvents(); - mContext.registerReceiver( + mContext.registerReceiverForAllUsers( new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -888,6 +1115,33 @@ public class WifiServiceImpl extends BaseWifiService { null, new Handler(mWifiHandlerThread.getLooper())); updateLocationMode(); + + if (SdkLevel.isAtLeastT()) { + UwbManager uwbManager = + mContext.getSystemService(UwbManager.class); + if (uwbManager != null) { + uwbManager.registerAdapterStateCallback(new HandlerExecutor(new Handler( + mWifiHandlerThread.getLooper())), new UwbAdapterStateListener()); + } + } + + if (SdkLevel.isAtLeastV()) { + ThreadNetworkManager threadManager = + mContext.getSystemService(ThreadNetworkManager.class); + if (threadManager != null) { + List<ThreadNetworkController> threadNetworkControllers = + threadManager.getAllThreadNetworkControllers(); + if (threadNetworkControllers.size() > 0) { + ThreadNetworkController threadNetworkController = + threadNetworkControllers.get(0); + if (threadNetworkController != null) { + threadNetworkController.registerStateCallback( + new HandlerExecutor(new Handler(mWifiHandlerThread.getLooper())), + new ThreadStateListener()); + } + } + } + } }, TAG + "#handleBootCompleted"); } @@ -1313,9 +1567,10 @@ public class WifiServiceImpl extends BaseWifiService { */ @Override public synchronized boolean setWifiEnabled(String packageName, boolean enable) { - if (!isValidCallingUser() || enforceChangePermission(packageName) != MODE_ALLOWED) { + if (enforceChangePermission(packageName) != MODE_ALLOWED) { return false; } + enforceValidCallingUser(); int callingUid = Binder.getCallingUid(); int callingPid = Binder.getCallingPid(); boolean isPrivileged = isPrivileged(callingPid, callingUid); @@ -1613,6 +1868,36 @@ public class WifiServiceImpl extends BaseWifiService { } /** + * See {@link WifiManager#addWifiStateChangedListener(Executor, WifiStateChangedListener)} + */ + public void addWifiStateChangedListener(@NonNull IWifiStateChangedListener listener) { + enforceAccessPermission(); + if (listener == null) { + throw new IllegalArgumentException("listener must not be null"); + } + if (mVerboseLoggingEnabled) { + mLog.info("addWifiStateChangedListener uid=%").c(Binder.getCallingUid()).flush(); + } + mWifiThreadRunner.post(() -> mActiveModeWarden.addWifiStateChangedListener(listener), + TAG + "#addWifiStateChangedListener"); + } + + /** + * See {@link WifiManager#removeWifiStateChangedListener(WifiStateChangedListener)} + */ + public void removeWifiStateChangedListener(@NonNull IWifiStateChangedListener listener) { + enforceAccessPermission(); + if (listener == null) { + throw new IllegalArgumentException("listener must not be null"); + } + if (mVerboseLoggingEnabled) { + mLog.info("removeWifiStateChangedListener uid=%").c(Binder.getCallingUid()).flush(); + } + mWifiThreadRunner.post(() -> mActiveModeWarden.removeWifiStateChangedListener(listener), + TAG + "#removeWifiStateChangedListener"); + } + + /** * see {@link WifiManager#getWifiApState()} * @return One of {@link WifiManager#WIFI_AP_STATE_DISABLED}, * {@link WifiManager#WIFI_AP_STATE_DISABLING}, @@ -1690,6 +1975,7 @@ public class WifiServiceImpl extends BaseWifiService { /** * See {@link WifiManager#registerCoexCallback(WifiManager.CoexCallback)} */ + @Override @RequiresApi(Build.VERSION_CODES.S) public void registerCoexCallback(@NonNull ICoexCallback callback) { if (!SdkLevel.isAtLeastS()) { @@ -1728,6 +2014,7 @@ public class WifiServiceImpl extends BaseWifiService { /** * See {@link WifiManager#unregisterCoexCallback(WifiManager.CoexCallback)} */ + @Override @RequiresApi(Build.VERSION_CODES.S) public void unregisterCoexCallback(@NonNull ICoexCallback callback) { if (!SdkLevel.isAtLeastS()) { @@ -1862,7 +2149,9 @@ public class WifiServiceImpl extends BaseWifiService { mLog.info("startTetheredHotspot uid=%").c(callingUid).flush(); startTetheredHotspotInternal(new SoftApModeConfiguration( - WifiManager.IFACE_IP_MODE_TETHERED, null /* config */, + WifiManager.IFACE_IP_MODE_TETHERED, + com.android.net.flags.Flags.tetheringWithSoftApConfig() + ? request.getSoftApConfiguration() : null, mTetheredSoftApTracker.getSoftApCapability(), mCountryCode.getCountryCode(), request), callingUid, packageName, callback); } @@ -2049,9 +2338,6 @@ public class WifiServiceImpl extends BaseWifiService { Log.e(TAG, "Country code not consistent! expect " + countryCode + " actual " + mCountryCode.getCurrentDriverCountryCode()); } - // Store Soft AP channels for reference after a reboot before the driver is up. - mSettingsConfigStore.put(WifiSettingsConfigStore.WIFI_SOFT_AP_COUNTRY_CODE, - countryCode); List<Integer> freqs = new ArrayList<>(); SparseArray<int[]> channelMap = new SparseArray<>( SoftApConfiguration.BAND_TYPES.length); @@ -2060,7 +2346,7 @@ public class WifiServiceImpl extends BaseWifiService { continue; } List<Integer> freqsForBand = ApConfigUtil.getAvailableChannelFreqsForBand( - band, mWifiNative, mResourceCache, true); + band, mWifiNative, null, true); if (freqsForBand != null) { freqs.addAll(freqsForBand); int[] channel = new int[freqsForBand.size()]; @@ -2071,9 +2357,17 @@ public class WifiServiceImpl extends BaseWifiService { channelMap.put(band, channel); } } - mSettingsConfigStore.put( - WifiSettingsConfigStore.WIFI_AVAILABLE_SOFT_AP_FREQS_MHZ, - new JSONArray(freqs).toString()); + if (!mCountryCode.isDriverCountryCodeWorldMode() + || TextUtils.isEmpty(mSettingsConfigStore.get( + WifiSettingsConfigStore.WIFI_SOFT_AP_COUNTRY_CODE))) { + // Store Soft AP channels (non-world mode CC or no save before) for + // reference after a reboot before the driver is up. + mSettingsConfigStore.put(WifiSettingsConfigStore.WIFI_SOFT_AP_COUNTRY_CODE, + countryCode); + mSettingsConfigStore.put( + WifiSettingsConfigStore.WIFI_AVAILABLE_SOFT_AP_FREQS_MHZ, + new JSONArray(freqs).toString()); + } mTetheredSoftApTracker.updateAvailChannelListInSoftApCapability(countryCode, channelMap); mLohsSoftApTracker.updateAvailChannelListInSoftApCapability(countryCode, @@ -2118,6 +2412,31 @@ public class WifiServiceImpl extends BaseWifiService { } } + @RequiresApi(Build.VERSION_CODES.TIRAMISU) + class UwbAdapterStateListener implements UwbManager.AdapterStateCallback { + @Override + public void onStateChanged(int state, int reason) { + if (mVerboseLoggingEnabled) { + Log.d(TAG, "UwbManager.AdapterState=" + state); + } + mWifiMetrics.setLastUwbState(state); + } + } + + @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) + class ThreadStateListener implements ThreadNetworkController.StateCallback { + @Override + public void onDeviceRoleChanged(int mDeviceRole) { + if (mVerboseLoggingEnabled) { + Log.d(TAG, "ThreadNetworkController.DeviceRole=" + mDeviceRole); + } + mWifiMetrics.setLastThreadDeviceRole(mDeviceRole); + } + + @Override + public void onPartitionIdChanged(long mPartitionId) {} + } + /** * SoftAp callback */ @@ -2222,6 +2541,10 @@ public class WifiServiceImpl extends BaseWifiService { // Default country code mSoftApCapability = updateSoftApCapabilityWithAvailableChannelList( mSoftApCapability, mCountryCode.getCountryCode(), null); + if (mWifiNative.isMLDApSupportMLO()) { + mSoftApCapability.setSupportedFeatures( + true, SoftApCapability.SOFTAP_FEATURE_MLO); + } } return mSoftApCapability; } @@ -2248,8 +2571,8 @@ public class WifiServiceImpl extends BaseWifiService { getSoftApCapability(), countryCode, channelMap)); } - public boolean registerSoftApCallback(ISoftApCallback callback) { - if (!mRegisteredSoftApCallbacks.register(callback)) { + public boolean registerSoftApCallback(ISoftApCallback callback, Object cookie) { + if (!mRegisteredSoftApCallbacks.register(callback, cookie)) { return false; } @@ -2342,6 +2665,19 @@ public class WifiServiceImpl extends BaseWifiService { notifyRegisterOnBlockedClientConnecting(mRegisteredSoftApCallbacks, client, blockedReason); } + + /** + * Called when clients disconnect from a soft AP instance. + * + * @param info The {@link SoftApInfo} of the AP. + * @param clients The clients that have disconnected from the AP instance specified by + * {@code info}. + */ + @Override + public void onClientsDisconnected(@NonNull SoftApInfo info, + @NonNull List<WifiClient> clients) { + notifyRegisterOnClientsDisconnected(mRegisteredSoftApCallbacks, info, clients); + } } private final class TetheredSoftApTracker extends BaseSoftApTracker { @@ -2374,6 +2710,8 @@ public class WifiServiceImpl extends BaseWifiService { * Implements LOHS behavior on top of the existing SoftAp API. */ private final class LohsSoftApTracker extends BaseSoftApTracker { + private static final int UNSPECIFIED_PID = -1; + @GuardedBy("mLocalOnlyHotspotRequests") private final HashMap<Integer, LocalOnlyHotspotRequestInfo> mLocalOnlyHotspotRequests = new HashMap<>(); @@ -2390,11 +2728,30 @@ public class WifiServiceImpl extends BaseWifiService { private boolean mIsExclusive = false; @GuardedBy("mLocalOnlyHotspotRequests") + private WorkSource mCurrentWs = null; + + @GuardedBy("mLocalOnlyHotspotRequests") private String mLohsInterfaceName; @GuardedBy("mLocalOnlyHotspotRequests") private int mLohsInterfaceMode = WifiManager.IFACE_IP_MODE_UNSPECIFIED; + @GuardedBy("mLocalOnlyHotspotRequests") + private int mPidRestartingLohsFor = UNSPECIFIED_PID; + @Override + public boolean checkCallbackPermission(@Nullable Object broadcastCookie) { + if (!SdkLevel.isAtLeastS()) { + // AttributionSource requires at least S. + return false; + } + if (!(broadcastCookie instanceof AttributionSource)) { + return false; + } + return mWifiPermissionsUtil.checkNearbyDevicesPermission( + (AttributionSource) broadcastCookie, + false /* checkForLocation */, + TAG + " " + this.getClass().getSimpleName() + "#checkCallbackPermission"); + } public void updateInterfaceIpState(String ifaceName, int mode) { // update interface IP state related to local-only hotspot synchronized (mLocalOnlyHotspotRequests) { @@ -2480,6 +2837,7 @@ public class WifiServiceImpl extends BaseWifiService { // Since all callers were notified, now clear the registrations. mLocalOnlyHotspotRequests.clear(); + mPidRestartingLohsFor = UNSPECIFIED_PID; } /** @@ -2518,14 +2876,35 @@ public class WifiServiceImpl extends BaseWifiService { // Never accept exclusive requests (with custom configuration) at the same time as // shared requests. if (!mLocalOnlyHotspotRequests.isEmpty()) { - boolean requestIsExclusive = request.getCustomConfig() != null; - if (mIsExclusive || requestIsExclusive) { + final boolean requestIsExclusive = request.getCustomConfig() != null; + final int newRequestorWsPriority = mWifiInjector + .makeWsHelper(request.getWorkSource()).getRequestorWsPriority(); + final int currentWsPriority = mWifiInjector + .makeWsHelper(mCurrentWs).getRequestorWsPriority(); + final boolean isCurrentLohsWorksOnNonDefaultBand = mActiveConfig != null + && mActiveConfig.getSoftApConfiguration().getBand() + != SoftApConfiguration.BAND_2GHZ; + Log.d(TAG, "Receive multiple LOHS requestors," + + ", requestIsExclusive: " + requestIsExclusive + + ", mIsExclusive: " + mIsExclusive + + ", currentWsPriority: " + currentWsPriority + + ", newRequestorWsPriority: " + newRequestorWsPriority); + if (requestIsExclusive || mIsExclusive + || (currentWsPriority >= newRequestorWsPriority + && isCurrentLohsWorksOnNonDefaultBand)) { mLog.trace("Cannot share with existing LOHS request due to custom config") .flush(); return LocalOnlyHotspotCallback.ERROR_GENERIC; } + if (mFeatureFlags.publicBandsForLohs() && isCurrentLohsWorksOnNonDefaultBand) { + // Current LOHS is not using 2.4 band, and new caller priority is higher. + // stop all and logging the new requestor pid. + mLog.trace("Restarting LOHS to default band for new requestor") + .flush(); + mLohsSoftApTracker.stopAll(); + mPidRestartingLohsFor = pid; + } } - // At this point, the request is accepted. if (mLocalOnlyHotspotRequests.isEmpty()) { mWifiThreadRunner.post(() -> { @@ -2542,7 +2921,6 @@ public class WifiServiceImpl extends BaseWifiService { return LocalOnlyHotspotCallback.ERROR_GENERIC; } } - mLocalOnlyHotspotRequests.put(pid, request); return LocalOnlyHotspotCallback.REQUEST_REGISTERED; } @@ -2550,14 +2928,22 @@ public class WifiServiceImpl extends BaseWifiService { @GuardedBy("mLocalOnlyHotspotRequests") private void startForFirstRequestLocked(LocalOnlyHotspotRequestInfo request) { + mCurrentWs = request.getWorkSource(); + final int currentWsPriority = mWifiInjector.makeWsHelper(mCurrentWs) + .getRequestorWsPriority(); + if (mFeatureFlags.publicBandsForLohs() && Environment.isSdkAtLeastB()) { + mIsExclusive = (request.getCustomConfig() != null) + && currentWsPriority >= WorkSourceHelper.PRIORITY_SYSTEM; + } else { + mIsExclusive = (request.getCustomConfig() != null); + } final SoftApCapability lohsCapability = mLohsSoftApTracker.getSoftApCapability(); SoftApConfiguration softApConfig = mWifiApConfigStore.generateLocalOnlyHotspotConfig( - mContext, request.getCustomConfig(), lohsCapability); + mContext, request.getCustomConfig(), lohsCapability, mIsExclusive); mActiveConfig = new SoftApModeConfiguration( WifiManager.IFACE_IP_MODE_LOCAL_ONLY, softApConfig, lohsCapability, mCountryCode.getCountryCode(), null); - mIsExclusive = (request.getCustomConfig() != null); // Report the error if we got failure in startSoftApInternal if (!startSoftApInternal(mActiveConfig, request.getWorkSource(), null)) { onStateChanged(new SoftApState( @@ -2613,7 +2999,9 @@ public class WifiServiceImpl extends BaseWifiService { if (mLocalOnlyHotspotRequests.isEmpty()) { mActiveConfig = null; mIsExclusive = false; + mCurrentWs = null; mLohsInterfaceName = null; + mPidRestartingLohsFor = UNSPECIFIED_PID; mLohsInterfaceMode = WifiManager.IFACE_IP_MODE_UNSPECIFIED; stopSoftApInternal(WifiManager.IFACE_IP_MODE_LOCAL_ONLY); } @@ -2663,6 +3051,8 @@ public class WifiServiceImpl extends BaseWifiService { // also need to clear interface ip state updateInterfaceIpState(mLohsInterfaceName, WifiManager.IFACE_IP_MODE_UNSPECIFIED); + // failed, reset restarting pid info + mPidRestartingLohsFor = UNSPECIFIED_PID; } else if (state == WIFI_AP_STATE_DISABLING || state == WIFI_AP_STATE_DISABLED) { // softap is shutting down or is down... let requestors know via the // onStopped call @@ -2673,12 +3063,22 @@ public class WifiServiceImpl extends BaseWifiService { // holding the required lock: send message to requestors and clear the list sendHotspotStoppedMessageToAllLOHSRequestInfoEntriesLocked(); } else { - // LOHS not active: report an error (still holding the required lock) - sendHotspotFailedMessageToAllLOHSRequestInfoEntriesLocked(ERROR_GENERIC); + // The LOHS is being restarted since there is a mPidRestartingLohsFor, + // We should expect there will be DISABLING and DISABLED. + // No need to report error. + if (mPidRestartingLohsFor == UNSPECIFIED_PID) { + // LOHS not active: report an error. + sendHotspotFailedMessageToAllLOHSRequestInfoEntriesLocked( + ERROR_GENERIC); + } } // also clear interface ip state updateInterfaceIpState(mLohsInterfaceName, WifiManager.IFACE_IP_MODE_UNSPECIFIED); + } else if (state == WIFI_AP_STATE_ENABLING + && mPidRestartingLohsFor != UNSPECIFIED_PID) { + // restarting, reset pid info + mPidRestartingLohsFor = UNSPECIFIED_PID; } // For enabling and enabled, just record the new state setState(softApState); @@ -2720,7 +3120,7 @@ public class WifiServiceImpl extends BaseWifiService { // post operation to handler thread mWifiThreadRunner.post(() -> { - if (!mTetheredSoftApTracker.registerSoftApCallback(callback)) { + if (!mTetheredSoftApTracker.registerSoftApCallback(callback, null)) { Log.e(TAG, "registerSoftApCallback: Failed to add callback"); return; } @@ -2762,8 +3162,8 @@ public class WifiServiceImpl extends BaseWifiService { * softap mode changes. */ @VisibleForTesting - void registerLOHSForTest(int pid, LocalOnlyHotspotRequestInfo request) { - mLohsSoftApTracker.start(pid, request); + int registerLOHSForTest(int pid, LocalOnlyHotspotRequestInfo request) { + return mLohsSoftApTracker.start(pid, request); } /** @@ -2792,7 +3192,8 @@ public class WifiServiceImpl extends BaseWifiService { */ @Override public int startLocalOnlyHotspot(ILocalOnlyHotspotCallback callback, String packageName, - String featureId, SoftApConfiguration customConfig, Bundle extras) { + String featureId, SoftApConfiguration customConfig, Bundle extras, + boolean isCalledFromSystemApi) { // first check if the caller has permission to start a local only hotspot // need to check for WIFI_STATE_CHANGE and location permission final int uid = Binder.getCallingUid(); @@ -2802,7 +3203,9 @@ public class WifiServiceImpl extends BaseWifiService { mLog.info("start lohs uid=% pid=%").c(uid).c(pid).flush(); // Permission requirements are different with/without custom config. - if (customConfig == null) { + // From B, the custom config may from public API, check isCalledFromSystemApi + if (customConfig == null + || (Environment.isSdkAtLeastB() && !isCalledFromSystemApi)) { if (enforceChangePermission(packageName) != MODE_ALLOWED) { return LocalOnlyHotspotCallback.ERROR_GENERIC; } @@ -2899,15 +3302,18 @@ public class WifiServiceImpl extends BaseWifiService { @Override public void registerLocalOnlyHotspotSoftApCallback(ISoftApCallback callback, Bundle extras) { + if (!SdkLevel.isAtLeastT()) { + throw new UnsupportedOperationException(); + } + // verify arguments if (callback == null) { throw new IllegalArgumentException("Callback must not be null"); } - int uid = Binder.getCallingUid(); - int pid = Binder.getCallingPid(); - mWifiPermissionsUtil.enforceNearbyDevicesPermission( - extras.getParcelable(WifiManager.EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE), + AttributionSource attributionSource = + extras.getParcelable(WifiManager.EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE); + mWifiPermissionsUtil.enforceNearbyDevicesPermission(attributionSource, false, TAG + " registerLocalOnlyHotspotSoftApCallback"); if (mVerboseLoggingEnabled) { @@ -2917,9 +3323,8 @@ public class WifiServiceImpl extends BaseWifiService { // post operation to handler thread mWifiThreadRunner.post(() -> { - if (!mLohsSoftApTracker.registerSoftApCallback(callback)) { + if (!mLohsSoftApTracker.registerSoftApCallback(callback, attributionSource)) { Log.e(TAG, "registerLocalOnlyHotspotSoftApCallback: Failed to add callback"); - return; } }, TAG + "#registerLocalOnlyHotspotSoftApCallback"); } @@ -2930,8 +3335,6 @@ public class WifiServiceImpl extends BaseWifiService { if (callback == null) { throw new IllegalArgumentException("Callback must not be null"); } - int uid = Binder.getCallingUid(); - int pid = Binder.getCallingPid(); mWifiPermissionsUtil.enforceNearbyDevicesPermission( extras.getParcelable(WifiManager.EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE), @@ -3218,18 +3621,18 @@ public class WifiServiceImpl extends BaseWifiService { } /** - * Returns true if we should log the call to getSupportedFeatures. + * Returns true if we should log the call to isFeatureSupported. * - * Because of the way getSupportedFeatures is used in WifiManager, there are + * Because of the way isFeatureSupported is used in WifiManager, there are * often clusters of several back-to-back calls; avoid repeated logging if * the feature set has not changed and the time interval is short. */ - private boolean needToLogSupportedFeatures(long features) { + private boolean needToLogSupportedFeatures(BitSet features) { if (mVerboseLoggingEnabled) { long now = mClock.getElapsedSinceBootMillis(); synchronized (this) { if (now > mLastLoggedSupportedFeaturesTimestamp + A_FEW_MILLISECONDS - || features != mLastLoggedSupportedFeatures) { + || !features.equals(mLastLoggedSupportedFeatures)) { mLastLoggedSupportedFeaturesTimestamp = now; mLastLoggedSupportedFeatures = features; return true; @@ -3239,23 +3642,20 @@ public class WifiServiceImpl extends BaseWifiService { return false; } private static final int A_FEW_MILLISECONDS = 250; - private long mLastLoggedSupportedFeatures = -1; + private BitSet mLastLoggedSupportedFeatures = new BitSet(); private long mLastLoggedSupportedFeaturesTimestamp = 0; - /** - * see {@link android.net.wifi.WifiManager#getSupportedFeatures} - */ @Override - public long getSupportedFeatures() { + public boolean isFeatureSupported(int feature) { enforceAccessPermission(); - long features = getSupportedFeaturesInternal(); - if (needToLogSupportedFeatures(features)) { - mLog.info("getSupportedFeatures uid=% returns %") + BitSet supportedFeatures = getSupportedFeaturesInternal(); + if (needToLogSupportedFeatures(supportedFeatures)) { + mLog.info("isFeatureSupported uid=% returns %") .c(Binder.getCallingUid()) - .c(Long.toHexString(features)) + .c(FeatureBitsetUtils.formatSupportedFeatures(supportedFeatures)) .flush(); } - return features; + return supportedFeatures.get(feature); } @Override @@ -3270,7 +3670,7 @@ public class WifiServiceImpl extends BaseWifiService { .c(Binder.getCallingUid()) .flush(); } - if ((getSupportedFeatures() & WifiManager.WIFI_FEATURE_LINK_LAYER_STATS) == 0) { + if (!isFeatureSupported(WifiManager.WIFI_FEATURE_LINK_LAYER_STATS)) { try { listener.onWifiActivityEnergyInfo(null); } catch (RemoteException e) { @@ -3448,6 +3848,7 @@ public class WifiServiceImpl extends BaseWifiService { /** * See {@link WifiManager#getPrivilegedConnectedNetwork()} */ + @Override public WifiConfiguration getPrivilegedConnectedNetwork(String packageName, String featureId, Bundle extras) { enforceReadCredentialPermission(); @@ -5615,7 +6016,7 @@ public class WifiServiceImpl extends BaseWifiService { mContext, Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0)); pw.println("mInIdleMode " + mInIdleMode); pw.println("mScanPending " + mScanPending); - pw.println("SupportedFeatures:" + Long.toHexString(getSupportedFeaturesInternal())); + pw.println("SupportedFeatures: " + getSupportedFeaturesString()); pw.println("SettingsStore:"); mSettingsStore.dump(fd, pw, args); mActiveModeWarden.dump(fd, pw, args); @@ -5688,6 +6089,17 @@ public class WifiServiceImpl extends BaseWifiService { } pw.println(); mResourceCache.dump(pw); + if (mFeatureFlags.wepDisabledInApm() + && mWepNetworkUsageController != null) { + pw.println(); + mWepNetworkUsageController.dump(fd, pw, args); + } + if (mScorerServiceConnection != null) { + pw.println("boundToExternalScorer successfully"); + } else { + pw.println("boundToExternalScorer=failure, lastScorerBindingState=" + + mLastScorerBindingState); + } } }, TAG + "#dump"); } @@ -5755,21 +6167,23 @@ public class WifiServiceImpl extends BaseWifiService { public void initializeMulticastFiltering() { enforceMulticastChangePermission(); mLog.info("initializeMulticastFiltering uid=%").c(Binder.getCallingUid()).flush(); - mWifiMulticastLockManager.initializeFiltering(); + mWifiMulticastLockManager.startFilteringMulticastPackets(); } @Override public void acquireMulticastLock(IBinder binder, String tag) { enforceMulticastChangePermission(); - mLog.info("acquireMulticastLock uid=% tag=%").c(Binder.getCallingUid()).c(tag).flush(); - mWifiMulticastLockManager.acquireLock(binder, tag); + int uid = Binder.getCallingUid(); + mLog.info("acquireMulticastLock uid=% tag=%").c(uid).c(tag).flush(); + mWifiMulticastLockManager.acquireLock(uid, binder, tag); } @Override - public void releaseMulticastLock(String tag) { + public void releaseMulticastLock(IBinder binder, String tag) { enforceMulticastChangePermission(); - mLog.info("releaseMulticastLock uid=% tag=%").c(Binder.getCallingUid()).c(tag).flush(); - mWifiMulticastLockManager.releaseLock(tag); + int uid = Binder.getCallingUid(); + mLog.info("releaseMulticastLock uid=% tag=%").c(uid).c(tag).flush(); + mWifiMulticastLockManager.releaseLock(uid, binder, tag); } @Override @@ -5868,6 +6282,10 @@ public class WifiServiceImpl extends BaseWifiService { if (SdkLevel.isAtLeastV() && mWifiInjector.getWifiVoipDetector() != null) { mWifiInjector.getWifiVoipDetector().enableVerboseLogging(mVerboseLoggingEnabled); } + if (mFeatureFlags.wepDisabledInApm() + && mWepNetworkUsageController != null) { + mWepNetworkUsageController.enableVerboseLogging(mVerboseLoggingEnabled); + } } @Override @@ -6246,7 +6664,11 @@ public class WifiServiceImpl extends BaseWifiService { TAG + "#unregisterTrafficStateCallback"); } - private long getSupportedFeaturesInternal() { + protected String getSupportedFeaturesString() { + return FeatureBitsetUtils.formatSupportedFeatures(getSupportedFeaturesInternal()); + } + + private BitSet getSupportedFeaturesInternal() { return mActiveModeWarden.getSupportedFeatureSet(); } @@ -7042,6 +7464,7 @@ public class WifiServiceImpl extends BaseWifiService { /** * See {@link WifiManager#registerScanResultsCallback(WifiManager.ScanResultsCallback)} */ + @Override public void registerScanResultsCallback(@NonNull IScanResultsCallback callback) { if (callback == null) { throw new IllegalArgumentException("callback must not be null"); @@ -7354,6 +7777,7 @@ public class WifiServiceImpl extends BaseWifiService { if (mVerboseLoggingEnabled) { mLog.info("setWifiConnectedNetworkScorer uid=%").c(callingUid).flush(); } + mWifiThreadRunner.post(() -> bindScorerService(callingUid)); // Post operation to handler thread return mWifiThreadRunner.call( () -> mActiveModeWarden.setWifiConnectedNetworkScorer(binder, scorer, callingUid), @@ -7370,6 +7794,7 @@ public class WifiServiceImpl extends BaseWifiService { if (mVerboseLoggingEnabled) { mLog.info("clearWifiConnectedNetworkScorer uid=%").c(Binder.getCallingUid()).flush(); } + mWifiThreadRunner.post(() -> unbindScorerService(SCORER_BINDING_STATE_CLEARED)); // Post operation to handler thread mWifiThreadRunner.post(() -> mActiveModeWarden.clearWifiConnectedNetworkScorer(), TAG + "#clearWifiConnectedNetworkScorer"); @@ -7569,6 +7994,29 @@ public class WifiServiceImpl extends BaseWifiService { return mSettingsStore.handleWifiScoringEnabled(enabled); } + /** + * See {@link android.net.wifi.WifiManager#storeCapturedData(Executor, IntConsumer, int, + * booloan, long, long)}. + */ + @Override + public void storeCapturedData(int triggerType, boolean isFullCapture, + long triggerStartTimeMillis, long triggerStopTimeMillis, + @NonNull IIntegerListener listener) { + if (listener == null) { + throw new IllegalArgumentException("listener should not be null"); + } + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE, "WifiService"); + mWifiThreadRunner.post(() -> { + try { + listener.onResult(mWifiMetrics.storeCapturedData(triggerType, isFullCapture, + triggerStartTimeMillis, triggerStopTimeMillis)); + } catch (RemoteException e) { + Log.e(TAG, e.getMessage(), e); + } + }, TAG + "#storeCapturedData"); + } + @VisibleForTesting static boolean isValidBandForGetUsableChannels(@WifiScanner.WifiBand int band) { switch (band) { @@ -7741,21 +8189,20 @@ public class WifiServiceImpl extends BaseWifiService { */ @Override public boolean isPnoSupported() { + boolean featureSetSupportsPno = isFeatureSupported(WifiManager.WIFI_FEATURE_PNO); return mWifiGlobals.isSwPnoEnabled() - || (mWifiGlobals.isBackgroundScanSupported() - && (getSupportedFeatures() & WifiManager.WIFI_FEATURE_PNO) != 0); + || (mWifiGlobals.isBackgroundScanSupported() && featureSetSupportsPno); } private boolean isAggressiveRoamingModeSupported() { - return (getSupportedFeatures() & WifiManager.WIFI_FEATURE_AGGRESSIVE_ROAMING_MODE_SUPPORT) - != 0; + return isFeatureSupported(WifiManager.WIFI_FEATURE_AGGRESSIVE_ROAMING_MODE_SUPPORT); } /** * @return true if this device supports Trust On First Use */ private boolean isTrustOnFirstUseSupported() { - return (getSupportedFeatures() & WIFI_FEATURE_TRUST_ON_FIRST_USE) != 0; + return isFeatureSupported(WifiManager.WIFI_FEATURE_TRUST_ON_FIRST_USE); } /** @@ -8319,6 +8766,7 @@ public class WifiServiceImpl extends BaseWifiService { * See {@link WifiManager#removeWifiLowLatencyLockListener( * WifiManager.WifiLowLatencyLockListener)} */ + @Override public void removeWifiLowLatencyLockListener(IWifiLowLatencyLockListener listener) { if (listener == null) { throw new IllegalArgumentException(); @@ -8348,6 +8796,7 @@ public class WifiServiceImpl extends BaseWifiService { * See {@link WifiManager#getMaxMloAssociationLinkCount(Executor, Consumer)} */ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Override public void getMaxMloAssociationLinkCount(@NonNull IIntegerListener listener, Bundle extras) { // SDK check. if (!SdkLevel.isAtLeastU()) { @@ -8379,6 +8828,7 @@ public class WifiServiceImpl extends BaseWifiService { /** * See {@link WifiManager#getMaxMloStrLinkCount(Executor, Consumer)} */ + @Override @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) public void getMaxMloStrLinkCount(@NonNull IIntegerListener listener, Bundle extras) { // SDK check. @@ -8411,6 +8861,7 @@ public class WifiServiceImpl extends BaseWifiService { /** * See {@link WifiManager#getSupportedSimultaneousBandCombinations(Executor, Consumer)}. */ + @Override public void getSupportedSimultaneousBandCombinations(@NonNull IWifiBandsListener listener, Bundle extras) { // SDK check. @@ -8488,12 +8939,12 @@ public class WifiServiceImpl extends BaseWifiService { + " is not allowed to set wifi web allowed by user"); } mLog.info("setWepAllowed=% uid=%").c(isAllowed).c(callingUid).flush(); - mWifiThreadRunner.post(() -> { - mSettingsConfigStore.put(WIFI_WEP_ALLOWED, isAllowed); - handleWepAllowedChanged(isAllowed); - }, TAG + "#setWepAllowed"); + mSettingsConfigStore.put(WIFI_WEP_ALLOWED, isAllowed); } + /** + * @deprecated Use mWepNetworkUsageController.handleWepAllowedChanged() instead. + */ private void handleWepAllowedChanged(boolean isAllowed) { mWifiGlobals.setWepAllowed(isAllowed); if (!isAllowed) { @@ -8721,7 +9172,7 @@ public class WifiServiceImpl extends BaseWifiService { isDeviceOwner); listener.onResult(roamingPolicies); } catch (RemoteException e) { - Log.e(TAG, e.getMessage()); + Log.e(TAG, e.getMessage(), e); } }, TAG + "#getPerSsidRoamingModes"); } @@ -8861,4 +9312,151 @@ public class WifiServiceImpl extends BaseWifiService { } }, TAG + "#queryD2dAllowedWhenInfraStaDisabled"); } + + /** + * See {@link WifiManager#setAutojoinDisallowedSecurityTypes(int)} + * @param restrictions The autojoin restriction security types to be set. + * @throws SecurityException if the caller does not have permission. + * @throws IllegalArgumentException if the arguments are null or invalid + */ + @Override + @RequiresApi(Build.VERSION_CODES.TIRAMISU) + public void setAutojoinDisallowedSecurityTypes(int restrictions, @NonNull Bundle extras) { + // SDK check. + if (!SdkLevel.isAtLeastT()) { + throw new UnsupportedOperationException("SDK level too old"); + } + // Check null argument + if (extras == null) { + throw new IllegalArgumentException("extras cannot be null"); + } + // Check invalid argument + if ((restrictions & (0x1 << WifiInfo.SECURITY_TYPE_OPEN)) == 0 + && (restrictions & (0x1 << WifiInfo.SECURITY_TYPE_OWE)) != 0) { + throw new IllegalArgumentException("Restricting OWE but not OPEN is not allowed"); + } + if ((restrictions & (0x1 << WifiInfo.SECURITY_TYPE_PSK)) == 0 + && (restrictions & (0x1 << WifiInfo.SECURITY_TYPE_SAE)) != 0) { + throw new IllegalArgumentException("Restricting SAE but not PSK is not allowed"); + } + if ((restrictions & (0x1 << WifiInfo.SECURITY_TYPE_EAP)) == 0 + && (restrictions & (0x1 << WifiInfo.SECURITY_TYPE_EAP_WPA3_ENTERPRISE)) != 0) { + throw new IllegalArgumentException( + "Restricting EAP_WPA3_ENTERPRISE but not EAP is not allowed"); + } + // Permission check. + int uid = Binder.getCallingUid(); + if (!mWifiPermissionsUtil.checkManageWifiNetworkSelectionPermission(uid) + && !mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)) { + throw new SecurityException( + "Uid=" + uid + " is not allowed to set AutoJoinRestrictionSecurityTypes"); + } + if (mVerboseLoggingEnabled) { + mLog.info("setAutojoinDisallowedSecurityTypes uid=% Package Name=% restrictions=%") + .c(uid).c(getPackageName(extras)).c(restrictions).flush(); + } + mWifiThreadRunner.post(() -> { + mWifiConnectivityManager.setAutojoinDisallowedSecurityTypes(restrictions); + }, TAG + "#setAutojoinDisallowedSecurityTypes"); + } + + /** + * See {@link WifiManager#getAutojoinDisallowedSecurityTypes(Executor, Consumer)} + */ + @Override + @RequiresApi(Build.VERSION_CODES.TIRAMISU) + public void getAutojoinDisallowedSecurityTypes(@NonNull IIntegerListener listener, + @NonNull Bundle extras) { + // SDK check. + if (!SdkLevel.isAtLeastT()) { + throw new UnsupportedOperationException("SDK level too old"); + } + // Argument check + if (listener == null) { + throw new IllegalArgumentException("listener cannot be null"); + } + if (extras == null) { + throw new IllegalArgumentException("extras cannot be null"); + } + // Permission check. + int uid = Binder.getCallingUid(); + if (!mWifiPermissionsUtil.checkManageWifiNetworkSelectionPermission(uid) + && !mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)) { + throw new SecurityException( + "Uid=" + uid + " is not allowed to get AutoJoinRestrictionSecurityTypes"); + } + if (mVerboseLoggingEnabled) { + mLog.info("getAutojoinDisallowedSecurityTypes: Uid=% Package Name=%").c( + Binder.getCallingUid()).c(getPackageName(extras)).flush(); + } + + mWifiThreadRunner.post(() -> { + try { + listener.onResult(mWifiConnectivityManager.getAutojoinDisallowedSecurityTypes()); + } catch (RemoteException e) { + Log.e(TAG, e.getMessage(), e); + } + }, TAG + "#getAutojoinDisallowedSecurityTypes"); + } + + @Override + public void disallowCurrentSuggestedNetwork(@NonNull BlockingOption option, + @NonNull String packageName) { + Objects.requireNonNull(option, "blockingOption cannot be null"); + int callingUid = Binder.getCallingUid(); + mWifiPermissionsUtil.checkPackage(callingUid, packageName); + if (enforceChangePermission(packageName) != MODE_ALLOWED) { + throw new SecurityException("Caller does not hold CHANGE_WIFI_STATE permission"); + } + if (mVerboseLoggingEnabled) { + mLog.info("disallowCurrentSuggestedNetwork: Uid=% Package Name=%").c( + callingUid).c(option.toString()).flush(); + } + if (mActiveModeWarden.getWifiState() != WIFI_STATE_ENABLED) { + return; + } + WifiInfo info = mActiveModeWarden.getConnectionInfo(); + if (!packageName.equals(info.getRequestingPackageName())) { + return; + } + mWifiThreadRunner.post( + () -> mActiveModeWarden.getPrimaryClientModeManager().blockNetwork(option), + "disallowCurrentSuggestedNetwork"); + } + + /** + * See {@link WifiManager#isUsdSubscriberSupported()} + */ + @Override + public boolean isUsdSubscriberSupported() { + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException("SDK level too old"); + } + int uid = getMockableCallingUid(); + if (!mWifiPermissionsUtil.checkManageWifiNetworkSelectionPermission(uid)) { + throw new SecurityException("App not allowed to use USD (uid = " + uid + ")"); + } + if (!mIsUsdSupported) { + return false; + } + return mWifiNative.isUsdSubscriberSupported(); + } + + /** + * See {@link WifiManager#isUsdPublisherSupported()} + */ + @Override + public boolean isUsdPublisherSupported() { + if (!Environment.isSdkAtLeastB()) { + throw new UnsupportedOperationException("SDK level too old"); + } + int uid = getMockableCallingUid(); + if (!mWifiPermissionsUtil.checkManageWifiNetworkSelectionPermission(uid)) { + throw new SecurityException("App not allowed to use USD (uid = " + uid + ")"); + } + if (!mIsUsdSupported) { + return false; + } + return mWifiNative.isUsdPublisherSupported(); + } } diff --git a/service/java/com/android/server/wifi/WifiSettingsConfigStore.java b/service/java/com/android/server/wifi/WifiSettingsConfigStore.java index a5ba58dd13..1aab1722d3 100644 --- a/service/java/com/android/server/wifi/WifiSettingsConfigStore.java +++ b/service/java/com/android/server/wifi/WifiSettingsConfigStore.java @@ -136,6 +136,12 @@ public class WifiSettingsConfigStore { new Key<>("wifi_default_country_code", WifiCountryCode.getOemDefaultCountryCode()); /** + * Store the supported P2P features. + */ + public static final Key<Long> WIFI_P2P_SUPPORTED_FEATURES = + new Key<>("wifi_p2p_supported_features", 0L); + + /** * Store the supported features retrieved from WiFi HAL and Supplicant HAL. Note that this * value is deprecated and is replaced by {@link #WIFI_NATIVE_EXTENDED_SUPPORTED_FEATURES} */ diff --git a/service/java/com/android/server/wifi/WifiSettingsStore.java b/service/java/com/android/server/wifi/WifiSettingsStore.java index a84346b77f..a8e89158fa 100644 --- a/service/java/com/android/server/wifi/WifiSettingsStore.java +++ b/service/java/com/android/server/wifi/WifiSettingsStore.java @@ -27,6 +27,8 @@ import android.net.Uri; import android.net.wifi.WifiContext; import android.provider.Settings; +import androidx.annotation.Keep; + import com.android.internal.annotations.VisibleForTesting; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.wifi.resources.R; @@ -192,6 +194,7 @@ public class WifiSettingsStore { * Returns true if airplane mode is currently on. * @return {@code true} if airplane mode is on. */ + @Keep public synchronized boolean isAirplaneModeOn() { return mAirplaneModeOn; } @@ -250,6 +253,7 @@ public class WifiSettingsStore { mNotificationManager.notify(SystemMessage.NOTE_WIFI_APM_NOTIFICATION, builder.build()); } + @Keep public synchronized boolean handleWifiToggled(boolean wifiEnabled) { // Can Wi-Fi be toggled in airplane mode ? if (mAirplaneModeOn && !isAirplaneToggleable()) { diff --git a/service/java/com/android/server/wifi/WifiShellCommand.java b/service/java/com/android/server/wifi/WifiShellCommand.java index 1439406afb..aad4de03ca 100644 --- a/service/java/com/android/server/wifi/WifiShellCommand.java +++ b/service/java/com/android/server/wifi/WifiShellCommand.java @@ -25,13 +25,13 @@ import static android.net.wifi.WifiConfiguration.METERED_OVERRIDE_METERED; import static android.net.wifi.WifiManager.ACTION_REMOVE_SUGGESTION_DISCONNECT; import static android.net.wifi.WifiManager.ACTION_REMOVE_SUGGESTION_LINGER; import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.REQUEST_REGISTERED; +import static android.net.wifi.WifiManager.ROAMING_MODE_AGGRESSIVE; +import static android.net.wifi.WifiManager.ROAMING_MODE_NONE; +import static android.net.wifi.WifiManager.ROAMING_MODE_NORMAL; import static android.net.wifi.WifiManager.VERBOSE_LOGGING_LEVEL_DISABLED; import static android.net.wifi.WifiManager.VERBOSE_LOGGING_LEVEL_WIFI_AWARE_ENABLED_ONLY; import static android.net.wifi.WifiManager.WIFI_STATE_DISABLED; import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED; -import static android.net.wifi.WifiManager.ROAMING_MODE_NONE; -import static android.net.wifi.WifiManager.ROAMING_MODE_NORMAL; -import static android.net.wifi.WifiManager.ROAMING_MODE_AGGRESSIVE; import static com.android.server.wifi.HalDeviceManager.HDM_CREATE_IFACE_AP; import static com.android.server.wifi.HalDeviceManager.HDM_CREATE_IFACE_AP_BRIDGE; @@ -188,6 +188,7 @@ public class WifiShellCommand extends BasicShellCommandHandler { "set-mock-wifimodem-methods", "force-overlay-config-value", "get-softap-supported-features", + "get-wifi-supported-features", "get-overlay-config-values" }; @@ -275,6 +276,11 @@ public class WifiShellCommand extends BasicShellCommandHandler { @Override public void onBlockedClientConnecting(WifiClient client, int reason) { } + + @Override + public void onClientsDisconnected(SoftApInfo info, List<WifiClient> clients) { + mPrintWriter.println("onClientsDisconnected, info: " + info + ", clients: " + clients); + } } /** @@ -903,7 +909,7 @@ public class WifiShellCommand extends BasicShellCommandHandler { mWifiService.registerLocalOnlyHotspotSoftApCallback(softApCallback, extras); if (REQUEST_REGISTERED != mWifiService.startLocalOnlyHotspot( lohsCallback, SHELL_PACKAGE_NAME, null /* featureId */, - config, extras)) { + config, extras, false)) { pw.println("Lohs failed to start. Please check config parameters"); } // Wait for lohs to start and complete callback @@ -1002,17 +1008,20 @@ public class WifiShellCommand extends BasicShellCommandHandler { if (ApConfigUtil.isWpa3SaeSupported(mContext)) { pw.println("wifi_softap_wpa3_sae_supported"); } - if ((mWifiService.getSupportedFeatures() - & WifiManager.WIFI_FEATURE_BRIDGED_AP) - == WifiManager.WIFI_FEATURE_BRIDGED_AP) { + if (mWifiService.isFeatureSupported(WifiManager.WIFI_FEATURE_BRIDGED_AP)) { pw.println("wifi_softap_bridged_ap_supported"); } - if ((mWifiService.getSupportedFeatures() - & WifiManager.WIFI_FEATURE_STA_BRIDGED_AP) - == WifiManager.WIFI_FEATURE_STA_BRIDGED_AP) { + if (mWifiService.isFeatureSupported(WifiManager.WIFI_FEATURE_STA_BRIDGED_AP)) { pw.println("wifi_softap_bridged_ap_with_sta_supported"); } + if (mWifiNative.isMLDApSupportMLO()) { + pw.println("wifi_softap_mlo_supported"); + } return 0; + case "get-wifi-supported-features": { + pw.println(mWifiService.getSupportedFeaturesString()); + return 0; + } case "settings-reset": mWifiNative.stopFakingScanDetails(); mWifiNative.resetFakeScanDetails(); @@ -1509,7 +1518,7 @@ public class WifiShellCommand extends BasicShellCommandHandler { String neutralButtonText = null; String dialogOption = getNextOption(); boolean simpleTimeoutSpecified = false; - long simpleTimeoutMs = 0; + long simpleTimeoutMs = 15 * 1000; boolean useLegacy = false; while (dialogOption != null) { switch (dialogOption) { @@ -1595,53 +1604,47 @@ public class WifiShellCommand extends BasicShellCommandHandler { wifiEnableRequestCallback, mWifiThreadRunner); } - if (simpleTimeoutSpecified) { - simpleDialogHandle.launchDialog(simpleTimeoutMs); - pw.println("Launched dialog with " + simpleTimeoutMs + " millisecond" - + " timeout. Waiting for user response..."); - pw.flush(); - String dialogResponse = simpleQueue.take(); - if (dialogResponse == null) { - pw.println("No response received."); - } else { - pw.println(dialogResponse); - } + simpleDialogHandle.launchDialog(); + pw.println("Launched dialog. Waiting up to " + simpleTimeoutMs + " ms for" + + " user response before dismissing..."); + String simpleDialogResponse = simpleQueue.poll(simpleTimeoutMs, + TimeUnit.MILLISECONDS); + if (simpleDialogResponse == null) { + pw.println("No response received. Dismissing dialog."); + simpleDialogHandle.dismissDialog(); } else { - simpleDialogHandle.launchDialog(); - pw.println("Launched dialog. Waiting up to 15 seconds for user response" - + " before dismissing..."); - pw.flush(); - String dialogResponse = simpleQueue.poll(15, TimeUnit.SECONDS); - if (dialogResponse == null) { - pw.println("No response received. Dismissing dialog."); - simpleDialogHandle.dismissDialog(); - } else { - pw.println(dialogResponse); - } + pw.println(simpleDialogResponse); } return 0; case "launch-dialog-p2p-invitation-sent": { int displayId = Display.DEFAULT_DISPLAY; String deviceName = getNextArgRequired(); - String displayPin = getNextArgRequired(); String cmdOption = getNextOption(); - if (cmdOption != null && cmdOption.equals("-i")) { - String displayIdStr = getNextArgRequired(); - try { - displayId = Integer.parseInt(displayIdStr); - } catch (NumberFormatException e) { - pw.println("Invalid <display-id> argument to " - + "'launch-dialog-p2p-invitation-sent' " - + "- must be an integer: " - + displayIdStr); - return -1; - } - DisplayManager dm = mContext.getSystemService(DisplayManager.class); - Display[] displays = dm.getDisplays(); - for (Display display : displays) { - pw.println("Display: id=" + display.getDisplayId() + ", info=" - + display.getDeviceProductInfo()); + String displayPin = null; + while (cmdOption != null) { + if (cmdOption.equals("-d")) { + displayPin = getNextArgRequired(); + } else if (cmdOption.equals("-i")) { + String displayIdStr = getNextArgRequired(); + try { + displayId = Integer.parseInt(displayIdStr); + } catch (NumberFormatException e) { + pw.println("Invalid <display-id> argument to " + + "'launch-dialog-p2p-invitation-sent' " + + "- must be an integer: " + + displayIdStr); + return -1; + } + DisplayManager dm = mContext.getSystemService(DisplayManager.class); + Display[] displays = dm.getDisplays(); + for (Display display : displays) { + pw.println("Display: id=" + display.getDisplayId() + ", info=" + + display.getDeviceProductInfo()); + } + } else { + pw.println("Ignoring unknown option " + cmdOption); } + cmdOption = getNextOption(); } mWifiDialogManager.createP2pInvitationSentDialog(deviceName, displayPin, displayId).launchDialog(); @@ -1655,7 +1658,7 @@ public class WifiShellCommand extends BasicShellCommandHandler { String pinOption = getNextOption(); int displayId = Display.DEFAULT_DISPLAY; boolean p2pInvRecTimeoutSpecified = false; - long p2pInvRecTimeout = 0; + int p2pInvRecTimeout = 0; while (pinOption != null) { if (pinOption.equals("-p")) { isPinRequested = true; @@ -1705,11 +1708,12 @@ public class WifiShellCommand extends BasicShellCommandHandler { deviceName, isPinRequested, displayPin, + p2pInvRecTimeout, displayId, callback, mWifiThreadRunner); + p2pInvitationReceivedDialogHandle.launchDialog(); if (p2pInvRecTimeoutSpecified) { - p2pInvitationReceivedDialogHandle.launchDialog(p2pInvRecTimeout); pw.println("Launched dialog with " + p2pInvRecTimeout + " millisecond" + " timeout. Waiting for user response..."); pw.flush(); @@ -1720,7 +1724,6 @@ public class WifiShellCommand extends BasicShellCommandHandler { pw.println(dialogResponse); } } else { - p2pInvitationReceivedDialogHandle.launchDialog(); pw.println("Launched dialog. Waiting up to 15 seconds for user response" + " before dismissing..."); pw.flush(); @@ -2314,6 +2317,10 @@ public class WifiShellCommand extends BasicShellCommandHandler { mWifiService.setPerSsidRoamingMode(wifiSsid, mode, SHELL_PACKAGE_NAME); return 0; + case "set-scan-throttling-enabled": + mWifiService.setScanThrottleEnabled( + getNextArgRequiredTrueOrFalse("enabled", "disabled")); + return 0; default: return handleDefaultCommands(cmd); } @@ -3055,7 +3062,8 @@ public class WifiShellCommand extends BasicShellCommandHandler { pw.println(" -x - Neutral Button Text"); pw.println(" -c - Optional timeout in milliseconds"); pw.println(" -s - Use the legacy dialog implementation on the system process"); - pw.println(" launch-dialog-p2p-invitation-sent <device_name> <pin> [-i <display_id>]"); + pw.println(" launch-dialog-p2p-invitation-sent <device_name> [-d <pin>]" + + " [-i <display_id>]"); pw.println(" Launches a P2P Invitation Sent dialog."); pw.println(" <device_name> - Name of the device the invitation was sent to"); pw.println(" <pin> - PIN for the invited device to input"); @@ -3126,7 +3134,10 @@ public class WifiShellCommand extends BasicShellCommandHandler { pw.println(" and/or 'wifi_softap_wpa3_sae_supported',"); pw.println(" and/or 'wifi_softap_bridged_ap_supported',"); pw.println(" and/or 'wifi_softap_bridged_ap_with_sta_supported',"); + pw.println(" and/or 'wifi_softap_mlo_supported',"); pw.println(" each on a separate line."); + pw.println(" get-wifi-supported-features"); + pw.println(" Gets the features supported by WifiManager"); } private void onHelpPrivileged(PrintWriter pw) { @@ -3409,6 +3420,8 @@ public class WifiShellCommand extends BasicShellCommandHandler { pw.println(" Sets the roaming mode for the given SSID."); pw.println(" -x - Specifies the SSID as hex digits instead of plain text."); pw.println(" Example: set-ssid-roaming-mode test_ssid aggressive"); + pw.println(" set-scan-throttling-enabled enabled|disabled"); + pw.println(" Set wifi scan throttling for 3P apps enabled or disabled."); } @Override diff --git a/service/java/com/android/server/wifi/WifiSignalPollResults.java b/service/java/com/android/server/wifi/WifiSignalPollResults.java index 9e83c001e2..5ab8aebeea 100644 --- a/service/java/com/android/server/wifi/WifiSignalPollResults.java +++ b/service/java/com/android/server/wifi/WifiSignalPollResults.java @@ -18,6 +18,8 @@ package com.android.server.wifi; import android.util.Log; +import androidx.annotation.Keep; + import java.util.HashMap; import java.util.Map; @@ -110,6 +112,7 @@ public class WifiSignalPollResults { * * @return rssi in dBm or {@link WifiSignalPollResults#MIN_RSSI} if no poll results. */ + @Keep public int getRssi() { return mEntries.getOrDefault(mBestLinkId, mDefault).currentRssiDbm; } @@ -167,6 +170,7 @@ public class WifiSignalPollResults { * * @return frequency in MHz or 0 if no poll results. */ + @Keep public int getFrequency() { return mEntries.getOrDefault(mBestLinkId, mDefault).frequencyMHz; } diff --git a/service/java/com/android/server/wifi/WifiThreadRunner.java b/service/java/com/android/server/wifi/WifiThreadRunner.java index 859d96d078..08ebe3868e 100644 --- a/service/java/com/android/server/wifi/WifiThreadRunner.java +++ b/service/java/com/android/server/wifi/WifiThreadRunner.java @@ -61,6 +61,10 @@ public class WifiThreadRunner { mHandler = handler; } + public Handler getHandler() { + return mHandler; + } + /** * Synchronously runs code on the main Wifi thread and return a value. * <b>Blocks</b> the calling thread until the callable completes execution on the main Wifi diff --git a/service/java/com/android/server/wifi/WifiVendorHal.java b/service/java/com/android/server/wifi/WifiVendorHal.java index 9d2f1d5ffa..13b659f36a 100644 --- a/service/java/com/android/server/wifi/WifiVendorHal.java +++ b/service/java/com/android/server/wifi/WifiVendorHal.java @@ -18,7 +18,6 @@ package com.android.server.wifi; import static com.android.server.wifi.HalDeviceManager.HDM_CREATE_IFACE_AP; import static com.android.server.wifi.HalDeviceManager.HDM_CREATE_IFACE_AP_BRIDGE; import static com.android.server.wifi.HalDeviceManager.HDM_CREATE_IFACE_STA; -import static com.android.server.wifi.util.GeneralUtil.getCapabilityIndex; import android.annotation.NonNull; import android.annotation.Nullable; @@ -380,10 +379,10 @@ public class WifiVendorHal { } } - private long getNecessaryCapabilitiesForSoftApMode(@SoftApConfiguration.BandType int band) { - long caps = HalDeviceManager.CHIP_CAPABILITY_ANY; + private BitSet getNecessaryCapabilitiesForSoftApMode(@SoftApConfiguration.BandType int band) { + BitSet caps = new BitSet(); if ((band & SoftApConfiguration.BAND_60GHZ) != 0) { - caps |= WifiManager.WIFI_FEATURE_INFRA_60G; + caps.set(WifiManager.WIFI_FEATURE_INFRA_60G); } return caps; } @@ -795,7 +794,7 @@ public class WifiVendorHal { final PackageManager pm = sContext.getPackageManager(); for (Pair pair: sSystemFeatureCapabilityTranslation) { if (pm.hasSystemFeature((String) pair.second)) { - featureSet.set(getCapabilityIndex((long) pair.first)); + featureSet.set((int) pair.first); } } enter("System feature set: %").c(featureSet.toString()).flush(); @@ -898,28 +897,27 @@ public class WifiVendorHal { featureSet.or(iface.getCapabilities()); if (mHalDeviceManager.is24g5gDbsSupported(iface) || mHalDeviceManager.is5g6gDbsSupported(iface)) { - featureSet.set( - getCapabilityIndex(WifiManager.WIFI_FEATURE_DUAL_BAND_SIMULTANEOUS)); + featureSet.set(WifiManager.WIFI_FEATURE_DUAL_BAND_SIMULTANEOUS); } } } if (mWifiGlobals.isWpa3SaeH2eSupported()) { - featureSet.set(getCapabilityIndex(WifiManager.WIFI_FEATURE_SAE_H2E)); + featureSet.set(WifiManager.WIFI_FEATURE_SAE_H2E); } Set<Integer> supportedIfaceTypes = mHalDeviceManager.getSupportedIfaceTypes(); if (supportedIfaceTypes.contains(WifiChip.IFACE_TYPE_STA)) { - featureSet.set(getCapabilityIndex(WifiManager.WIFI_FEATURE_INFRA)); + featureSet.set(WifiManager.WIFI_FEATURE_INFRA); } if (supportedIfaceTypes.contains(WifiChip.IFACE_TYPE_AP)) { - featureSet.set(getCapabilityIndex(WifiManager.WIFI_FEATURE_MOBILE_HOTSPOT)); + featureSet.set(WifiManager.WIFI_FEATURE_MOBILE_HOTSPOT); } if (supportedIfaceTypes.contains(WifiChip.IFACE_TYPE_P2P)) { - featureSet.set(getCapabilityIndex(WifiManager.WIFI_FEATURE_P2P)); + featureSet.set(WifiManager.WIFI_FEATURE_P2P); } if (supportedIfaceTypes.contains(WifiChip.IFACE_TYPE_NAN)) { - featureSet.set(getCapabilityIndex(WifiManager.WIFI_FEATURE_AWARE)); + featureSet.set(WifiManager.WIFI_FEATURE_AWARE); } return featureSet; diff --git a/service/java/com/android/server/wifi/WifiVoipDetector.java b/service/java/com/android/server/wifi/WifiVoipDetector.java index 5754bc3ef8..e8c274b410 100644 --- a/service/java/com/android/server/wifi/WifiVoipDetector.java +++ b/service/java/com/android/server/wifi/WifiVoipDetector.java @@ -181,6 +181,7 @@ public class WifiVoipDetector { log = "Failed to set Voip Mode to " + newMode + " (maybe not supported?)"; } else { mCurrentMode = newMode; + mWifiInjector.getWifiMetrics().setVoipMode(mCurrentMode); } mLocalLog.log(log); if (mVerboseLoggingEnabled) { diff --git a/service/java/com/android/server/wifi/aware/Capabilities.java b/service/java/com/android/server/wifi/aware/Capabilities.java index b47eca20d1..59c78aa2f6 100644 --- a/service/java/com/android/server/wifi/aware/Capabilities.java +++ b/service/java/com/android/server/wifi/aware/Capabilities.java @@ -50,6 +50,9 @@ public class Capabilities { public boolean isSuspensionSupported; public boolean is6gSupported; public boolean isHeSupported; + public boolean isPeriodicRangingSupported; + public int maxSupportedRangingPktBandWidth; + public int maxSupportedRxChains; /** * Converts the internal capabilities to a parcelable & potentially app-facing @@ -75,6 +78,10 @@ public class Capabilities { bundle.putBoolean(Characteristics.KEY_SUPPORT_NAN_PAIRING, isNanPairingSupported); bundle.putBoolean(Characteristics.KEY_SUPPORT_SUSPENSION, deviceConfigFacade.isAwareSuspensionEnabled() && isSuspensionSupported); + bundle.putBoolean(Characteristics.KEY_SUPPORT_PERIODIC_RANGING, isPeriodicRangingSupported); + bundle.putInt(Characteristics.KEY_MAX_SUPPORTED_RANGING_PKT_BANDWIDTH, + maxSupportedRangingPktBandWidth); + bundle.putInt(Characteristics.KEY_MAX_SUPPORTED_RX_CHAINS, maxSupportedRxChains); return new Characteristics(bundle); } @@ -97,6 +104,9 @@ public class Capabilities { j.put("isSetClusterIdSupported", isSetClusterIdSupported); j.put("isNanPairingSupported", isNanPairingSupported); j.put("isSuspensionSupported", isSuspensionSupported); + j.put("isPeriodicRangingSupported", isPeriodicRangingSupported); + j.put("maxSupportedRangingPktBandWidth", maxSupportedRangingPktBandWidth); + j.put("maxSupportedRxChains", maxSupportedRxChains); return j; } @@ -142,6 +152,12 @@ public class Capabilities { + is6gSupported + ", isHeSupported=" + isHeSupported + + ", isPeriodicRangingSupported=" + + isPeriodicRangingSupported + + ", maxSupportedRangingPktBandWidth=" + + maxSupportedRangingPktBandWidth + + ",maxSupportedRxChains=" + + maxSupportedRxChains + "]"; } } diff --git a/service/java/com/android/server/wifi/aware/WifiAwareDiscoverySessionState.java b/service/java/com/android/server/wifi/aware/WifiAwareDiscoverySessionState.java index e5fdeed2ff..503e56a099 100644 --- a/service/java/com/android/server/wifi/aware/WifiAwareDiscoverySessionState.java +++ b/service/java/com/android/server/wifi/aware/WifiAwareDiscoverySessionState.java @@ -29,9 +29,11 @@ import android.net.wifi.OuiKeyedData; import android.net.wifi.WifiScanner; import android.net.wifi.aware.AwarePairingConfig; import android.net.wifi.aware.IWifiAwareDiscoverySessionCallback; +import android.net.wifi.aware.PeerHandle; import android.net.wifi.aware.PublishConfig; import android.net.wifi.aware.SubscribeConfig; import android.net.wifi.aware.WifiAwareManager; +import android.net.wifi.rtt.RangingResult; import android.net.wifi.util.HexEncoding; import android.os.RemoteException; import android.os.SystemClock; @@ -42,6 +44,7 @@ import com.android.server.wifi.hal.WifiNanIface.NanStatusCode; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -75,10 +78,12 @@ public class WifiAwareDiscoverySessionState { PeerInfo(int instanceId, byte[] mac) { mInstanceId = instanceId; mMac = mac; + mPeerHandle = new PeerHandle(instanceId); } int mInstanceId; byte[] mMac; + PeerHandle mPeerHandle; @Override public String toString() { @@ -688,6 +693,29 @@ public class WifiAwareDiscoverySessionState { } /** + * Callback from HAL when ranging results are available + */ + public void onRangingResultsReceived(List<RangingResult> rangingResults) { + List<RangingResult> validResults = new ArrayList<>(); + try { + for (RangingResult rangingResult : rangingResults) { + PeerHandle peerHandle = + getPeerHandleFromPeerMac(rangingResult.getMacAddress().toByteArray()); + if (peerHandle == null) { + Log.e(TAG, "Could not find Peer Handle for the ranging result"); + continue; + } + RangingResult result = new RangingResult.Builder( + rangingResult).setPeerHandle(peerHandle).build(); + validResults.add(result); + } + mCallback.onRangingResultsReceived(validResults); + } catch (RemoteException e) { + Log.w(TAG, "onRangingResultsReceived: RemoteException (FYI): " + e); + } + } + + /** * Event that receive the bootstrapping request finished */ public void onBootStrappingConfirmReceived(int peerId, boolean accept, int method) { @@ -720,6 +748,19 @@ public class WifiAwareDiscoverySessionState { } /** + * Get the peerHandle assigned by the framework from the peer mac. + */ + public PeerHandle getPeerHandleFromPeerMac(byte[] peerMac) { + for (int i = 0; i < mPeerInfoByRequestorInstanceId.size(); ++i) { + PeerInfo peerInfo = mPeerInfoByRequestorInstanceId.valueAt(i); + if (Arrays.equals(peerMac, peerInfo.mMac)) { + return peerInfo.mPeerHandle; + } + } + return null; + } + + /** * Dump the internal state of the class. */ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { diff --git a/service/java/com/android/server/wifi/aware/WifiAwareNativeCallback.java b/service/java/com/android/server/wifi/aware/WifiAwareNativeCallback.java index d6f02bcd78..2f4b0827d2 100644 --- a/service/java/com/android/server/wifi/aware/WifiAwareNativeCallback.java +++ b/service/java/com/android/server/wifi/aware/WifiAwareNativeCallback.java @@ -21,6 +21,7 @@ import android.net.wifi.OuiKeyedData; import android.net.wifi.aware.AwarePairingConfig; import android.net.wifi.aware.IdentityChangedListener; import android.net.wifi.aware.WifiAwareChannelInfo; +import android.net.wifi.rtt.RangingResult; import android.util.Log; import android.util.SparseArray; import android.util.SparseIntArray; @@ -161,6 +162,11 @@ public class WifiAwareNativeCallback implements WifiNanIface.Callback, } @Override + public void notifyRangingResults(ArrayList<RangingResult> rangingResults, byte sessionId) { + mWifiAwareStateManager.onRangingResults(rangingResults, sessionId); + } + + @Override public void notifyEnableResponse(short id, int status) { if (status == NanStatusCode.SUCCESS || status == NanStatusCode.ALREADY_ENABLED) { diff --git a/service/java/com/android/server/wifi/aware/WifiAwareServiceImpl.java b/service/java/com/android/server/wifi/aware/WifiAwareServiceImpl.java index 468b8b84b8..a44d90a76e 100644 --- a/service/java/com/android/server/wifi/aware/WifiAwareServiceImpl.java +++ b/service/java/com/android/server/wifi/aware/WifiAwareServiceImpl.java @@ -43,7 +43,6 @@ import android.net.wifi.aware.SubscribeConfig; import android.os.Binder; import android.os.Build; import android.os.Bundle; -import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.os.ParcelFileDescriptor; @@ -89,7 +88,7 @@ public class WifiAwareServiceImpl extends IWifiAwareManager.Stub { private WifiAwareNativeApi mWifiAwareNativeApi; private WifiAwareNativeCallback mWifiAwareNativeCallback; private WifiAwareShellCommand mShellCommand; - private Handler mHandler; + private RunnerHandler mHandler; private final Object mLock = new Object(); private final SparseArray<IBinder.DeathRecipient> mDeathRecipientsByClientId = @@ -160,6 +159,7 @@ public class WifiAwareServiceImpl extends IWifiAwareManager.Stub { mWifiAwareNativeManager.enableVerboseLogging(mVerboseLoggingEnabled, mVerboseLoggingEnabled); mWifiAwareNativeApi.enableVerboseLogging(mVerboseLoggingEnabled, vDbg); + mHandler.enableVerboseLogging(mVerboseLoggingEnabled); } /** diff --git a/service/java/com/android/server/wifi/aware/WifiAwareStateManager.java b/service/java/com/android/server/wifi/aware/WifiAwareStateManager.java index 3df13e88a9..19047477dc 100644 --- a/service/java/com/android/server/wifi/aware/WifiAwareStateManager.java +++ b/service/java/com/android/server/wifi/aware/WifiAwareStateManager.java @@ -89,6 +89,7 @@ import android.net.wifi.aware.WifiAwareChannelInfo; import android.net.wifi.aware.WifiAwareDataPathSecurityConfig; import android.net.wifi.aware.WifiAwareManager; import android.net.wifi.aware.WifiAwareNetworkSpecifier; +import android.net.wifi.rtt.RangingResult; import android.net.wifi.util.HexEncoding; import android.os.Bundle; import android.os.Handler; @@ -281,6 +282,7 @@ public class WifiAwareStateManager implements WifiAwareShellCommand.DelegatedShe private static final int NOTIFICATION_TYPE_ON_BOOTSTRAPPING_REQUEST = 316; private static final int NOTIFICATION_TYPE_ON_BOOTSTRAPPING_CONFIRM = 317; private static final int NOTIFICATION_TYPE_ON_SUSPENSION_MODE_CHANGED = 318; + private static final int NOTIFICATION_TYPE_RANGING_RESULTS = 319; private static final SparseArray<String> sSmToString = MessageUtils.findMessageNames( new Class[]{WifiAwareStateManager.class}, @@ -717,41 +719,39 @@ public class WifiAwareStateManager implements WifiAwareShellCommand.DelegatedShe } if (action.equals(Intent.ACTION_SCREEN_ON) || action.equals(Intent.ACTION_SCREEN_OFF)) { - reconfigure(); + mHandler.post(() -> reconfigure()); } if (action.equals(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)) { - reconfigure(); + mHandler.post(() -> reconfigure()); } } }, - intentFilter, - null, - mHandler); + intentFilter); intentFilter = new IntentFilter(); intentFilter.addAction(LocationManager.MODE_CHANGED_ACTION); - mContext.registerReceiver( + mContext.registerReceiverForAllUsers( new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (mVerboseLoggingEnabled) { Log.v(TAG, "onReceive: MODE_CHANGED_ACTION: intent=" + intent); } - if (mWifiPermissionsUtil.isLocationModeEnabled()) { - enableUsage(); - } else { - if (SdkLevel.isAtLeastT()) { - handleLocationModeDisabled(); + mHandler.post(() -> { + if (mWifiPermissionsUtil.isLocationModeEnabled()) { + enableUsage(); } else { - disableUsage(false); + if (SdkLevel.isAtLeastT()) { + handleLocationModeDisabled(); + } else { + disableUsage(false); + } } - } + }); } }, - intentFilter, - null, - mHandler); + intentFilter, null, null); intentFilter = new IntentFilter(); intentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); @@ -767,18 +767,18 @@ public class WifiAwareStateManager implements WifiAwareShellCommand.DelegatedShe WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED; - if (isEnabled) { - enableUsage(); - } else { - if (!isD2dAllowedWhenStaDisabled()) { - disableUsage(false); + mHandler.post(() -> { + if (isEnabled) { + enableUsage(); + } else { + if (!isD2dAllowedWhenStaDisabled()) { + disableUsage(false); + } } - } + }); } }, - intentFilter, - null, - mHandler); + intentFilter); mSettingsConfigStore.registerChangeListener(D2D_ALLOWED_WHEN_INFRA_STA_DISABLED, (key, value) -> { // Check setting & wifi enabled status only when feature is supported. @@ -1721,6 +1721,18 @@ public class WifiAwareStateManager implements WifiAwareShellCommand.DelegatedShe } /** + * Place a callback request on the state machine queue: update vendor + * capabilities of the Aware stack. + */ + public void onRangingResults(List<RangingResult> rangingResults, int sessionId) { + Message msg = mSm.obtainMessage(MESSAGE_TYPE_NOTIFICATION); + msg.arg1 = NOTIFICATION_TYPE_RANGING_RESULTS; + msg.obj = rangingResults; + msg.getData().putInt(MESSAGE_BUNDLE_KEY_SESSION_ID, sessionId); + mSm.sendMessage(msg); + } + + /** * Places a callback request on the state machine queue: data-path interface creation command * completed. */ @@ -2822,6 +2834,11 @@ public class WifiAwareStateManager implements WifiAwareShellCommand.DelegatedShe onSuspensionModeChangedLocal(isSuspended); break; } + case NOTIFICATION_TYPE_RANGING_RESULTS: { + int sessionId = msg.getData().getInt(MESSAGE_BUNDLE_KEY_SESSION_ID); + onRangingResultsReceivedLocal((List<RangingResult>) msg.obj, sessionId); + break; + } default: Log.wtf(TAG, "processNotification: this isn't a NOTIFICATION -- msg=" + msg); } @@ -5175,6 +5192,21 @@ public class WifiAwareStateManager implements WifiAwareShellCommand.DelegatedShe mAwareMetrics.recordEnableAware(); } + private void onRangingResultsReceivedLocal(List<RangingResult> rangingResults, + int pubSubId) { + if (mVdbg) { + Log.v(TAG, + "onRangingResultsReceivedNotification: pubSubId=" + pubSubId); + } + Pair<WifiAwareClientState, WifiAwareDiscoverySessionState> data = + getClientSessionForPubSubId(pubSubId); + if (data == null) { + Log.e(TAG, "onRangingResultsReceivedLocal: no session found for pubSubId=" + pubSubId); + return; + } + data.second.onRangingResultsReceived(rangingResults); + } + private void onClusterChangeLocal(int clusterEventType, byte[] clusterId) { mClusterId = clusterId; mClusterEventType = clusterEventType; diff --git a/service/java/com/android/server/wifi/coex/CoexUtils.java b/service/java/com/android/server/wifi/coex/CoexUtils.java index 786fe785b3..35d988bb5b 100644 --- a/service/java/com/android/server/wifi/coex/CoexUtils.java +++ b/service/java/com/android/server/wifi/coex/CoexUtils.java @@ -650,8 +650,9 @@ public class CoexUtils { public CoexCellChannel(@Annotation.NetworkType int rat, int band, int downlinkFreqKhz, int downlinkBandwidthKhz, int uplinkFreqKhz, int uplinkBandwidthKhz, int subId) { - if (band < 1 || band > 261) { - Log.wtf(TAG, "Band is " + band + " but should be a value from 1 to 261"); + if ((band < 1 || band > 261) && band != PhysicalChannelConfig.BAND_UNKNOWN) { + Log.wtf(TAG, "Band is " + band + " but should be a value from 1 to 261" + + " or PhysicalChannelConfig.BAND_UNKNOWN"); } if (downlinkFreqKhz < 0 && downlinkFreqKhz != PhysicalChannelConfig.FREQUENCY_UNKNOWN) { Log.wtf(TAG, "Downlink frequency is " + downlinkFreqKhz + " but should be >= 0" diff --git a/service/java/com/android/server/wifi/hal/IWifiChip.java b/service/java/com/android/server/wifi/hal/IWifiChip.java index 7c8235c1db..b421124152 100644 --- a/service/java/com/android/server/wifi/hal/IWifiChip.java +++ b/service/java/com/android/server/wifi/hal/IWifiChip.java @@ -58,10 +58,14 @@ public interface IWifiChip { * * @param vendorData List of {@link OuiKeyedData} containing vendor-provided * configuration data. Empty list indicates no vendor data. + * @param isUsingMultiLinkOperation whether the bridged AP is using multi-links + * operation soft ap. + * * @return {@link WifiApIface} object, or null if a failure occurred. */ @Nullable - WifiApIface createBridgedApIface(@NonNull List<OuiKeyedData> vendorData); + WifiApIface createBridgedApIface(@NonNull List<OuiKeyedData> vendorData, + boolean isUsingMultiLinkOperation); /** * Create a NAN interface on the chip. diff --git a/service/java/com/android/server/wifi/hal/WifiChip.java b/service/java/com/android/server/wifi/hal/WifiChip.java index 557573f207..45c6de2a07 100644 --- a/service/java/com/android/server/wifi/hal/WifiChip.java +++ b/service/java/com/android/server/wifi/hal/WifiChip.java @@ -556,16 +556,17 @@ public class WifiChip { } /** - * See comments for {@link IWifiChip#createBridgedApIface(List)} + * See comments for {@link IWifiChip#createBridgedApIface(List, boolean)} */ @Nullable - public WifiApIface createBridgedApIface(@NonNull List<OuiKeyedData> vendorData) { + public WifiApIface createBridgedApIface(@NonNull List<OuiKeyedData> vendorData, + boolean isUsingMultiLinkOperation) { if (vendorData == null) { Log.e(TAG, "createBridgedApIface received null vendorData"); return null; } return validateAndCall("createBridgedApIface", null, - () -> mWifiChip.createBridgedApIface(vendorData)); + () -> mWifiChip.createBridgedApIface(vendorData, isUsingMultiLinkOperation)); } /** diff --git a/service/java/com/android/server/wifi/hal/WifiChipAidlImpl.java b/service/java/com/android/server/wifi/hal/WifiChipAidlImpl.java index 6ce53df724..1fe36ded44 100644 --- a/service/java/com/android/server/wifi/hal/WifiChipAidlImpl.java +++ b/service/java/com/android/server/wifi/hal/WifiChipAidlImpl.java @@ -23,8 +23,6 @@ import static android.hardware.wifi.WifiChannelWidthInMhz.WIDTH_80; import static android.hardware.wifi.WifiChannelWidthInMhz.WIDTH_80P80; import static android.net.wifi.CoexUnsafeChannel.POWER_CAP_NONE; -import static com.android.server.wifi.util.GeneralUtil.getCapabilityIndex; - import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; @@ -32,6 +30,7 @@ import android.hardware.wifi.AfcChannelAllowance; import android.hardware.wifi.AvailableAfcChannelInfo; import android.hardware.wifi.AvailableAfcFrequencyInfo; import android.hardware.wifi.IWifiApIface; +import android.hardware.wifi.IWifiChip.ApIfaceParams; import android.hardware.wifi.IWifiChip.ChannelCategoryMask; import android.hardware.wifi.IWifiChip.CoexRestriction; import android.hardware.wifi.IWifiChip.FeatureSetMask; @@ -134,7 +133,11 @@ public class WifiChipAidlImpl implements IWifiChip { try { if (!checkIfaceAndLogFailure(methodStr)) return null; IWifiApIface iface; - if (WifiHalAidlImpl.isServiceVersionAtLeast(2) && !vendorData.isEmpty()) { + if (WifiHalAidlImpl.isServiceVersionAtLeast(3)) { + iface = mWifiChip.createApOrBridgedApIfaceWithParams(prepareApIfaceParams( + IfaceConcurrencyType.AP, vendorData, + false /* usesMlo */)); + } else if (WifiHalAidlImpl.isServiceVersionAtLeast(2) && !vendorData.isEmpty()) { android.hardware.wifi.common.OuiKeyedData[] halVendorData = HalAidlUtil.frameworkToHalOuiKeyedDataList(vendorData); iface = mWifiChip.createApOrBridgedApIface( @@ -152,18 +155,34 @@ public class WifiChipAidlImpl implements IWifiChip { } } + private ApIfaceParams prepareApIfaceParams(int ifaceType, + @NonNull List<OuiKeyedData> vendorData, boolean usesMlo) + throws IllegalArgumentException { + ApIfaceParams ifaceParams = new ApIfaceParams(); + ifaceParams.ifaceType = ifaceType; + ifaceParams.vendorData = + HalAidlUtil.frameworkToHalOuiKeyedDataList(vendorData); + ifaceParams.usesMlo = usesMlo; + return ifaceParams; + } + /** * See comments for {@link IWifiChip#createBridgedApIface(List)} */ @Override @Nullable - public WifiApIface createBridgedApIface(@NonNull List<OuiKeyedData> vendorData) { + public WifiApIface createBridgedApIface(@NonNull List<OuiKeyedData> vendorData, + boolean isUsingMultiLinkOperation) { final String methodStr = "createBridgedApIface"; synchronized (mLock) { try { if (!checkIfaceAndLogFailure(methodStr)) return null; IWifiApIface iface; - if (WifiHalAidlImpl.isServiceVersionAtLeast(2) && !vendorData.isEmpty()) { + if (WifiHalAidlImpl.isServiceVersionAtLeast(3)) { + iface = mWifiChip.createApOrBridgedApIfaceWithParams(prepareApIfaceParams( + IfaceConcurrencyType.AP_BRIDGED, vendorData, + isUsingMultiLinkOperation)); + } else if (WifiHalAidlImpl.isServiceVersionAtLeast(2) && !vendorData.isEmpty()) { android.hardware.wifi.common.OuiKeyedData[] halVendorData = HalAidlUtil.frameworkToHalOuiKeyedDataList(vendorData); iface = mWifiChip.createApOrBridgedApIface( @@ -1596,25 +1615,31 @@ public class WifiChipAidlImpl implements IWifiChip { protected static BitSet halToFrameworkChipFeatureSet(long halFeatureSet) { BitSet features = new BitSet(); if (bitmapContains(halFeatureSet, FeatureSetMask.SET_TX_POWER_LIMIT)) { - features.set(getCapabilityIndex(WifiManager.WIFI_FEATURE_TX_POWER_LIMIT)); + features.set(WifiManager.WIFI_FEATURE_TX_POWER_LIMIT); } if (bitmapContains(halFeatureSet, FeatureSetMask.D2D_RTT)) { - features.set(getCapabilityIndex(WifiManager.WIFI_FEATURE_D2D_RTT)); + features.set(WifiManager.WIFI_FEATURE_D2D_RTT); } if (bitmapContains(halFeatureSet, FeatureSetMask.D2AP_RTT)) { - features.set(getCapabilityIndex(WifiManager.WIFI_FEATURE_D2AP_RTT)); + features.set(WifiManager.WIFI_FEATURE_D2AP_RTT); } if (bitmapContains(halFeatureSet, FeatureSetMask.SET_LATENCY_MODE)) { - features.set(getCapabilityIndex(WifiManager.WIFI_FEATURE_LOW_LATENCY)); + features.set(WifiManager.WIFI_FEATURE_LOW_LATENCY); } if (bitmapContains(halFeatureSet, FeatureSetMask.P2P_RAND_MAC)) { - features.set(getCapabilityIndex(WifiManager.WIFI_FEATURE_P2P_RAND_MAC)); + features.set(WifiManager.WIFI_FEATURE_P2P_RAND_MAC); } if (bitmapContains(halFeatureSet, FeatureSetMask.WIGIG)) { - features.set(getCapabilityIndex(WifiManager.WIFI_FEATURE_INFRA_60G)); + features.set(WifiManager.WIFI_FEATURE_INFRA_60G); } if (bitmapContains(halFeatureSet, FeatureSetMask.T2LM_NEGOTIATION)) { - features.set(getCapabilityIndex(WifiManager.WIFI_FEATURE_T2LM_NEGOTIATION)); + features.set(WifiManager.WIFI_FEATURE_T2LM_NEGOTIATION); + } + if (bitmapContains(halFeatureSet, FeatureSetMask.MLO_SAP)) { + features.set(WifiManager.WIFI_FEATURE_SOFTAP_MLO); + } + if (bitmapContains(halFeatureSet, FeatureSetMask.MULTIPLE_MLD_ON_SAP)) { + features.set(WifiManager.WIFI_FEATURE_MULTIPLE_MLD_ON_SAP); } return features; } diff --git a/service/java/com/android/server/wifi/hal/WifiChipHidlImpl.java b/service/java/com/android/server/wifi/hal/WifiChipHidlImpl.java index 505b6545d4..f7a977958d 100644 --- a/service/java/com/android/server/wifi/hal/WifiChipHidlImpl.java +++ b/service/java/com/android/server/wifi/hal/WifiChipHidlImpl.java @@ -23,8 +23,6 @@ import static android.hardware.wifi.V1_6.WifiChannelWidthInMhz.WIDTH_80; import static android.hardware.wifi.V1_6.WifiChannelWidthInMhz.WIDTH_80P80; import static android.net.wifi.CoexUnsafeChannel.POWER_CAP_NONE; -import static com.android.server.wifi.util.GeneralUtil.getCapabilityIndex; - import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; @@ -122,7 +120,8 @@ public class WifiChipHidlImpl implements IWifiChip { */ @Override @Nullable - public WifiApIface createBridgedApIface(@NonNull List<OuiKeyedData> vendorData) { + public WifiApIface createBridgedApIface(@NonNull List<OuiKeyedData> vendorData, + boolean isUsingMultiLinkOperation) { String methodStr = "createBridgedApIface"; return validateAndCall(methodStr, null, () -> createBridgedApIfaceInternal(methodStr)); @@ -1750,7 +1749,7 @@ public class WifiChipHidlImpl implements IWifiChip { } } - private static final long[][] sChipFeatureCapabilityTranslation = { + private static final int[][] sChipFeatureCapabilityTranslation = { {WifiManager.WIFI_FEATURE_TX_POWER_LIMIT, android.hardware.wifi.V1_1.IWifiChip.ChipCapabilityMask.SET_TX_POWER_LIMIT }, @@ -1766,7 +1765,7 @@ public class WifiChipHidlImpl implements IWifiChip { * Translation table used by getSupportedFeatureSet for translating IWifiChip caps for * additional capabilities introduced in V1.5 */ - private static final long[][] sChipFeatureCapabilityTranslation15 = { + private static final int[][] sChipFeatureCapabilityTranslation15 = { {WifiManager.WIFI_FEATURE_INFRA_60G, android.hardware.wifi.V1_5.IWifiChip.ChipCapabilityMask.WIGIG } @@ -1776,7 +1775,7 @@ public class WifiChipHidlImpl implements IWifiChip { * Translation table used by getSupportedFeatureSet for translating IWifiChip caps for * additional capabilities introduced in V1.3 */ - private static final long[][] sChipFeatureCapabilityTranslation13 = { + private static final int[][] sChipFeatureCapabilityTranslation13 = { {WifiManager.WIFI_FEATURE_LOW_LATENCY, android.hardware.wifi.V1_3.IWifiChip.ChipCapabilityMask.SET_LATENCY_MODE }, @@ -1797,7 +1796,7 @@ public class WifiChipHidlImpl implements IWifiChip { BitSet features = new BitSet(); for (int i = 0; i < sChipFeatureCapabilityTranslation.length; i++) { if ((capabilities & sChipFeatureCapabilityTranslation[i][1]) != 0) { - features.set(getCapabilityIndex(sChipFeatureCapabilityTranslation[i][0])); + features.set(sChipFeatureCapabilityTranslation[i][0]); } } return features; @@ -1817,7 +1816,7 @@ public class WifiChipHidlImpl implements IWifiChip { // Next collect features for V1_5 version for (int i = 0; i < sChipFeatureCapabilityTranslation15.length; i++) { if ((capabilities & sChipFeatureCapabilityTranslation15[i][1]) != 0) { - features.set(getCapabilityIndex(sChipFeatureCapabilityTranslation15[i][0])); + features.set(sChipFeatureCapabilityTranslation15[i][0]); } } return features; @@ -1837,7 +1836,7 @@ public class WifiChipHidlImpl implements IWifiChip { // Next collect features for V1_3 version for (int i = 0; i < sChipFeatureCapabilityTranslation13.length; i++) { if ((capabilities & sChipFeatureCapabilityTranslation13[i][1]) != 0) { - features.set(getCapabilityIndex(sChipFeatureCapabilityTranslation13[i][0])); + features.set(sChipFeatureCapabilityTranslation13[i][0]); } } return features; diff --git a/service/java/com/android/server/wifi/hal/WifiNanIface.java b/service/java/com/android/server/wifi/hal/WifiNanIface.java index 9fcf9e2038..8a2a8f8ec1 100644 --- a/service/java/com/android/server/wifi/hal/WifiNanIface.java +++ b/service/java/com/android/server/wifi/hal/WifiNanIface.java @@ -26,6 +26,7 @@ import android.net.wifi.aware.PublishConfig; import android.net.wifi.aware.SubscribeConfig; import android.net.wifi.aware.WifiAwareChannelInfo; import android.net.wifi.aware.WifiAwareDataPathSecurityConfig; +import android.net.wifi.rtt.RangingResult; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; @@ -857,5 +858,13 @@ public class WifiNanIface implements WifiHal.WifiInterface { * the suspension mode */ void eventSuspensionModeChanged(boolean isSuspended); + + /** + * Invoked when ranging results are available. + * @param rangingResults Rtt results data. + * @param sessionId ID of an active publish or subscribe discovery session. + */ + void notifyRangingResults(ArrayList<RangingResult> rangingResults, byte sessionId); + } } diff --git a/service/java/com/android/server/wifi/hal/WifiNanIfaceAidlImpl.java b/service/java/com/android/server/wifi/hal/WifiNanIfaceAidlImpl.java index ecefe9bf33..4b329f2f18 100644 --- a/service/java/com/android/server/wifi/hal/WifiNanIfaceAidlImpl.java +++ b/service/java/com/android/server/wifi/hal/WifiNanIfaceAidlImpl.java @@ -55,6 +55,7 @@ import android.hardware.wifi.NanRespondToPairingIndicationRequest; import android.hardware.wifi.NanSubscribeRequest; import android.hardware.wifi.NanTransmitFollowupRequest; import android.hardware.wifi.NanTxType; +import android.hardware.wifi.WifiChannelInfo; import android.net.MacAddress; import android.net.wifi.aware.AwarePairingConfig; import android.net.wifi.aware.ConfigRequest; @@ -866,6 +867,7 @@ public class WifiNanIfaceAidlImpl implements IWifiNanIface { req.baseConfigs.enableSessionSuspendability = SdkLevel.isAtLeastU() && publishConfig.isSuspendable(); + req.rangingResultsRequired = publishConfig.mEnablePeriodicRangingResults; req.publishType = publishConfig.mPublishType; req.txType = NanTxType.BROADCAST; req.pairingConfig = createAidlPairingConfig(publishConfig.getPairingConfig()); @@ -953,6 +955,21 @@ public class WifiNanIfaceAidlImpl implements IWifiNanIface { HalAidlUtil.frameworkToHalOuiKeyedDataList(subscribeConfig.getVendorData()); } + if (subscribeConfig.mPeriodicRangingEnabled) { + req.baseConfigs.configRangingIndications |= + NanRangingIndication.CONTINUOUS_INDICATION_MASK; + req.baseConfigs.rangingIntervalMs = subscribeConfig.mPeriodicRangingInterval; + req.baseConfigs.rttBurstSize = subscribeConfig.mRttBurstSize; + req.baseConfigs.preamble = WifiRttControllerAidlImpl + .frameworkToHalResponderPreamble(subscribeConfig.mPreamble); + req.baseConfigs.channelInfo = new WifiChannelInfo(); + req.baseConfigs.channelInfo.width = WifiRttControllerAidlImpl + .frameworkToHalChannelWidth(subscribeConfig.mChannelWidth); + req.baseConfigs.channelInfo.centerFreq = subscribeConfig.mFrequencyMhz; + req.baseConfigs.channelInfo.centerFreq0 = subscribeConfig.mCenterFrequency0Mhz; + req.baseConfigs.channelInfo.centerFreq1 = subscribeConfig.mCenterFrequency1Mhz; + } + return req; } diff --git a/service/java/com/android/server/wifi/hal/WifiNanIfaceCallbackAidlImpl.java b/service/java/com/android/server/wifi/hal/WifiNanIfaceCallbackAidlImpl.java index f3c410badf..125d9a37a6 100644 --- a/service/java/com/android/server/wifi/hal/WifiNanIfaceCallbackAidlImpl.java +++ b/service/java/com/android/server/wifi/hal/WifiNanIfaceCallbackAidlImpl.java @@ -47,14 +47,19 @@ import android.hardware.wifi.NanStatus; import android.hardware.wifi.NanStatusCode; import android.hardware.wifi.NanSuspensionModeChangeInd; import android.hardware.wifi.NpkSecurityAssociation; +import android.hardware.wifi.RttResult; +import android.hardware.wifi.RttType; import android.net.MacAddress; import android.net.wifi.OuiKeyedData; import android.net.wifi.aware.AwarePairingConfig; import android.net.wifi.aware.Characteristics; import android.net.wifi.aware.WifiAwareChannelInfo; +import android.net.wifi.rtt.RangingResult; +import android.net.wifi.rtt.ResponderLocation; import android.net.wifi.util.HexEncoding; import android.util.Log; +import com.android.modules.utils.build.SdkLevel; import com.android.server.wifi.aware.Capabilities; import com.android.server.wifi.aware.PairingConfigManager.PairingSecurityAssociationInfo; import com.android.server.wifi.hal.WifiNanIface.NanClusterEventType; @@ -74,6 +79,11 @@ public class WifiNanIfaceCallbackAidlImpl extends IWifiNanIfaceEventCallback.Stu private boolean mVerboseLoggingEnabled; private final WifiNanIfaceAidlImpl mWifiNanIface; + private static final int SUPPORTED_RX_CHAINS_1 = 1; + private static final int SUPPORTED_RX_CHAINS_2 = 2; + private static final int SUPPORTED_RX_CHAINS_3 = 3; + private static final int SUPPORTED_RX_CHAINS_4 = 4; + public WifiNanIfaceCallbackAidlImpl(WifiNanIfaceAidlImpl wifiNanIface) { mWifiNanIface = wifiNanIface; @@ -312,6 +322,18 @@ public class WifiNanIfaceCallbackAidlImpl extends IWifiNanIfaceEventCallback.Stu } @Override + public void notifyRangingResults(RttResult[] results, byte sessionId) { + if (!checkFrameworkCallback()) return; + if (mVerboseLoggingEnabled) { + int numResults = results != null ? results.length : -1; + Log.v(TAG, "notifyRangingResults: number of ranging results: " + numResults + + ", session_id=" + sessionId); + } + ArrayList<RangingResult> rangingResults = convertToFrameworkRangingResults(results); + mWifiNanIface.getFrameworkCallback().notifyRangingResults(rangingResults, sessionId); + } + + @Override public void notifyTerminateDataPathResponse(char id, NanStatus status) { if (!checkFrameworkCallback()) return; if (mVerboseLoggingEnabled) { @@ -736,6 +758,11 @@ public class WifiNanIfaceCallbackAidlImpl extends IWifiNanIfaceEventCallback.Stu frameworkCapabilities.isSuspensionSupported = capabilities.supportsSuspension; frameworkCapabilities.is6gSupported = capabilities.supports6g; frameworkCapabilities.isHeSupported = capabilities.supportsHe; + frameworkCapabilities.isPeriodicRangingSupported = capabilities.supportsPeriodicRanging; + frameworkCapabilities.maxSupportedRangingPktBandWidth = WifiRttControllerAidlImpl + .halToFrameworkChannelBandwidth(capabilities.maxSupportedBandwidth); + frameworkCapabilities.maxSupportedRxChains = + toFrameworkChainsSupported(capabilities.maxNumRxChainsSupported); return frameworkCapabilities; } @@ -771,6 +798,21 @@ public class WifiNanIfaceCallbackAidlImpl extends IWifiNanIfaceEventCallback.Stu return publicCipherSuites; } + private static int toFrameworkChainsSupported(int supportedRxChains) { + switch(supportedRxChains) { + case SUPPORTED_RX_CHAINS_1: + return Characteristics.SUPPORTED_RX_CHAINS_1; + case SUPPORTED_RX_CHAINS_2: + return Characteristics.SUPPORTED_RX_CHAINS_2; + case SUPPORTED_RX_CHAINS_3: + return Characteristics.SUPPORTED_RX_CHAINS_3; + case SUPPORTED_RX_CHAINS_4: + return Characteristics.SUPPORTED_RX_CHAINS_4; + default: + return Characteristics.SUPPORTED_RX_CHAINS_UNSPECIFIED; + } + } + private static String statusString(NanStatus status) { if (status == null) { return "status=null"; @@ -808,4 +850,49 @@ public class WifiNanIfaceCallbackAidlImpl extends IWifiNanIfaceEventCallback.Stu } return true; } + + private ArrayList<RangingResult> convertToFrameworkRangingResults(RttResult[] halResults) { + ArrayList<RangingResult> rangingResults = new ArrayList(); + for (RttResult rttResult : halResults) { + if (rttResult == null) continue; + byte[] lci = rttResult.lci.data; + byte[] lcr = rttResult.lcr.data; + ResponderLocation responderLocation; + try { + responderLocation = new ResponderLocation(lci, lcr); + if (!responderLocation.isValid()) { + responderLocation = null; + } + } catch (Exception e) { + responderLocation = null; + Log.e(TAG, "ResponderLocation: lci/lcr parser failed exception -- " + e); + } + if (rttResult.successNumber <= 1 && rttResult.distanceSdInMm != 0) { + if (mVerboseLoggingEnabled) { + Log.w(TAG, "postProcessResults: non-zero distance stdev with 0||1 num " + + "samples!? result=" + rttResult); + } + rttResult.distanceSdInMm = 0; + } + RangingResult.Builder resultBuilder = new RangingResult.Builder() + .setStatus(WifiRttControllerAidlImpl.halToFrameworkRttStatus(rttResult.status)) + .setMacAddress(MacAddress.fromBytes(rttResult.addr)) + .setDistanceMm(rttResult.distanceInMm) + .setDistanceStdDevMm(rttResult.distanceSdInMm) + .setRssi(rttResult.rssi / -2) + .setNumAttemptedMeasurements(rttResult.numberPerBurstPeer) + .setNumSuccessfulMeasurements(rttResult.successNumber) + .setUnverifiedResponderLocation(responderLocation) + .setRangingTimestampMillis( + rttResult.timeStampInUs / WifiRttController.CONVERSION_US_TO_MS) + .set80211mcMeasurement(rttResult.type == RttType.TWO_SIDED_11MC); + if (SdkLevel.isAtLeastV() && WifiHalAidlImpl.isServiceVersionAtLeast(2) + && rttResult.vendorData != null) { + resultBuilder.setVendorData( + HalAidlUtil.halToFrameworkOuiKeyedDataList(rttResult.vendorData)); + } + rangingResults.add(resultBuilder.build()); + } + return rangingResults; + } } diff --git a/service/java/com/android/server/wifi/hal/WifiRttController.java b/service/java/com/android/server/wifi/hal/WifiRttController.java index 9061a01515..f8eb6280d4 100644 --- a/service/java/com/android/server/wifi/hal/WifiRttController.java +++ b/service/java/com/android/server/wifi/hal/WifiRttController.java @@ -19,7 +19,10 @@ package com.android.server.wifi.hal; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.hardware.wifi.Akm; +import android.hardware.wifi.CipherSuite; import android.net.MacAddress; +import android.net.wifi.rtt.PasnConfig; import android.net.wifi.rtt.RangingRequest; import android.net.wifi.rtt.RangingResult; import android.util.Log; @@ -116,6 +119,16 @@ public class WifiRttController { public boolean ntbInitiatorSupported; // Whether IEEE 802.11az Non-Trigger-based (non-TB) responder mode is supported. public boolean ntbResponderSupported; + // Bitmap of AKM values indicating the set of supported AKMs. + public @PasnConfig.AkmType int akmsSupported; + // Bitmap of cipher values indicating the set of supported pairwise cipher suites. + public @PasnConfig.Cipher int cipherSuitesSupported; + // Whether secure HE-LTF (Long Training Field) is supported + public boolean secureHeLtfSupported; + // Whether ranging frame protection is supported + public boolean rangingFrameProtectionSupported; + // Maximum supported secure HE-LTF protocol version + public int maxSupportedSecureHeLtfProtocolVersion; public Capabilities() { } @@ -166,9 +179,66 @@ public class WifiRttController { azBwSupported = rttHalCapabilities.azBwSupport; ntbInitiatorSupported = rttHalCapabilities.ntbInitiatorSupported; ntbResponderSupported = rttHalCapabilities.ntbResponderSupported; + secureHeLtfSupported = rttHalCapabilities.secureHeLtfSupported; + rangingFrameProtectionSupported = rttHalCapabilities.rangingFrameProtectionSupported; + maxSupportedSecureHeLtfProtocolVersion = + rttHalCapabilities.maxSupportedSecureHeLtfProtocolVersion; + akmsSupported = convertAkmsToFramework(rttHalCapabilities.akmsSupported); + cipherSuitesSupported = convertCiphersToFramework( + rttHalCapabilities.cipherSuitesSupported); } } + @PasnConfig.Cipher + private static int convertCiphersToFramework(long ciphersSupported) { + @PasnConfig.Cipher int ciphers = PasnConfig.CIPHER_NONE; + if ((ciphersSupported & CipherSuite.GCMP_256) != 0) { + ciphers |= PasnConfig.CIPHER_GCMP_256; + } + if ((ciphersSupported & CipherSuite.GCMP_128) != 0) { + ciphers |= PasnConfig.CIPHER_GCMP_128; + } + if ((ciphersSupported & CipherSuite.CCMP_256) != 0) { + ciphers |= PasnConfig.CIPHER_CCMP_256; + } + if ((ciphersSupported & CipherSuite.CCMP_128) != 0) { + ciphers |= PasnConfig.CIPHER_CCMP_128; + } + return ciphers; + } + + @PasnConfig.AkmType + private static int convertAkmsToFramework(long akmsSupported) { + @PasnConfig.AkmType int akms = PasnConfig.AKM_NONE; + if ((akmsSupported & Akm.FT_EAP_SHA384) != 0) { + akms |= PasnConfig.AKM_FT_EAP_SHA384; + } + if ((akmsSupported & Akm.FILS_EAP_SHA384) != 0) { + akms |= PasnConfig.AKM_FILS_EAP_SHA384; + } + if ((akmsSupported & Akm.FILS_EAP_SHA256) != 0) { + akms |= PasnConfig.AKM_FILS_EAP_SHA256; + } + if ((akmsSupported & Akm.FT_EAP_SHA256) != 0) { + akms |= PasnConfig.AKM_FT_EAP_SHA256; + } + if ((akmsSupported & Akm.FT_PSK_SHA384) != 0) { + akms |= PasnConfig.AKM_FT_PSK_SHA384; + } + if ((akmsSupported & Akm.FT_PSK_SHA256) != 0) { + akms |= PasnConfig.AKM_FT_PSK_SHA256; + } + if ((akmsSupported & Akm.SAE) != 0) { + akms |= PasnConfig.AKM_SAE; + } + if ((akmsSupported & Akm.PASN) != 0) { + akms |= PasnConfig.AKM_PASN; + } + return akms; + } + + + /** * Callback to receive ranging results. */ @@ -276,4 +346,51 @@ public class WifiRttController { mWifiRttController.dump(pw); } } + + /** + * Get optimum burst duration corresponding to a burst size. + * + * IEEE 802.11 spec, Section 11.21.6.3 Fine timing measurement procedure negotiation, burst + * duration is defined as + * + * Burst duration = (N_FTMPB * (K + 1)) – 1) * T_MDFTM + T_FTM + aSIFSTime + T_Ack, where + * - N_FTMPB is the value of the FTMs Per Burst subfield + * - K is the maximum number of Fine Timing Measurement frame retransmissions the + * responding STA might attempt + * - T_MDFTM is the duration indicated by the Min Delta FTM subfield of the Fine Timing + * Measurement Parameters field of the initial Fine Timing Measurement frame (FTM_1) + * - T_FTM is the duration of the initial Fine Timing Measurement frame if the FTMs Per Burst + * subfield of the Fine Timing Measurement Parameters field of FTM_1 is set to 1, + * and the duration of the non-initial Fine Timing Measurement frame otherwise + * T_Ack is the duration of the Ack frame expected as a response + * + * Since many of the parameters are dependent on the chip and the vendor software, framework is + * doing a simple conversion with experimented values. Vendor Software may override the burst + * duration with more optimal values. + * + * Section '9.4.2.167 Fine Timing Measurement Parameters element' defines Burst Duration + * subfield encoding as, + * +--------------------+ + * |Value| Represents | + * +--------------------+ + * | 0-1 | Reserved | + * | 2 | 250 us | + * | 3 | 500 us | + * | 4 | 1 ms | + * | 5 | 2 ms | + * | 6 | 4 ms | + * | 7 | 8 ms | + * | 8 | 16 ms | + * | 9 | 32 ms | + * | 10 | 64 ms | + * | 11 | 128 ms | + * |12-14| Reserved | + * | 15 | No Preference| + * +-----+--------------+ + */ + public static int getOptimumBurstDuration(int burstSize) { + if (burstSize <= 8) return 9; // 32 ms + if (burstSize <= 24) return 10; // 64 ms + return 11; // 128 ms + } } diff --git a/service/java/com/android/server/wifi/hal/WifiRttControllerAidlImpl.java b/service/java/com/android/server/wifi/hal/WifiRttControllerAidlImpl.java index ba32e3f93b..6611e60562 100644 --- a/service/java/com/android/server/wifi/hal/WifiRttControllerAidlImpl.java +++ b/service/java/com/android/server/wifi/hal/WifiRttControllerAidlImpl.java @@ -18,6 +18,7 @@ package com.android.server.wifi.hal; import android.annotation.NonNull; import android.annotation.Nullable; +import android.hardware.wifi.Akm; import android.hardware.wifi.IWifiRttControllerEventCallback; import android.hardware.wifi.RttBw; import android.hardware.wifi.RttCapabilities; @@ -25,6 +26,7 @@ import android.hardware.wifi.RttConfig; import android.hardware.wifi.RttPeerType; import android.hardware.wifi.RttPreamble; import android.hardware.wifi.RttResult; +import android.hardware.wifi.RttSecureConfig; import android.hardware.wifi.RttStatus; import android.hardware.wifi.RttType; import android.hardware.wifi.WifiChannelInfo; @@ -33,10 +35,12 @@ import android.hardware.wifi.common.OuiKeyedData; import android.net.MacAddress; import android.net.wifi.ScanResult; import android.net.wifi.WifiAnnotations; +import android.net.wifi.rtt.PasnConfig; import android.net.wifi.rtt.RangingRequest; import android.net.wifi.rtt.RangingResult; import android.net.wifi.rtt.ResponderConfig; import android.net.wifi.rtt.ResponderLocation; +import android.net.wifi.rtt.SecureRangingConfig; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.util.Log; @@ -45,6 +49,7 @@ import com.android.modules.utils.build.SdkLevel; import com.android.server.wifi.util.HalAidlUtil; import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -301,7 +306,17 @@ public class WifiRttControllerAidlImpl implements IWifiRttController { .set80211azInitiatorTxLtfRepetitionsCount(rttResult.i2rTxLtfRepetitionCount) .set80211azResponderTxLtfRepetitionsCount(rttResult.r2iTxLtfRepetitionCount) .set80211azNumberOfTxSpatialStreams(rttResult.numTxSpatialStreams) - .set80211azNumberOfRxSpatialStreams(rttResult.numRxSpatialStreams); + .set80211azNumberOfRxSpatialStreams(rttResult.numRxSpatialStreams) + .setPasnComebackAfterMillis(rttResult.pasnComebackAfterMillis) + .setRangingAuthenticated((rttResult.baseAkm & ~Akm.PASN) != 0) + .setRangingFrameProtected(rttResult.isRangingFrameProtectionEnabled) + .setSecureHeLtfEnabled(rttResult.isSecureLtfEnabled) + .setSecureHeLtfProtocolVersion(rttResult.secureHeLtfProtocolVersion); + + if (rttResult.pasnComebackCookie != null) { + resultBuilder.setPasnComebackCookie(rttResult.pasnComebackCookie); + } + if (SdkLevel.isAtLeastV() && WifiHalAidlImpl.isServiceVersionAtLeast(2) && rttResult.vendorData != null) { resultBuilder.setVendorData( @@ -312,7 +327,10 @@ public class WifiRttControllerAidlImpl implements IWifiRttController { return rangingResults; } - private static @WifiAnnotations.ChannelWidth int halToFrameworkChannelBandwidth( + /** + * AIDL Hal to framework mapping for Channel width + */ + public static @WifiAnnotations.ChannelWidth int halToFrameworkChannelBandwidth( @RttBw int packetBw) { switch (packetBw) { case RttBw.BW_20MHZ: @@ -330,7 +348,10 @@ public class WifiRttControllerAidlImpl implements IWifiRttController { } } - private static @WifiRttController.FrameworkRttStatus int halToFrameworkRttStatus( + /** + * AIDL Hal to framework mapping for RTT status + */ + public static @WifiRttController.FrameworkRttStatus int halToFrameworkRttStatus( int halStatus) { switch (halStatus) { case RttStatus.SUCCESS: @@ -391,53 +412,6 @@ public class WifiRttControllerAidlImpl implements IWifiRttController { } } - /** - * Get optimum burst duration corresponding to a burst size. - * - * IEEE 802.11 spec, Section 11.21.6.3 Fine timing measurement procedure negotiation, burst - * duration is defined as - * - * Burst duration = (N_FTMPB * (K + 1)) – 1) * T_MDFTM + T_FTM + aSIFSTime + T_Ack, where - * - N_FTMPB is the value of the FTMs Per Burst subfield - * - K is the maximum number of Fine Timing Measurement frame retransmissions the - * responding STA might attempt - * - T_MDFTM is the duration indicated by the Min Delta FTM subfield of the Fine Timing - * Measurement Parameters field of the initial Fine Timing Measurement frame (FTM_1) - * - T_FTM is the duration of the initial Fine Timing Measurement frame if the FTMs Per Burst - * subfield of the Fine Timing Measurement Parameters field of FTM_1 is set to 1, - * and the duration of the non-initial Fine Timing Measurement frame otherwise - * T_Ack is the duration of the Ack frame expected as a response - * - * Since many of the parameters are dependent on the chip and the vendor software, framework is - * doing a simple conversion with experimented values. Vendor Software may override the burst - * duration with more optimal values. - * - * Section '9.4.2.167 Fine Timing Measurement Parameters element' defines Burst Duration - * subfield encoding as, - * +--------------------+ - * |Value| Represents | - * +--------------------+ - * | 0-1 | Reserved | - * | 2 | 250 us | - * | 3 | 500 us | - * | 4 | 1 ms | - * | 5 | 2 ms | - * | 6 | 4 ms | - * | 7 | 8 ms | - * | 8 | 16 ms | - * | 9 | 32 ms | - * | 10 | 64 ms | - * | 11 | 128 ms | - * |12-14| Reserved | - * | 15 | No Preference| - * +-----+--------------+ - */ - private static int getOptimumBurstDuration(int burstSize) { - if (burstSize <= 8) return 9; // 32 ms - if (burstSize <= 24) return 10; // 64 ms - return 11; // 128 ms - } - private static RttConfig[] convertRangingRequestToRttConfigs(RangingRequest request, WifiRttController.Capabilities cap) { ArrayList<RttConfig> rttConfigs = new ArrayList<>(); @@ -504,7 +478,8 @@ public class WifiRttControllerAidlImpl implements IWifiRttController { config.numFramesPerBurst = request.mRttBurstSize; config.numRetriesPerRttFrame = 0; // irrelevant for 2-sided RTT config.numRetriesPerFtmr = 3; - config.burstDuration = getOptimumBurstDuration(request.mRttBurstSize); + config.burstDuration = WifiRttController.getOptimumBurstDuration( + request.mRttBurstSize); } else { // AP + all non-NAN requests config.mustRequestLci = true; config.mustRequestLcr = true; @@ -513,7 +488,8 @@ public class WifiRttControllerAidlImpl implements IWifiRttController { config.numFramesPerBurst = request.mRttBurstSize; config.numRetriesPerRttFrame = (config.type == RttType.TWO_SIDED ? 0 : 3); config.numRetriesPerFtmr = 3; - config.burstDuration = getOptimumBurstDuration(request.mRttBurstSize); + config.burstDuration = WifiRttController.getOptimumBurstDuration( + request.mRttBurstSize); if (cap != null) { // constrain parameters per device capabilities config.mustRequestLci = config.mustRequestLci && cap.lciSupported; @@ -521,7 +497,32 @@ public class WifiRttControllerAidlImpl implements IWifiRttController { config.bw = halRttChannelBandwidthCapabilityLimiter(config.bw, cap, config.type); config.preamble = halRttPreambleCapabilityLimiter(config.preamble, cap, - config.type); + config.type, responder.frequency); + } + + // Update secure ranging configuration + SecureRangingConfig secureConfig = responder.getSecureRangingConfig(); + @PasnConfig.AkmType int baseAkm = PasnConfig.AKM_NONE; + @PasnConfig.Cipher int cipherSuite = PasnConfig.CIPHER_NONE; + PasnConfig pasnConfig = null; + if (secureConfig != null && cap != null + && request.getSecurityMode() != RangingRequest.SECURITY_MODE_OPEN) { + pasnConfig = secureConfig.getPasnConfig(); + baseAkm = getBestBaseAkm(pasnConfig.getBaseAkms(), cap.akmsSupported); + cipherSuite = getBestCipherSuite(pasnConfig.getCiphers(), + cap.cipherSuitesSupported); + + } + if (baseAkm != PasnConfig.AKM_NONE && cipherSuite != PasnConfig.CIPHER_NONE) { + config.secureConfig = new RttSecureConfig(); + config.secureConfig.enableSecureHeLtf = secureConfig.isSecureHeLtfEnabled(); + config.secureConfig.enableRangingFrameProtection = true; + config.secureConfig.pasnConfig = new android.hardware.wifi.PasnConfig(); + config.secureConfig.pasnConfig.baseAkm = baseAkm; + config.secureConfig.pasnConfig.cipherSuite = cipherSuite; + config.secureConfig.pasnConfig.passphrase = + pasnConfig.getPassword().getBytes(StandardCharsets.UTF_8); + config.secureConfig.pasnComebackCookie = pasnConfig.getPasnComebackCookie(); } } } catch (IllegalArgumentException e) { @@ -539,6 +540,62 @@ public class WifiRttControllerAidlImpl implements IWifiRttController { return configArray; } + /** + * Get the best Cipher based on security. + */ + @PasnConfig.Cipher + private static int getBestCipherSuite(@PasnConfig.Cipher int requiredAkms, + @PasnConfig.Cipher int supportedAkms) { + int possibleAkms = requiredAkms & supportedAkms; + if ((possibleAkms & PasnConfig.CIPHER_GCMP_256) != 0) { + return PasnConfig.CIPHER_GCMP_256; + } + if ((possibleAkms & PasnConfig.CIPHER_GCMP_128) != 0) { + return PasnConfig.CIPHER_GCMP_128; + } + if ((possibleAkms & PasnConfig.CIPHER_CCMP_256) != 0) { + return PasnConfig.CIPHER_CCMP_256; + } + if ((possibleAkms & PasnConfig.CIPHER_CCMP_128) != 0) { + return PasnConfig.CIPHER_CCMP_128; + } + return PasnConfig.CIPHER_NONE; + } + + /** + * Get the best AKM based on security. + */ + @PasnConfig.AkmType + private static int getBestBaseAkm(@PasnConfig.AkmType int requiredAkms, + @PasnConfig.AkmType int supportedAkms) { + int possibleAkms = requiredAkms & supportedAkms; + if ((possibleAkms & PasnConfig.AKM_FT_EAP_SHA384) != 0) { + return PasnConfig.AKM_FT_EAP_SHA384; + } + if ((possibleAkms & PasnConfig.AKM_FILS_EAP_SHA384) != 0) { + return PasnConfig.AKM_FILS_EAP_SHA384; + } + if ((possibleAkms & PasnConfig.AKM_FILS_EAP_SHA256) != 0) { + return PasnConfig.AKM_FILS_EAP_SHA256; + } + if ((possibleAkms & PasnConfig.AKM_FT_EAP_SHA256) != 0) { + return PasnConfig.AKM_FT_EAP_SHA256; + } + if ((possibleAkms & PasnConfig.AKM_FT_PSK_SHA384) != 0) { + return PasnConfig.AKM_FT_PSK_SHA384; + } + if ((possibleAkms & PasnConfig.AKM_FT_PSK_SHA256) != 0) { + return PasnConfig.AKM_FT_PSK_SHA256; + } + if ((possibleAkms & PasnConfig.AKM_SAE) != 0) { + return PasnConfig.AKM_SAE; + } + if ((possibleAkms & PasnConfig.AKM_PASN) != 0) { + return PasnConfig.AKM_PASN; + } + return PasnConfig.AKM_NONE; + } + private static void validateBwAndPreambleCombination(int bw, int preamble) { if (bw <= RttBw.BW_20MHZ) { return; @@ -575,7 +632,10 @@ public class WifiRttControllerAidlImpl implements IWifiRttController { } } - private static int frameworkToHalChannelWidth(int responderChannelWidth) + /** + * Framework to AIDL Hal mapping for Channel Width + */ + public static int frameworkToHalChannelWidth(int responderChannelWidth) throws IllegalArgumentException { switch (responderChannelWidth) { case ResponderConfig.CHANNEL_WIDTH_20MHZ: @@ -616,7 +676,10 @@ public class WifiRttControllerAidlImpl implements IWifiRttController { } } - private static int frameworkToHalResponderPreamble(int responderPreamble) + /** + * Framework to AIDL Hal mapping for Preamble + */ + public static int frameworkToHalResponderPreamble(int responderPreamble) throws IllegalArgumentException { switch (responderPreamble) { case ResponderConfig.PREAMBLE_LEGACY: @@ -627,6 +690,8 @@ public class WifiRttControllerAidlImpl implements IWifiRttController { return RttPreamble.VHT; case ResponderConfig.PREAMBLE_HE: return RttPreamble.HE; + case ResponderConfig.PREAMBLE_EHT: + return RttPreamble.EHT; default: throw new IllegalArgumentException( "frameworkToHalResponderPreamble: bad " + responderPreamble); @@ -661,17 +726,33 @@ public class WifiRttControllerAidlImpl implements IWifiRttController { } /** - * Check whether the selected RTT preamble is supported by the device. - * If supported, return the requested preamble. - * If not supported, return the next "lower" preamble which is supported. - * If none, throw an IllegalArgumentException. + * Check whether the selected RTT preamble is supported by the device and the RTT type. + * <ul> + * <li>If supported, return the requested preamble. + * <li>If not supported, return the next "lower" preamble which is supported. + * <li>If none, throw an IllegalArgumentException. + * </ul> + * + * <p>Note: the halRttPreamble is a single bit flag from the HAL RttPreamble type. * - * Note: the halRttPreamble is a single bit flag from the HAL RttPreamble type. + * <p>Note: The IEEE 802.11mc is only compatible with HE and EHT when using the 6 GHz band. + * However, the IEEE 802.11az supports HE and EHT across all Wi-Fi bands (2.4GHz, 5 GHz, and + * 6 GHz). */ private static int halRttPreambleCapabilityLimiter(int halRttPreamble, - WifiRttController.Capabilities cap, @RttType int rttType) + WifiRttController.Capabilities cap, @RttType int rttType, int frequency) throws IllegalArgumentException { + // Note: requestedPreamble is only used for the error logging int requestedPreamble = halRttPreamble; + // Since RTT type is limited based on device capability, check preamble for any adjustment. + // The IEEE 802.11mc is only compatible with HE and EHT when using the 6 GHz band. So + // adjust the preamble accordingly. + if (rttType <= RttType.TWO_SIDED_11MC && !ScanResult.is6GHz(frequency)) { + if (halRttPreamble >= RttPreamble.HE) { + halRttPreamble = RttPreamble.VHT; + } + } + // Check device capability whether preamble is supported by the device, otherwise adjust it. int preambleSupported = (rttType == RttType.TWO_SIDED_11AZ_NTB) ? cap.azPreambleSupported : cap.preambleSupported; while ((halRttPreamble != 0) && ((halRttPreamble & preambleSupported) == 0)) { diff --git a/service/java/com/android/server/wifi/hal/WifiRttControllerHidlImpl.java b/service/java/com/android/server/wifi/hal/WifiRttControllerHidlImpl.java index cce8bb1372..4130c2248c 100644 --- a/service/java/com/android/server/wifi/hal/WifiRttControllerHidlImpl.java +++ b/service/java/com/android/server/wifi/hal/WifiRttControllerHidlImpl.java @@ -641,7 +641,8 @@ public class WifiRttControllerHidlImpl implements IWifiRttController { config.numFramesPerBurst = request.mRttBurstSize; config.numRetriesPerRttFrame = 0; // irrelevant for 2-sided RTT config.numRetriesPerFtmr = 3; - config.burstDuration = 9; + config.burstDuration = WifiRttController.getOptimumBurstDuration( + request.mRttBurstSize); } else { // AP + all non-NAN requests config.mustRequestLci = true; config.mustRequestLcr = true; @@ -650,7 +651,8 @@ public class WifiRttControllerHidlImpl implements IWifiRttController { config.numFramesPerBurst = request.mRttBurstSize; config.numRetriesPerRttFrame = (config.type == RttType.TWO_SIDED ? 0 : 3); config.numRetriesPerFtmr = 3; - config.burstDuration = 9; + config.burstDuration = WifiRttController.getOptimumBurstDuration( + request.mRttBurstSize); if (cap != null) { // constrain parameters per device capabilities config.mustRequestLci = config.mustRequestLci && cap.lciSupported; diff --git a/service/java/com/android/server/wifi/hal/WifiStaIfaceAidlImpl.java b/service/java/com/android/server/wifi/hal/WifiStaIfaceAidlImpl.java index ee64cfef71..583c63709d 100644 --- a/service/java/com/android/server/wifi/hal/WifiStaIfaceAidlImpl.java +++ b/service/java/com/android/server/wifi/hal/WifiStaIfaceAidlImpl.java @@ -17,7 +17,6 @@ package com.android.server.wifi.hal; import static com.android.server.wifi.hal.WifiHalAidlImpl.isServiceVersionAtLeast; -import static com.android.server.wifi.util.GeneralUtil.getCapabilityIndex; import android.annotation.NonNull; import android.annotation.Nullable; @@ -394,7 +393,8 @@ public class WifiStaIfaceAidlImpl implements IWifiStaIface { final String methodStr = "getCachedScanData"; synchronized (mLock) { try { - if (!checkIfaceAndLogFailure(methodStr)) return null; + if (!isServiceVersionAtLeast(2) + || !checkIfaceAndLogFailure(methodStr)) return null; CachedScanData scanData = mWifiStaIface.getCachedScanData(); return halToFrameworkCachedScanData(scanData); } catch (RemoteException e) { @@ -1271,56 +1271,55 @@ public class WifiStaIfaceAidlImpl implements IWifiStaIface { BitSet features = new BitSet(); if (hasCapability(halFeatureSet, android.hardware.wifi.IWifiStaIface.FeatureSetMask.HOTSPOT)) { - features.set(getCapabilityIndex(WifiManager.WIFI_FEATURE_PASSPOINT)); + features.set(WifiManager.WIFI_FEATURE_PASSPOINT); } if (hasCapability(halFeatureSet, android.hardware.wifi.IWifiStaIface.FeatureSetMask.BACKGROUND_SCAN)) { - features.set(getCapabilityIndex(WifiManager.WIFI_FEATURE_SCANNER)); + features.set(WifiManager.WIFI_FEATURE_SCANNER); } if (hasCapability(halFeatureSet, android.hardware.wifi.IWifiStaIface.FeatureSetMask.PNO)) { - features.set(getCapabilityIndex(WifiManager.WIFI_FEATURE_PNO)); + features.set(WifiManager.WIFI_FEATURE_PNO); } if (hasCapability(halFeatureSet, android.hardware.wifi.IWifiStaIface.FeatureSetMask.TDLS)) { - features.set(getCapabilityIndex(WifiManager.WIFI_FEATURE_TDLS)); + features.set(WifiManager.WIFI_FEATURE_TDLS); } if (hasCapability(halFeatureSet, android.hardware.wifi.IWifiStaIface.FeatureSetMask.TDLS_OFFCHANNEL)) { - features.set(getCapabilityIndex(WifiManager.WIFI_FEATURE_TDLS_OFFCHANNEL)); + features.set(WifiManager.WIFI_FEATURE_TDLS_OFFCHANNEL); } if (hasCapability(halFeatureSet, android.hardware.wifi.IWifiStaIface.FeatureSetMask.LINK_LAYER_STATS)) { - features.set(getCapabilityIndex(WifiManager.WIFI_FEATURE_LINK_LAYER_STATS)); + features.set(WifiManager.WIFI_FEATURE_LINK_LAYER_STATS); } if (hasCapability(halFeatureSet, android.hardware.wifi.IWifiStaIface.FeatureSetMask.RSSI_MONITOR)) { - features.set(getCapabilityIndex(WifiManager.WIFI_FEATURE_RSSI_MONITOR)); + features.set(WifiManager.WIFI_FEATURE_RSSI_MONITOR); } if (hasCapability(halFeatureSet, android.hardware.wifi.IWifiStaIface.FeatureSetMask.KEEP_ALIVE)) { - features.set(getCapabilityIndex(WifiManager.WIFI_FEATURE_MKEEP_ALIVE)); + features.set(WifiManager.WIFI_FEATURE_MKEEP_ALIVE); } if (hasCapability(halFeatureSet, android.hardware.wifi.IWifiStaIface.FeatureSetMask.ND_OFFLOAD)) { - features.set(getCapabilityIndex(WifiManager.WIFI_FEATURE_CONFIG_NDO)); + features.set(WifiManager.WIFI_FEATURE_CONFIG_NDO); } if (hasCapability(halFeatureSet, android.hardware.wifi.IWifiStaIface.FeatureSetMask.CONTROL_ROAMING)) { - features.set(getCapabilityIndex(WifiManager.WIFI_FEATURE_CONTROL_ROAMING)); + features.set(WifiManager.WIFI_FEATURE_CONTROL_ROAMING); } if (hasCapability(halFeatureSet, android.hardware.wifi.IWifiStaIface.FeatureSetMask.PROBE_IE_ALLOWLIST)) { - features.set(getCapabilityIndex(WifiManager.WIFI_FEATURE_IE_WHITELIST)); + features.set(WifiManager.WIFI_FEATURE_IE_WHITELIST); } if (hasCapability(halFeatureSet, android.hardware.wifi.IWifiStaIface.FeatureSetMask.SCAN_RAND)) { - features.set(getCapabilityIndex(WifiManager.WIFI_FEATURE_SCAN_RAND)); + features.set(WifiManager.WIFI_FEATURE_SCAN_RAND); } if (hasCapability(halFeatureSet, android.hardware.wifi.IWifiStaIface.FeatureSetMask.ROAMING_MODE_CONTROL)) { - features.set( - getCapabilityIndex(WifiManager.WIFI_FEATURE_AGGRESSIVE_ROAMING_MODE_SUPPORT)); + features.set(WifiManager.WIFI_FEATURE_AGGRESSIVE_ROAMING_MODE_SUPPORT); } return features; } @@ -1479,11 +1478,21 @@ public class WifiStaIfaceAidlImpl implements IWifiStaIface { radio.on_time_roam_scan = aidlRadioStats.onTimeInMsForRoamScan; radio.on_time_pno_scan = aidlRadioStats.onTimeInMsForPnoScan; radio.on_time_hs20_scan = aidlRadioStats.onTimeInMsForHs20Scan; + if (aidlRadioStats.txTimeInMsPerLevel != null + && aidlRadioStats.txTimeInMsPerLevel.length > 0) { + radio.tx_time_in_ms_per_level = new int[aidlRadioStats.txTimeInMsPerLevel.length]; + for (int i = 0; i < aidlRadioStats.txTimeInMsPerLevel.length; ++i) { + radio.tx_time_in_ms_per_level[i] = aidlRadioStats.txTimeInMsPerLevel[i]; + } + } /* Copy list of channel stats */ for (WifiChannelStats channelStats : aidlRadioStats.channelStats) { WifiLinkLayerStats.ChannelStats channelStatsEntry = new WifiLinkLayerStats.ChannelStats(); channelStatsEntry.frequency = channelStats.channel.centerFreq; + channelStatsEntry.frequencyFirstSegment = channelStats.channel.centerFreq0; + channelStatsEntry.frequencySecondSegment = channelStats.channel.centerFreq1; + channelStatsEntry.channelWidth = channelStats.channel.width; channelStatsEntry.radioOnTimeMs = channelStats.onTimeInMs; channelStatsEntry.ccaBusyTimeMs = channelStats.ccaBusyTimeInMs; radio.channelStatsMap.put(channelStats.channel.centerFreq, channelStatsEntry); diff --git a/service/java/com/android/server/wifi/hal/WifiStaIfaceHidlImpl.java b/service/java/com/android/server/wifi/hal/WifiStaIfaceHidlImpl.java index bdcecdeb03..a5deeaeef2 100644 --- a/service/java/com/android/server/wifi/hal/WifiStaIfaceHidlImpl.java +++ b/service/java/com/android/server/wifi/hal/WifiStaIfaceHidlImpl.java @@ -18,8 +18,6 @@ package com.android.server.wifi.hal; import static android.net.wifi.WifiUsabilityStatsEntry.LINK_STATE_UNKNOWN; -import static com.android.server.wifi.util.GeneralUtil.getCapabilityIndex; - import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; @@ -896,52 +894,52 @@ public class WifiStaIfaceHidlImpl implements IWifiStaIface { BitSet features = new BitSet(); if (hasCapability(caps, android.hardware.wifi.V1_0.IWifiStaIface.StaIfaceCapabilityMask.HOTSPOT)) { - features.set(getCapabilityIndex(WifiManager.WIFI_FEATURE_PASSPOINT)); + features.set(WifiManager.WIFI_FEATURE_PASSPOINT); } if (hasCapability(caps, android.hardware.wifi.V1_0.IWifiStaIface.StaIfaceCapabilityMask.BACKGROUND_SCAN)) { - features.set(getCapabilityIndex(WifiManager.WIFI_FEATURE_SCANNER)); + features.set(WifiManager.WIFI_FEATURE_SCANNER); } if (hasCapability(caps, android.hardware.wifi.V1_0.IWifiStaIface.StaIfaceCapabilityMask.PNO)) { - features.set(getCapabilityIndex(WifiManager.WIFI_FEATURE_PNO)); + features.set(WifiManager.WIFI_FEATURE_PNO); } if (hasCapability(caps, android.hardware.wifi.V1_0.IWifiStaIface.StaIfaceCapabilityMask.TDLS)) { - features.set(getCapabilityIndex(WifiManager.WIFI_FEATURE_TDLS)); + features.set(WifiManager.WIFI_FEATURE_TDLS); } if (hasCapability(caps, android.hardware.wifi.V1_0.IWifiStaIface.StaIfaceCapabilityMask.TDLS_OFFCHANNEL)) { - features.set(getCapabilityIndex(WifiManager.WIFI_FEATURE_TDLS_OFFCHANNEL)); + features.set(WifiManager.WIFI_FEATURE_TDLS_OFFCHANNEL); } if (hasCapability(caps, android.hardware.wifi.V1_0.IWifiStaIface.StaIfaceCapabilityMask.LINK_LAYER_STATS)) { - features.set(getCapabilityIndex(WifiManager.WIFI_FEATURE_LINK_LAYER_STATS)); + features.set(WifiManager.WIFI_FEATURE_LINK_LAYER_STATS); } if (hasCapability(caps, android.hardware.wifi.V1_0.IWifiStaIface.StaIfaceCapabilityMask.RSSI_MONITOR)) { - features.set(getCapabilityIndex(WifiManager.WIFI_FEATURE_RSSI_MONITOR)); + features.set(WifiManager.WIFI_FEATURE_RSSI_MONITOR); } if (hasCapability(caps, android.hardware.wifi.V1_0.IWifiStaIface.StaIfaceCapabilityMask.KEEP_ALIVE)) { - features.set(getCapabilityIndex(WifiManager.WIFI_FEATURE_MKEEP_ALIVE)); + features.set(WifiManager.WIFI_FEATURE_MKEEP_ALIVE); } if (hasCapability(caps, android.hardware.wifi.V1_0.IWifiStaIface.StaIfaceCapabilityMask.ND_OFFLOAD)) { - features.set(getCapabilityIndex(WifiManager.WIFI_FEATURE_CONFIG_NDO)); + features.set(WifiManager.WIFI_FEATURE_CONFIG_NDO); } if (hasCapability(caps, android.hardware.wifi.V1_0.IWifiStaIface.StaIfaceCapabilityMask.CONTROL_ROAMING)) { - features.set(getCapabilityIndex(WifiManager.WIFI_FEATURE_CONTROL_ROAMING)); + features.set(WifiManager.WIFI_FEATURE_CONTROL_ROAMING); } if (hasCapability(caps, android.hardware.wifi.V1_0.IWifiStaIface.StaIfaceCapabilityMask .PROBE_IE_WHITELIST)) { - features.set(getCapabilityIndex(WifiManager.WIFI_FEATURE_IE_WHITELIST)); + features.set(WifiManager.WIFI_FEATURE_IE_WHITELIST); } if (hasCapability(caps, android.hardware.wifi.V1_0.IWifiStaIface.StaIfaceCapabilityMask.SCAN_RAND)) { - features.set(getCapabilityIndex(WifiManager.WIFI_FEATURE_SCAN_RAND)); + features.set(WifiManager.WIFI_FEATURE_SCAN_RAND); } return features; } diff --git a/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java b/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java index e2facfe698..3bcd659559 100644 --- a/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java +++ b/service/java/com/android/server/wifi/hotspot2/NetworkDetail.java @@ -17,9 +17,11 @@ import java.nio.charset.CharacterCodingException; import java.nio.charset.CharsetDecoder; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Objects; public class NetworkDetail { @@ -170,6 +172,8 @@ public class NetworkDetail { private int mMloLinkId = MloLink.INVALID_MLO_LINK_ID; private List<MloLink> mAffiliatedMloLinks = Collections.emptyList(); private byte[] mDisabledSubchannelBitmap; + private final boolean mIsSecureHeLtfSupported; + private final boolean mIsRangingFrameProtectionRequired; public NetworkDetail(String bssid, ScanResult.InformationElement[] infoElements, List<String> anqpLines, int freq) { @@ -226,6 +230,8 @@ public class NetworkDetail { InformationElementUtil.SupportedRates extendedSupportedRates = new InformationElementUtil.SupportedRates(); + InformationElementUtil.Rsnxe rsnxe = new InformationElementUtil.Rsnxe(); + RuntimeException exception = null; ArrayList<Integer> iesFound = new ArrayList<Integer>(); @@ -299,6 +305,9 @@ public class NetworkDetail { break; } break; + case ScanResult.InformationElement.EID_RSN_EXTENSION: + rsnxe.from(ie); + break; default: break; } @@ -378,6 +387,8 @@ public class NetworkDetail { int channelWidth = ScanResult.UNSPECIFIED; int centerFreq0 = mPrimaryFreq; int centerFreq1 = 0; + mIsSecureHeLtfSupported = rsnxe.isSecureHeLtfSupported(); + mIsRangingFrameProtectionRequired = rsnxe.isRangingFrameProtectionRequired(); // Check if EHT Operation Info is present in EHT operation IE. if (ehtOperation.isEhtOperationInfoPresent()) { @@ -567,6 +578,8 @@ public class NetworkDetail { mApType6GHz = base.mApType6GHz; mIs11azNtbResponder = base.mIs11azNtbResponder; mIs11azTbResponder = base.mIs11azTbResponder; + mIsSecureHeLtfSupported = base.mIsSecureHeLtfSupported; + mIsRangingFrameProtectionRequired = base.mIsRangingFrameProtectionRequired; } public NetworkDetail complete(Map<Constants.ANQPElementType, ANQPElement> anqpElements) { @@ -715,36 +728,90 @@ public class NetworkDetail { } @Override - public boolean equals(Object thatObject) { - if (this == thatObject) { - return true; - } - if (thatObject == null || getClass() != thatObject.getClass()) { - return false; - } - - NetworkDetail that = (NetworkDetail)thatObject; - - return getSSID().equals(that.getSSID()) && getBSSID() == that.getBSSID(); + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof NetworkDetail that)) return false; + return mHESSID == that.mHESSID && mBSSID == that.mBSSID + && mIsHiddenSsid == that.mIsHiddenSsid + && mStationCount == that.mStationCount + && mChannelUtilization == that.mChannelUtilization && mCapacity == that.mCapacity + && mChannelWidth == that.mChannelWidth && mPrimaryFreq == that.mPrimaryFreq + && mCenterfreq0 == that.mCenterfreq0 && mCenterfreq1 == that.mCenterfreq1 + && mWifiMode == that.mWifiMode && mMaxRate == that.mMaxRate + && mMaxNumberSpatialStreams == that.mMaxNumberSpatialStreams + && mInternet == that.mInternet && mAnqpDomainID == that.mAnqpDomainID + && mAnqpOICount == that.mAnqpOICount && mDtimInterval == that.mDtimInterval + && mMboAssociationDisallowedReasonCode == that.mMboAssociationDisallowedReasonCode + && mMboSupported == that.mMboSupported + && mMboCellularDataAware == that.mMboCellularDataAware + && mOceSupported == that.mOceSupported && mTwtRequired == that.mTwtRequired + && mIndividualTwtSupported == that.mIndividualTwtSupported + && mBroadcastTwtSupported == that.mBroadcastTwtSupported + && mRestrictedTwtSupported == that.mRestrictedTwtSupported + && mEpcsPriorityAccessSupported == that.mEpcsPriorityAccessSupported + && mFilsCapable == that.mFilsCapable + && mIs11azNtbResponder == that.mIs11azNtbResponder + && mIs11azTbResponder == that.mIs11azTbResponder && mMloLinkId == that.mMloLinkId + && mIsSecureHeLtfSupported == that.mIsSecureHeLtfSupported + && mIsRangingFrameProtectionRequired == that.mIsRangingFrameProtectionRequired + && Objects.equals(mSSID, that.mSSID) && mAnt == that.mAnt + && mHSRelease == that.mHSRelease && Arrays.equals(mRoamingConsortiums, + that.mRoamingConsortiums) && Objects.equals(mCountryCode, that.mCountryCode) + && Objects.equals(mExtendedCapabilities, that.mExtendedCapabilities) + && Objects.equals(mANQPElements, that.mANQPElements) + && mApType6GHz == that.mApType6GHz && Objects.equals(mMldMacAddress, + that.mMldMacAddress) && Objects.equals(mAffiliatedMloLinks, + that.mAffiliatedMloLinks) && Arrays.equals(mDisabledSubchannelBitmap, + that.mDisabledSubchannelBitmap); } @Override - public int hashCode() { - return ((mSSID.hashCode() * 31) + (int)(mBSSID >>> 32)) * 31 + (int)mBSSID; + public String toString() { + return "NetworkDetail{" + "mSSID='" + mSSID + '\'' + ", mHESSID='" + Utils.macToString( + mHESSID) + '\'' + ", mBSSID='" + Utils.macToString(mBSSID) + '\'' + + ", mIsHiddenSsid=" + mIsHiddenSsid + ", mStationCount=" + mStationCount + + ", mChannelUtilization=" + mChannelUtilization + ", mCapacity=" + mCapacity + + ", mChannelWidth=" + mChannelWidth + ", mPrimaryFreq=" + mPrimaryFreq + + ", mCenterfreq0=" + mCenterfreq0 + ", mCenterfreq1=" + mCenterfreq1 + + ", mWifiMode=" + mWifiMode + ", mMaxRate=" + mMaxRate + + ", mMaxNumberSpatialStreams=" + mMaxNumberSpatialStreams + ", mAnt=" + mAnt + + ", mInternet=" + mInternet + ", mHSRelease=" + mHSRelease + ", mAnqpDomainID=" + + mAnqpDomainID + ", mAnqpOICount=" + mAnqpOICount + ", mRoamingConsortiums=" + + Utils.roamingConsortiumsToString(mRoamingConsortiums) + ", mDtimInterval=" + + mDtimInterval + ", mCountryCode='" + mCountryCode + '\'' + + ", mExtendedCapabilities=" + mExtendedCapabilities + ", mANQPElements=" + + mANQPElements + ", mMboAssociationDisallowedReasonCode=" + + mMboAssociationDisallowedReasonCode + ", mMboSupported=" + mMboSupported + + ", mMboCellularDataAware=" + mMboCellularDataAware + ", mOceSupported=" + + mOceSupported + ", mTwtRequired=" + mTwtRequired + ", mIndividualTwtSupported=" + + mIndividualTwtSupported + ", mBroadcastTwtSupported=" + mBroadcastTwtSupported + + ", mRestrictedTwtSupported=" + mRestrictedTwtSupported + + ", mEpcsPriorityAccessSupported=" + mEpcsPriorityAccessSupported + + ", mFilsCapable=" + mFilsCapable + ", mApType6GHz=" + mApType6GHz + + ", mIs11azNtbResponder=" + mIs11azNtbResponder + ", mIs11azTbResponder=" + + mIs11azTbResponder + ", mMldMacAddress=" + mMldMacAddress + ", mMloLinkId=" + + mMloLinkId + ", mAffiliatedMloLinks=" + mAffiliatedMloLinks + + ", mDisabledSubchannelBitmap=" + Arrays.toString(mDisabledSubchannelBitmap) + + ", mIsSecureHeLtfSupported=" + mIsSecureHeLtfSupported + + ", mIsRangingFrameProtectionRequired=" + mIsRangingFrameProtectionRequired + '}'; } @Override - public String toString() { - return "NetworkInfo{SSID='" + mSSID - + "', HESSID=" + Utils.macToString(mHESSID) - + ", BSSID=" + Utils.macToString(mBSSID) - + ", StationCount=" + mStationCount - + ", ChannelUtilization=" + mChannelUtilization - + ", Capacity=" + mCapacity - + ", Ant=" + mAnt + ", Internet=" + mInternet + ", HSRelease=" - + mHSRelease + ", AnqpDomainID" + mAnqpDomainID + ", AnqpOICount" + mAnqpOICount - + ", RoamingConsortiums=" + Utils.roamingConsortiumsToString(mRoamingConsortiums) - + "}"; + public int hashCode() { + int result = Objects.hash(mSSID, mHESSID, mBSSID, mIsHiddenSsid, mStationCount, + mChannelUtilization, mCapacity, mChannelWidth, mPrimaryFreq, mCenterfreq0, + mCenterfreq1, + mWifiMode, mMaxRate, mMaxNumberSpatialStreams, mAnt, mInternet, mHSRelease, + mAnqpDomainID, mAnqpOICount, mDtimInterval, mCountryCode, mExtendedCapabilities, + mANQPElements, mMboAssociationDisallowedReasonCode, mMboSupported, + mMboCellularDataAware, mOceSupported, mTwtRequired, mIndividualTwtSupported, + mBroadcastTwtSupported, mRestrictedTwtSupported, mEpcsPriorityAccessSupported, + mFilsCapable, mApType6GHz, mIs11azNtbResponder, mIs11azTbResponder, mMldMacAddress, + mMloLinkId, mAffiliatedMloLinks, mIsSecureHeLtfSupported, + mIsRangingFrameProtectionRequired); + result = 31 * result + Arrays.hashCode(mRoamingConsortiums); + result = 31 * result + Arrays.hashCode(mDisabledSubchannelBitmap); + return result; } public String toKeyString() { @@ -856,4 +923,14 @@ public class NetworkDetail { public InformationElementUtil.ApType6GHz getApType6GHz() { return mApType6GHz; } + + /** Return whether secure HE-LTF is supported or not. */ + public boolean isSecureHeLtfSupported() { + return mIsSecureHeLtfSupported; + } + + /** Return whether ranging frame protection is required or not */ + public boolean isRangingFrameProtectionRequired() { + return mIsRangingFrameProtectionRequired; + } } diff --git a/service/java/com/android/server/wifi/mainline_supplicant/MainlineSupplicant.java b/service/java/com/android/server/wifi/mainline_supplicant/MainlineSupplicant.java new file mode 100644 index 0000000000..e347122942 --- /dev/null +++ b/service/java/com/android/server/wifi/mainline_supplicant/MainlineSupplicant.java @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2024 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.mainline_supplicant; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.wifi.util.Environment; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceSpecificException; +import android.system.wifi.mainline_supplicant.IMainlineSupplicant; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * Allows us to bring up, tear down, and make calls into the mainline supplicant process. + * <p> + * The mainline supplicant is a separate wpa_supplicant binary stored in the Wifi mainline module, + * which provides specific functionalities such as USD. + */ +public class MainlineSupplicant { + private static final String TAG = "MainlineSupplicant"; + private static final String MAINLINE_SUPPLICANT_SERVICE_NAME = "wifi_mainline_supplicant"; + private static final long WAIT_FOR_DEATH_TIMEOUT_MS = 50L; + + private IMainlineSupplicant mIMainlineSupplicant; + private final Object mLock = new Object(); + private SupplicantDeathRecipient mDeathRecipient; + private CountDownLatch mWaitForDeathLatch; + + public MainlineSupplicant() { + mDeathRecipient = new SupplicantDeathRecipient(); + } + + @VisibleForTesting + protected IMainlineSupplicant getNewServiceBinderMockable() { + return IMainlineSupplicant.Stub.asInterface( + ServiceManagerWrapper.waitForService(MAINLINE_SUPPLICANT_SERVICE_NAME)); + } + + private @Nullable IBinder getCurrentServiceBinder() { + synchronized (mLock) { + if (mIMainlineSupplicant == null) { + return null; + } + return mIMainlineSupplicant.asBinder(); + } + } + + private class SupplicantDeathRecipient implements IBinder.DeathRecipient { + @Override + public void binderDied() { + } + + @Override + public void binderDied(@NonNull IBinder who) { + synchronized (mLock) { + IBinder currentBinder = getCurrentServiceBinder(); + Log.i(TAG, "Death notification received. who=" + who + + ", currentBinder=" + currentBinder); + if (currentBinder == null || currentBinder != who) { + Log.i(TAG, "Ignoring stale death notification"); + return; + } + if (mWaitForDeathLatch != null) { + // Latch indicates that this event was triggered by stopService + mWaitForDeathLatch.countDown(); + } + mIMainlineSupplicant = null; + Log.i(TAG, "Service death was handled successfully"); + } + } + } + + /** + * Start the mainline supplicant process. + * + * @return true if the process was started, false otherwise. + */ + public boolean startService() { + synchronized (mLock) { + if (!Environment.isSdkAtLeastB()) { + Log.e(TAG, "Service is not available before Android B"); + return false; + } + if (mIMainlineSupplicant != null) { + Log.i(TAG, "Service has already been started"); + return true; + } + + mIMainlineSupplicant = getNewServiceBinderMockable(); + if (mIMainlineSupplicant == null) { + Log.e(TAG, "Unable to retrieve binder from the ServiceManager"); + return false; + } + + try { + mWaitForDeathLatch = null; + mIMainlineSupplicant.asBinder().linkToDeath(mDeathRecipient, /* flags= */ 0); + } catch (RemoteException e) { + handleRemoteException(e, "startService"); + return false; + } + + Log.i(TAG, "Service was started successfully"); + return true; + } + } + + /** + * Check whether this instance is active. + */ + @VisibleForTesting + protected boolean isActive() { + synchronized (mLock) { + return mIMainlineSupplicant != null; + } + } + + /** + * Stop the mainline supplicant process. + */ + public void stopService() { + synchronized (mLock) { + if (mIMainlineSupplicant == null) { + Log.i(TAG, "Service has already been stopped"); + return; + } + try { + Log.i(TAG, "Attempting to stop the service"); + mWaitForDeathLatch = new CountDownLatch(1); + mIMainlineSupplicant.terminate(); + } catch (RemoteException e) { + handleRemoteException(e, "stopService"); + return; + } + } + + // Wait for latch to confirm the service death + try { + if (mWaitForDeathLatch.await(WAIT_FOR_DEATH_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { + Log.i(TAG, "Service death confirmation was received"); + } else { + Log.e(TAG, "Timed out waiting for confirmation of service death"); + } + } catch (InterruptedException e) { + Log.e(TAG, "Failed to wait for service death"); + } + } + + private void handleServiceSpecificException(ServiceSpecificException e, String methodName) { + Log.e(TAG, methodName + " encountered ServiceSpecificException " + e); + } + + private void handleRemoteException(RemoteException e, String methodName) { + synchronized (mLock) { + Log.e(TAG, methodName + " encountered RemoteException " + e); + mIMainlineSupplicant = null; + } + } +} diff --git a/service/java/com/android/server/wifi/mainline_supplicant/ServiceManagerWrapper.java b/service/java/com/android/server/wifi/mainline_supplicant/ServiceManagerWrapper.java new file mode 100644 index 0000000000..828d694431 --- /dev/null +++ b/service/java/com/android/server/wifi/mainline_supplicant/ServiceManagerWrapper.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2024 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.mainline_supplicant; + +import android.annotation.Nullable; +import android.os.IBinder; + +import com.android.modules.utils.build.SdkLevel; + +/** + * Wrapper around ServiceManager APIs that are not directly available to the mainline module. + */ +public final class ServiceManagerWrapper { + static { + System.loadLibrary("service-wifi-jni"); + } + + /** + * Returns the specified service from the service manager. + * + * If the service is not running, service manager will attempt to start it, and this function + * will wait for it to be ready. + * + * @return {@code null} only if there are permission problems or fatal errors + */ + public static @Nullable IBinder waitForService(String serviceName) { + // Underlying implementation requires SDK 31+ + if (!SdkLevel.isAtLeastS()) { + return null; + } + return nativeWaitForService(serviceName); + } + + private static native IBinder nativeWaitForService(String serviceName); +} diff --git a/service/java/com/android/server/wifi/p2p/ISupplicantP2pIfaceHal.java b/service/java/com/android/server/wifi/p2p/ISupplicantP2pIfaceHal.java index 72f669bb53..8737047f5b 100644 --- a/service/java/com/android/server/wifi/p2p/ISupplicantP2pIfaceHal.java +++ b/service/java/com/android/server/wifi/p2p/ISupplicantP2pIfaceHal.java @@ -21,12 +21,16 @@ import android.annotation.Nullable; import android.net.wifi.CoexUnsafeChannel; import android.net.wifi.ScanResult; import android.net.wifi.p2p.WifiP2pConfig; +import android.net.wifi.p2p.WifiP2pDirInfo; import android.net.wifi.p2p.WifiP2pDiscoveryConfig; import android.net.wifi.p2p.WifiP2pExtListenParams; import android.net.wifi.p2p.WifiP2pGroup; import android.net.wifi.p2p.WifiP2pGroupList; import android.net.wifi.p2p.WifiP2pManager; +import android.net.wifi.p2p.WifiP2pUsdBasedLocalServiceAdvertisementConfig; +import android.net.wifi.p2p.WifiP2pUsdBasedServiceDiscoveryConfig; import android.net.wifi.p2p.nsd.WifiP2pServiceInfo; +import android.net.wifi.p2p.nsd.WifiP2pUsdBasedServiceConfig; import com.android.server.wifi.WifiNative; @@ -280,10 +284,11 @@ interface ISupplicantP2pIfaceHal { * * @param networkId Used to specify the restart of a persistent group. * @param isPersistent Used to request a persistent group to be formed. + * @param isP2pV2 Used to start a Group Owner that support P2P2 IE * * @return true, if operation was successful. */ - boolean groupAdd(int networkId, boolean isPersistent); + boolean groupAdd(int networkId, boolean isPersistent, boolean isP2pV2); /** * Set up a P2P group as Group Owner or join a group with a configuration. @@ -300,6 +305,7 @@ interface ISupplicantP2pIfaceHal { * @return true, if operation was successful. */ boolean groupAdd(String networkName, String passphrase, + @WifiP2pConfig.PccModeConnectionType int connectionType, boolean isPersistent, int freq, String peerAddress, boolean join); /** @@ -607,6 +613,73 @@ interface ISupplicantP2pIfaceHal { int ipAddressStart, int ipAddressEnd); /** + * Start an Un-synchronized Service Discovery (USD) based P2P service discovery. + * + * @param usdServiceConfig is the USD based service configuration. + * @param discoveryConfig is the configuration for this service discovery request. + * @param timeoutInSeconds is the maximum time to be spent for this service discovery request. + */ + int startUsdBasedServiceDiscovery(WifiP2pUsdBasedServiceConfig usdServiceConfig, + WifiP2pUsdBasedServiceDiscoveryConfig discoveryConfig, int timeoutInSeconds); + + /** + * Stop an Un-synchronized Service Discovery (USD) based P2P service discovery. + * + * @param sessionId Identifier to cancel the service discovery instance. + * Use zero to cancel all the service discovery instances. + */ + void stopUsdBasedServiceDiscovery(int sessionId); + + /** + * Start an Un-synchronized Service Discovery (USD) based P2P service advertisement. + * + * @param usdServiceConfig is the USD based service configuration. + * @param advertisementConfig is the configuration for this service advertisement. + * @param timeoutInSeconds is the maximum time to be spent for this service advertisement. + */ + int startUsdBasedServiceAdvertisement(WifiP2pUsdBasedServiceConfig usdServiceConfig, + WifiP2pUsdBasedLocalServiceAdvertisementConfig advertisementConfig, + int timeoutInSeconds); + + /** + * Stop an Un-synchronized Service Discovery (USD) based P2P service advertisement. + * + * @param sessionId Identifier to cancel the service advertisement. + * Use zero to cancel all the service advertisement instances. + */ + void stopUsdBasedServiceAdvertisement(int sessionId); + + /** + * Get the Device Identity Resolution (DIR) Information. + * See {@link WifiP2pDirInfo} for details + * + * @return {@link WifiP2pDirInfo} instance on success, null on failure. + */ + WifiP2pDirInfo getDirInfo(); + + /** + * Validate the Device Identity Resolution (DIR) Information of a P2P device. + * See {@link WifiP2pDirInfo} for details. + * + * @param dirInfo {@link WifiP2pDirInfo} to validate. + * @return The identifier of device identity key on success, -1 on failure. + */ + int validateDirInfo(@NonNull WifiP2pDirInfo dirInfo); + + /** + * Used to authorize a connection request to an existing Group Owner + * interface, to allow a peer device to connect. + * + * @param config Configuration to use for connection. + * @param groupOwnerInterfaceName Group Owner interface name on which the request to connect + * needs to be authorized. + * + * @return boolean value indicating whether operation was successful. + */ + boolean authorizeConnectRequestOnGroupOwner(WifiP2pConfig config, + String groupOwnerInterfaceName); + + /** * Terminate the supplicant daemon & wait for its death. * Note: Aidl only since it was added from HIDL 1.1 */ diff --git a/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceCallbackAidlImpl.java b/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceCallbackAidlImpl.java index e3eabad46c..0b1c9ae70f 100644 --- a/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceCallbackAidlImpl.java +++ b/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceCallbackAidlImpl.java @@ -17,20 +17,25 @@ package com.android.server.wifi.p2p; import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTL; +import static com.android.wifi.flags.Flags.wifiDirectR2; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.hardware.wifi.supplicant.ISupplicantP2pIfaceCallback; +import android.hardware.wifi.supplicant.KeyMgmtMask; import android.hardware.wifi.supplicant.P2pClientEapolIpAddressInfo; import android.hardware.wifi.supplicant.P2pDeviceFoundEventParams; import android.hardware.wifi.supplicant.P2pGoNegotiationReqEventParams; import android.hardware.wifi.supplicant.P2pGroupStartedEventParams; import android.hardware.wifi.supplicant.P2pInvitationEventParams; +import android.hardware.wifi.supplicant.P2pPairingBootstrappingMethodMask; import android.hardware.wifi.supplicant.P2pPeerClientDisconnectedEventParams; import android.hardware.wifi.supplicant.P2pPeerClientJoinedEventParams; import android.hardware.wifi.supplicant.P2pProvDiscStatusCode; import android.hardware.wifi.supplicant.P2pProvisionDiscoveryCompletedEventParams; import android.hardware.wifi.supplicant.P2pStatusCode; +import android.hardware.wifi.supplicant.P2pUsdBasedServiceDiscoveryResultParams; import android.hardware.wifi.supplicant.WpsConfigMethods; import android.hardware.wifi.supplicant.WpsDevPasswordId; import android.net.MacAddress; @@ -39,13 +44,18 @@ import android.net.wifi.ScanResult; import android.net.wifi.WpsInfo; import android.net.wifi.p2p.WifiP2pConfig; import android.net.wifi.p2p.WifiP2pDevice; +import android.net.wifi.p2p.WifiP2pDirInfo; import android.net.wifi.p2p.WifiP2pGroup; +import android.net.wifi.p2p.WifiP2pPairingBootstrappingConfig; import android.net.wifi.p2p.WifiP2pProvDiscEvent; import android.net.wifi.p2p.WifiP2pWfdInfo; import android.net.wifi.p2p.nsd.WifiP2pServiceResponse; +import android.net.wifi.p2p.nsd.WifiP2pUsdBasedServiceResponse; +import android.net.wifi.util.Environment; import android.text.TextUtils; import android.util.Log; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.HexDump; import com.android.modules.utils.build.SdkLevel; import com.android.server.wifi.util.HalAidlUtil; @@ -112,7 +122,7 @@ public class SupplicantP2pIfaceCallbackAidlImpl extends ISupplicantP2pIfaceCallb byte[] wfdDeviceInfo) { handleDeviceFound(srcAddress, p2pDeviceAddress, primaryDeviceType, deviceName, configMethods, deviceCapabilities, groupCapabilities, wfdDeviceInfo, - null, null, null); + null, null, null, 0, null); } /** @@ -271,7 +281,7 @@ public class SupplicantP2pIfaceCallbackAidlImpl extends ISupplicantP2pIfaceCallb boolean isPersistent) { onGroupStarted(groupIfName, isGroupOwner, ssid, frequency, psk, passphrase, goDeviceAddress, isPersistent, /* goInterfaceAddress */ null, /*p2pClientIpInfo */ null, - /* vendorData */ null); + /* vendorData */ null, 0); } /** @@ -282,10 +292,14 @@ public class SupplicantP2pIfaceCallbackAidlImpl extends ISupplicantP2pIfaceCallb @Override public void onGroupStartedWithParams(P2pGroupStartedEventParams groupStartedEventParams) { List<OuiKeyedData> vendorData = null; + int keyMgmtMask = 0; if (mServiceVersion >= 3 && groupStartedEventParams.vendorData != null) { vendorData = HalAidlUtil.halToFrameworkOuiKeyedDataList( groupStartedEventParams.vendorData); } + if (mServiceVersion >= 4) { + keyMgmtMask = groupStartedEventParams.keyMgmtMask; + } onGroupStarted(groupStartedEventParams.groupInterfaceName, groupStartedEventParams.isGroupOwner, groupStartedEventParams.ssid, groupStartedEventParams.frequencyMHz, groupStartedEventParams.psk, @@ -293,14 +307,16 @@ public class SupplicantP2pIfaceCallbackAidlImpl extends ISupplicantP2pIfaceCallb groupStartedEventParams.isPersistent, groupStartedEventParams.goInterfaceAddress, groupStartedEventParams.isP2pClientEapolIpAddressInfoPresent ? groupStartedEventParams.p2pClientIpInfo : null, - vendorData); + vendorData, + keyMgmtMask); } private void onGroupStarted(String groupIfName, boolean isGroupOwner, byte[] ssid, int frequency, byte[] psk, String passphrase, byte[] goDeviceAddress, boolean isPersistent, byte[] goInterfaceAddress, P2pClientEapolIpAddressInfo p2pClientIpInfo, - @Nullable List<OuiKeyedData> vendorData) { + @Nullable List<OuiKeyedData> vendorData, + int keyMgmtMask) { if (groupIfName == null) { Log.e(TAG, "Missing group interface name."); return; @@ -361,9 +377,29 @@ public class SupplicantP2pIfaceCallbackAidlImpl extends ISupplicantP2pIfaceCallb group.setVendorData(vendorData); } + if (Environment.isSdkAtLeastB() + && wifiDirectR2()) { + group.setSecurityType(convertHalKeyMgmtMaskToP2pGroupSecurityType(keyMgmtMask)); + } + mMonitor.broadcastP2pGroupStarted(mInterface, group); } + private @WifiP2pGroup.SecurityType int convertHalKeyMgmtMaskToP2pGroupSecurityType( + int keyMgmtMask) { + if (keyMgmtMask == KeyMgmtMask.WPA_PSK) { + return WifiP2pGroup.SECURITY_TYPE_WPA2_PSK; + } else if (keyMgmtMask == KeyMgmtMask.SAE) { + return WifiP2pGroup.SECURITY_TYPE_WPA3_SAE; + } else if ((keyMgmtMask & (KeyMgmtMask.SAE | KeyMgmtMask.WPA_PSK)) + == (KeyMgmtMask.SAE | KeyMgmtMask.WPA_PSK)) { + return WifiP2pGroup.SECURITY_TYPE_WPA3_COMPATIBILITY; + } else { + Log.e(TAG, "Unknown Key management mask: " + keyMgmtMask); + return WifiP2pGroup.SECURITY_TYPE_UNKNOWN; + } + } + /** * Used to indicate the removal of a P2P group. * @@ -506,7 +542,8 @@ public class SupplicantP2pIfaceCallbackAidlImpl extends ISupplicantP2pIfaceCallb public void onProvisionDiscoveryCompleted(byte[] p2pDeviceAddress, boolean isRequest, byte status, int configMethods, String generatedPin) { handleProvisionDiscoveryCompletedEvent( - p2pDeviceAddress, isRequest, status, configMethods, generatedPin, null, null); + p2pDeviceAddress, isRequest, status, configMethods, generatedPin, null, null, + 0, null); } /** @@ -519,10 +556,25 @@ public class SupplicantP2pIfaceCallbackAidlImpl extends ISupplicantP2pIfaceCallb public void onProvisionDiscoveryCompletedEvent( P2pProvisionDiscoveryCompletedEventParams provisionDiscoveryCompletedEventParams) { List<OuiKeyedData> vendorData = null; + int pairingBootstrappingMethod = 0; + String pairingPinorPassphrase = null; if (mServiceVersion >= 3 && provisionDiscoveryCompletedEventParams.vendorData != null) { vendorData = HalAidlUtil.halToFrameworkOuiKeyedDataList( provisionDiscoveryCompletedEventParams.vendorData); } + + if (mServiceVersion >= 4 && provisionDiscoveryCompletedEventParams + .pairingBootstrappingMethod != 0) { + pairingBootstrappingMethod = convertAidlPairingBootstrappingMethodsToFramework( + provisionDiscoveryCompletedEventParams.pairingBootstrappingMethod); + if (pairingBootstrappingMethod == 0) { + Log.e(TAG, "Unsupported : aidl pairing bootstrapping Method" + + provisionDiscoveryCompletedEventParams.pairingBootstrappingMethod); + return; + } + pairingPinorPassphrase = provisionDiscoveryCompletedEventParams.password; + } + handleProvisionDiscoveryCompletedEvent( provisionDiscoveryCompletedEventParams.p2pDeviceAddress, provisionDiscoveryCompletedEventParams.isRequest, @@ -530,7 +582,9 @@ public class SupplicantP2pIfaceCallbackAidlImpl extends ISupplicantP2pIfaceCallb provisionDiscoveryCompletedEventParams.configMethods, provisionDiscoveryCompletedEventParams.generatedPin, provisionDiscoveryCompletedEventParams.groupInterfaceName, - vendorData); + vendorData, + pairingBootstrappingMethod, + pairingPinorPassphrase); } private void handleProvisionDiscoveryCompletedEvent( @@ -540,7 +594,9 @@ public class SupplicantP2pIfaceCallbackAidlImpl extends ISupplicantP2pIfaceCallb int configMethods, String generatedPin, String groupIfName, - @Nullable List<OuiKeyedData> vendorData) { + @Nullable List<OuiKeyedData> vendorData, + int pairingBootstrappingMethod, + String pairingPinorPassphrase) { logd( "Provision discovery " + (isRequest ? "request" : "response") @@ -549,26 +605,49 @@ public class SupplicantP2pIfaceCallbackAidlImpl extends ISupplicantP2pIfaceCallb + " status: " + status + " groupIfName: " - + (TextUtils.isEmpty(groupIfName) ? "null" : groupIfName)); - - WifiP2pProvDiscEvent event = new WifiP2pProvDiscEvent(); - event.device = new WifiP2pDevice(); + + (TextUtils.isEmpty(groupIfName) ? "null" : groupIfName) + + " pairingBootstrappingMethod: " + pairingBootstrappingMethod); + String deviceAddress; try { - event.device.deviceAddress = NativeUtil.macAddressFromByteArray(p2pDeviceAddress); + deviceAddress = NativeUtil.macAddressFromByteArray(p2pDeviceAddress); } catch (Exception e) { Log.e(TAG, "Could not decode MAC address.", e); - event.device.deviceAddress = null; + deviceAddress = null; } if (status != P2pProvDiscStatusCode.SUCCESS) { Log.e(TAG, "Provision discovery failed, status code: " + status); + WifiP2pProvDiscEvent event = new WifiP2pProvDiscEvent(); + event.device = new WifiP2pDevice(); + event.device.deviceAddress = deviceAddress; mMonitor.broadcastP2pProvisionDiscoveryFailure(mInterface, convertHalProvDiscStatusToFrameworkStatus(status), event); return; } - if (TextUtils.isEmpty(event.device.deviceAddress)) return; + if (TextUtils.isEmpty(deviceAddress)) return; + + if (pairingBootstrappingMethod != 0) { + handlePairingBootstrappingProvisionDiscoveryCompletedEvent(deviceAddress, isRequest, + pairingBootstrappingMethod, pairingPinorPassphrase, vendorData); + } else { + handleWpsProvisionDiscoveryCompletedEvent(deviceAddress, isRequest, configMethods, + generatedPin, vendorData); + } + + } + + private void handleWpsProvisionDiscoveryCompletedEvent( + String p2pDeviceAddress, + boolean isRequest, + int configMethods, + String generatedPin, + @Nullable List<OuiKeyedData> vendorData) { + WifiP2pProvDiscEvent event = new WifiP2pProvDiscEvent(); + event.device = new WifiP2pDevice(); + + event.device.deviceAddress = p2pDeviceAddress; if (SdkLevel.isAtLeastV() && vendorData != null) { event.setVendorData(vendorData); @@ -576,29 +655,110 @@ public class SupplicantP2pIfaceCallbackAidlImpl extends ISupplicantP2pIfaceCallb if ((configMethods & WpsConfigMethods.PUSHBUTTON) != 0) { if (isRequest) { - event.event = WifiP2pProvDiscEvent.PBC_REQ; + event.event = WifiP2pProvDiscEvent.WPS_PBC_REQ; mMonitor.broadcastP2pProvisionDiscoveryPbcRequest(mInterface, event); } else { - event.event = WifiP2pProvDiscEvent.PBC_RSP; + event.event = WifiP2pProvDiscEvent.WPS_PBC_RSP; mMonitor.broadcastP2pProvisionDiscoveryPbcResponse(mInterface, event); } } else if (!isRequest && (configMethods & WpsConfigMethods.KEYPAD) != 0) { - event.event = WifiP2pProvDiscEvent.SHOW_PIN; - event.pin = generatedPin; + event.event = WifiP2pProvDiscEvent.WPS_SHOW_PIN; + event.wpsPin = generatedPin; mMonitor.broadcastP2pProvisionDiscoveryShowPin(mInterface, event); } else if (!isRequest && (configMethods & WpsConfigMethods.DISPLAY) != 0) { - event.event = WifiP2pProvDiscEvent.ENTER_PIN; - event.pin = generatedPin; + event.event = WifiP2pProvDiscEvent.WPS_ENTER_PIN; + event.wpsPin = generatedPin; mMonitor.broadcastP2pProvisionDiscoveryEnterPin(mInterface, event); } else if (isRequest && (configMethods & WpsConfigMethods.DISPLAY) != 0) { - event.event = WifiP2pProvDiscEvent.SHOW_PIN; - event.pin = generatedPin; + event.event = WifiP2pProvDiscEvent.WPS_SHOW_PIN; + event.wpsPin = generatedPin; mMonitor.broadcastP2pProvisionDiscoveryShowPin(mInterface, event); } else if (isRequest && (configMethods & WpsConfigMethods.KEYPAD) != 0) { - event.event = WifiP2pProvDiscEvent.ENTER_PIN; + event.event = WifiP2pProvDiscEvent.WPS_ENTER_PIN; mMonitor.broadcastP2pProvisionDiscoveryEnterPin(mInterface, event); } else { - Log.e(TAG, "Unsupported config methods: " + configMethods); + Log.e(TAG, "Unsupported WPS config methods: " + configMethods); + } + } + + private void handlePairingBootstrappingProvisionDiscoveryCompletedEvent( + String p2pDeviceAddress, + boolean isRequest, + int pairingBootstrappingMethod, + String pairingPasswordOrPin, + @Nullable List<OuiKeyedData> vendorData) { + WifiP2pProvDiscEvent event = new WifiP2pProvDiscEvent(); + event.device = new WifiP2pDevice(); + + event.device.deviceAddress = p2pDeviceAddress; + + if (SdkLevel.isAtLeastV() && vendorData != null) { + event.setVendorData(vendorData); + } + + if (pairingBootstrappingMethod + == WifiP2pPairingBootstrappingConfig.PAIRING_BOOTSTRAPPING_METHOD_OPPORTUNISTIC) { + if (isRequest) { + event.event = WifiP2pProvDiscEvent.PAIRING_BOOTSTRAPPING_OPPORTUNISTIC_REQ; + mMonitor.broadcastP2pProvisionDiscoveryPairingBootstrappingOpportunisticRequest( + mInterface, event); + } else { + event.event = WifiP2pProvDiscEvent.PAIRING_BOOTSTRAPPING_OPPORTUNISTIC_RSP; + mMonitor.broadcastP2pProvisionDiscoveryPairingBootstrappingOpportunisticResponse( + mInterface, event); + } + } else if (!isRequest && (pairingBootstrappingMethod + == WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PINCODE)) { + event.event = WifiP2pProvDiscEvent.PAIRING_BOOTSTRAPPING_SHOW_PIN; + event.pairingPinOrPassphrase = pairingPasswordOrPin; + mMonitor.broadcastP2pProvisionDiscoveryShowPairingBootstrappingPinOrPassphrase( + mInterface, event); + } else if (!isRequest && (pairingBootstrappingMethod + == WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_DISPLAY_PINCODE)) { + event.event = WifiP2pProvDiscEvent.PAIRING_BOOTSTRAPPING_ENTER_PIN; + mMonitor.broadcastP2pProvisionDiscoveryEnterPairingBootstrappingPinOrPassphrase( + mInterface, event); + } else if (isRequest && (pairingBootstrappingMethod + == WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_DISPLAY_PINCODE)) { + event.event = WifiP2pProvDiscEvent.PAIRING_BOOTSTRAPPING_SHOW_PIN; + event.pairingPinOrPassphrase = pairingPasswordOrPin; + mMonitor.broadcastP2pProvisionDiscoveryShowPairingBootstrappingPinOrPassphrase( + mInterface, event); + } else if (isRequest && (pairingBootstrappingMethod == WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PINCODE)) { + event.event = WifiP2pProvDiscEvent.PAIRING_BOOTSTRAPPING_ENTER_PIN; + mMonitor.broadcastP2pProvisionDiscoveryEnterPairingBootstrappingPinOrPassphrase( + mInterface, event); + } else if (!isRequest && (pairingBootstrappingMethod + == WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PASSPHRASE)) { + event.event = WifiP2pProvDiscEvent.PAIRING_BOOTSTRAPPING_SHOW_PASSPHRASE; + event.pairingPinOrPassphrase = pairingPasswordOrPin; + mMonitor.broadcastP2pProvisionDiscoveryShowPairingBootstrappingPinOrPassphrase( + mInterface, event); + } else if (!isRequest && (pairingBootstrappingMethod + == WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_DISPLAY_PASSPHRASE)) { + event.event = WifiP2pProvDiscEvent.PAIRING_BOOTSTRAPPING_ENTER_PASSPHRASE; + mMonitor.broadcastP2pProvisionDiscoveryEnterPairingBootstrappingPinOrPassphrase( + mInterface, event); + } else if (isRequest && (pairingBootstrappingMethod + == WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_DISPLAY_PASSPHRASE)) { + event.event = WifiP2pProvDiscEvent.PAIRING_BOOTSTRAPPING_SHOW_PASSPHRASE; + event.pairingPinOrPassphrase = pairingPasswordOrPin; + mMonitor.broadcastP2pProvisionDiscoveryShowPairingBootstrappingPinOrPassphrase( + mInterface, event); + } else if (isRequest && (pairingBootstrappingMethod == WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PASSPHRASE)) { + event.event = WifiP2pProvDiscEvent.PAIRING_BOOTSTRAPPING_ENTER_PASSPHRASE; + mMonitor.broadcastP2pProvisionDiscoveryEnterPairingBootstrappingPinOrPassphrase( + mInterface, event); + } else { + Log.e(TAG, "Unsupported bootstrapping method: " + pairingBootstrappingMethod); } } @@ -627,6 +787,58 @@ public class SupplicantP2pIfaceCallbackAidlImpl extends ISupplicantP2pIfaceCallb mMonitor.broadcastP2pServiceDiscoveryResponse(mInterface, response); } + /** + * Used to indicate the reception of a USD based service discovery response. + * + * @param params Parameters associated with the USD based service discovery result. + */ + @SuppressLint("NewApi") + @Override + public void onUsdBasedServiceDiscoveryResult(P2pUsdBasedServiceDiscoveryResultParams params) { + logd("Usd based service discovery result received on " + mInterface); + if (Environment.isSdkAtLeastB() && wifiDirectR2()) { + WifiP2pUsdBasedServiceResponse usdBasedServiceResponse = + new WifiP2pUsdBasedServiceResponse(params.serviceProtocolType, + params.serviceSpecificInfo); + WifiP2pDevice dev = new WifiP2pDevice(); + try { + dev.deviceAddress = NativeUtil.macAddressFromByteArray(params.peerMacAddress); + } catch (Exception e) { + Log.e(TAG, "Could not decode device address.", e); + return; + } + List<WifiP2pServiceResponse> respList = new ArrayList<>(); + respList.add( + new WifiP2pServiceResponse(dev, usdBasedServiceResponse, params.sessionId)); + mMonitor.broadcastP2pServiceDiscoveryResponse(mInterface, respList); + } + } + + /** + * Used to indicate the termination of USD based service discovery. + * + * @param sessionId Identifier to identify the instance of a service discovery. + * @param reasonCode The reason for termination of service discovery. + */ + @Override + public void onUsdBasedServiceDiscoveryTerminated(int sessionId, int reasonCode) { + logd("Usd based service discovery terminated on " + mInterface); + mMonitor.broadcastUsdBasedServiceDiscoveryTerminated(mInterface, sessionId, reasonCode); + } + + /** + * Used to indicate the termination of USD based service Advertisement + * + * @param sessionId Identifier to identify the instance of a service advertisement. + * @param reasonCode The reason for termination of service advertisement. + */ + @Override + public void onUsdBasedServiceAdvertisementTerminated(int sessionId, int reasonCode) { + logd("Usd based service advertisement terminated on " + mInterface); + mMonitor.broadcastUsdBasedServiceAdvertisementTerminated(mInterface, sessionId, + reasonCode); + } + private WifiP2pDevice createStaEventDevice(byte[] interfaceAddress, byte[] p2pDeviceAddress, InetAddress ipAddress) { WifiP2pDevice device = new WifiP2pDevice(); @@ -857,7 +1069,7 @@ public class SupplicantP2pIfaceCallbackAidlImpl extends ISupplicantP2pIfaceCallb byte[] wfdR2DeviceInfo, byte[] vendorElemBytes) { handleDeviceFound(srcAddress, p2pDeviceAddress, primaryDeviceType, deviceName, configMethods, deviceCapabilities, groupCapabilities, wfdDeviceInfo, - wfdR2DeviceInfo, vendorElemBytes, null); + wfdR2DeviceInfo, vendorElemBytes, null, 0, null); } /** @@ -865,13 +1077,34 @@ public class SupplicantP2pIfaceCallbackAidlImpl extends ISupplicantP2pIfaceCallb * * @param deviceFoundEventParams Parameters associated with the device found event. */ + @SuppressLint("NewApi") @Override public void onDeviceFoundWithParams(P2pDeviceFoundEventParams deviceFoundEventParams) { List<OuiKeyedData> vendorData = null; + int pairingBootstrappingMethods = 0; + WifiP2pDirInfo dirInfo = null; + if (mServiceVersion >= 3 && deviceFoundEventParams.vendorData != null) { vendorData = HalAidlUtil.halToFrameworkOuiKeyedDataList( deviceFoundEventParams.vendorData); } + + if (mServiceVersion >= 4 && Environment.isSdkAtLeastB() && wifiDirectR2()) { + pairingBootstrappingMethods = convertAidlPairingBootstrappingMethodsToFramework( + deviceFoundEventParams.pairingBootstrappingMethods); + if (deviceFoundEventParams.dirInfo != null) { + try { + dirInfo = new WifiP2pDirInfo(MacAddress.fromBytes( + deviceFoundEventParams.dirInfo.deviceInterfaceMacAddress), + deviceFoundEventParams.dirInfo.nonce, + deviceFoundEventParams.dirInfo.dirTag); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Could not decode MAC Address from DirInfo ", e); + dirInfo = null; + } + } + } + handleDeviceFound( deviceFoundEventParams.srcAddress, deviceFoundEventParams.p2pDeviceAddress, @@ -883,14 +1116,39 @@ public class SupplicantP2pIfaceCallbackAidlImpl extends ISupplicantP2pIfaceCallb deviceFoundEventParams.wfdDeviceInfo, deviceFoundEventParams.wfdR2DeviceInfo, deviceFoundEventParams.vendorElemBytes, - vendorData); + vendorData, pairingBootstrappingMethods, dirInfo); } + /* + * Prepare broadcast message that a P2P device has been found. + * + * @param srcAddress MAC address of the device found. This must either + * be the P2P device address or the P2P interface address. + * @param p2pDeviceAddress P2P device address. + * @param primaryDeviceType Type of device. Refer to section B.1 of Wifi P2P + * Technical specification v1.2. + * @param deviceName Name of the device. + * @param configMethods Mask of WPS configuration methods supported by the + * device. + * @param deviceCapabilities Refer to section 4.1.4 of Wifi P2P Technical + * specification v1.2. + * @param groupCapabilities Refer to section 4.1.4 of Wifi P2P Technical + * specification v1.2. + * @param wfdDeviceInfo WFD device info as described in section 5.1.2 of WFD + * technical specification v1.0.0. + * @param wfdR2DeviceInfo WFD R2 device info as described in section 5.1.12 of WFD + * technical specification v2.1. + * @param vendorElemBytes vendor-specific information elements. + * @param vendorData vendor-specific data. + * @param pairingBootstrappingMethods Supported pairing bootstrapping methods. + * @param dirInfo Pairing DIR information. + */ private void handleDeviceFound(byte[] srcAddress, byte[] p2pDeviceAddress, byte[] primaryDeviceType, String deviceName, int configMethods, byte deviceCapabilities, int groupCapabilities, byte[] wfdDeviceInfo, @Nullable byte[] wfdR2DeviceInfo, @Nullable byte[] vendorElemBytes, - @Nullable List<OuiKeyedData> vendorData) { + @Nullable List<OuiKeyedData> vendorData, + int pairingBootstrappingMethods, @Nullable WifiP2pDirInfo dirInfo) { WifiP2pDevice device = new WifiP2pDevice(); device.deviceName = deviceName; if (deviceName == null) { @@ -905,11 +1163,17 @@ public class SupplicantP2pIfaceCallbackAidlImpl extends ISupplicantP2pIfaceCallb return; } - try { - device.primaryDeviceType = NativeUtil.wpsDevTypeStringFromByteArray(primaryDeviceType); - } catch (Exception e) { - Log.e(TAG, "Could not encode device primary type.", e); - return; + // Device Type is present only in WFD R1 device discovery. So in case of USD based + // discovery where pairing bootstrapping method is advertised, skip checking the + // Device type. + if (pairingBootstrappingMethods == 0) { + try { + device.primaryDeviceType = NativeUtil.wpsDevTypeStringFromByteArray( + primaryDeviceType); + } catch (Exception e) { + Log.e(TAG, "Could not encode device primary type.", e); + return; + } } device.deviceCapability = deviceCapabilities; @@ -955,6 +1219,12 @@ public class SupplicantP2pIfaceCallbackAidlImpl extends ISupplicantP2pIfaceCallb device.setVendorData(vendorData); } + + if (Environment.isSdkAtLeastB()) { + device.setPairingBootStrappingMethods(pairingBootstrappingMethods); + device.dirInfo = dirInfo; + } + logd("Device discovered on " + mInterface + ": " + device); mMonitor.broadcastP2pDeviceFound(mInterface, device); } @@ -1015,6 +1285,41 @@ public class SupplicantP2pIfaceCallbackAidlImpl extends ISupplicantP2pIfaceCallb return result; } + @VisibleForTesting + private static int convertAidlPairingBootstrappingMethodsToFramework( + int aidlPairingBootstrappingMethods) { + int pairingBootstrappingMethods = 0; + + if ((aidlPairingBootstrappingMethods + & P2pPairingBootstrappingMethodMask.BOOTSTRAPPING_OPPORTUNISTIC) != 0) { + pairingBootstrappingMethods |= WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_OPPORTUNISTIC; + } + if ((aidlPairingBootstrappingMethods + & P2pPairingBootstrappingMethodMask.BOOTSTRAPPING_DISPLAY_PINCODE) != 0) { + pairingBootstrappingMethods |= WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_DISPLAY_PINCODE; + } + if ((aidlPairingBootstrappingMethods + & P2pPairingBootstrappingMethodMask.BOOTSTRAPPING_DISPLAY_PASSPHRASE) != 0) { + pairingBootstrappingMethods |= WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_DISPLAY_PASSPHRASE; + } + if ((aidlPairingBootstrappingMethods + & P2pPairingBootstrappingMethodMask.BOOTSTRAPPING_KEYPAD_PINCODE) != 0) { + pairingBootstrappingMethods |= WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PINCODE; + } + if ((aidlPairingBootstrappingMethods + & P2pPairingBootstrappingMethodMask.BOOTSTRAPPING_KEYPAD_PASSPHRASE) != 0) { + pairingBootstrappingMethods |= WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PASSPHRASE; + } + + return pairingBootstrappingMethods; + + } + @Override public String getInterfaceHash() { return ISupplicantP2pIfaceCallback.HASH; diff --git a/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceCallbackHidlImpl.java b/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceCallbackHidlImpl.java index 0cb0cd2354..3deee61d61 100644 --- a/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceCallbackHidlImpl.java +++ b/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceCallbackHidlImpl.java @@ -438,26 +438,26 @@ public class SupplicantP2pIfaceCallbackHidlImpl extends ISupplicantP2pIfaceCallb if ((configMethods & WpsConfigMethods.PUSHBUTTON) != 0) { if (isRequest) { - event.event = WifiP2pProvDiscEvent.PBC_REQ; + event.event = WifiP2pProvDiscEvent.WPS_PBC_REQ; mMonitor.broadcastP2pProvisionDiscoveryPbcRequest(mInterface, event); } else { - event.event = WifiP2pProvDiscEvent.PBC_RSP; + event.event = WifiP2pProvDiscEvent.WPS_PBC_RSP; mMonitor.broadcastP2pProvisionDiscoveryPbcResponse(mInterface, event); } } else if (!isRequest && (configMethods & WpsConfigMethods.KEYPAD) != 0) { - event.event = WifiP2pProvDiscEvent.SHOW_PIN; - event.pin = generatedPin; + event.event = WifiP2pProvDiscEvent.WPS_SHOW_PIN; + event.wpsPin = generatedPin; mMonitor.broadcastP2pProvisionDiscoveryShowPin(mInterface, event); } else if (!isRequest && (configMethods & WpsConfigMethods.DISPLAY) != 0) { - event.event = WifiP2pProvDiscEvent.ENTER_PIN; - event.pin = generatedPin; + event.event = WifiP2pProvDiscEvent.WPS_ENTER_PIN; + event.wpsPin = generatedPin; mMonitor.broadcastP2pProvisionDiscoveryEnterPin(mInterface, event); } else if (isRequest && (configMethods & WpsConfigMethods.DISPLAY) != 0) { - event.event = WifiP2pProvDiscEvent.SHOW_PIN; - event.pin = generatedPin; + event.event = WifiP2pProvDiscEvent.WPS_SHOW_PIN; + event.wpsPin = generatedPin; mMonitor.broadcastP2pProvisionDiscoveryShowPin(mInterface, event); } else if (isRequest && (configMethods & WpsConfigMethods.KEYPAD) != 0) { - event.event = WifiP2pProvDiscEvent.ENTER_PIN; + event.event = WifiP2pProvDiscEvent.WPS_ENTER_PIN; mMonitor.broadcastP2pProvisionDiscoveryEnterPin(mInterface, event); } else { Log.e(TAG, "Unsupported config methods: " + configMethods); diff --git a/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceHal.java b/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceHal.java index b621e76f6a..f56500e84e 100644 --- a/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceHal.java +++ b/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceHal.java @@ -21,12 +21,16 @@ import android.annotation.Nullable; import android.net.wifi.CoexUnsafeChannel; import android.net.wifi.ScanResult; import android.net.wifi.p2p.WifiP2pConfig; +import android.net.wifi.p2p.WifiP2pDirInfo; import android.net.wifi.p2p.WifiP2pDiscoveryConfig; import android.net.wifi.p2p.WifiP2pExtListenParams; import android.net.wifi.p2p.WifiP2pGroup; import android.net.wifi.p2p.WifiP2pGroupList; import android.net.wifi.p2p.WifiP2pManager; +import android.net.wifi.p2p.WifiP2pUsdBasedLocalServiceAdvertisementConfig; +import android.net.wifi.p2p.WifiP2pUsdBasedServiceDiscoveryConfig; import android.net.wifi.p2p.nsd.WifiP2pServiceInfo; +import android.net.wifi.p2p.nsd.WifiP2pUsdBasedServiceConfig; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; @@ -525,16 +529,17 @@ public class SupplicantP2pIfaceHal { * * @param networkId Used to specify the restart of a persistent group. * @param isPersistent Used to request a persistent group to be formed. + * @param isP2pV2 Used to start a Group Owner that support P2P2 IE. * * @return true, if operation was successful. */ - public boolean groupAdd(int networkId, boolean isPersistent) { + public boolean groupAdd(int networkId, boolean isPersistent, boolean isP2pV2) { synchronized (mLock) { String methodStr = "groupAdd"; if (mP2pIfaceHal == null) { return handleNullHal(methodStr); } - return mP2pIfaceHal.groupAdd(networkId, isPersistent); + return mP2pIfaceHal.groupAdd(networkId, isPersistent, isP2pV2); } } @@ -543,13 +548,14 @@ public class SupplicantP2pIfaceHal { * This is a helper method that invokes groupAdd(networkId, isPersistent) internally. * * @param isPersistent Used to request a persistent group to be formed. + * @param isP2pV2 Used to start a Group Owner that support P2P2 IE. * * @return true, if operation was successful. */ - public boolean groupAdd(boolean isPersistent) { + public boolean groupAdd(boolean isPersistent, boolean isP2pV2) { synchronized (mLock) { // Supplicant expects networkId to be -1 if not supplied. - return groupAdd(-1, isPersistent); + return groupAdd(-1, isPersistent, isP2pV2); } } @@ -568,13 +574,14 @@ public class SupplicantP2pIfaceHal { * @return true, if operation was successful. */ public boolean groupAdd(String networkName, String passphrase, + @WifiP2pConfig.PccModeConnectionType int connectionType, boolean isPersistent, int freq, String peerAddress, boolean join) { synchronized (mLock) { String methodStr = "groupAdd"; if (mP2pIfaceHal == null) { return handleNullHal(methodStr); } - return mP2pIfaceHal.groupAdd(networkName, passphrase, + return mP2pIfaceHal.groupAdd(networkName, passphrase, connectionType, isPersistent, freq, peerAddress, join); } } @@ -1161,6 +1168,139 @@ public class SupplicantP2pIfaceHal { } /** + * Start an Un-synchronized Service Discovery (USD) based P2P service discovery. + * + * @param usdServiceConfig is the USD based service configuration. + * @param discoveryConfig is the configuration for this service discovery request. + * @param timeoutInSeconds is the maximum time to be spent for this service discovery request. + */ + public int startUsdBasedServiceDiscovery(WifiP2pUsdBasedServiceConfig usdServiceConfig, + WifiP2pUsdBasedServiceDiscoveryConfig discoveryConfig, int timeoutInSeconds) { + synchronized (mLock) { + String methodStr = "startUsdBasedServiceDiscovery"; + if (mP2pIfaceHal == null) { + handleNullHal(methodStr); + return -1; + } + return mP2pIfaceHal.startUsdBasedServiceDiscovery(usdServiceConfig, discoveryConfig, + timeoutInSeconds); + } + } + + /** + * Stop an Un-synchronized Service Discovery (USD) based P2P service discovery. + * + * @param sessionId Identifier to cancel the service discovery instance. + * Use zero to cancel all the service discovery instances. + */ + public void stopUsdBasedServiceDiscovery(int sessionId) { + synchronized (mLock) { + String methodStr = "stopUsdBasedServiceDiscovery"; + if (mP2pIfaceHal == null) { + handleNullHal(methodStr); + return; + } + mP2pIfaceHal.stopUsdBasedServiceDiscovery(sessionId); + } + } + + /** + * Start an Un-synchronized Service Discovery (USD) based P2P service advertisement. + * + * @param usdServiceConfig is the USD based service configuration. + * @param advertisementConfig is the configuration for this service advertisement. + * @param timeoutInSeconds is the maximum time to be spent for this service advertisement. + */ + public int startUsdBasedServiceAdvertisement(WifiP2pUsdBasedServiceConfig usdServiceConfig, + WifiP2pUsdBasedLocalServiceAdvertisementConfig advertisementConfig, + int timeoutInSeconds) { + synchronized (mLock) { + String methodStr = "startUsdBasedServiceAdvertisement"; + if (mP2pIfaceHal == null) { + handleNullHal(methodStr); + return -1; + } + return mP2pIfaceHal.startUsdBasedServiceAdvertisement(usdServiceConfig, + advertisementConfig, timeoutInSeconds); + } + } + + /** + * Stop an Un-synchronized Service Discovery (USD) based P2P service advertisement. + * + * @param sessionId Identifier to cancel the service advertisement. + * Use zero to cancel all the service advertisement instances. + */ + public void stopUsdBasedServiceAdvertisement(int sessionId) { + synchronized (mLock) { + String methodStr = "stopUsdBasedServiceAdvertisement"; + if (mP2pIfaceHal == null) { + handleNullHal(methodStr); + return; + } + mP2pIfaceHal.stopUsdBasedServiceAdvertisement(sessionId); + } + } + + /** + * Get the Device Identity Resolution (DIR) Information. + * See {@link WifiP2pDirInfo} for details + * + * @return {@link WifiP2pDirInfo} instance on success, null on failure. + */ + public WifiP2pDirInfo getDirInfo() { + synchronized (mLock) { + String methodStr = "getDirInfo"; + if (mP2pIfaceHal == null) { + handleNullHal(methodStr); + return null; + } + return mP2pIfaceHal.getDirInfo(); + } + } + + /** + * Validate the Device Identity Resolution (DIR) Information of a P2P device. + * See {@link WifiP2pDirInfo} for details. + * + * @param dirInfo {@link WifiP2pDirInfo} to validate. + * @return The identifier of device identity key on success, -1 on failure. + */ + public int validateDirInfo(@NonNull WifiP2pDirInfo dirInfo) { + synchronized (mLock) { + String methodStr = "validateDirInfo"; + if (mP2pIfaceHal == null) { + handleNullHal(methodStr); + return -1; + } + return mP2pIfaceHal.validateDirInfo(dirInfo); + } + } + + /** + * Used to authorize a connection request to an existing Group Owner + * interface, to allow a peer device to connect. + * + * @param config Configuration to use for connection. + * @param groupOwnerInterfaceName Group Owner interface name on which the request to connect + * needs to be authorized. + * + * @return boolean value indicating whether operation was successful. + */ + public boolean authorizeConnectRequestOnGroupOwner( + WifiP2pConfig config, String groupOwnerInterfaceName) { + synchronized (mLock) { + String methodStr = "connect"; + if (mP2pIfaceHal == null) { + handleNullHal(methodStr); + return false; + } + return mP2pIfaceHal.authorizeConnectRequestOnGroupOwner(config, + groupOwnerInterfaceName); + } + } + + /** * Terminate the supplicant daemon & wait for its death. */ public void terminate() { diff --git a/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceHalAidlImpl.java b/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceHalAidlImpl.java index a8956b4e18..b2b70805c2 100644 --- a/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceHalAidlImpl.java +++ b/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceHalAidlImpl.java @@ -16,8 +16,13 @@ package com.android.server.wifi.p2p; +import static android.net.wifi.p2p.WifiP2pManager.FEATURE_PCC_MODE_ALLOW_LEGACY_AND_R2_CONNECTION; +import static android.net.wifi.p2p.WifiP2pManager.FEATURE_WIFI_DIRECT_R2; + import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; +import android.hardware.wifi.supplicant.BandMask; import android.hardware.wifi.supplicant.DebugLevel; import android.hardware.wifi.supplicant.FreqRange; import android.hardware.wifi.supplicant.ISupplicant; @@ -26,25 +31,41 @@ import android.hardware.wifi.supplicant.ISupplicantP2pIfaceCallback; import android.hardware.wifi.supplicant.ISupplicantP2pNetwork; import android.hardware.wifi.supplicant.IfaceInfo; import android.hardware.wifi.supplicant.IfaceType; +import android.hardware.wifi.supplicant.KeyMgmtMask; import android.hardware.wifi.supplicant.MiracastMode; +import android.hardware.wifi.supplicant.P2pAddGroupConfigurationParams; import android.hardware.wifi.supplicant.P2pConnectInfo; +import android.hardware.wifi.supplicant.P2pCreateGroupOwnerInfo; +import android.hardware.wifi.supplicant.P2pDirInfo; import android.hardware.wifi.supplicant.P2pDiscoveryInfo; import android.hardware.wifi.supplicant.P2pExtListenInfo; import android.hardware.wifi.supplicant.P2pFrameTypeMask; +import android.hardware.wifi.supplicant.P2pPairingBootstrappingMethodMask; +import android.hardware.wifi.supplicant.P2pProvisionDiscoveryParams; import android.hardware.wifi.supplicant.P2pScanType; +import android.hardware.wifi.supplicant.P2pUsdBasedServiceAdvertisementConfig; +import android.hardware.wifi.supplicant.P2pUsdBasedServiceDiscoveryConfig; import android.hardware.wifi.supplicant.WpsConfigMethods; import android.hardware.wifi.supplicant.WpsProvisionMethod; +import android.net.MacAddress; import android.net.wifi.CoexUnsafeChannel; +import android.net.wifi.OuiKeyedData; import android.net.wifi.ScanResult; import android.net.wifi.WpsInfo; import android.net.wifi.p2p.WifiP2pConfig; import android.net.wifi.p2p.WifiP2pDevice; +import android.net.wifi.p2p.WifiP2pDirInfo; import android.net.wifi.p2p.WifiP2pDiscoveryConfig; import android.net.wifi.p2p.WifiP2pExtListenParams; import android.net.wifi.p2p.WifiP2pGroup; import android.net.wifi.p2p.WifiP2pGroupList; import android.net.wifi.p2p.WifiP2pManager; +import android.net.wifi.p2p.WifiP2pPairingBootstrappingConfig; +import android.net.wifi.p2p.WifiP2pUsdBasedLocalServiceAdvertisementConfig; +import android.net.wifi.p2p.WifiP2pUsdBasedServiceDiscoveryConfig; import android.net.wifi.p2p.nsd.WifiP2pServiceInfo; +import android.net.wifi.p2p.nsd.WifiP2pUsdBasedServiceConfig; +import android.net.wifi.util.Environment; import android.os.IBinder; import android.os.IBinder.DeathRecipient; import android.os.RemoteException; @@ -61,6 +82,7 @@ import com.android.server.wifi.WifiSettingsConfigStore; import com.android.server.wifi.util.ArrayUtils; import com.android.server.wifi.util.HalAidlUtil; import com.android.server.wifi.util.NativeUtil; +import com.android.wifi.flags.Flags; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -806,23 +828,58 @@ public class SupplicantP2pIfaceHalAidlImpl implements ISupplicantP2pIfaceHal { } } - /** - * Start P2P group formation with a discovered P2P peer. This includes - * optional group owner negotiation, group interface setup, provisioning, - * and establishing data connection. - * - * @param config Configuration to use to connect to remote device. - * @param joinExistingGroup Indicates that this is a command to join an - * existing group as a client. It skips the group owner negotiation - * part. This must send a Provision Discovery Request message to the - * target group owner before associating for WPS provisioning. - * - * @return String containing generated pin, if selected provision method - * uses PIN. - */ - public String connect(WifiP2pConfig config, boolean joinExistingGroup) { + private String connectWithParams(boolean joinExistingGroup, byte[] peerAddress, + int provisionMethod, String preSelectedPin, boolean persistent, int groupOwnerIntent, + @NonNull List<OuiKeyedData> vendorData, int pairingBootstrappingMethod, + String pairingPassword, int frequencyMHz, boolean authorize, + String groupInterfaceName) { synchronized (mLock) { - String methodStr = "connect"; + String methodStr = "connectWithParams"; + + // Parameters should be pre-validated. + P2pConnectInfo info = new P2pConnectInfo(); + info.joinExistingGroup = joinExistingGroup; + info.peerAddress = peerAddress; + info.provisionMethod = provisionMethod; + info.preSelectedPin = preSelectedPin; + info.persistent = persistent; + info.goIntent = groupOwnerIntent; + + if (!vendorData.isEmpty()) { + info.vendorData = HalAidlUtil.frameworkToHalOuiKeyedDataList(vendorData); + } + if (getCachedServiceVersion() >= 4 && pairingBootstrappingMethod != 0) { + info.pairingBootstrappingMethod = pairingBootstrappingMethod; + info.password = pairingPassword; + info.frequencyMHz = frequencyMHz; + info.authorizeConnectionFromPeer = authorize; + info.groupInterfaceName = groupInterfaceName; + } + + + try { + return mISupplicantP2pIface.connectWithParams(info); + } catch (RemoteException e) { + handleRemoteException(e, methodStr); + } catch (ServiceSpecificException e) { + handleServiceSpecificException(e, methodStr); + } + return null; + } + } + + @SuppressLint("NewApi") + private String connectInternal(WifiP2pConfig config, boolean joinExistingGroup, + String groupInterfaceName) { + synchronized (mLock) { + String methodStr = "connectInternal"; + int provisionMethod = WpsProvisionMethod.NONE; + String preSelectedPin = ""; + int pairingBootstrappingMethod = 0; + String pairingPassword = ""; + int frequencyMHz = 0; + boolean authorize = false; + List<OuiKeyedData> vendorData = new ArrayList<>(); if (!checkP2pIfaceAndLogFailure(methodStr)) { return null; @@ -835,9 +892,54 @@ public class SupplicantP2pIfaceHalAidlImpl implements ISupplicantP2pIfaceHal { Log.e(TAG, "Could not parse null mac address."); return null; } - if (config.wps.setup == WpsInfo.PBC && !TextUtils.isEmpty(config.wps.pin)) { - Log.e(TAG, "Expected empty pin for PBC."); - return null; + + if (Environment.isSdkAtLeastB() && Flags.wifiDirectR2() + && config.getPairingBootstrappingConfig() != null) { + pairingBootstrappingMethod = convertPairingBootstrappingMethodToAidl( + config.getPairingBootstrappingConfig().getPairingBootstrappingMethod()); + if (pairingBootstrappingMethod == RESULT_NOT_VALID) { + Log.e(TAG, "Unrecognized pairing bootstrapping method: " + + config.getPairingBootstrappingConfig() + .getPairingBootstrappingMethod()); + return null; + } + pairingPassword = config.getPairingBootstrappingConfig() + .getPairingBootstrappingPassword(); + // All other pairing bootstrapping methods other than opportunistic method requires + // a pairing pin/password. + if (pairingBootstrappingMethod == WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_OPPORTUNISTIC) { + if (!TextUtils.isEmpty( + pairingPassword)) { + Log.e(TAG, "Expected empty Pin/Password for opportunistic" + + "bootstrapping"); + return null; + } + } else { // Non-opportunistic methods + if (TextUtils.isEmpty(pairingPassword)) { + Log.e(TAG, "Pin/Password is not set for AIDL bootstrapping method: " + + pairingBootstrappingMethod); + return null; + } + } + // WifiP2pConfig Builder treat it as frequency. + if (config.groupOwnerBand != 0 && config.groupOwnerBand != 2 + && config.groupOwnerBand != 5 && config.groupOwnerBand != 6) { + frequencyMHz = config.groupOwnerBand; + } + authorize = config.isAuthorizeConnectionFromPeerEnabled(); + } else { + if (config.wps.setup == WpsInfo.PBC && !TextUtils.isEmpty(config.wps.pin)) { + Log.e(TAG, "Expected empty pin for PBC."); + return null; + } + provisionMethod = wpsInfoToConfigMethod(config.wps.setup); + if (provisionMethod == RESULT_NOT_VALID) { + Log.e(TAG, "Invalid WPS config method: " + config.wps.setup); + return null; + } + // NOTE: preSelectedPin cannot be null, otherwise hal would crash. + preSelectedPin = TextUtils.isEmpty(config.wps.pin) ? "" : config.wps.pin; } byte[] peerAddress = null; @@ -848,13 +950,6 @@ public class SupplicantP2pIfaceHalAidlImpl implements ISupplicantP2pIfaceHal { return null; } - int provisionMethod = wpsInfoToConfigMethod(config.wps.setup); - if (provisionMethod == RESULT_NOT_VALID) { - Log.e(TAG, "Invalid WPS config method: " + config.wps.setup); - return null; - } - // NOTE: preSelectedPin cannot be null, otherwise hal would crash. - String preSelectedPin = TextUtils.isEmpty(config.wps.pin) ? "" : config.wps.pin; boolean persistent = (config.netId == WifiP2pGroup.NETWORK_ID_PERSISTENT); if (config.groupOwnerIntent < 0 || config.groupOwnerIntent > 15) { @@ -863,8 +958,14 @@ public class SupplicantP2pIfaceHalAidlImpl implements ISupplicantP2pIfaceHal { } if (getCachedServiceVersion() >= 3) { + if (SdkLevel.isAtLeastV() && config.getVendorData() != null + && !config.getVendorData().isEmpty()) { + vendorData = config.getVendorData(); + } return connectWithParams(joinExistingGroup, peerAddress, provisionMethod, - preSelectedPin, persistent, config.groupOwnerIntent, config); + preSelectedPin, persistent, config.groupOwnerIntent, vendorData, + pairingBootstrappingMethod, pairingPassword, frequencyMHz, authorize, + groupInterfaceName); } try { @@ -881,6 +982,43 @@ public class SupplicantP2pIfaceHalAidlImpl implements ISupplicantP2pIfaceHal { } /** + * Used to authorize a connection request to an existing Group Owner + * interface, to allow a peer device to connect. + * + * @param config Configuration to use for connection. + * @param groupOwnerInterfaceName Group Owner interface name on which the request to connect + * needs to be authorized. + * + * @return boolean value indicating whether operation was successful. + */ + public boolean authorizeConnectRequestOnGroupOwner( + WifiP2pConfig config, String groupOwnerInterfaceName) { + if (TextUtils.isEmpty(groupOwnerInterfaceName)) { + Log.e(TAG, "authorizeConnectRequestOnGroupOwner: group owner interface is null"); + return false; + } + return connectInternal(config, false, groupOwnerInterfaceName) != null; + } + + /** + * Start P2P group formation with a discovered P2P peer. This includes + * optional group owner negotiation, group interface setup, provisioning, + * and establishing data connection. + * + * @param config Configuration to use to connect to remote device. + * @param joinExistingGroup Indicates that this is a command to join an + * existing group as a client. It skips the group owner negotiation + * part. This must send a Provision Discovery Request message to the + * target group owner before associating for WPS provisioning. + * + * @return String containing generated pin, if selected provision method + * uses PIN. + */ + public String connect(WifiP2pConfig config, boolean joinExistingGroup) { + return connectInternal(config, joinExistingGroup, null); + } + + /** * Cancel an ongoing P2P group formation and joining-a-group related * operation. This operation unauthorizes the specific peer device (if any * had been authorized to start group formation), stops P2P find (if in @@ -918,9 +1056,12 @@ public class SupplicantP2pIfaceHalAidlImpl implements ISupplicantP2pIfaceHal { * * @return boolean value indicating whether operation was successful. */ + @SuppressLint("NewApi") public boolean provisionDiscovery(WifiP2pConfig config) { synchronized (mLock) { String methodStr = "provisionDiscovery"; + int pairingBootstrappingMethod = 0; + int targetWpsMethod = 0; if (!checkP2pIfaceAndLogFailure("provisionDiscovery")) { return false; } @@ -928,17 +1069,53 @@ public class SupplicantP2pIfaceHalAidlImpl implements ISupplicantP2pIfaceHal { return false; } - int targetMethod = wpsInfoToConfigMethod(config.wps.setup); - if (targetMethod == RESULT_NOT_VALID) { - Log.e(TAG, "Unrecognized WPS configuration method: " + config.wps.setup); - return false; - } - if (targetMethod == WpsProvisionMethod.DISPLAY) { - // We are doing display, so provision discovery is keypad. - targetMethod = WpsProvisionMethod.KEYPAD; - } else if (targetMethod == WpsProvisionMethod.KEYPAD) { - // We are doing keypad, so provision discovery is display. - targetMethod = WpsProvisionMethod.DISPLAY; + if (Environment.isSdkAtLeastB() && Flags.wifiDirectR2() + && config.getPairingBootstrappingConfig() != null) { + pairingBootstrappingMethod = convertPairingBootstrappingMethodToAidl( + config.getPairingBootstrappingConfig().getPairingBootstrappingMethod()); + if (pairingBootstrappingMethod == RESULT_NOT_VALID) { + Log.e(TAG, "Unrecognized pairing bootstrapping method: " + + config.getPairingBootstrappingConfig() + .getPairingBootstrappingMethod()); + return false; + } + if (pairingBootstrappingMethod + == P2pPairingBootstrappingMethodMask.BOOTSTRAPPING_OUT_OF_BAND) { + Log.e(TAG, "Provisioning phase is not required for Bootstrapping method" + + " out of band! Fail"); + return false; + } + if (pairingBootstrappingMethod + == P2pPairingBootstrappingMethodMask.BOOTSTRAPPING_DISPLAY_PINCODE) { + pairingBootstrappingMethod = + P2pPairingBootstrappingMethodMask.BOOTSTRAPPING_KEYPAD_PINCODE; + } else if (pairingBootstrappingMethod + == P2pPairingBootstrappingMethodMask.BOOTSTRAPPING_KEYPAD_PINCODE) { + pairingBootstrappingMethod = + P2pPairingBootstrappingMethodMask.BOOTSTRAPPING_DISPLAY_PINCODE; + } else if (pairingBootstrappingMethod + == P2pPairingBootstrappingMethodMask.BOOTSTRAPPING_DISPLAY_PASSPHRASE) { + pairingBootstrappingMethod = + P2pPairingBootstrappingMethodMask.BOOTSTRAPPING_KEYPAD_PASSPHRASE; + } else if (pairingBootstrappingMethod + == P2pPairingBootstrappingMethodMask.BOOTSTRAPPING_KEYPAD_PASSPHRASE) { + pairingBootstrappingMethod = + P2pPairingBootstrappingMethodMask.BOOTSTRAPPING_DISPLAY_PASSPHRASE; + } + targetWpsMethod = WpsProvisionMethod.NONE; + } else { + targetWpsMethod = wpsInfoToConfigMethod(config.wps.setup); + if (targetWpsMethod == RESULT_NOT_VALID) { + Log.e(TAG, "Unrecognized WPS configuration method: " + config.wps.setup); + return false; + } + if (targetWpsMethod == WpsProvisionMethod.DISPLAY) { + // We are doing display, so provision discovery is keypad. + targetWpsMethod = WpsProvisionMethod.KEYPAD; + } else if (targetWpsMethod == WpsProvisionMethod.KEYPAD) { + // We are doing keypad, so provision discovery is display. + targetWpsMethod = WpsProvisionMethod.DISPLAY; + } } if (config.deviceAddress == null) { @@ -953,8 +1130,13 @@ public class SupplicantP2pIfaceHalAidlImpl implements ISupplicantP2pIfaceHal { return false; } + if (getCachedServiceVersion() >= 4) { + return provisionDiscoveryWithParams(macAddress, targetWpsMethod, + pairingBootstrappingMethod); + } + try { - mISupplicantP2pIface.provisionDiscovery(macAddress, targetMethod); + mISupplicantP2pIface.provisionDiscovery(macAddress, targetWpsMethod); return true; } catch (RemoteException e) { handleRemoteException(e, methodStr); @@ -965,6 +1147,48 @@ public class SupplicantP2pIfaceHalAidlImpl implements ISupplicantP2pIfaceHal { } } + private boolean provisionDiscoveryWithParams(byte[] macAddress, int targetMethod, + int pairingBootstrappingMethod) { + String methodStr = "provisionDiscoveryWithParams"; + + // Expect that these parameters are already validated. + P2pProvisionDiscoveryParams params = new P2pProvisionDiscoveryParams(); + params.peerMacAddress = macAddress; + params.provisionMethod = targetMethod; + params.pairingBootstrappingMethod = pairingBootstrappingMethod; + try { + mISupplicantP2pIface.provisionDiscoveryWithParams(params); + return true; + } catch (RemoteException e) { + handleRemoteException(e, methodStr); + } catch (ServiceSpecificException e) { + handleServiceSpecificException(e, methodStr); + } + return false; + } + + @VisibleForTesting + protected static int convertPairingBootstrappingMethodToAidl(int pairingBootstrappingMethod) { + switch (pairingBootstrappingMethod) { + case WifiP2pPairingBootstrappingConfig.PAIRING_BOOTSTRAPPING_METHOD_OPPORTUNISTIC: + return P2pPairingBootstrappingMethodMask.BOOTSTRAPPING_OPPORTUNISTIC; + case WifiP2pPairingBootstrappingConfig.PAIRING_BOOTSTRAPPING_METHOD_DISPLAY_PINCODE: + return P2pPairingBootstrappingMethodMask.BOOTSTRAPPING_DISPLAY_PINCODE; + case WifiP2pPairingBootstrappingConfig.PAIRING_BOOTSTRAPPING_METHOD_DISPLAY_PASSPHRASE: + return P2pPairingBootstrappingMethodMask.BOOTSTRAPPING_DISPLAY_PASSPHRASE; + case WifiP2pPairingBootstrappingConfig.PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PINCODE: + return P2pPairingBootstrappingMethodMask.BOOTSTRAPPING_KEYPAD_PINCODE; + case WifiP2pPairingBootstrappingConfig.PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PASSPHRASE: + return P2pPairingBootstrappingMethodMask.BOOTSTRAPPING_KEYPAD_PASSPHRASE; + case WifiP2pPairingBootstrappingConfig.PAIRING_BOOTSTRAPPING_METHOD_OUT_OF_BAND: + return P2pPairingBootstrappingMethodMask.BOOTSTRAPPING_OUT_OF_BAND; + default: + Log.e(TAG, "Unsupported pairingBootstrappingMethod: " + + pairingBootstrappingMethod); + return RESULT_NOT_VALID; + } + } + /** * Invite a device to a persistent group. * If the peer device is the group owner of the persistent group, the peer @@ -1185,15 +1409,19 @@ public class SupplicantP2pIfaceHalAidlImpl implements ISupplicantP2pIfaceHal { * * @param networkId Used to specify the restart of a persistent group. * @param isPersistent Used to request a persistent group to be formed. + * @param isP2pV2 Used to start a Group Owner that support P2P2 IE * * @return true, if operation was successful. */ - public boolean groupAdd(int networkId, boolean isPersistent) { + public boolean groupAdd(int networkId, boolean isPersistent, boolean isP2pV2) { synchronized (mLock) { String methodStr = "groupAdd"; if (!checkP2pIfaceAndLogFailure(methodStr)) { return false; } + if (getCachedServiceVersion() >= 4) { + return createGroupOwner(networkId, isPersistent, isP2pV2); + } try { mISupplicantP2pIface.addGroup(isPersistent, networkId); return true; @@ -1206,6 +1434,26 @@ public class SupplicantP2pIfaceHalAidlImpl implements ISupplicantP2pIfaceHal { } } + private boolean createGroupOwner(int networkId, boolean isPersistent, boolean isP2pV2) { + String methodStr = "createGroupOwner"; + + // Expect that these parameters are already validated. + P2pCreateGroupOwnerInfo groupOwnerInfo = + new P2pCreateGroupOwnerInfo(); + groupOwnerInfo.persistent = isPersistent; + groupOwnerInfo.persistentNetworkId = networkId; + groupOwnerInfo.isP2pV2 = isP2pV2; + try { + mISupplicantP2pIface.createGroupOwner(groupOwnerInfo); + return true; + } catch (RemoteException e) { + handleRemoteException(e, methodStr); + } catch (ServiceSpecificException e) { + handleServiceSpecificException(e, methodStr); + } + return false; + } + /** * Set up a P2P group as Group Owner or join a group with a configuration. * @@ -1221,6 +1469,7 @@ public class SupplicantP2pIfaceHalAidlImpl implements ISupplicantP2pIfaceHal { * @return true, if operation was successful. */ public boolean groupAdd(String networkName, String passphrase, + @WifiP2pConfig.PccModeConnectionType int connectionType, boolean isPersistent, int freq, String peerAddress, boolean join) { synchronized (mLock) { String methodStr = "groupAdd"; @@ -1245,6 +1494,11 @@ public class SupplicantP2pIfaceHalAidlImpl implements ISupplicantP2pIfaceHal { return false; } + if (getCachedServiceVersion() >= 4) { + return addGroupWithConfigurationParams( + ssid, passphrase, connectionType, isPersistent, freq, macAddress, join); + } + try { mISupplicantP2pIface.addGroupWithConfig( ssid, passphrase, isPersistent, freq, macAddress, join); @@ -1258,6 +1512,46 @@ public class SupplicantP2pIfaceHalAidlImpl implements ISupplicantP2pIfaceHal { } } + private boolean addGroupWithConfigurationParams(byte[] ssid, String passphrase, + @WifiP2pConfig.PccModeConnectionType int connectionType, + boolean isPersistent, int freq, byte[] macAddress, boolean join) { + String methodStr = "addGroupWithConfigurationParams"; + + // Expect that these parameters are already validated. + P2pAddGroupConfigurationParams groupConfigurationParams = + new P2pAddGroupConfigurationParams(); + groupConfigurationParams.ssid = ssid; + groupConfigurationParams.passphrase = passphrase; + groupConfigurationParams.isPersistent = isPersistent; + groupConfigurationParams.frequencyMHzOrBand = freq; + groupConfigurationParams.goInterfaceAddress = macAddress; + groupConfigurationParams.joinExistingGroup = join; + groupConfigurationParams.keyMgmtMask = p2pConfigConnectionTypeToSupplicantKeyMgmtMask( + connectionType); + try { + mISupplicantP2pIface.addGroupWithConfigurationParams(groupConfigurationParams); + return true; + } catch (RemoteException e) { + handleRemoteException(e, methodStr); + } catch (ServiceSpecificException e) { + handleServiceSpecificException(e, methodStr); + } + return false; + } + + private static int p2pConfigConnectionTypeToSupplicantKeyMgmtMask( + @WifiP2pConfig.PccModeConnectionType int connectionType) { + int keyMgmtMask = 0; + if (WifiP2pConfig.PCC_MODE_CONNECTION_TYPE_R2_ONLY == connectionType) { + keyMgmtMask = KeyMgmtMask.SAE; + } else if (WifiP2pConfig.PCC_MODE_CONNECTION_TYPE_LEGACY_OR_R2 == connectionType) { + keyMgmtMask = KeyMgmtMask.WPA_PSK | KeyMgmtMask.SAE; + } else { + keyMgmtMask = KeyMgmtMask.WPA_PSK; + } + return keyMgmtMask; + } + /** * Terminate a P2P group. If a new virtual network interface was used for * the group, it must also be removed. The network interface name of the @@ -2602,14 +2896,34 @@ public class SupplicantP2pIfaceHalAidlImpl implements ISupplicantP2pIfaceHal { * @return bitmask defined by WifiP2pManager.FEATURE_* */ public long getSupportedFeatures() { - // First AIDL version supports these three features. - long result = WifiP2pManager.FEATURE_SET_VENDOR_ELEMENTS - | WifiP2pManager.FEATURE_FLEXIBLE_DISCOVERY - | WifiP2pManager.FEATURE_GROUP_CLIENT_REMOVAL; - if (getCachedServiceVersion() >= 2) { - result |= WifiP2pManager.FEATURE_GROUP_OWNER_IPV6_LINK_LOCAL_ADDRESS_PROVIDED; + synchronized (mLock) { + String methodStr = "getSupportedFeatures"; + long features = 0; + if (!checkP2pIfaceAndLogFailure(methodStr)) { + return 0; + } + if (getCachedServiceVersion() < 4) { + return 0; + } + + try { + long p2pHalfeatures = mISupplicantP2pIface.getFeatureSet(); + if ((p2pHalfeatures & mISupplicantP2pIface.P2P_FEATURE_V2) != 0) { + features |= FEATURE_WIFI_DIRECT_R2; + Log.i(TAG, "WIFI_DIRECT_R2 supported "); + } + if ((p2pHalfeatures & mISupplicantP2pIface.P2P_FEATURE_PCC_MODE_WPA3_COMPATIBILITY) + != 0) { + features |= FEATURE_PCC_MODE_ALLOW_LEGACY_AND_R2_CONNECTION; + Log.i(TAG, "PCC_MODE_ALLOW_LEGACY_AND_R2_CONNECTION supported "); + } + } catch (RemoteException e) { + handleRemoteException(e, methodStr); + } catch (ServiceSpecificException e) { + handleServiceSpecificException(e, methodStr); + } + return features; } - return result; } /** @@ -2648,6 +2962,229 @@ public class SupplicantP2pIfaceHalAidlImpl implements ISupplicantP2pIfaceHal { } } + /** + * Start an Un-synchronized Service Discovery (USD) based P2P service discovery. + * + * @param usdServiceConfig is the USD based service configuration. + * @param discoveryConfig is the configuration for this service discovery request. + * @param timeoutInSeconds is the maximum time to be spent for this service discovery request. + */ + @SuppressLint("NewApi") + public int startUsdBasedServiceDiscovery(WifiP2pUsdBasedServiceConfig usdServiceConfig, + WifiP2pUsdBasedServiceDiscoveryConfig discoveryConfig, int timeoutInSeconds) { + synchronized (mLock) { + String methodStr = "startUsdBasedServiceDiscovery"; + if (!checkP2pIfaceAndLogFailure(methodStr)) { + return -1; + } + if (getCachedServiceVersion() < 4) { + return -1; + } + if (usdServiceConfig == null || discoveryConfig == null) { + return -1; + } + try { + P2pUsdBasedServiceDiscoveryConfig aidlUsdBasedServiceDiscoveryConfig = + new P2pUsdBasedServiceDiscoveryConfig(); + aidlUsdBasedServiceDiscoveryConfig.serviceName = usdServiceConfig.getServiceName(); + aidlUsdBasedServiceDiscoveryConfig.serviceProtocolType = usdServiceConfig + .getServiceProtocolType(); + aidlUsdBasedServiceDiscoveryConfig.serviceSpecificInfo = usdServiceConfig + .getServiceSpecificInfo(); + if (discoveryConfig.getBand() != ScanResult.UNSPECIFIED) { + aidlUsdBasedServiceDiscoveryConfig.bandMask = + scanResultBandMaskToSupplicantHalWifiBandMask( + discoveryConfig.getBand()); + } else { + aidlUsdBasedServiceDiscoveryConfig.bandMask = 0; + } + aidlUsdBasedServiceDiscoveryConfig.frequencyListMhz = discoveryConfig + .getFrequenciesMhz(); + aidlUsdBasedServiceDiscoveryConfig.timeoutInSeconds = timeoutInSeconds; + return mISupplicantP2pIface.startUsdBasedServiceDiscovery( + aidlUsdBasedServiceDiscoveryConfig); + } catch (RemoteException e) { + handleRemoteException(e, methodStr); + } catch (ServiceSpecificException e) { + handleServiceSpecificException(e, methodStr); + } + return -1; + } + } + + /** + * Stop an Un-synchronized Service Discovery (USD) based P2P service discovery. + * + * @param sessionId Identifier to cancel the service discovery instance. + * Use zero to cancel all the service discovery instances. + */ + public void stopUsdBasedServiceDiscovery(int sessionId) { + synchronized (mLock) { + String methodStr = "stopUsdBasedServiceDiscovery"; + if (!checkP2pIfaceAndLogFailure(methodStr)) { + return; + } + if (getCachedServiceVersion() < 4) { + return; + } + try { + mISupplicantP2pIface.stopUsdBasedServiceDiscovery(sessionId); + } catch (RemoteException e) { + handleRemoteException(e, methodStr); + } catch (ServiceSpecificException e) { + handleServiceSpecificException(e, methodStr); + } + } + } + + /** + * Start an Un-synchronized Service Discovery (USD) based P2P service advertisement. + * + * @param usdServiceConfig is the USD based service configuration. + * @param advertisementConfig is the configuration for this service advertisement. + * @param timeoutInSeconds is the maximum time to be spent for this service advertisement. + */ + @SuppressLint("NewApi") + public int startUsdBasedServiceAdvertisement(WifiP2pUsdBasedServiceConfig usdServiceConfig, + WifiP2pUsdBasedLocalServiceAdvertisementConfig advertisementConfig, + int timeoutInSeconds) { + synchronized (mLock) { + String methodStr = "startUsdBasedServiceAdvertisement"; + if (!checkP2pIfaceAndLogFailure(methodStr)) { + return -1; + } + if (getCachedServiceVersion() < 4) { + return -1; + } + if (usdServiceConfig == null || advertisementConfig == null) { + return -1; + } + try { + P2pUsdBasedServiceAdvertisementConfig aidlServiceAdvertisementConfig = + new P2pUsdBasedServiceAdvertisementConfig(); + aidlServiceAdvertisementConfig.serviceName = usdServiceConfig.getServiceName(); + aidlServiceAdvertisementConfig.serviceProtocolType = usdServiceConfig + .getServiceProtocolType(); + aidlServiceAdvertisementConfig.serviceSpecificInfo = usdServiceConfig + .getServiceSpecificInfo(); + aidlServiceAdvertisementConfig.frequencyMHz = advertisementConfig.getFrequencyMhz(); + aidlServiceAdvertisementConfig.timeoutInSeconds = timeoutInSeconds; + return mISupplicantP2pIface.startUsdBasedServiceAdvertisement( + aidlServiceAdvertisementConfig); + } catch (RemoteException e) { + handleRemoteException(e, methodStr); + } catch (ServiceSpecificException e) { + handleServiceSpecificException(e, methodStr); + } + return -1; + } + } + + /** + * Stop an Un-synchronized Service Discovery (USD) based P2P service advertisement. + * + * @param sessionId Identifier to cancel the service advertisement. + * Use zero to cancel all the service advertisement instances. + */ + public void stopUsdBasedServiceAdvertisement(int sessionId) { + synchronized (mLock) { + String methodStr = "stopUsdBasedServiceAdvertisement"; + if (!checkP2pIfaceAndLogFailure(methodStr)) { + return; + } + if (getCachedServiceVersion() < 4) { + return; + } + try { + mISupplicantP2pIface.stopUsdBasedServiceAdvertisement(sessionId); + } catch (RemoteException e) { + handleRemoteException(e, methodStr); + } catch (ServiceSpecificException e) { + handleServiceSpecificException(e, methodStr); + } + } + } + + private static int scanResultBandMaskToSupplicantHalWifiBandMask(int bandMask) { + int halBandMask = 0; + if ((bandMask & ScanResult.WIFI_BAND_24_GHZ) != 0) { + halBandMask |= BandMask.BAND_2_GHZ; + } + if ((bandMask & ScanResult.WIFI_BAND_5_GHZ) != 0) { + halBandMask |= BandMask.BAND_5_GHZ; + } + if ((bandMask & ScanResult.WIFI_BAND_6_GHZ) != 0) { + halBandMask |= BandMask.BAND_6_GHZ; + } + return halBandMask; + } + + /** + * Get the Device Identity Resolution (DIR) Information. + * See {@link WifiP2pDirInfo} for details + * + * @return {@link WifiP2pDirInfo} instance on success, null on failure. + */ + @SuppressLint("NewApi") + public WifiP2pDirInfo getDirInfo() { + synchronized (mLock) { + String methodStr = "getDirInfo"; + if (!checkP2pIfaceAndLogFailure(methodStr)) { + return null; + } + if (getCachedServiceVersion() < 4) { + return null; + } + try { + P2pDirInfo aidlDirInfo = mISupplicantP2pIface.getDirInfo(); + WifiP2pDirInfo dirInfo = new WifiP2pDirInfo(MacAddress.fromBytes( + aidlDirInfo.deviceInterfaceMacAddress), + aidlDirInfo.nonce, aidlDirInfo.dirTag); + return dirInfo; + } catch (RemoteException e) { + handleRemoteException(e, methodStr); + } catch (ServiceSpecificException e) { + handleServiceSpecificException(e, methodStr); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Could not decode MAC Address.", e); + } + return null; + } + } + + /** + * Validate the Device Identity Resolution (DIR) Information of a P2P device. + * See {@link WifiP2pDirInfo} for details. + * + * @param dirInfo {@link WifiP2pDirInfo} to validate. + * @return The identifier of device identity key on success, -1 on failure. + */ + @SuppressLint("NewApi") + public int validateDirInfo(@NonNull WifiP2pDirInfo dirInfo) { + synchronized (mLock) { + String methodStr = "validateDirInfo"; + if (!checkP2pIfaceAndLogFailure(methodStr)) { + return -1; + } + if (getCachedServiceVersion() < 4) { + return -1; + } + try { + P2pDirInfo aidlDirInfo = new P2pDirInfo(); + aidlDirInfo.cipherVersion = P2pDirInfo.CipherVersion.DIRA_CIPHER_VERSION_128_BIT; + aidlDirInfo.deviceInterfaceMacAddress = dirInfo.getMacAddress().toByteArray(); + aidlDirInfo.nonce = dirInfo.getNonce(); + aidlDirInfo.dirTag = dirInfo.getDirTag(); + return mISupplicantP2pIface.validateDirInfo(aidlDirInfo); + } catch (RemoteException e) { + handleRemoteException(e, methodStr); + } catch (ServiceSpecificException e) { + handleServiceSpecificException(e, methodStr); + } + return -1; + } + } + private byte[] convertInformationElementSetToBytes( Set<ScanResult.InformationElement> ies) { try { diff --git a/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceHalHidlImpl.java b/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceHalHidlImpl.java index 7f28476699..1feb5b986e 100644 --- a/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceHalHidlImpl.java +++ b/service/java/com/android/server/wifi/p2p/SupplicantP2pIfaceHalHidlImpl.java @@ -35,11 +35,15 @@ import android.net.wifi.ScanResult; import android.net.wifi.WpsInfo; import android.net.wifi.p2p.WifiP2pConfig; import android.net.wifi.p2p.WifiP2pDevice; +import android.net.wifi.p2p.WifiP2pDirInfo; import android.net.wifi.p2p.WifiP2pExtListenParams; import android.net.wifi.p2p.WifiP2pGroup; import android.net.wifi.p2p.WifiP2pGroupList; import android.net.wifi.p2p.WifiP2pManager; +import android.net.wifi.p2p.WifiP2pUsdBasedLocalServiceAdvertisementConfig; +import android.net.wifi.p2p.WifiP2pUsdBasedServiceDiscoveryConfig; import android.net.wifi.p2p.nsd.WifiP2pServiceInfo; +import android.net.wifi.p2p.nsd.WifiP2pUsdBasedServiceConfig; import android.os.IHwBinder.DeathRecipient; import android.os.RemoteException; import android.text.TextUtils; @@ -1337,10 +1341,11 @@ public class SupplicantP2pIfaceHalHidlImpl implements ISupplicantP2pIfaceHal { * * @param networkId Used to specify the restart of a persistent group. * @param isPersistent Used to request a persistent group to be formed. + * @param isP2pV2 Used to start a Group Owner that support P2P2 IE * * @return true, if operation was successful. */ - public boolean groupAdd(int networkId, boolean isPersistent) { + public boolean groupAdd(int networkId, boolean isPersistent, boolean isP2pV2) { synchronized (mLock) { if (!checkSupplicantP2pIfaceAndLogFailure("groupAdd")) return false; SupplicantResult<Void> result = @@ -1370,6 +1375,7 @@ public class SupplicantP2pIfaceHalHidlImpl implements ISupplicantP2pIfaceHal { * @return true, if operation was successful. */ public boolean groupAdd(String networkName, String passphrase, + @WifiP2pConfig.PccModeConnectionType int connectionType, boolean isPersistent, int freq, String peerAddress, boolean join) { synchronized (mLock) { android.hardware.wifi.supplicant.V1_2.ISupplicantP2pIface ifaceV12 = @@ -2645,6 +2651,92 @@ public class SupplicantP2pIfaceHalHidlImpl implements ISupplicantP2pIfaceHal { } /** + * Start an Un-synchronized Service Discovery (USD) based P2P service discovery. + * + * @param usdServiceConfig is the USD based service configuration. + * @param discoveryConfig is the configuration for this service discovery request. + * @param timeoutInSeconds is the maximum time to be spent for this service discovery request. + */ + public int startUsdBasedServiceDiscovery(WifiP2pUsdBasedServiceConfig usdServiceConfig, + WifiP2pUsdBasedServiceDiscoveryConfig discoveryConfig, int timeoutInSeconds) { + Log.d(TAG, "startUsdBasedServiceDiscovery() is not supported."); + return -1; + } + + /** + * Stop an Un-synchronized Service Discovery (USD) based P2P service discovery. + * + * @param sessionId Identifier to cancel the service discovery instance. + * Use zero to cancel all the service discovery instances. + */ + public void stopUsdBasedServiceDiscovery(int sessionId) { + Log.d(TAG, "stopUsdBasedServiceDiscovery() is not supported."); + } + + /** + * Start an Un-synchronized Service Discovery (USD) based P2P service advertisement. + * + * @param usdServiceConfig is the USD based service configuration. + * @param advertisementConfig is the configuration for this service advertisement. + * @param timeoutInSeconds is the maximum time to be spent for this service advertisement. + */ + public int startUsdBasedServiceAdvertisement(WifiP2pUsdBasedServiceConfig usdServiceConfig, + WifiP2pUsdBasedLocalServiceAdvertisementConfig advertisementConfig, + int timeoutInSeconds) { + Log.d(TAG, "startUsdBasedServiceAdvertisement() is not supported."); + return -1; + } + + /** + * Stop an Un-synchronized Service Discovery (USD) based P2P service advertisement. + * + * @param sessionId Identifier to cancel the service advertisement. + * Use zero to cancel all the service advertisement instances. + */ + public void stopUsdBasedServiceAdvertisement(int sessionId) { + Log.d(TAG, "stopUsdBasedServiceAdvertisement() is not supported."); + } + + /** + * Get the Device Identity Resolution (DIR) Information. + * See {@link WifiP2pDirInfo} for details + * + * @return {@link WifiP2pDirInfo} instance on success, null on failure. + */ + public WifiP2pDirInfo getDirInfo() { + Log.d(TAG, "getDirInfo() is not supported."); + return null; + } + + /** + * Validate the Device Identity Resolution (DIR) Information of a P2P device. + * See {@link WifiP2pDirInfo} for details. + * + * @param dirInfo {@link WifiP2pDirInfo} to validate. + * @return The identifier of device identity key on success, -1 on failure. + */ + public int validateDirInfo(@NonNull WifiP2pDirInfo dirInfo) { + Log.d(TAG, "validateDirInfo() is not supported."); + return -1; + } + + /** + * Used to authorize a connection request to an existing Group Owner + * interface, to allow a peer device to connect. + * + * @param config Configuration to use for connection. + * @param groupOwnerInterfaceName Group Owner interface name on which the request to connect + * needs to be authorized. + * + * @return boolean value indicating whether operation was successful. + */ + public boolean authorizeConnectRequestOnGroupOwner(WifiP2pConfig config, + String groupOwnerInterfaceName) { + Log.d(TAG, "authorizeConnectRequestOnGroupOwner() is not supported."); + return false; + } + + /** * Converts the Wps config method string to the equivalent enum value. */ private static short stringToWpsConfigMethod(String configMethod) { diff --git a/service/java/com/android/server/wifi/p2p/WifiP2pMonitor.java b/service/java/com/android/server/wifi/p2p/WifiP2pMonitor.java index 0d1efd7791..d91ef121cd 100644 --- a/service/java/com/android/server/wifi/p2p/WifiP2pMonitor.java +++ b/service/java/com/android/server/wifi/p2p/WifiP2pMonitor.java @@ -80,6 +80,18 @@ public class WifiP2pMonitor { public static final int AP_STA_DISCONNECTED_EVENT = BASE + 41; public static final int AP_STA_CONNECTED_EVENT = BASE + 42; + /* USD service discovery events */ + public static final int USD_BASED_SERVICE_DISCOVERY_TERMINATED_EVENT = BASE + 43; + public static final int USD_BASED_SERVICE_ADVERTISEMENT_TERMINATED_EVENT = BASE + 44; + + /* Provision Discovery event with Pairing bootstrapping method */ + public static final int P2P_PROV_DISC_PAIRING_BOOTSTRAPPING_OPPORTUNISTIC_REQ_EVENT = BASE + 45; + public static final int P2P_PROV_DISC_PAIRING_BOOTSTRAPPING_OPPORTUNISTIC_RSP_EVENT = BASE + 46; + public static final int P2P_PROV_DISC_ENTER_PAIRING_BOOTSTRAPPING_PIN_OR_PASSPHRASE_EVENT = + BASE + 47; + public static final int P2P_PROV_DISC_SHOW_PAIRING_BOOTSTRAPPING_PIN_OR_PASSPHRASE_EVENT = + BASE + 48; + public static final int PROV_DISC_STATUS_SUCCESS = 0; public static final int PROV_DISC_STATUS_TIMEOUT = 1; public static final int PROV_DISC_STATUS_REJECTED = 2; @@ -439,6 +451,67 @@ public class WifiP2pMonitor { } /** + * Broadcast provision discovery request event with requested method opportunistic + * bootstrapping to all handlers registered for this event. + * + * @param iface Name of iface on which this occurred. + * @param event Provision discovery request event. + */ + public void broadcastP2pProvisionDiscoveryPairingBootstrappingOpportunisticRequest( + String iface, WifiP2pProvDiscEvent event) { + if (event != null) { + sendMessage(iface, P2P_PROV_DISC_PAIRING_BOOTSTRAPPING_OPPORTUNISTIC_REQ_EVENT, + event); + } + } + + /** + * Broadcast provision discovery response event with requested method opportunistic + * bootstrapping to all handlers registered for this event. + * + * @param iface Name of iface on which this occurred. + * @param event Provision discovery response event. + */ + public void broadcastP2pProvisionDiscoveryPairingBootstrappingOpportunisticResponse( + String iface, WifiP2pProvDiscEvent event) { + if (event != null) { + sendMessage(iface, P2P_PROV_DISC_PAIRING_BOOTSTRAPPING_OPPORTUNISTIC_RSP_EVENT, + event); + } + } + + /** + * Broadcast provision discovery event to enter pairing bootstrapping PIN (Keypad pin-code only + * method) or Pairing Passphrase (Keypad passphrase method) to all handlers registered for + * this event. + * + * @param iface Name of iface on which this occurred. + * @param event Provision discovery request event. + */ + public void broadcastP2pProvisionDiscoveryEnterPairingBootstrappingPinOrPassphrase(String iface, + WifiP2pProvDiscEvent event) { + if (event != null) { + sendMessage(iface, P2P_PROV_DISC_ENTER_PAIRING_BOOTSTRAPPING_PIN_OR_PASSPHRASE_EVENT, + event); + } + } + + /** + * Broadcast provision discovery event to show pairing bootstrapping PIN (Display pin-code only + * method) or Passphrase (Display passphrase method) to all handlers registered for this event. + * + * @param iface Name of iface on which this occurred. + * @param event Provision discovery response event. + */ + public void broadcastP2pProvisionDiscoveryShowPairingBootstrappingPinOrPassphrase(String iface, + WifiP2pProvDiscEvent event) { + if (event != null) { + sendMessage(iface, P2P_PROV_DISC_SHOW_PAIRING_BOOTSTRAPPING_PIN_OR_PASSPHRASE_EVENT, + event); + } + } + + /** * Broadcast P2P discovery failure event to all handlers registered for this event. * * @param iface Name of iface on which this occurred. @@ -488,4 +561,28 @@ public class WifiP2pMonitor { public void broadcastP2pFrequencyChanged(String iface, int frequency) { sendMessage(iface, P2P_FREQUENCY_CHANGED_EVENT, frequency); } + + /** + * Broadcast the termination of USD based service discovery. + * + * @param iface Name of iface on which this occurred. + * @param sessionId Identifier to identify the instance of a service discovery. + * @param reasonCode The reason for termination of service discovery. + */ + public void broadcastUsdBasedServiceDiscoveryTerminated(@NonNull String iface, + int sessionId, int reasonCode) { + sendMessage(iface, USD_BASED_SERVICE_DISCOVERY_TERMINATED_EVENT, sessionId, reasonCode); + } + + /** + * Broadcast the termination of USD based service advertisement. + * + * @param iface Name of iface on which this occurred. + * @param sessionId Identifier to identify the instance of a service advertisement. + * @param reasonCode The reason for termination of service advertisement. + */ + public void broadcastUsdBasedServiceAdvertisementTerminated(@NonNull String iface, + int sessionId, int reasonCode) { + sendMessage(iface, USD_BASED_SERVICE_ADVERTISEMENT_TERMINATED_EVENT, sessionId, reasonCode); + } } diff --git a/service/java/com/android/server/wifi/p2p/WifiP2pNative.java b/service/java/com/android/server/wifi/p2p/WifiP2pNative.java index 3be642d651..9149ff3b4d 100644 --- a/service/java/com/android/server/wifi/p2p/WifiP2pNative.java +++ b/service/java/com/android/server/wifi/p2p/WifiP2pNative.java @@ -17,32 +17,42 @@ package com.android.server.wifi.p2p; import static com.android.server.wifi.HalDeviceManager.HDM_CREATE_IFACE_P2P; +import static com.android.server.wifi.WifiSettingsConfigStore.WIFI_P2P_SUPPORTED_FEATURES; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.net.wifi.CoexUnsafeChannel; import android.net.wifi.ScanResult; import android.net.wifi.nl80211.WifiNl80211Manager; import android.net.wifi.p2p.WifiP2pConfig; +import android.net.wifi.p2p.WifiP2pDirInfo; import android.net.wifi.p2p.WifiP2pDiscoveryConfig; import android.net.wifi.p2p.WifiP2pExtListenParams; import android.net.wifi.p2p.WifiP2pGroup; import android.net.wifi.p2p.WifiP2pGroupList; import android.net.wifi.p2p.WifiP2pManager; +import android.net.wifi.p2p.WifiP2pUsdBasedLocalServiceAdvertisementConfig; +import android.net.wifi.p2p.WifiP2pUsdBasedServiceDiscoveryConfig; import android.net.wifi.p2p.nsd.WifiP2pServiceInfo; +import android.net.wifi.p2p.nsd.WifiP2pUsdBasedServiceConfig; +import android.net.wifi.util.Environment; import android.os.Handler; import android.os.WorkSource; import android.text.TextUtils; import android.util.Log; -import android.util.SparseArray; + +import androidx.annotation.Keep; import com.android.server.wifi.HalDeviceManager; import com.android.server.wifi.PropertyService; import com.android.server.wifi.WifiInjector; import com.android.server.wifi.WifiMetrics; import com.android.server.wifi.WifiNative; +import com.android.server.wifi.WifiSettingsConfigStore; import com.android.server.wifi.WifiVendorHal; import com.android.wifi.flags.FeatureFlags; +import com.android.wifi.flags.Flags; import java.util.HashSet; import java.util.List; @@ -68,6 +78,8 @@ public class WifiP2pNative { private WifiNative.Iface mP2pIface; private String mP2pIfaceName; private InterfaceDestroyedListenerInternal mInterfaceDestroyedListener; + private int mServiceVersion = -1; + private long mCachedFeatureSet = 0; /** * Death handler for the supplicant daemon. @@ -270,6 +282,11 @@ public class WifiP2pNative { mWifiMetrics.incrementNumSetupP2pInterfaceFailureDueToSupplicant(); return null; } + long featureSet = mSupplicantP2pIfaceHal.getSupportedFeatures(); + mWifiInjector.getSettingsConfigStore() + .put(WIFI_P2P_SUPPORTED_FEATURES, featureSet); + mCachedFeatureSet = featureSet | getDriverIndependentFeatures(); + Log.i(TAG, "P2P Supported features: " + mCachedFeatureSet); Log.i(TAG, "P2P interface setup completed"); return mP2pIfaceName; } else { @@ -329,7 +346,34 @@ public class WifiP2pNative { * @return bitmask defined by WifiP2pManager.FEATURE_* */ public long getSupportedFeatures() { - return mSupplicantP2pIfaceHal.getSupportedFeatures(); + if (mCachedFeatureSet == 0) { + mCachedFeatureSet = getDriverIndependentFeatures() + | mWifiInjector.getSettingsConfigStore().get( + WifiSettingsConfigStore.WIFI_P2P_SUPPORTED_FEATURES); + } + return mCachedFeatureSet; + } + + private long getDriverIndependentFeatures() { + long features = 0; + // First AIDL version supports these three features. + if (getCachedServiceVersion() >= 1) { + features = WifiP2pManager.FEATURE_SET_VENDOR_ELEMENTS + | WifiP2pManager.FEATURE_FLEXIBLE_DISCOVERY + | WifiP2pManager.FEATURE_GROUP_CLIENT_REMOVAL; + if (mServiceVersion >= 2) { + features |= WifiP2pManager.FEATURE_GROUP_OWNER_IPV6_LINK_LOCAL_ADDRESS_PROVIDED; + } + } + return features; + } + + private int getCachedServiceVersion() { + if (mServiceVersion == -1) { + mServiceVersion = mWifiInjector.getSettingsConfigStore().get( + WifiSettingsConfigStore.SUPPLICANT_HAL_AIDL_SERVICE_VERSION); + } + return mServiceVersion; } /** @@ -451,6 +495,7 @@ public class WifiP2pNative { * * @return boolean value indicating whether operation was successful. */ + @Keep public boolean setP2pPowerSave(String iface, boolean enabled) { return mSupplicantP2pIfaceHal.setPowerSave(iface, enabled); } @@ -657,11 +702,12 @@ public class WifiP2pNative { * This is a helper method that invokes groupAdd(networkId, isPersistent) internally. * * @param persistent Used to request a persistent group to be formed. + * @param isP2pV2 Used to start a Group Owner that support P2P2 IE. * * @return true, if operation was successful. */ - public boolean p2pGroupAdd(boolean persistent) { - return mSupplicantP2pIfaceHal.groupAdd(persistent); + public boolean p2pGroupAdd(boolean persistent, boolean isP2pV2) { + return mSupplicantP2pIfaceHal.groupAdd(persistent, isP2pV2); } /** @@ -670,11 +716,12 @@ public class WifiP2pNative { * group owner. * * @param netId Used to specify the restart of a persistent group. + * @param isP2pV2 Used to start a Group Owner that support P2P2 IE. * * @return true, if operation was successful. */ - public boolean p2pGroupAdd(int netId) { - return mSupplicantP2pIfaceHal.groupAdd(netId, true); + public boolean p2pGroupAdd(int netId, boolean isP2pV2) { + return mSupplicantP2pIfaceHal.groupAdd(netId, true, isP2pV2); } /** @@ -684,8 +731,13 @@ public class WifiP2pNative { * * @return true, if operation was successful. */ + @SuppressLint("NewApi") public boolean p2pGroupAdd(WifiP2pConfig config, boolean join) { int freq = 0; + int connectionType = Environment.isSdkAtLeastB() && Flags.wifiDirectR2() + ? config.getPccModeConnectionType() + : WifiP2pConfig.PCC_MODE_DEFAULT_CONNECTION_TYPE_LEGACY_ONLY; + switch (config.groupOwnerBand) { case WifiP2pConfig.GROUP_OWNER_BAND_2GHZ: freq = 2; @@ -693,18 +745,70 @@ public class WifiP2pNative { case WifiP2pConfig.GROUP_OWNER_BAND_5GHZ: freq = 5; break; + case WifiP2pConfig.GROUP_OWNER_BAND_6GHZ: + freq = 6; + break; // treat it as frequency. default: freq = config.groupOwnerBand; } + if (Environment.isSdkAtLeastB() && Flags.wifiDirectR2()) { + /* Check if the device supports Wi-Fi Direct R2 */ + if ((WifiP2pConfig.GROUP_OWNER_BAND_6GHZ == config.groupOwnerBand + || WifiP2pConfig.PCC_MODE_CONNECTION_TYPE_R2_ONLY == connectionType) + && !isWiFiDirectR2Supported()) { + Log.e(TAG, "Failed to add the group - Wi-Fi Direct R2 not supported"); + return false; + } + + /* Check if the device supports Wi-Fi Direct R1/R2 Compatibility Mode */ + if (WifiP2pConfig.PCC_MODE_CONNECTION_TYPE_LEGACY_OR_R2 == connectionType + && !isPccModeAllowLegacyAndR2ConnectionSupported()) { + Log.e(TAG, "Failed to add the group - R1/R2 compatibility not supported"); + return false; + } + + /* Check if this is a valid configuration for 6GHz band */ + if (WifiP2pConfig.GROUP_OWNER_BAND_6GHZ == config.groupOwnerBand + && WifiP2pConfig.PCC_MODE_CONNECTION_TYPE_R2_ONLY != connectionType) { + Log.e(TAG, "Failed to add the group in 6GHz band - ConnectionType: " + + connectionType); + return false; + } + + /* Check if we can upgrade LEGACY to R2 */ + if (WifiP2pConfig.PCC_MODE_CONNECTION_TYPE_LEGACY_ONLY == connectionType + && isPccModeAllowLegacyAndR2ConnectionSupported()) { + Log.e(TAG, "Upgrade Legacy connection to R1/R2 compatibility"); + connectionType = WifiP2pConfig.PCC_MODE_CONNECTION_TYPE_LEGACY_OR_R2; + } + } + + abortWifiRunningScanIfNeeded(join); return mSupplicantP2pIfaceHal.groupAdd( config.networkName, config.passphrase, + connectionType, (config.netId == WifiP2pGroup.NETWORK_ID_PERSISTENT), freq, config.deviceAddress, join); } + /** + * @return true if this device supports Wi-Fi Direct R2 + */ + private boolean isWiFiDirectR2Supported() { + return (mCachedFeatureSet & WifiP2pManager.FEATURE_WIFI_DIRECT_R2) != 0; + } + + /** + * @return true if this device supports R1/R2 Compatibility Mode. + */ + private boolean isPccModeAllowLegacyAndR2ConnectionSupported() { + return (mCachedFeatureSet + & WifiP2pManager.FEATURE_PCC_MODE_ALLOW_LEGACY_AND_R2_CONNECTION) != 0; + } + private void abortWifiRunningScanIfNeeded(boolean isJoin) { if (!isJoin) return; @@ -997,18 +1101,6 @@ public class WifiP2pNative { } /** - * Returns whether P2P + P2P concurrency is supported or not. - */ - public boolean isP2pP2pConcurrencySupported() { - synchronized (mLock) { - return mWifiVendorHal.canDeviceSupportCreateTypeCombo( - new SparseArray<Integer>() {{ - put(HDM_CREATE_IFACE_P2P, 2); - }}); - } - } - - /** * Configure the IP addresses in supplicant for P2P GO to provide the IP address to * client in EAPOL handshake. Refer Wi-Fi P2P Technical Specification v1.7 - Section 4.2.8 * IP Address Allocation in EAPOL-Key Frames (4-Way Handshake) for more details. @@ -1026,4 +1118,89 @@ public class WifiP2pNative { return mSupplicantP2pIfaceHal.configureEapolIpAddressAllocationParams(ipAddressGo, ipAddressMask, ipAddressStart, ipAddressEnd); } + + /** + * Start an Un-synchronized Service Discovery (USD) based P2P service discovery. + * + * @param usdServiceConfig is the USD based service configuration. + * @param discoveryConfig is the configuration for this service discovery request. + * @param timeoutInSeconds is the maximum time to be spent for this service discovery request. + */ + public int startUsdBasedServiceDiscovery(WifiP2pUsdBasedServiceConfig usdServiceConfig, + WifiP2pUsdBasedServiceDiscoveryConfig discoveryConfig, int timeoutInSeconds) { + return mSupplicantP2pIfaceHal.startUsdBasedServiceDiscovery(usdServiceConfig, + discoveryConfig, timeoutInSeconds); + } + + /** + * Stop an Un-synchronized Service Discovery (USD) based P2P service discovery. + * + * @param sessionId Identifier to cancel the service discovery instance. + * Use zero to cancel all the service discovery instances. + */ + public void stopUsdBasedServiceDiscovery(int sessionId) { + mSupplicantP2pIfaceHal.stopUsdBasedServiceDiscovery(sessionId); + } + + /** + * Start an Un-synchronized Service Discovery (USD) based P2P service advertisement. + * + * @param usdServiceConfig is the USD based service configuration. + * @param advertisementConfig is the configuration for this service advertisement. + * @param timeoutInSeconds is the maximum time to be spent for this service advertisement. + */ + public int startUsdBasedServiceAdvertisement(WifiP2pUsdBasedServiceConfig usdServiceConfig, + WifiP2pUsdBasedLocalServiceAdvertisementConfig advertisementConfig, + int timeoutInSeconds) { + return mSupplicantP2pIfaceHal.startUsdBasedServiceAdvertisement(usdServiceConfig, + advertisementConfig, timeoutInSeconds); + } + + /** + * Stop an Un-synchronized Service Discovery (USD) based P2P service advertisement. + * + * @param sessionId Identifier to cancel the service advertisement. + * Use zero to cancel all the service advertisement instances. + */ + public void stopUsdBasedServiceAdvertisement(int sessionId) { + mSupplicantP2pIfaceHal.stopUsdBasedServiceAdvertisement(sessionId); + } + + /** + * Get the Device Identity Resolution (DIR) Information. + * See {@link WifiP2pDirInfo} for details + * + * @return {@link WifiP2pDirInfo} instance on success, null on failure. + */ + public WifiP2pDirInfo getDirInfo() { + return mSupplicantP2pIfaceHal.getDirInfo(); + } + + /** + * Validate the Device Identity Resolution (DIR) Information of a P2P device. + * See {@link WifiP2pDirInfo} for details. + * + * @param dirInfo {@link WifiP2pDirInfo} to validate. + * @return The identifier of device identity key on success, -1 on failure. + */ + public int validateDirInfo(@NonNull WifiP2pDirInfo dirInfo) { + return mSupplicantP2pIfaceHal.validateDirInfo(dirInfo); + } + + /** + * Used to authorize a connection request to an existing Group Owner + * interface, to allow a peer device to connect. + * + * @param config Configuration to use for connection. + * @param groupOwnerInterfaceName Group Owner interface name on which the request to connect + * needs to be authorized. + * + * @return boolean value indicating whether operation was successful. + */ + public boolean authorizeConnectRequestOnGroupOwner( + WifiP2pConfig config, String groupOwnerInterfaceName) { + return mSupplicantP2pIfaceHal.authorizeConnectRequestOnGroupOwner(config, + groupOwnerInterfaceName); + } + } diff --git a/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java b/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java index 6fb46f4667..301240f3fd 100644 --- a/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java +++ b/service/java/com/android/server/wifi/p2p/WifiP2pServiceImpl.java @@ -18,6 +18,7 @@ package com.android.server.wifi.p2p; import static android.net.wifi.p2p.WifiP2pConfig.GROUP_CLIENT_IP_PROVISIONING_MODE_IPV4_DHCP; import static android.net.wifi.p2p.WifiP2pConfig.GROUP_CLIENT_IP_PROVISIONING_MODE_IPV6_LINK_LOCAL; +import static android.net.wifi.p2p.WifiP2pConfig.P2P_VERSION_2; import static com.android.net.module.util.Inet4AddressUtils.inet4AddressToIntHTL; import static com.android.net.module.util.Inet4AddressUtils.netmaskToPrefixLength; @@ -70,6 +71,7 @@ import android.net.wifi.p2p.IWifiP2pManager; import android.net.wifi.p2p.WifiP2pConfig; import android.net.wifi.p2p.WifiP2pDevice; import android.net.wifi.p2p.WifiP2pDeviceList; +import android.net.wifi.p2p.WifiP2pDirInfo; import android.net.wifi.p2p.WifiP2pDiscoveryConfig; import android.net.wifi.p2p.WifiP2pExtListenParams; import android.net.wifi.p2p.WifiP2pGroup; @@ -78,11 +80,16 @@ import android.net.wifi.p2p.WifiP2pGroupList.GroupDeleteListener; import android.net.wifi.p2p.WifiP2pInfo; import android.net.wifi.p2p.WifiP2pManager; import android.net.wifi.p2p.WifiP2pManager.ExternalApproverRequestListener; +import android.net.wifi.p2p.WifiP2pPairingBootstrappingConfig; import android.net.wifi.p2p.WifiP2pProvDiscEvent; +import android.net.wifi.p2p.WifiP2pUsdBasedLocalServiceAdvertisementConfig; +import android.net.wifi.p2p.WifiP2pUsdBasedServiceDiscoveryConfig; import android.net.wifi.p2p.WifiP2pWfdInfo; import android.net.wifi.p2p.nsd.WifiP2pServiceInfo; import android.net.wifi.p2p.nsd.WifiP2pServiceRequest; import android.net.wifi.p2p.nsd.WifiP2pServiceResponse; +import android.net.wifi.p2p.nsd.WifiP2pUsdBasedServiceConfig; +import android.net.wifi.util.Environment; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -146,6 +153,7 @@ import com.android.server.wifi.util.WaitingState; import com.android.server.wifi.util.WifiPermissionsUtil; import com.android.server.wifi.util.WifiPermissionsWrapper; import com.android.wifi.flags.FeatureFlags; +import com.android.wifi.flags.Flags; import com.android.wifi.resources.R; import java.io.FileDescriptor; @@ -169,13 +177,13 @@ import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; /** @@ -289,6 +297,9 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { // Set a two minute discover timeout to avoid STA scans from being blocked private static final int DISCOVER_TIMEOUT_S = 120; + // Set a 30 seconds timeout for USD service discovery and advertisement. + private static final int USD_BASED_SERVICE_ADVERTISEMENT_DISCOVERY_TIMEOUT_S = 30; + // Idle time after a peer is gone when the group is torn down private static final int GROUP_IDLE_TIME_S = 10; @@ -343,8 +354,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { // Messages for interaction with IpClient. private static final int IPC_PRE_DHCP_ACTION = BASE + 30; private static final int IPC_POST_DHCP_ACTION = BASE + 31; - @VisibleForTesting - static final int IPC_DHCP_RESULTS = BASE + 32; + private static final int IPC_DHCP_RESULTS = BASE + 32; private static final int IPC_PROVISIONING_SUCCESS = BASE + 33; private static final int IPC_PROVISIONING_FAILURE = BASE + 34; @VisibleForTesting @@ -411,10 +421,6 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { // The transaction Id of service discovery request private int mServiceTransactionId = 0; - // Service discovery request ID of wpa_supplicant. - // null means it's not set yet. - private String mServiceDiscReqId; - // clients(application) information list private final HashMap<Messenger, ClientInfo> mClientInfoList = new HashMap<>(); @@ -431,16 +437,6 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { private final Map<String, HashSet<ScanResult.InformationElement>> mVendorElements = new HashMap<>(); - // client(application) P2P group info list, key = package name - private final Map<String, WifiP2pGroupInfo> mOwnershipMap = new HashMap<>(); - - // Max number of P2P groups supported - private static final int MAX_NUM_GROUP = 1; - // Package name used to represent all shared connections - private static final String SHARED_PKG_NAME = "__SHARED_PACKAGE_NAME"; - // Ongoing connection request package name with the default value set for shared connection - private String mConnectionPkgName = SHARED_PKG_NAME; - // peer authorizing timestamp which is indexed by the peer MAC address. private final Map<String, Long> mPeerAuthorizingTimestamp = new HashMap<>(); @@ -475,8 +471,6 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { private final RemoteCallbackList<IWifiP2pListener> mWifiP2pListeners = new RemoteCallbackList<>(); - // clients(application) P2P listener map, key = package name - private final Map<String, IWifiP2pListener> mP2pListenerMap = new HashMap<>(); /** * Error code definition. @@ -594,15 +588,52 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { mP2pStateMachine.sendMessage(ENABLE_P2P); } - /** - * Stores P2P group information - */ - private static class WifiP2pGroupInfo { - public WifiP2pGroup p2pGroup; - public WifiP2pInfo p2pInfo; - WifiP2pGroupInfo(WifiP2pGroup group, WifiP2pInfo info) { - p2pGroup = group; - p2pInfo = info; + // Tracks the ongoing Service discovery request to wpa_supplicant. + private final WifiP2pOngoingServiceDiscoveryRequestInfo mServiceDiscoveryInfo = + new WifiP2pOngoingServiceDiscoveryRequestInfo(); + private static class WifiP2pOngoingServiceDiscoveryRequestInfo { + public static final int WIFI_P2P_GAS_FRAME_BASED_SERVICE_DISCOVERY = 1; + public static final int WIFI_P2P_USD_FRAME_BASED_SERVICE_DISCOVERY = 2; + private String mSessionId = ""; + private int mServiceDiscoveryType = 0; + WifiP2pOngoingServiceDiscoveryRequestInfo() { + } + + public String getSessionId() { + return mSessionId; + } + + public int getServiceDiscoveryType() { + return mServiceDiscoveryType; + } + + public void update(int serviceDiscoveryType, String sessionId) { + mServiceDiscoveryType = serviceDiscoveryType; + mSessionId = sessionId; + } + + public int getSessionIdInt() { + return Integer.parseInt(mSessionId); + } + + public void update(int serviceDiscoveryType, int sessionId) { + mServiceDiscoveryType = serviceDiscoveryType; + mSessionId = Integer.toString(sessionId); + } + + public void invalidate() { + mServiceDiscoveryType = 0; + mSessionId = ""; + } + + public boolean isValid() { + return mServiceDiscoveryType != 0 && !TextUtils.isEmpty(mSessionId); + } + public String toString() { + StringBuilder sbuf = new StringBuilder("WifiP2pOngoingServiceDiscoveryRequestInfo:"); + sbuf.append("\n serviceDiscoveryType: ").append(mServiceDiscoveryType); + sbuf.append("\n sessionId: ").append(mSessionId); + return sbuf.toString(); } } @@ -657,6 +688,8 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { case WifiP2pManager.REMOVE_EXTERNAL_APPROVER: case WifiP2pManager.SET_CONNECTION_REQUEST_RESULT: case WifiP2pManager.SET_VENDOR_ELEMENTS: + case WifiP2pManager.GET_DIR_INFO: + case WifiP2pManager.VALIDATE_DIR_INFO: mP2pStateMachine.sendMessage(Message.obtain(msg)); break; default: @@ -1078,8 +1111,6 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { AttributionSource source = mClientAttributionSource.remove(binder); if (null != source) { mVendorElements.remove(source.getPackageName()); - mOwnershipMap.remove(source.getPackageName()); - mP2pListenerMap.remove(source.getPackageName()); } } @@ -1098,24 +1129,6 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { } } - /** - * Get WifiP2pGroupInfo owned by the provided package name - */ - private WifiP2pGroupInfo getP2pGroupInfo(String packageName) { - if (mOwnershipMap.containsKey(packageName)) { - return mOwnershipMap.get(packageName); - } else if (mOwnershipMap.containsKey(SHARED_PKG_NAME)) { - return mOwnershipMap.get(SHARED_PKG_NAME); - } - return null; - } - - private boolean checkIfPackageIsGroupOwner(String packageName) { - return !mFeatureFlags.p2pOwnership() - || mOwnershipMap.containsKey(packageName) - || mOwnershipMap.containsKey(SHARED_PKG_NAME); - } - /** This is used to provide information to drivers to optimize performance depending * on the current mode of operation. * 0 - disabled @@ -1166,7 +1179,6 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { TAG + " registerWifiP2pListener"); Log.i(TAG, "registerWifiP2pListener uid=" + Binder.getCallingUid()); mWifiP2pListeners.register(listener); - mP2pListenerMap.put(packageName, listener); } /** @@ -1180,15 +1192,6 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { } Log.i(TAG, "unregisterWifiP2pListener uid=" + Binder.getCallingUid()); mWifiP2pListeners.unregister(listener); - Iterator<Map.Entry<String, IWifiP2pListener>> iterator = - mP2pListenerMap.entrySet().iterator(); - while (iterator.hasNext()) { - Map.Entry<String, IWifiP2pListener> entry = iterator.next(); - if (entry.getValue() == listener) { - iterator.remove(); - break; - } - } } private void onP2pStateChanged(@WifiP2pManager.WifiP2pState int state) { @@ -1299,57 +1302,53 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { mWifiP2pListeners.finishBroadcast(); } - private void onGroupCreated(WifiP2pInfo p2pInfo, WifiP2pGroup p2pGroup, - RemoteCallbackList<IWifiP2pListener> callbackList) { - int numCallbacks = callbackList.beginBroadcast(); + private void onGroupCreated(WifiP2pInfo p2pInfo, WifiP2pGroup p2pGroup) { + int numCallbacks = mWifiP2pListeners.beginBroadcast(); for (int i = 0; i < numCallbacks; i++) { try { - callbackList.getBroadcastItem(i).onGroupCreated(p2pInfo, p2pGroup); + mWifiP2pListeners.getBroadcastItem(i).onGroupCreated(p2pInfo, p2pGroup); } catch (RemoteException e) { Log.e(TAG, "Failure calling onGroupCreated" + e); } } - callbackList.finishBroadcast(); + mWifiP2pListeners.finishBroadcast(); } - private void onPeerClientJoined(WifiP2pInfo p2pInfo, WifiP2pGroup p2pGroup, - RemoteCallbackList<IWifiP2pListener> callbackList) { - int numCallbacks = callbackList.beginBroadcast(); + private void onPeerClientJoined(WifiP2pInfo p2pInfo, WifiP2pGroup p2pGroup) { + int numCallbacks = mWifiP2pListeners.beginBroadcast(); for (int i = 0; i < numCallbacks; i++) { try { - callbackList.getBroadcastItem(i).onPeerClientJoined(p2pInfo, p2pGroup); + mWifiP2pListeners.getBroadcastItem(i).onPeerClientJoined(p2pInfo, p2pGroup); } catch (RemoteException e) { Log.e(TAG, "Failure calling onPeerClientJoined" + e); } } - callbackList.finishBroadcast(); + mWifiP2pListeners.finishBroadcast(); } - private void onPeerClientDisconnected(WifiP2pInfo p2pInfo, WifiP2pGroup p2pGroup, - RemoteCallbackList<IWifiP2pListener> callbackList) { - int numCallbacks = callbackList.beginBroadcast(); + private void onPeerClientDisconnected(WifiP2pInfo p2pInfo, WifiP2pGroup p2pGroup) { + int numCallbacks = mWifiP2pListeners.beginBroadcast(); for (int i = 0; i < numCallbacks; i++) { try { - callbackList.getBroadcastItem(i).onPeerClientDisconnected(p2pInfo, + mWifiP2pListeners.getBroadcastItem(i).onPeerClientDisconnected(p2pInfo, p2pGroup); } catch (RemoteException e) { Log.e(TAG, "Failure calling onPeerClientDisconnected" + e); } } - callbackList.finishBroadcast(); + mWifiP2pListeners.finishBroadcast(); } - private void onFrequencyChanged(WifiP2pInfo p2pInfo, WifiP2pGroup p2pGroup, - RemoteCallbackList<IWifiP2pListener> callbackList) { - int numCallbacks = callbackList.beginBroadcast(); + private void onFrequencyChanged(WifiP2pInfo p2pInfo, WifiP2pGroup p2pGroup) { + int numCallbacks = mWifiP2pListeners.beginBroadcast(); for (int i = 0; i < numCallbacks; i++) { try { - callbackList.getBroadcastItem(i).onFrequencyChanged(p2pInfo, p2pGroup); + mWifiP2pListeners.getBroadcastItem(i).onFrequencyChanged(p2pInfo, p2pGroup); } catch (RemoteException e) { Log.e(TAG, "Failure calling onFrequencyChanged" + e); } } - callbackList.finishBroadcast(); + mWifiP2pListeners.finishBroadcast(); } private void onGroupRemoved() { @@ -1371,12 +1370,6 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { != PackageManager.PERMISSION_DENIED; } - private boolean isDualP2pSupported() { - return mFeatureFlags.p2pDual() - && mFeatureFlags.p2pOwnership() - && mWifiNative.isP2pP2pConcurrencySupported(); - } - @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) @@ -1394,12 +1387,11 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { pw.println("mListenStarted " + mListenStarted); pw.println("mDetailedState " + mDetailedState); pw.println("mTemporarilyDisconnectedWifi " + mTemporarilyDisconnectedWifi); - pw.println("mServiceDiscReqId " + mServiceDiscReqId); + pw.println("ServiceDiscoveryInfo " + mServiceDiscoveryInfo); pw.println("mDeathDataByBinder " + mDeathDataByBinder); pw.println("mClientInfoList " + mClientInfoList.size()); pw.println("mActiveClients " + mActiveClients); pw.println("mPeerAuthorizingTimestamp" + mPeerAuthorizingTimestamp); - pw.println("isDualP2pSupported" + isDualP2pSupported()); pw.println(); final IIpClient ipClient = mIpClient; @@ -1443,8 +1435,6 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { new P2pEnabledState(mThreshold, mThreadLocalLog); // Inactive is when p2p is enabled with no connectivity private final InactiveState mInactiveState = new InactiveState(mThreshold, mThreadLocalLog); - // Idle is when p2p is enabled and there's no ongoing connection attempt - private final IdleState mIdleState = new IdleState(mThreshold, mThreadLocalLog); private final GroupCreatingState mGroupCreatingState = new GroupCreatingState(mThreshold, mThreadLocalLog); private final UserAuthorizingInviteRequestState mUserAuthorizingInviteRequestState = @@ -1538,7 +1528,6 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { addState(mWaitingState, mP2pDisabledContainerState); addState(mP2pEnabledState, mDefaultState); addState(mInactiveState, mP2pEnabledState); - addState(mIdleState, mP2pEnabledState); addState(mGroupCreatingState, mP2pEnabledState); addState(mUserAuthorizingInviteRequestState, mGroupCreatingState); addState(mUserAuthorizingNegotiationRequestState, mGroupCreatingState); @@ -1596,7 +1585,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { mSettingsConfigStore.registerChangeListener(D2D_ALLOWED_WHEN_INFRA_STA_DISABLED, new D2DAllowWhenInfraStaDisabledValueListener(), this.getHandler()); // Register for location mode on/off broadcasts - mContext.registerReceiver(new BroadcastReceiver() { + mContext.registerReceiverForAllUsers(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { /* if location mode is off, ongoing discovery should be stopped. @@ -1610,7 +1599,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { sendMessage(WifiP2pManager.STOP_DISCOVERY); } } - }, new IntentFilter(LocationManager.MODE_CHANGED_ACTION)); + }, new IntentFilter(LocationManager.MODE_CHANGED_ACTION), null, getHandler()); // Register for tethering state if (!SdkLevel.isAtLeastS()) { mContext.registerReceiver(new BroadcastReceiver() { @@ -1665,25 +1654,6 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { } } - private RemoteCallbackList<IWifiP2pListener> generateCallbackList(WifiP2pGroup group) { - if (!mFeatureFlags.p2pOwnership() || mOwnershipMap.containsKey(SHARED_PKG_NAME)) { - return mWifiP2pListeners; - } - - for (Map.Entry<String, WifiP2pGroupInfo> entry : mOwnershipMap.entrySet()) { - if (entry.getValue().p2pGroup.getInterface().equals(group.getInterface())) { - IWifiP2pListener p2pListener = mP2pListenerMap.get(entry.getKey()); - RemoteCallbackList<IWifiP2pListener> listener = new RemoteCallbackList<>(); - if (p2pListener != null) { - listener.register(p2pListener); - logd("WifiP2pListener callback generated for " + entry.getKey()); - } - return listener; - } - } - return mWifiP2pListeners; - } - @Override protected String getLogRecString(Message msg) { StringBuilder sb = new StringBuilder(); @@ -1888,6 +1858,24 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { return "WifiP2pMonitor.SUP_DISCONNECTION_EVENT"; case WifiP2pMonitor.P2P_FREQUENCY_CHANGED_EVENT: return "WifiP2pMonitor.P2P_FREQUENCY_CHANGED_EVENT"; + case WifiP2pMonitor.USD_BASED_SERVICE_DISCOVERY_TERMINATED_EVENT: + return "WifiP2pMonitor.USD_BASED_SERVICE_DISCOVERY_TERMINATED_EVENT"; + case WifiP2pMonitor.USD_BASED_SERVICE_ADVERTISEMENT_TERMINATED_EVENT: + return "WifiP2pMonitor.USD_BASED_SERVICE_ADVERTISEMENT_TERMINATED_EVENT"; + case WifiP2pMonitor.P2P_PROV_DISC_PAIRING_BOOTSTRAPPING_OPPORTUNISTIC_REQ_EVENT: + return "WifiP2pMonitor" + + ".P2P_PROV_DISC_PAIRING_BOOTSTRAPPING_OPPORTUNISTIC_REQ_EVENT"; + case WifiP2pMonitor.P2P_PROV_DISC_PAIRING_BOOTSTRAPPING_OPPORTUNISTIC_RSP_EVENT: + return "WifiP2pMonitor" + + ".P2P_PROV_DISC_PAIRING_BOOTSTRAPPING_OPPORTUNISTIC_RSP_EVENT"; + case WifiP2pMonitor + .P2P_PROV_DISC_ENTER_PAIRING_BOOTSTRAPPING_PIN_OR_PASSPHRASE_EVENT: + return "WifiP2pMonitor" + + ".P2P_PROV_DISC_ENTER_PAIRING_BOOTSTRAPPING_PIN_OR_PASSPHRASE_EVENT"; + case WifiP2pMonitor + .P2P_PROV_DISC_SHOW_PAIRING_BOOTSTRAPPING_PIN_OR_PASSPHRASE_EVENT: + return "WifiP2pMonitor" + + ".P2P_PROV_DISC_SHOW_PAIRING_BOOTSTRAPPING_PIN_OR_PASSPHRASE_EVENT"; case WpsInfo.DISPLAY: return "WpsInfo.DISPLAY"; case WpsInfo.KEYPAD: @@ -1896,6 +1884,10 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { return "WifiP2pManager.SET_VENDOR_ELEMENTS"; case P2P_REJECTION_RESUME_AFTER_DELAY: return "P2P_REJECTION_RESUME_AFTER_DELAY"; + case WifiP2pManager.GET_DIR_INFO: + return "WifiP2pManager.GET_DIR_INFO"; + case WifiP2pManager.VALIDATE_DIR_INFO: + return "WifiP2pManager.VALIDATE_DIR_INFO"; case RunnerState.STATE_ENTER_CMD: return "Enter"; case RunnerState.STATE_EXIT_CMD: @@ -2077,6 +2069,26 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { WifiP2pMonitor.SUP_DISCONNECTION_EVENT, getHandler()); mWifiMonitor.registerHandler(mInterfaceName, WifiP2pMonitor.P2P_FREQUENCY_CHANGED_EVENT, getHandler()); + mWifiMonitor.registerHandler(mInterfaceName, + WifiP2pMonitor.USD_BASED_SERVICE_DISCOVERY_TERMINATED_EVENT, getHandler()); + mWifiMonitor.registerHandler(mInterfaceName, + WifiP2pMonitor.USD_BASED_SERVICE_ADVERTISEMENT_TERMINATED_EVENT, getHandler()); + mWifiMonitor.registerHandler(mInterfaceName, + WifiP2pMonitor + .P2P_PROV_DISC_PAIRING_BOOTSTRAPPING_OPPORTUNISTIC_REQ_EVENT, + getHandler()); + mWifiMonitor.registerHandler(mInterfaceName, + WifiP2pMonitor + .P2P_PROV_DISC_PAIRING_BOOTSTRAPPING_OPPORTUNISTIC_RSP_EVENT, + getHandler()); + mWifiMonitor.registerHandler(mInterfaceName, + WifiP2pMonitor + .P2P_PROV_DISC_ENTER_PAIRING_BOOTSTRAPPING_PIN_OR_PASSPHRASE_EVENT, + getHandler()); + mWifiMonitor.registerHandler(mInterfaceName, + WifiP2pMonitor + .P2P_PROV_DISC_SHOW_PAIRING_BOOTSTRAPPING_PIN_OR_PASSPHRASE_EVENT, + getHandler()); mWifiMonitor.startMonitoring(mInterfaceName); } @@ -2309,22 +2321,8 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { message.obj)); break; case WifiP2pManager.REQUEST_CONNECTION_INFO: - if (mFeatureFlags.p2pOwnership()) { - String packageName = getCallingPkgName( - message.sendingUid, message.replyTo); - WifiP2pGroupInfo groupInfo = getP2pGroupInfo(packageName); - if (groupInfo != null) { - replyToMessage(message, WifiP2pManager.RESPONSE_CONNECTION_INFO, - new WifiP2pInfo(groupInfo.p2pInfo)); - } else { - logd("No group owned by caller - returning empty info"); - replyToMessage(message, WifiP2pManager.RESPONSE_CONNECTION_INFO, - new WifiP2pInfo()); - } - } else { - replyToMessage(message, WifiP2pManager.RESPONSE_CONNECTION_INFO, - new WifiP2pInfo(mWifiP2pInfo)); - } + replyToMessage(message, WifiP2pManager.RESPONSE_CONNECTION_INFO, + new WifiP2pInfo(mWifiP2pInfo)); break; case WifiP2pManager.REQUEST_GROUP_INFO: { String packageName = getCallingPkgName(message.sendingUid, message.replyTo); @@ -2350,19 +2348,8 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { // remain at this state. break; } - if (mFeatureFlags.p2pOwnership()) { - WifiP2pGroupInfo groupInfo = getP2pGroupInfo(packageName); - if (groupInfo != null) { - replyToMessage(message, WifiP2pManager.RESPONSE_GROUP_INFO, - maybeEraseOwnDeviceAddress(groupInfo.p2pGroup, uid)); - } else { - logd("No group owned by caller - returning null group"); - replyToMessage(message, WifiP2pManager.RESPONSE_GROUP_INFO, null); - } - } else { - replyToMessage(message, WifiP2pManager.RESPONSE_GROUP_INFO, - maybeEraseOwnDeviceAddress(mGroup, message.sendingUid)); - } + replyToMessage(message, WifiP2pManager.RESPONSE_GROUP_INFO, + maybeEraseOwnDeviceAddress(mGroup, message.sendingUid)); break; } case WifiP2pManager.REQUEST_PERSISTENT_GROUP_INFO: { @@ -2468,6 +2455,9 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { case WifiP2pMonitor.P2P_DEVICE_LOST_EVENT: case WifiP2pMonitor.P2P_FIND_STOPPED_EVENT: case WifiP2pMonitor.P2P_SERV_DISC_RESP_EVENT: + case WifiP2pMonitor.P2P_PROV_DISC_FAILURE_EVENT: + case WifiP2pMonitor.USD_BASED_SERVICE_DISCOVERY_TERMINATED_EVENT: + case WifiP2pMonitor.USD_BASED_SERVICE_ADVERTISEMENT_TERMINATED_EVENT: case PEER_CONNECTION_USER_ACCEPT: case PEER_CONNECTION_USER_REJECT: case DISCONNECT_WIFI_RESPONSE: @@ -2483,7 +2473,6 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { case TETHER_INTERFACE_STATE_CHANGED: case TETHER_INTERFACE_CLIENTS_CHANGED: case UPDATE_P2P_DISALLOWED_CHANNELS: - case WifiP2pMonitor.P2P_PROV_DISC_FAILURE_EVENT: case SET_MIRACAST_MODE: break; case WifiP2pManager.START_LISTEN: @@ -2953,7 +2942,6 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { } resetWifiP2pInfo(); mGroup = null; - mOwnershipMap.clear(); } @Override @@ -3078,11 +3066,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { } else if (proceedWithOperation == InterfaceConflictManager.ICM_EXECUTE_COMMAND) { if (setupInterface()) { - if (mFeatureFlags.p2pOwnership()) { - smTransition(this, mIdleState); - } else { - smTransition(this, mInactiveState); - } + smTransition(this, mInactiveState); } } // else InterfaceConflictManager.ICM_SKIP_COMMAND_WAIT_FOR_USER: nop break; @@ -3160,11 +3144,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { == InterfaceConflictManager.ICM_EXECUTE_COMMAND) { if (!setupInterface()) return NOT_HANDLED; deferMessage(message); - if (mFeatureFlags.p2pOwnership()) { - smTransition(this, mIdleState); - } else { - smTransition(this, mInactiveState); - } + smTransition(this, mInactiveState); } // else InterfaceConflictManager.ICM_SKIP_COMMAND_WAIT_FOR_USER: nop break; } @@ -3335,7 +3315,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { mLastCallerInfoManager.put(apiType, Process.myTid(), uid, 0, packageName, true); // do not send service discovery request while normal find operation. - clearSupplicantServiceRequest(); + clearGasFrameBasedServiceDiscoveryRequests(); if (p2pFind(scanType, freq, DISCOVER_TIMEOUT_S, discoveryConfig)) { mWifiP2pMetrics.incrementPeerScans(); replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_SUCCEEDED); @@ -3361,6 +3341,48 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { WifiP2pManager.ERROR); } break; + case WifiP2pMonitor.USD_BASED_SERVICE_ADVERTISEMENT_TERMINATED_EVENT: { + int sessionId = message.arg1; + for (ClientInfo c : mClientInfoList.values()) { + for (WifiP2pServiceInfo serviceInfo : c.mUsdServiceAdvertiseList) { + if (serviceInfo.getUsdSessionId() == sessionId) { + serviceInfo.setUsdSessionId(0); + break; + } + } + } + // TODO check the reason code and set to false only for timeout + sendP2pListenChangedBroadcast(false); + break; + } + case WifiP2pMonitor.USD_BASED_SERVICE_DISCOVERY_TERMINATED_EVENT: { + int sessionId = message.arg1; + if (mServiceDiscoveryInfo.isValid()) { + if (sessionId == mServiceDiscoveryInfo.getSessionIdInt()) { + mServiceDiscoveryInfo.invalidate(); + sendP2pDiscoveryChangedBroadcast(false); + } else { + Log.e(TAG, "USD terminate event received from non-active" + + " session ID: " + sessionId + " . Active session ID: " + + mServiceDiscoveryInfo.getSessionIdInt()); + } + } else { + Log.e(TAG, "USD terminate event received from non-active session" + + " ID: " + sessionId + " when there is no active session" + + " running"); + } + + for (ClientInfo c : mClientInfoList.values()) { + for (WifiP2pServiceRequest serviceRequest : c.mUsdServiceDiscoverList) { + if (serviceRequest.getUsdSessionId() == sessionId) { + Log.d(TAG, "Clear service request session ID: " + sessionId); + serviceRequest.setUsdSessionId(0); + break; + } + } + } + break; + } case WifiP2pManager.DISCOVER_SERVICES: { String packageName = getCallingPkgName(message.sendingUid, message.replyTo); if (packageName == null) { @@ -3392,19 +3414,13 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { WifiP2pManager.BUSY); break; } - if (mVerboseLoggingEnabled) logd(getName() + " discover services"); - if (!updateSupplicantServiceRequest()) { - replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED, - WifiP2pManager.NO_SERVICE_REQUESTS); - break; - } - if (p2pFind(DISCOVER_TIMEOUT_S)) { - sendP2pDiscoveryChangedBroadcast(true); - mWifiP2pMetrics.incrementServiceScans(); - replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_SUCCEEDED); + int serviceDiscoveryType = message.arg1; + logd(getName() + " discover services - Type: " + serviceDiscoveryType); + if (serviceDiscoveryType + == WifiP2pManager.WIFI_P2P_USD_BASED_SERVICE_DISCOVERY) { + processUsdFrameBasedServiceDiscoveryRequestMessage(message); } else { - replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED, - WifiP2pManager.ERROR); + processGasFrameBasedServiceDiscoveryRequestMessage(message); } break; } @@ -3455,9 +3471,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { break; } if (mVerboseLoggingEnabled) logd(getName() + " add service"); - WifiP2pServiceInfo servInfo = - extras.getParcelable(WifiP2pManager.EXTRA_PARAM_KEY_SERVICE_INFO); - if (addLocalService(message.replyTo, servInfo)) { + if (addLocalService(message)) { replyToMessage(message, WifiP2pManager.ADD_LOCAL_SERVICE_SUCCEEDED); } else { replyToMessage(message, WifiP2pManager.ADD_LOCAL_SERVICE_FAILED); @@ -3477,9 +3491,10 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { break; case WifiP2pManager.ADD_SERVICE_REQUEST: if (mVerboseLoggingEnabled) logd(getName() + " add service request"); - if (!addServiceRequest(message.replyTo, - (WifiP2pServiceRequest) message.obj)) { - replyToMessage(message, WifiP2pManager.ADD_SERVICE_REQUEST_FAILED); + AtomicInteger errorCode = new AtomicInteger(0); + if (!addServiceRequest(message, errorCode)) { + replyToMessage(message, WifiP2pManager.ADD_SERVICE_REQUEST_FAILED, + errorCode.get()); break; } replyToMessage(message, WifiP2pManager.ADD_SERVICE_REQUEST_SUCCEEDED); @@ -3529,7 +3544,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { case SET_MIRACAST_MODE: mWifiNative.setMiracastMode(message.arg1); break; - case WifiP2pManager.START_LISTEN: + case WifiP2pManager.START_LISTEN: { String packageName = getCallingPkgName(message.sendingUid, message.replyTo); if (packageName == null) { replyToMessage(message, WifiP2pManager.START_LISTEN_FAILED); @@ -3546,10 +3561,10 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { .getBundle(WifiP2pManager.EXTRA_PARAM_KEY_BUNDLE); WifiP2pExtListenParams extListenParams = SdkLevel.isAtLeastV() && (listenType == WifiP2pManager.WIFI_P2P_EXT_LISTEN_WITH_PARAMS) - ? extras.getParcelable( - WifiP2pManager.EXTRA_PARAM_KEY_EXT_LISTEN_PARAMS, - WifiP2pExtListenParams.class) - : null; + ? extras.getParcelable( + WifiP2pManager.EXTRA_PARAM_KEY_EXT_LISTEN_PARAMS, + WifiP2pExtListenParams.class) + : null; boolean hasPermission; if (isPlatformOrTargetSdkLessThanT(packageName, uid)) { hasPermission = mWifiPermissionsUtil.checkCanAccessWifiDirect( @@ -3580,6 +3595,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { replyToMessage(message, WifiP2pManager.START_LISTEN_FAILED); } break; + } case WifiP2pManager.STOP_LISTEN: mLastCallerInfoManager.put(WifiManager.API_P2P_STOP_LISTENING, Process.myTid(), message.sendingUid, 0, @@ -3640,6 +3656,68 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { } updateP2pChannels(); break; + case WifiP2pManager.GET_DIR_INFO: { + String packageName = getCallingPkgName(message.sendingUid, message.replyTo); + if (packageName == null) { + replyToMessage(message, WifiP2pManager.GET_DIR_INFO_FAILED, + WifiP2pManager.ERROR); + break; + } + if (!isWifiDirect2Enabled()) { + replyToMessage(message, WifiP2pManager.GET_DIR_INFO_FAILED, + WifiP2pManager.ERROR); + break; + } + if (!checkNearbyDevicesPermission(message, "GET_DIR_INFO")) { + replyToMessage(message, WifiP2pManager.GET_DIR_INFO_FAILED, + WifiP2pManager.NO_PERMISSION); + break; + } + + WifiP2pDirInfo dirInfo = mWifiNative.getDirInfo(); + if (mVerboseLoggingEnabled) { + Log.d(TAG, " DIR info received: " + dirInfo); + } + replyToMessage(message, WifiP2pManager.RESPONSE_GET_DIR_INFO, dirInfo); + break; + } + case WifiP2pManager.VALIDATE_DIR_INFO: { + String packageName = getCallingPkgName(message.sendingUid, message.replyTo); + if (packageName == null) { + replyToMessage(message, WifiP2pManager.VALIDATE_DIR_INFO_FAILED, + WifiP2pManager.ERROR); + break; + } + if (!isWifiDirect2Enabled()) { + replyToMessage(message, WifiP2pManager.GET_DIR_INFO_FAILED, + WifiP2pManager.ERROR); + break; + } + if (!checkNearbyDevicesPermission(message, "VALIDATE_DIR_INFO")) { + replyToMessage(message, WifiP2pManager.VALIDATE_DIR_INFO_FAILED, + WifiP2pManager.NO_PERMISSION); + break; + } + Bundle extras = message.getData() + .getBundle(WifiP2pManager.EXTRA_PARAM_KEY_BUNDLE); + WifiP2pDirInfo dirInfo = extras.getParcelable( + WifiP2pManager.EXTRA_PARAM_KEY_DIR_INFO); + if (dirInfo == null) { + replyToMessage(message, WifiP2pManager.VALIDATE_DIR_INFO_FAILED, + WifiP2pManager.ERROR); + break; + } + boolean isValid = false; + if (mWifiNative.validateDirInfo(dirInfo) >= 0) { + isValid = true; + } + if (mVerboseLoggingEnabled) { + Log.d(TAG, " DIR info validated. isValid: " + isValid); + } + replyToMessage(message, WifiP2pManager.RESPONSE_VALIDATE_DIR_INFO, + isValid ? 1 : 0); + break; + } default: return NOT_HANDLED; } @@ -3692,6 +3770,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { cancelIdleShutdown(); } + @SuppressLint("NewApi") @Override public boolean processMessageImpl(Message message) { logSmMessage(getName(), message); @@ -3752,6 +3831,24 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { isConnectFailed = true; replyToMessage(message, WifiP2pManager.CONNECT_FAILED); } + } else if (isConfigForV2Connection(config)) { + // TODO add support for V2 persistent connection + if (isWifiDirect2Enabled()) { + mAutonomousGroup = false; + mWifiNative.p2pStopFind(); + if (isConfigForBootstrappingMethodOutOfBand(config)) { + if (mWifiNative.p2pConnect(config, FORM_GROUP) != null) { + smTransition(this, mGroupNegotiationState); + } else { + isConnectFailed = true; + } + } else { + smTransition(this, mProvisionDiscoveryState); + } + } else { + isConnectFailed = true; + replyToMessage(message, WifiP2pManager.CONNECT_FAILED); + } } else { if (isConfigInvalid(config)) { loge("Dropping connect request " + config); @@ -3791,7 +3888,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { // When discovery stops in inactive state, flush to clear // state peer data mWifiNative.p2pFlush(); - mServiceDiscReqId = null; + mServiceDiscoveryInfo.invalidate(); replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_SUCCEEDED); } else { replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_FAILED, @@ -3873,7 +3970,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { // Handling provision discovery and issuing a p2p_connect before // group negotiation comes through causes issues break; - case WifiP2pMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT: + case WifiP2pMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT: { if (message.obj == null) { Log.e(TAG, "Illegal argument(s)"); break; @@ -3887,471 +3984,39 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { mSavedPeerConfig = new WifiP2pConfig(); mSavedPeerConfig.wps.setup = WpsInfo.DISPLAY; mSavedPeerConfig.deviceAddress = device.deviceAddress; - mSavedPeerConfig.wps.pin = provDisc.pin; + mSavedPeerConfig.wps.pin = provDisc.wpsPin; if (SdkLevel.isAtLeastV() && provDisc.getVendorData() != null) { mSavedPeerConfig.setVendorData(provDisc.getVendorData()); } - notifyP2pProvDiscShowPinRequest(provDisc.pin, device.deviceAddress); + notifyP2pProvDiscShowPinRequest(provDisc.wpsPin, device.deviceAddress); mPeers.updateStatus(device.deviceAddress, WifiP2pDevice.INVITED); sendPeersChangedBroadcast(); smTransition(this, mUserAuthorizingNegotiationRequestState); break; - case WifiP2pManager.CREATE_GROUP: { - String packageName = getCallingPkgName(message.sendingUid, message.replyTo); - if (packageName == null) { - replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED, - WifiP2pManager.ERROR); - break; - } - int uid = message.sendingUid; - String attributionTag = getCallingFeatureId(uid, message.replyTo); - Bundle extras = message.getData() - .getBundle(WifiP2pManager.EXTRA_PARAM_KEY_BUNDLE); - boolean hasPermission; - if (isPlatformOrTargetSdkLessThanT(packageName, uid)) { - hasPermission = mWifiPermissionsUtil.checkCanAccessWifiDirect( - packageName, - attributionTag, - uid, false); - } else { - hasPermission = checkNearbyDevicesPermission(uid, packageName, - extras, "CREATE_GROUP", message.obj); - } - if (!hasPermission) { - replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED, - WifiP2pManager.ERROR); - // remain at this state. - break; - } - mAutonomousGroup = true; - int netId = message.arg1; - config = extras.getParcelable(WifiP2pManager.EXTRA_PARAM_KEY_CONFIG); - mLastCallerInfoManager.put(config == null - ? WifiManager.API_P2P_CREATE_GROUP - : WifiManager.API_P2P_CREATE_GROUP_P2P_CONFIG, - Process.myTid(), uid, 0, packageName, true); - boolean ret = false; - if (config != null) { - if (isConfigValidAsGroup(config)) { - if (mVerboseLoggingEnabled) { - logd("FAST_CONNECTION GO band freq: " - + getGroupOwnerBandToString(config.groupOwnerBand)); - } - reportConnectionEventTakeBugReportIfOverlapped( - P2pConnectionEvent.CONNECTION_FAST, - config, GroupEvent.GROUP_OWNER, uid, attributionTag); - ret = mWifiNative.p2pGroupAdd(config, false); - } - } else if (netId == WifiP2pGroup.NETWORK_ID_PERSISTENT) { - // check if the go persistent group is present. - netId = mGroups.getNetworkId(mThisDevice.deviceAddress); - if (netId != -1) { - mWifiP2pMetrics.startConnectionEvent( - P2pConnectionEvent.CONNECTION_REINVOKE, - null, GroupEvent.GROUP_OWNER, uid, attributionTag); - ret = mWifiNative.p2pGroupAdd(netId); - } else { - mWifiP2pMetrics.startConnectionEvent( - P2pConnectionEvent.CONNECTION_LOCAL, - null, GroupEvent.GROUP_OWNER, uid, attributionTag); - ret = mWifiNative.p2pGroupAdd(true); - } - } else { - mWifiP2pMetrics.startConnectionEvent( - P2pConnectionEvent.CONNECTION_LOCAL, - null, GroupEvent.GROUP_OWNER, uid, attributionTag); - ret = mWifiNative.p2pGroupAdd(false); - } - - if (ret) { - replyToMessage(message, WifiP2pManager.CREATE_GROUP_SUCCEEDED); - smTransition(this, mGroupNegotiationState); - } else { - replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED, - WifiP2pManager.ERROR); - // remain at this state. - String errorMsg = "P2P group creating failed"; - if (mVerboseLoggingEnabled) logd(getName() + errorMsg); - if (mWifiP2pMetrics.isP2pFastConnectionType()) { - takeBugReportP2pFailureIfNeeded("Wi-Fi BugReport (P2P " - + mWifiP2pMetrics.getP2pGroupRoleString() - + " creation failure)", errorMsg); - } - mWifiP2pMetrics.endConnectionEvent( - P2pConnectionEvent.CLF_CREATE_GROUP_FAILED); - } - break; } - case WifiP2pMonitor.P2P_GROUP_STARTED_EVENT: - if (message.obj == null) { - Log.e(TAG, "Invalid argument(s)"); - break; - } - mGroup = (WifiP2pGroup) message.obj; - if (mVerboseLoggingEnabled) logd(getName() + " group started"); - if (mGroup.isGroupOwner() - && EMPTY_DEVICE_ADDRESS.equals(mGroup.getOwner().deviceAddress)) { - // wpa_supplicant doesn't set own device address to go_dev_addr. - mGroup.getOwner().deviceAddress = mThisDevice.deviceAddress; - } - // We hit this scenario when a persistent group is reinvoked - if (mGroup.getNetworkId() == WifiP2pGroup.NETWORK_ID_PERSISTENT) { + case WifiP2pMonitor + .P2P_PROV_DISC_PAIRING_BOOTSTRAPPING_OPPORTUNISTIC_REQ_EVENT: { + if (processProvDiscPairingBootstrappingOpportunisticRequestEvent( + (WifiP2pProvDiscEvent) message.obj)) { mAutonomousGroup = false; - deferMessage(message); - smTransition(this, mGroupNegotiationState); - } else { - loge("Unexpected group creation, remove " + mGroup); - mWifiNative.p2pGroupRemove(mGroup.getInterface()); - mGroup = null; + mJoinExistingGroup = false; + smTransition(this, mUserAuthorizingNegotiationRequestState); } break; - case WifiP2pManager.START_LISTEN: - String packageName = getCallingPkgName(message.sendingUid, message.replyTo); - if (packageName == null) { - replyToMessage(message, WifiP2pManager.START_LISTEN_FAILED); - break; - } - int uid = message.sendingUid; - int listenType = message.arg1; - if (listenType == WifiP2pManager.WIFI_P2P_EXT_LISTEN_WITH_PARAMS - && !SdkLevel.isAtLeastV()) { - replyToMessage(message, WifiP2pManager.START_LISTEN_FAILED); - break; - } - Bundle extras = message.getData() - .getBundle(WifiP2pManager.EXTRA_PARAM_KEY_BUNDLE); - WifiP2pExtListenParams extListenParams = SdkLevel.isAtLeastV() - && (listenType == WifiP2pManager.WIFI_P2P_EXT_LISTEN_WITH_PARAMS) - ? extras.getParcelable( - WifiP2pManager.EXTRA_PARAM_KEY_EXT_LISTEN_PARAMS, - WifiP2pExtListenParams.class) - : null; - boolean hasPermission; - if (isPlatformOrTargetSdkLessThanT(packageName, uid)) { - hasPermission = mWifiPermissionsUtil.checkCanAccessWifiDirect( - packageName, - getCallingFeatureId(message.sendingUid, message.replyTo), - uid, true); - } else { - hasPermission = checkNearbyDevicesPermission(uid, packageName, - extras, "START_LISTEN", message.obj); - } - if (!hasPermission) { - replyToMessage(message, WifiP2pManager.START_LISTEN_FAILED); - break; - } - mLastCallerInfoManager.put(WifiManager.API_P2P_START_LISTENING, - Process.myTid(), uid, 0, packageName, true); - if (mVerboseLoggingEnabled) logd(getName() + " start listen mode"); - mWifiNative.p2pStopFind(); - if (mWifiNative.p2pExtListen(true, - mContext.getResources().getInteger( - R.integer.config_wifiP2pExtListenPeriodMs), - mContext.getResources().getInteger( - R.integer.config_wifiP2pExtListenIntervalMs), - extListenParams)) { - replyToMessage(message, WifiP2pManager.START_LISTEN_SUCCEEDED); - sendP2pListenChangedBroadcast(true); - } else { - replyToMessage(message, WifiP2pManager.START_LISTEN_FAILED); - } - break; - case WifiP2pManager.STOP_LISTEN: - mLastCallerInfoManager.put(WifiManager.API_P2P_STOP_LISTENING, - Process.myTid(), message.sendingUid, 0, - getCallingPkgName(message.sendingUid, message.replyTo), true); - if (mVerboseLoggingEnabled) logd(getName() + " stop listen mode"); - if (mWifiNative.p2pExtListen(false, 0, 0, null)) { - replyToMessage(message, WifiP2pManager.STOP_LISTEN_SUCCEEDED); - sendP2pListenChangedBroadcast(false); - } else { - replyToMessage(message, WifiP2pManager.STOP_LISTEN_FAILED); - } - mWifiNative.p2pStopFind(); - break; - case WifiP2pManager.SET_CHANNEL: - if (!checkNetworkSettingsOrNetworkStackOrOverrideWifiConfigPermission( - message.sendingUid)) { - loge("Permission violation - none of NETWORK_SETTING, NETWORK_STACK," - + " or OVERRIDE_WIFI_CONFIG permission, uid = " - + message.sendingUid); - replyToMessage(message, WifiP2pManager.SET_CHANNEL_FAILED, - WifiP2pManager.ERROR); - break; - } - if (message.obj == null) { - Log.e(TAG, "Illegal arguments(s)"); - break; - } - mLastCallerInfoManager.put(WifiManager.API_P2P_SET_CHANNELS, - Process.myTid(), message.sendingUid, 0, - getCallingPkgName(message.sendingUid, message.replyTo), true); - Bundle p2pChannels = (Bundle) message.obj; - mUserListenChannel = p2pChannels.getInt("lc", 0); - mUserOperatingChannel = p2pChannels.getInt("oc", 0); - if (updateP2pChannels()) { - replyToMessage(message, WifiP2pManager.SET_CHANNEL_SUCCEEDED); - } else { - replyToMessage(message, WifiP2pManager.SET_CHANNEL_FAILED); - } - break; - case WifiP2pManager.INITIATOR_REPORT_NFC_HANDOVER: - String handoverSelect = null; - - if (message.obj != null) { - handoverSelect = ((Bundle) message.obj) - .getString(WifiP2pManager.EXTRA_HANDOVER_MESSAGE); - } - - if (handoverSelect != null - && mWifiNative.initiatorReportNfcHandover(handoverSelect)) { - replyToMessage(message, WifiP2pManager.REPORT_NFC_HANDOVER_SUCCEEDED); - smTransition(this, mGroupCreatingState); - } else { - replyToMessage(message, WifiP2pManager.REPORT_NFC_HANDOVER_FAILED); - } - break; - case WifiP2pManager.RESPONDER_REPORT_NFC_HANDOVER: - String handoverRequest = null; - - if (message.obj != null) { - handoverRequest = ((Bundle) message.obj) - .getString(WifiP2pManager.EXTRA_HANDOVER_MESSAGE); - } - - if (handoverRequest != null - && mWifiNative.responderReportNfcHandover(handoverRequest)) { - replyToMessage(message, WifiP2pManager.REPORT_NFC_HANDOVER_SUCCEEDED); - smTransition(this, mGroupCreatingState); - } else { - replyToMessage(message, WifiP2pManager.REPORT_NFC_HANDOVER_FAILED); - } - break; - default: - return NOT_HANDLED; - } - return HANDLED; - } - - @Override - public String getMessageLogRec(int what) { - return P2pStateMachine.class.getSimpleName() + "." - + this.getClass().getSimpleName() - + "." + getWhatToString(what); - } - } - - class IdleState extends RunnerState { - - /** - * The Runner state Constructor - * - * @param threshold the running time threshold in milliseconds - */ - IdleState(int threshold, @NonNull LocalLog localLog) { - super(threshold, localLog); - } - - @Override - public void enterImpl() { - logSmStateName(this.getName(), - getCurrentState() != null ? getCurrentState().getName() : ""); - mPeerAuthorizingTimestamp.clear(); - mSavedPeerConfig.invalidate(); - mDetailedState = NetworkInfo.DetailedState.IDLE; - mConnectionPkgName = SHARED_PKG_NAME; - scheduleIdleShutdown(); - } - - @Override - public void exitImpl() { - cancelIdleShutdown(); - } - - @Override - public boolean processMessageImpl(Message message) { - logSmMessage(getName(), message); - // Re-schedule the shutdown timer since we got the new operation. - // only handle commands from clients. - if (message.what > Protocol.BASE_WIFI_P2P_MANAGER - && message.what < Protocol.BASE_WIFI_P2P_SERVICE) { - scheduleIdleShutdown(); - } - switch (message.what) { - case WifiP2pManager.CONNECT: { - String packageName = getCallingPkgName(message.sendingUid, message.replyTo); - if (packageName == null) { - replyToMessage(message, WifiP2pManager.CONNECT_FAILED); - break; - } - int uid = message.sendingUid; - String attributionTag = getCallingFeatureId(uid, message.replyTo); - Bundle extras = message.getData() - .getBundle(WifiP2pManager.EXTRA_PARAM_KEY_BUNDLE); - boolean hasPermission = false; - if (isPlatformOrTargetSdkLessThanT(packageName, uid)) { - hasPermission = mWifiPermissionsUtil.checkCanAccessWifiDirect( - packageName, - attributionTag, - uid, false); - } else { - hasPermission = checkNearbyDevicesPermission(uid, packageName, - extras, "CONNECT", message.obj); - } - if (!hasPermission) { - replyToMessage(message, WifiP2pManager.CONNECT_FAILED); - // remain at this state. - break; - } - mLastCallerInfoManager.put(WifiManager.API_P2P_CONNECT, - Process.myTid(), uid, 0, packageName, true); - if (mVerboseLoggingEnabled) logd(getName() + " sending connect"); - WifiP2pConfig config = (WifiP2pConfig) - extras.getParcelable(WifiP2pManager.EXTRA_PARAM_KEY_CONFIG); - - boolean isConnectFailed = false; - if (isConfigValidAsGroup(config)) { + } + case WifiP2pMonitor + .P2P_PROV_DISC_ENTER_PAIRING_BOOTSTRAPPING_PIN_OR_PASSPHRASE_EVENT: { + if (processProvDiscEnterPairingBootstrappingPinOrPassphraseEvent( + (WifiP2pProvDiscEvent) message.obj)) { mAutonomousGroup = false; - mWifiNative.p2pStopFind(); - if (mVerboseLoggingEnabled) { - logd("FAST_CONNECTION GC band freq: " + getGroupOwnerBandToString( - config.groupOwnerBand)); - } - if (mWifiNative.p2pGroupAdd(config, true)) { - reportConnectionEventTakeBugReportIfOverlapped( - P2pConnectionEvent.CONNECTION_FAST, - config, WifiMetricsProto.GroupEvent.GROUP_CLIENT, uid, - attributionTag); - mConnectionPkgName = packageName; - smTransition(this, mGroupNegotiationState); - } else { - loge("Cannot join a group with config."); - isConnectFailed = true; - replyToMessage(message, WifiP2pManager.CONNECT_FAILED); - } - } else { - if (isConfigInvalid(config)) { - loge("Dropping connect request " + config); - isConnectFailed = true; - replyToMessage(message, WifiP2pManager.CONNECT_FAILED); - } else { - mAutonomousGroup = false; - mWifiNative.p2pStopFind(); - if (reinvokePersistentGroup(config, false)) { - mWifiP2pMetrics.startConnectionEvent( - P2pConnectionEvent.CONNECTION_REINVOKE, - config, GroupEvent.GROUP_UNKNOWN, uid, attributionTag); - smTransition(this, mGroupNegotiationState); - } else { - mWifiP2pMetrics.startConnectionEvent( - P2pConnectionEvent.CONNECTION_FRESH, - config, GroupEvent.GROUP_UNKNOWN, uid, attributionTag); - smTransition(this, mProvisionDiscoveryState); - } - } - } - - if (!isConnectFailed) { - mSavedPeerConfig = config; - mPeers.updateStatus(mSavedPeerConfig.deviceAddress, - WifiP2pDevice.INVITED); - sendPeersChangedBroadcast(); - replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED); + mJoinExistingGroup = false; + smTransition(this, mUserAuthorizingNegotiationRequestState); } break; } - case WifiP2pManager.STOP_DISCOVERY: - mLastCallerInfoManager.put(WifiManager.API_P2P_STOP_PEER_DISCOVERY, - Process.myTid(), message.sendingUid, 0, - getCallingPkgName(message.sendingUid, message.replyTo), true); - if (mWifiNative.p2pStopFind()) { - // When discovery stops in inactive state, flush to clear - // state peer data - mWifiNative.p2pFlush(); - mServiceDiscReqId = null; - replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_SUCCEEDED); - } else { - replyToMessage(message, WifiP2pManager.STOP_DISCOVERY_FAILED, - WifiP2pManager.ERROR); - } - break; - case CMD_P2P_IDLE_SHUTDOWN: - Log.d(TAG, "IdleShutDown message received"); - sendMessage(DISABLE_P2P); - break; - case WifiP2pMonitor.P2P_GO_NEGOTIATION_REQUEST_EVENT: - WifiP2pConfig config = (WifiP2pConfig) message.obj; - if (isConfigInvalid(config)) { - loge("Dropping GO neg request " + config); - break; - } - mSavedPeerConfig = config; - mAutonomousGroup = false; - mJoinExistingGroup = false; - mWifiP2pMetrics.startConnectionEvent( - P2pConnectionEvent.CONNECTION_FRESH, - config, GroupEvent.GROUP_UNKNOWN, Process.SYSTEM_UID, null); - smTransition(this, mUserAuthorizingNegotiationRequestState); - break; - case WifiP2pMonitor.P2P_INVITATION_RECEIVED_EVENT: - if (message.obj == null) { - Log.e(TAG, "Invalid argument(s)"); - break; - } - WifiP2pGroup group = (WifiP2pGroup) message.obj; - WifiP2pDevice owner = group.getOwner(); - if (owner == null) { - int id = group.getNetworkId(); - if (id < 0) { - loge("Ignored invitation from null owner"); - break; - } - - String addr = mGroups.getOwnerAddr(id); - if (addr != null) { - group.setOwner(new WifiP2pDevice(addr)); - owner = group.getOwner(); - } else { - loge("Ignored invitation from null owner"); - break; - } - } - config = new WifiP2pConfig(); - config.deviceAddress = group.getOwner().deviceAddress; - if (isConfigInvalid(config)) { - loge("Dropping invitation request " + config); - break; - } - mSavedPeerConfig = config; - - // Check if we have the owner in peer list and use appropriate - // wps method. Default is to use PBC. - if (owner != null && ((owner = mPeers.get(owner.deviceAddress)) != null)) { - if (owner.wpsPbcSupported()) { - mSavedPeerConfig.wps.setup = WpsInfo.PBC; - } else if (owner.wpsKeypadSupported()) { - mSavedPeerConfig.wps.setup = WpsInfo.KEYPAD; - } else if (owner.wpsDisplaySupported()) { - mSavedPeerConfig.wps.setup = WpsInfo.DISPLAY; - } - } - - mAutonomousGroup = false; - mJoinExistingGroup = true; - mWifiP2pMetrics.startConnectionEvent( - P2pConnectionEvent.CONNECTION_FRESH, - config, GroupEvent.GROUP_UNKNOWN, Process.SYSTEM_UID, null); - smTransition(this, mUserAuthorizingInviteRequestState); - break; - case WifiP2pMonitor.P2P_PROV_DISC_PBC_REQ_EVENT: - case WifiP2pMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT: - // We let the supplicant handle the provision discovery response - // and wait instead for the GO_NEGOTIATION_REQUEST_EVENT. - // Handling provision discovery and issuing a p2p_connect before - // group negotiation comes through causes issues - break; - case WifiP2pMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT: + case WifiP2pMonitor + .P2P_PROV_DISC_SHOW_PAIRING_BOOTSTRAPPING_PIN_OR_PASSPHRASE_EVENT: { if (message.obj == null) { Log.e(TAG, "Illegal argument(s)"); break; @@ -4362,19 +4027,15 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { loge("Device entry is null"); break; } - mSavedPeerConfig = new WifiP2pConfig(); - mSavedPeerConfig.wps.setup = WpsInfo.DISPLAY; - mSavedPeerConfig.deviceAddress = device.deviceAddress; - mSavedPeerConfig.wps.pin = provDisc.pin; - if (SdkLevel.isAtLeastV() && provDisc.getVendorData() != null) { - mSavedPeerConfig.setVendorData(provDisc.getVendorData()); + if (processProvDiscShowPairingBootstrappingPinOrPassphraseEvent(provDisc)) { + mAutonomousGroup = false; + mJoinExistingGroup = false; + notifyP2pProvDiscShowPinRequest(provDisc.pairingPinOrPassphrase, + device.deviceAddress); + smTransition(this, mUserAuthorizingNegotiationRequestState); } - - notifyP2pProvDiscShowPinRequest(provDisc.pin, device.deviceAddress); - mPeers.updateStatus(device.deviceAddress, WifiP2pDevice.INVITED); - sendPeersChangedBroadcast(); - smTransition(this, mUserAuthorizingNegotiationRequestState); break; + } case WifiP2pManager.CREATE_GROUP: { String packageName = getCallingPkgName(message.sendingUid, message.replyTo); if (packageName == null) { @@ -4410,17 +4071,21 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { : WifiManager.API_P2P_CREATE_GROUP_P2P_CONFIG, Process.myTid(), uid, 0, packageName, true); boolean ret = false; - if (config != null) { - if (isConfigValidAsGroup(config)) { - mConnectionPkgName = packageName; - if (mVerboseLoggingEnabled) { - logd("FAST_CONNECTION GO band freq: " - + getGroupOwnerBandToString(config.groupOwnerBand)); - } - reportConnectionEventTakeBugReportIfOverlapped( - P2pConnectionEvent.CONNECTION_FAST, - config, GroupEvent.GROUP_OWNER, uid, attributionTag); - ret = mWifiNative.p2pGroupAdd(config, false); + if (isConfigValidAsGroup(config)) { + if (mVerboseLoggingEnabled) { + logd("FAST_CONNECTION GO band freq: " + + getGroupOwnerBandToString(config.groupOwnerBand)); + } + reportConnectionEventTakeBugReportIfOverlapped( + P2pConnectionEvent.CONNECTION_FAST, + config, GroupEvent.GROUP_OWNER, uid, attributionTag); + ret = mWifiNative.p2pGroupAdd(config, false); + } else if (isConfigForGroupOwnerV2(config)) { + logd("Requested to create Group Owner - V2"); + // TODO check if the configuration is to start persistent connection. + // TODO check if the persistent group is present + if (isWifiDirect2Enabled()) { + ret = mWifiNative.p2pGroupAdd(false, true); } } else if (netId == WifiP2pGroup.NETWORK_ID_PERSISTENT) { // check if the go persistent group is present. @@ -4429,25 +4094,24 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { mWifiP2pMetrics.startConnectionEvent( P2pConnectionEvent.CONNECTION_REINVOKE, null, GroupEvent.GROUP_OWNER, uid, attributionTag); - ret = mWifiNative.p2pGroupAdd(netId); + ret = mWifiNative.p2pGroupAdd(netId, false); } else { mWifiP2pMetrics.startConnectionEvent( P2pConnectionEvent.CONNECTION_LOCAL, null, GroupEvent.GROUP_OWNER, uid, attributionTag); - ret = mWifiNative.p2pGroupAdd(true); + ret = mWifiNative.p2pGroupAdd(true, false); } } else { mWifiP2pMetrics.startConnectionEvent( P2pConnectionEvent.CONNECTION_LOCAL, null, GroupEvent.GROUP_OWNER, uid, attributionTag); - ret = mWifiNative.p2pGroupAdd(false); + ret = mWifiNative.p2pGroupAdd(false, false); } if (ret) { replyToMessage(message, WifiP2pManager.CREATE_GROUP_SUCCEEDED); smTransition(this, mGroupNegotiationState); } else { - mConnectionPkgName = SHARED_PKG_NAME; replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED, WifiP2pManager.ERROR); // remain at this state. @@ -4503,10 +4167,10 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { .getBundle(WifiP2pManager.EXTRA_PARAM_KEY_BUNDLE); WifiP2pExtListenParams extListenParams = SdkLevel.isAtLeastV() && (listenType == WifiP2pManager.WIFI_P2P_EXT_LISTEN_WITH_PARAMS) - ? extras.getParcelable( - WifiP2pManager.EXTRA_PARAM_KEY_EXT_LISTEN_PARAMS, - WifiP2pExtListenParams.class) - : null; + ? extras.getParcelable( + WifiP2pManager.EXTRA_PARAM_KEY_EXT_LISTEN_PARAMS, + WifiP2pExtListenParams.class) + : null; boolean hasPermission; if (isPlatformOrTargetSdkLessThanT(packageName, uid)) { hasPermission = mWifiPermissionsUtil.checkCanAccessWifiDirect( @@ -4661,11 +4325,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { WifiP2pManager.CANCEL_CONNECT_SUCCEEDED); } } - if (mFeatureFlags.p2pOwnership()) { - smTransition(this, mIdleState); - } else { - smTransition(this, mInactiveState); - } + transitionTo(mInactiveState); } else { loge( "Stale P2p rejection resume after delay - cached index: " @@ -4735,11 +4395,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { P2pConnectionEvent.CLF_TIMEOUT); handleGroupCreationFailure( WifiP2pManager.GROUP_CREATION_FAILURE_REASON_TIMED_OUT); - if (mFeatureFlags.p2pOwnership()) { - smTransition(this, mIdleState); - } else { - smTransition(this, mInactiveState); - } + smTransition(this, mInactiveState); } break; case WifiP2pMonitor.P2P_DEVICE_LOST_EVENT: @@ -4791,16 +4447,6 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { // discovery or for a pending user action, but at the framework // level, we always treat cancel as succeeded and enter // an inactive state - String packageName = getCallingPkgName(message.sendingUid, message.replyTo); - if (mFeatureFlags.p2pOwnership() - && mConnectionPkgName != packageName - && mConnectionPkgName != SHARED_PKG_NAME) { - replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_FAILED, - WifiP2pManager.BUSY); - logd("Cancel connect requested by " + packageName - + " when connection is initiated by " + mConnectionPkgName); - break; - } mLastCallerInfoManager.put(WifiManager.API_P2P_CANCEL_CONNECT, Process.myTid(), message.sendingUid, 0, getCallingPkgName(message.sendingUid, message.replyTo), true); @@ -4858,7 +4504,9 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { logSmStateName(this.getName(), getCurrentState() != null ? getCurrentState().getName() : ""); if (mSavedPeerConfig.wps.setup == WpsInfo.PBC - || TextUtils.isEmpty(mSavedPeerConfig.wps.pin)) { + || (mSavedPeerConfig.wps.setup != WpsInfo.INVALID + && TextUtils.isEmpty(mSavedPeerConfig.wps.pin)) + || isConfigForV2Connection(mSavedPeerConfig)) { notifyInvitationReceived( WifiP2pManager.ExternalApproverRequestListener .REQUEST_TYPE_NEGOTIATION); @@ -4911,15 +4559,13 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { mWifiNative.p2pCancelConnect(); handleGroupCreationFailure( WifiP2pManager.GROUP_CREATION_FAILURE_REASON_USER_REJECTED); - if (mFeatureFlags.p2pOwnership()) { - smTransition(this, mIdleState); - } else { - smTransition(this, mInactiveState); - } + smTransition(this, mInactiveState); } break; case PEER_CONNECTION_USER_CONFIRM: - mSavedPeerConfig.wps.setup = WpsInfo.DISPLAY; + if (!isConfigForV2Connection(mSavedPeerConfig)) { + mSavedPeerConfig.wps.setup = WpsInfo.DISPLAY; + } mSavedPeerConfig.groupOwnerIntent = selectGroupOwnerIntentIfNecessary(mSavedPeerConfig); mWifiNative.p2pConnect(mSavedPeerConfig, FORM_GROUP); @@ -4929,11 +4575,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { loge("provision discovery failed status: " + message.arg1); handleGroupCreationFailure(WifiP2pManager .GROUP_CREATION_FAILURE_REASON_PROVISION_DISCOVERY_FAILED); - if (mFeatureFlags.p2pOwnership()) { - smTransition(this, mIdleState); - } else { - smTransition(this, mInactiveState); - } + smTransition(this, mInactiveState); break; case WifiP2pManager.SET_CONNECTION_REQUEST_RESULT: { if (!handleSetConnectionResult(message, @@ -5013,21 +4655,13 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { if (mVerboseLoggingEnabled) { logd("User rejected invitation " + mSavedPeerConfig); } - if (mFeatureFlags.p2pOwnership()) { - smTransition(this, mIdleState); - } else { - smTransition(this, mInactiveState); - } + smTransition(this, mInactiveState); break; case WifiP2pMonitor.P2P_PROV_DISC_FAILURE_EVENT: loge("provision discovery failed status: " + message.arg1); handleGroupCreationFailure(WifiP2pManager .GROUP_CREATION_FAILURE_REASON_PROVISION_DISCOVERY_FAILED); - if (mFeatureFlags.p2pOwnership()) { - smTransition(this, mIdleState); - } else { - smTransition(this, mInactiveState); - } + smTransition(this, mInactiveState); break; case WifiP2pManager.SET_CONNECTION_REQUEST_RESULT: if (!handleSetConnectionResult(message, @@ -5133,12 +4767,12 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { if (TextUtils.isEmpty(mSavedPeerConfig.wps.pin)) { // Some implementations get the PIN OOB and deliver it from // Supplicant. This is to avoid connecting with the dialog box - mSavedPeerConfig.wps.pin = provDisc.pin; + mSavedPeerConfig.wps.pin = provDisc.wpsPin; } // we already have the pin if (!TextUtils.isEmpty(mSavedPeerConfig.wps.pin)) { p2pConnectWithPinDisplay(mSavedPeerConfig, - P2P_CONNECT_TRIGGER_OTHER); + P2P_CONNECT_TRIGGER_OTHER); smTransition(this, mGroupNegotiationState); } else { mJoinExistingGroup = false; @@ -5164,12 +4798,89 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { if (mVerboseLoggingEnabled) { logd("Found a match " + mSavedPeerConfig); } - mSavedPeerConfig.wps.pin = provDisc.pin; + mSavedPeerConfig.wps.pin = provDisc.wpsPin; + p2pConnectWithPinDisplay(mSavedPeerConfig, P2P_CONNECT_TRIGGER_OTHER); + notifyInvitationSent(provDisc.wpsPin, device.deviceAddress); + smTransition(this, mGroupNegotiationState); + } + break; + case WifiP2pMonitor.P2P_PROV_DISC_PAIRING_BOOTSTRAPPING_OPPORTUNISTIC_RSP_EVENT: + if (message.obj == null) { + Log.e(TAG, "Invalid argument(s)"); + break; + } + provDisc = (WifiP2pProvDiscEvent) message.obj; + device = provDisc.device; + if (device != null + && !device.deviceAddress.equals(mSavedPeerConfig.deviceAddress)) { + break; + } + if (isConfigForBootstrappingMethodOpportunistic(mSavedPeerConfig)) { + if (mVerboseLoggingEnabled) { + logd("Found a match " + mSavedPeerConfig); + } + p2pConnectWithPinDisplay(mSavedPeerConfig, P2P_CONNECT_TRIGGER_OTHER); + smTransition(this, mGroupNegotiationState); + } else { + loge("Error in mapping pairingBootstrappingMethod"); + } + break; + case WifiP2pMonitor + .P2P_PROV_DISC_ENTER_PAIRING_BOOTSTRAPPING_PIN_OR_PASSPHRASE_EVENT: + if (message.obj == null) { + Log.e(TAG, "Illegal argument(s)"); + break; + } + provDisc = (WifiP2pProvDiscEvent) message.obj; + device = provDisc.device; + if (device != null + && !device.deviceAddress.equals(mSavedPeerConfig.deviceAddress)) { + break; + } + if (isConfigForBootstrappingMethodKeypadPinOrPassphrase(mSavedPeerConfig)) { + if (mVerboseLoggingEnabled) { + logd("Found a match " + mSavedPeerConfig); + } + mJoinExistingGroup = false; + smTransition(this, mUserAuthorizingNegotiationRequestState); + } else { + loge("Error in mapping pairingBootstrappingMethod"); + } + break; + case WifiP2pMonitor + .P2P_PROV_DISC_SHOW_PAIRING_BOOTSTRAPPING_PIN_OR_PASSPHRASE_EVENT: { + if (message.obj == null) { + Log.e(TAG, "Illegal argument(s)"); + break; + } + provDisc = (WifiP2pProvDiscEvent) message.obj; + device = provDisc.device; + if (device == null) { + Log.e(TAG, "Invalid device"); + break; + } + if (!device.deviceAddress.equals(mSavedPeerConfig.deviceAddress)) { + break; + } + if (isConfigForBootstrappingMethodDisplayPinOrPassphrase( + mSavedPeerConfig)) { + if (mVerboseLoggingEnabled) { + logd("Found a match " + mSavedPeerConfig); + } + if (TextUtils.isEmpty(provDisc.pairingPinOrPassphrase)) { + loge("Didn't receive pairing pin/passphrase from supplicant"); + break; + } + setPinOrPassphraseInSavedPeerConfig(provDisc.pairingPinOrPassphrase); p2pConnectWithPinDisplay(mSavedPeerConfig, P2P_CONNECT_TRIGGER_OTHER); - notifyInvitationSent(provDisc.pin, device.deviceAddress); + notifyInvitationSent(provDisc.pairingPinOrPassphrase, + device.deviceAddress); smTransition(this, mGroupNegotiationState); + } else { + loge("Error in mapping pairingBootstrappingMethod"); } break; + } case WifiP2pMonitor.P2P_PROV_DISC_FAILURE_EVENT: loge("provision discovery failed status: " + message.arg1); @@ -5184,11 +4895,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { P2pConnectionEvent.CLF_PROV_DISC_FAIL); handleGroupCreationFailure(WifiP2pManager .GROUP_CREATION_FAILURE_REASON_PROVISION_DISCOVERY_FAILED); - if (mFeatureFlags.p2pOwnership()) { - smTransition(this, mIdleState); - } else { - smTransition(this, mInactiveState); - } + smTransition(this, mInactiveState); break; default: return NOT_HANDLED; @@ -5259,6 +4966,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { mGroup.setNetworkId(mGroups.getNetworkId(devAddr, mGroup.getNetworkName())); } + if (mGroup.isGroupOwner()) { // Setting an idle time out on GO causes issues with certain scenarios // on clients where it can be off-channel for longer and with the power @@ -5288,12 +4996,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { } break; } - if (mOwnershipMap.size() >= MAX_NUM_GROUP) { - Log.wtf(TAG, "group size= " + mOwnershipMap.size() - + " exceeds max number of p2p group supported"); - } - mOwnershipMap.put( - mConnectionPkgName, new WifiP2pGroupInfo(mGroup, null)); + mWifiNative.setP2pGroupIdle(mGroup.getInterface(), GROUP_IDLE_TIME_S); Log.d(TAG, "start Ip client with provisioning mode: " + mSavedPeerConfig.getGroupClientIpProvisioningMode()); @@ -5332,15 +5035,6 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { if (!interfaces.contains(mGroup.getInterface())) break; Log.d(TAG, "tether " + mGroup.getInterface() + " ready"); - if (mOwnershipMap.size() > MAX_NUM_GROUP) { - Log.wtf(TAG, "group size= " + mOwnershipMap.size() - + " exceeds max number of p2p group supported"); - } - WifiP2pGroupInfo groupInfo = mOwnershipMap.putIfAbsent(mConnectionPkgName, - new WifiP2pGroupInfo(mGroup, null)); - if (groupInfo != null) { - groupInfo.p2pGroup = mGroup; - } smTransition(this, mGroupCreatedState); break; case WifiP2pMonitor.P2P_GO_NEGOTIATION_FAILURE_EVENT: @@ -5362,11 +5056,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { P2pConnectionEvent.CLF_GROUP_REMOVED); handleGroupCreationFailure( WifiP2pManager.GROUP_CREATION_FAILURE_REASON_GROUP_REMOVED); - if (mFeatureFlags.p2pOwnership()) { - smTransition(this, mIdleState); - } else { - smTransition(this, mInactiveState); - } + smTransition(this, mInactiveState); break; case WifiP2pMonitor.P2P_GROUP_FORMATION_FAILURE_EVENT: // A group formation failure is always followed by @@ -5438,11 +5128,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { P2pConnectionEvent.CLF_INVITATION_FAIL); handleGroupCreationFailure( WifiP2pManager.GROUP_CREATION_FAILURE_REASON_INVITATION_FAILED); - if (mFeatureFlags.p2pOwnership()) { - smTransition(this, mIdleState); - } else { - smTransition(this, mInactiveState); - } + smTransition(this, mInactiveState); } break; case WifiP2pMonitor.P2P_PROV_DISC_FAILURE_EVENT: @@ -5451,11 +5137,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { P2pConnectionEvent.CLF_GROUP_REMOVED); handleGroupCreationFailure(WifiP2pManager .GROUP_CREATION_FAILURE_REASON_PROVISION_DISCOVERY_FAILED); - if (mFeatureFlags.p2pOwnership()) { - smTransition(this, mIdleState); - } else { - smTransition(this, mInactiveState); - } + smTransition(this, mInactiveState); break; case WifiP2pMonitor.AP_STA_CONNECTED_EVENT: case WifiP2pMonitor.AP_STA_DISCONNECTED_EVENT: @@ -5606,11 +5288,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { P2pConnectionEvent.CLF_USER_REJECT); handleGroupCreationFailure( WifiP2pManager.GROUP_CREATION_FAILURE_REASON_USER_REJECTED); - if (mFeatureFlags.p2pOwnership()) { - smTransition(this, mIdleState); - } else { - smTransition(this, mInactiveState); - } + smTransition(this, mInactiveState); break; case DROP_WIFI_USER_ACCEPT: mFrequencyConflictDialog = null; @@ -5623,11 +5301,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { if (mVerboseLoggingEnabled) { logd(getName() + "Wifi disconnected, retry p2p"); } - if (mFeatureFlags.p2pOwnership()) { - smTransition(this, mIdleState); - } else { - smTransition(this, mInactiveState); - } + smTransition(this, mInactiveState); p2pReconnect(); break; default: @@ -5689,8 +5363,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { // In case of a negotiation group, connection changed is sent // after a client joins. For autonomous, send now if (mAutonomousGroup) { - onGroupCreated(new WifiP2pInfo(mWifiP2pInfo), eraseOwnDeviceAddress(mGroup), - generateCallbackList(mGroup)); + onGroupCreated(new WifiP2pInfo(mWifiP2pInfo), eraseOwnDeviceAddress(mGroup)); sendP2pConnectionChangedBroadcast(); } @@ -5737,12 +5410,10 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { } if (!mAutonomousGroup && mGroup.getClientList().size() == 1) { onGroupCreated(new WifiP2pInfo(mWifiP2pInfo), - eraseOwnDeviceAddress(mGroup), - generateCallbackList(mGroup)); + eraseOwnDeviceAddress(mGroup)); } onPeerClientJoined(new WifiP2pInfo(mWifiP2pInfo), - eraseOwnDeviceAddress(mGroup), - generateCallbackList(mGroup)); + eraseOwnDeviceAddress(mGroup)); sendP2pConnectionChangedBroadcast(); break; } @@ -5766,8 +5437,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { // when this happens at exit() } else { onPeerClientDisconnected(new WifiP2pInfo(mWifiP2pInfo), - eraseOwnDeviceAddress(mGroup), - generateCallbackList(mGroup)); + eraseOwnDeviceAddress(mGroup)); // Notify when a client disconnects from group sendP2pConnectionChangedBroadcast(); } @@ -5858,8 +5528,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { loge("Failed to add iface to local network " + e); } onGroupCreated(new WifiP2pInfo(mWifiP2pInfo), - eraseOwnDeviceAddress(mGroup), - generateCallbackList(mGroup)); + eraseOwnDeviceAddress(mGroup)); sendP2pConnectionChangedBroadcast(); break; case IPC_PROVISIONING_SUCCESS: @@ -5885,8 +5554,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { if (goInterfaceMacAddress == null) { setWifiP2pInfoOnGroupFormationWithInetAddress(null); onGroupCreated(new WifiP2pInfo(mWifiP2pInfo), - eraseOwnDeviceAddress(mGroup), - generateCallbackList(mGroup)); + eraseOwnDeviceAddress(mGroup)); sendP2pConnectionChangedBroadcast(); break; } @@ -5898,8 +5566,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { NetworkInterface.getByName(mGroup.getInterface())); setWifiP2pInfoOnGroupFormationWithInetAddress(goIp); onGroupCreated(new WifiP2pInfo(mWifiP2pInfo), - eraseOwnDeviceAddress(mGroup), - generateCallbackList(mGroup)); + eraseOwnDeviceAddress(mGroup)); sendP2pConnectionChangedBroadcast(); } catch (UnknownHostException | SocketException e) { loge("Unable to retrieve link-local IPv6 address of group owner " @@ -5911,32 +5578,21 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { loge("IP provisioning failed"); mWifiNative.p2pGroupRemove(mGroup.getInterface()); break; - case WifiP2pManager.REMOVE_GROUP: { - String packageName = getCallingPkgName(message.sendingUid, message.replyTo); - if (!checkIfPackageIsGroupOwner(packageName) - && message.sendingUid != Process.SYSTEM_UID) { - replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED, - WifiP2pManager.BUSY); - break; - } + case WifiP2pManager.REMOVE_GROUP: mLastCallerInfoManager.put(WifiManager.API_P2P_REMOVE_GROUP, - Process.myTid(), message.sendingUid, 0, packageName, true); + Process.myTid(), message.sendingUid, 0, + getCallingPkgName(message.sendingUid, message.replyTo), true); if (mVerboseLoggingEnabled) logd(getName() + " remove group"); if (mWifiNative.p2pGroupRemove(mGroup.getInterface())) { smTransition(this, mOngoingGroupRemovalState); replyToMessage(message, WifiP2pManager.REMOVE_GROUP_SUCCEEDED); } else { - handleGroupRemoved(packageName); - if (mFeatureFlags.p2pOwnership()) { - smTransition(this, mIdleState); - } else { - smTransition(this, mInactiveState); - } + handleGroupRemoved(); + smTransition(this, mInactiveState); replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED, WifiP2pManager.ERROR); } break; - } case WifiP2pMonitor.P2P_GROUP_REMOVED_EVENT: // We do not listen to NETWORK_DISCONNECTION_EVENT for group removal // handling since supplicant actually tries to reconnect after a temporary @@ -5949,26 +5605,8 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { // Treating network disconnection as group removal causes race conditions // since supplicant would still maintain the group at that stage. if (mVerboseLoggingEnabled) logd(getName() + " group removed"); - - if (message.obj == null) { - Log.e(TAG, "Illegal arguments"); - break; - } - String pkgName = SHARED_PKG_NAME; - WifiP2pGroup group = (WifiP2pGroup) message.obj; - for (Map.Entry<String, WifiP2pGroupInfo> entry : mOwnershipMap.entrySet()) { - if (entry.getValue().p2pGroup.getInterface().equals( - group.getInterface())) { - pkgName = entry.getKey(); - break; - } - } - handleGroupRemoved(pkgName); - if (mFeatureFlags.p2pOwnership()) { - smTransition(this, mIdleState); - } else { - smTransition(this, mInactiveState); - } + handleGroupRemoved(); + smTransition(this, mInactiveState); break; case WifiP2pMonitor.P2P_DEVICE_LOST_EVENT: if (message.obj == null) { @@ -6042,12 +5680,6 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { // remain at this state. break; } - if (!checkIfPackageIsGroupOwner(packageName)) { - replyToMessage(message, WifiP2pManager.CONNECT_FAILED, - WifiP2pManager.BUSY); - logd("Cannot perform invitation connection due to lack of ownership"); - break; - } mLastCallerInfoManager.put(WifiManager.API_P2P_CONNECT, Process.myTid(), uid, 0, packageName, true); WifiP2pConfig config = (WifiP2pConfig) @@ -6063,9 +5695,6 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { mPeers.updateStatus(config.deviceAddress, WifiP2pDevice.INVITED); sendPeersChangedBroadcast(); replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED); - if (mOwnershipMap.containsKey(packageName)) { - mConnectionPkgName = packageName; - } } else { replyToMessage(message, WifiP2pManager.CONNECT_FAILED, WifiP2pManager.ERROR); @@ -6097,7 +5726,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { break; case WifiP2pMonitor.P2P_PROV_DISC_PBC_REQ_EVENT: case WifiP2pMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT: - case WifiP2pMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT: + case WifiP2pMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT: { WifiP2pProvDiscEvent provDisc = (WifiP2pProvDiscEvent) message.obj; WifiP2pConfig newPeerConfig = new WifiP2pConfig(); if (provDisc != null && provDisc.device != null) { @@ -6114,7 +5743,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { newPeerConfig.wps.setup = WpsInfo.KEYPAD; } else if (message.what == WifiP2pMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT) { newPeerConfig.wps.setup = WpsInfo.DISPLAY; - newPeerConfig.wps.pin = provDisc.pin; + newPeerConfig.wps.pin = provDisc.wpsPin; } else { newPeerConfig.wps.setup = WpsInfo.PBC; } @@ -6138,20 +5767,31 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { } } break; + } + case WifiP2pMonitor + .P2P_PROV_DISC_PAIRING_BOOTSTRAPPING_OPPORTUNISTIC_REQ_EVENT: + case WifiP2pMonitor + .P2P_PROV_DISC_ENTER_PAIRING_BOOTSTRAPPING_PIN_OR_PASSPHRASE_EVENT: + case WifiP2pMonitor + .P2P_PROV_DISC_SHOW_PAIRING_BOOTSTRAPPING_PIN_OR_PASSPHRASE_EVENT: { + // According to section 3.2.3 in SPEC, only GO can handle group join. + // Multiple groups is not supported, ignore this discovery for GC. + if (mGroup.isGroupOwner()) { + if (processProvDiscEventOnGroupOwner( + (WifiP2pProvDiscEvent) message.obj)) { + smTransition(this, mUserAuthorizingJoinState); + } + } else { + if (mVerboseLoggingEnabled) { + logd("Ignore provision discovery for GC"); + } + } + break; + } case WifiP2pMonitor.P2P_GROUP_STARTED_EVENT: loge("Duplicate group creation event notice, ignore"); break; - case WifiP2pManager.CANCEL_CONNECT: { - String packageName = getCallingPkgName(message.sendingUid, message.replyTo); - if (mFeatureFlags.p2pOwnership() - && mConnectionPkgName != packageName - && mConnectionPkgName != SHARED_PKG_NAME) { - replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_FAILED, - WifiP2pManager.BUSY); - logd("Cancel invitation connect requested by " + packageName - + " when connection is initiated by " + mConnectionPkgName); - break; - } + case WifiP2pManager.CANCEL_CONNECT: mLastCallerInfoManager.put(WifiManager.API_P2P_CANCEL_CONNECT, Process.myTid(), message.sendingUid, 0, getCallingPkgName(message.sendingUid, message.replyTo), true); @@ -6171,13 +5811,11 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_SUCCEEDED); break; - } case WifiP2pMonitor.P2P_FREQUENCY_CHANGED_EVENT: if (mGroup != null) { mGroup.setFrequency(message.arg1); onFrequencyChanged(new WifiP2pInfo(mWifiP2pInfo), - eraseOwnDeviceAddress(mGroup), - generateCallbackList(mGroup)); + eraseOwnDeviceAddress(mGroup)); sendP2pConnectionChangedBroadcast(); } break; @@ -6270,6 +5908,11 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { case WifiP2pMonitor.P2P_PROV_DISC_PBC_REQ_EVENT: case WifiP2pMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT: case WifiP2pMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT: + case WifiP2pMonitor.P2P_PROV_DISC_PAIRING_BOOTSTRAPPING_OPPORTUNISTIC_REQ_EVENT: + case WifiP2pMonitor + .P2P_PROV_DISC_ENTER_PAIRING_BOOTSTRAPPING_PIN_OR_PASSPHRASE_EVENT: + case WifiP2pMonitor + .P2P_PROV_DISC_SHOW_PAIRING_BOOTSTRAPPING_PIN_OR_PASSPHRASE_EVENT: // Ignore more client requests break; case WifiP2pMonitor.P2P_PROV_DISC_FAILURE_EVENT: @@ -6285,16 +5928,21 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { if (mDiscoveryStarted) { mWifiNative.p2pStopFind(); } - if (mSavedPeerConfig.wps.setup == WpsInfo.PBC) { - mWifiNative.startWpsPbc(mGroup.getInterface(), null); + if (isConfigForV2Connection(mSavedPeerConfig)) { + mWifiNative.authorizeConnectRequestOnGroupOwner(mSavedPeerConfig, + mGroup.getInterface()); } else { - mWifiNative.startWpsPinKeypad(mGroup.getInterface(), - mSavedPeerConfig.wps.pin); + if (mSavedPeerConfig.wps.setup == WpsInfo.PBC) { + mWifiNative.startWpsPbc(mGroup.getInterface(), null); + } else { + mWifiNative.startWpsPinKeypad(mGroup.getInterface(), + mSavedPeerConfig.wps.pin); + } } - smTransition(this, mGroupCreatedState); break; case PEER_CONNECTION_USER_REJECT: if (mVerboseLoggingEnabled) logd("User rejected incoming request"); + mSavedPeerConfig.invalidate(); smTransition(this, mGroupCreatedState); break; case WifiP2pManager.SET_CONNECTION_REQUEST_RESULT: @@ -6499,12 +6147,6 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { broadcastOptions.setRequireNoneOfPermissions(excludedPermissionsList.toArray( new String[0])); } - // remove package name from intent for ownership - if (mFeatureFlags.p2pOwnership() - && intent.getAction().equals( - WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)) { - intent = getP2pConnectionChangedIntent(true); - } context.sendBroadcast(intent, null, broadcastOptions.toBundle()); } } @@ -6529,25 +6171,18 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { sendBroadcastWithExcludedPermissions(intent, null); } - private Intent getP2pConnectionChangedIntent(boolean tethering) { + private Intent getP2pConnectionChangedIntent() { Intent intent = new Intent(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO, new WifiP2pInfo(mWifiP2pInfo)); intent.putExtra(WifiP2pManager.EXTRA_NETWORK_INFO, makeNetworkInfo()); intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP, eraseOwnDeviceAddress(mGroup)); - if (tethering || !mFeatureFlags.p2pOwnership()) return intent; - if (!mOwnershipMap.containsKey(SHARED_PKG_NAME)) { - for (String pkg : mOwnershipMap.keySet()) { - intent.setPackage(pkg); - logd("sending p2p connection changed broadcast to only " + pkg); - } - } return intent; } private void sendP2pConnectionChangedBroadcast() { if (mVerboseLoggingEnabled) logd("sending p2p connection changed broadcast"); - Intent intent = getP2pConnectionChangedIntent(false); + Intent intent = getP2pConnectionChangedIntent(); if (SdkLevel.isAtLeastU()) { // First send direct foreground broadcast to Tethering package and system service // with same android.permission.MAINLINE_NETWORK_STACK @@ -6654,7 +6289,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { if (TextUtils.isEmpty(tetheringServicePackage)) return false; Log.i(TAG, "sending p2p tether request broadcast to " + tetheringServicePackage + " with permission " + Arrays.toString(permissions)); - Intent intent = getP2pConnectionChangedIntent(true); + Intent intent = getP2pConnectionChangedIntent(); if (setAdditionalFlags) { intent.addFlags(flags); } @@ -6666,7 +6301,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { private void sendBroadcastWithMainlineNetworkStackPermissionPostU() { String[] receiverPermissions = RECEIVER_PERMISSIONS_MAINLINE_NETWORK_STACK; - Intent intent = getP2pConnectionChangedIntent(true); + Intent intent = getP2pConnectionChangedIntent(); // Adding the flag to allow recipient to run at foreground priority with a shorter // timeout interval. intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); @@ -6812,6 +6447,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { deviceName, false /* isPinRequested */, pin, + 0, displayId, new WifiDialogManager.P2pInvitationReceivedDialogCallback() { @Override @@ -6927,6 +6563,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { mLegacyInvitationDialog.show(); } + @SuppressLint("NewApi") private void showInvitationReceivedDialog() { String deviceName = getDeviceName(mSavedPeerConfig.deviceAddress); boolean isPinRequested = false; @@ -6949,12 +6586,36 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { break; } + if (isConfigForV2Connection(mSavedPeerConfig)) { + int pairingBootstrappingMethod = mSavedPeerConfig.getPairingBootstrappingConfig() + .getPairingBootstrappingMethod(); + if (pairingBootstrappingMethod + == WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PINCODE || pairingBootstrappingMethod + == WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PASSPHRASE) { + isPinRequested = true; + } else if (pairingBootstrappingMethod + == WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_DISPLAY_PINCODE || pairingBootstrappingMethod + == WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_DISPLAY_PASSPHRASE) { + displayPin = mSavedPeerConfig.getPairingBootstrappingConfig() + .getPairingBootstrappingPassword(); + } + } + WifiDialogManager.P2pInvitationReceivedDialogCallback callback = new WifiDialogManager.P2pInvitationReceivedDialogCallback() { @Override public void onAccepted(@Nullable String optionalPin) { if (optionalPin != null) { - mSavedPeerConfig.wps.pin = optionalPin; + if (isConfigForV2Connection(mSavedPeerConfig)) { + mSavedPeerConfig.getPairingBootstrappingConfig() + .setPairingBootstrappingPassword(optionalPin); + } else { + mSavedPeerConfig.wps.pin = optionalPin; + } } if (mVerboseLoggingEnabled) { logd(getName() + " accept invitation " + mSavedPeerConfig); @@ -6978,11 +6639,12 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { deviceName, isPinRequested, displayPin, + mContext.getResources().getInteger( + R.integer.config_p2pInvitationReceivedDialogTimeoutMs), displayId, callback, new WifiThreadRunner(getHandler())); - mInvitationDialogHandle.launchDialog(mContext.getResources().getInteger( - R.integer.config_p2pInvitationReceivedDialogTimeoutMs)); + mInvitationDialogHandle.launchDialog(); } private void notifyInvitationReceived( @@ -7081,6 +6743,365 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { && !TextUtils.isEmpty(config.passphrase); } + /** + * Check if the Wi-Fi Direct R2 feature is enabled or not + * + * @return true if Wi-Fi Direct R2 feature is enabled, false otherwise. + */ + private boolean isWifiDirect2Enabled() { + if (Environment.isSdkAtLeastB() + && isFeatureSupported(WifiP2pManager.FEATURE_WIFI_DIRECT_R2) + && Flags.wifiDirectR2()) { + return true; + } + return false; + } + + /** + * Check the config is for starting P2P version 2 group owner. + * + * @param config config to be checked for P2P group owner version. + * @return true if it is version 2, false otherwise. + */ + @SuppressLint("NewApi") + private boolean isConfigForGroupOwnerV2(WifiP2pConfig config) { + if (config != null && Environment.isSdkAtLeastB() + && config.getGroupOwnerVersion() == P2P_VERSION_2) { + return true; + } + return false; + } + + /** + * Check the configuration is for forming or joining a P2P version 2 group. + * + * @param config config to be checked for version 2. + * @return true if it is version 2, false otherwise. + */ + @SuppressLint("NewApi") + private boolean isConfigForV2Connection(WifiP2pConfig config) { + if (config != null && Environment.isSdkAtLeastB() + && config.getPairingBootstrappingConfig() != null) { + return true; + } + return false; + } + + /** + * Convert the WifiP2pProvDiscEvent to + * |WifiP2pPairingBootstrappingConfig.PAIRING_BOOTSTRAPPING_*|. + * + * @param event The event ID carried in WifiP2pProvDiscEvent. + * @return The |WifiP2pPairingBootstrappingConfig.PAIRING_BOOTSTRAPPING_*|. + * if not found, return -1. + */ + @SuppressLint("NewApi") + private int convertWifiP2pProvDiscEventToPairingBootstrappingMethod(int event) { + switch (event) { + case WifiP2pProvDiscEvent.PAIRING_BOOTSTRAPPING_OPPORTUNISTIC_REQ: + case WifiP2pProvDiscEvent.PAIRING_BOOTSTRAPPING_OPPORTUNISTIC_RSP: + return WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_OPPORTUNISTIC; + case WifiP2pProvDiscEvent.PAIRING_BOOTSTRAPPING_ENTER_PASSPHRASE: + return WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PASSPHRASE; + case WifiP2pProvDiscEvent.PAIRING_BOOTSTRAPPING_ENTER_PIN: + return WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PINCODE; + case WifiP2pProvDiscEvent.PAIRING_BOOTSTRAPPING_SHOW_PIN: + return WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_DISPLAY_PINCODE; + case WifiP2pProvDiscEvent.PAIRING_BOOTSTRAPPING_SHOW_PASSPHRASE: + return WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_DISPLAY_PASSPHRASE; + default: + return -1; + } + } + + /** + * Check the configuration is for forming or joining a P2P version 2 group with pairing + * bootstrapping method: opportunistic + * + * @param config config to be checked for version 2. + * {@link WifiP2pPairingBootstrappingConfig#PAIRING_BOOTSTRAPPING_METHOD_OPPORTUNISTIC}, + * false otherwise. + */ + @SuppressLint("NewApi") + private boolean isConfigForBootstrappingMethodOpportunistic(WifiP2pConfig config) { + if (config != null && Environment.isSdkAtLeastB() + && config.getPairingBootstrappingConfig() != null + && (config.getPairingBootstrappingConfig().getPairingBootstrappingMethod() + == WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_OPPORTUNISTIC)) { + return true; + } + return false; + } + + /** + * Check the configuration is for forming or joining a P2P version 2 group with pairing + * bootstrapping method: Display pin-code or Display passphrase. + * + * @param config config to be checked for version 2. + * {@link WifiP2pPairingBootstrappingConfig#PAIRING_BOOTSTRAPPING_METHOD_DISPLAY_PINCODE} or + * {@link WifiP2pPairingBootstrappingConfig#PAIRING_BOOTSTRAPPING_METHOD_DISPLAY_PASSPHRASE} + * , false otherwise. + */ + @SuppressLint("NewApi") + private boolean isConfigForBootstrappingMethodDisplayPinOrPassphrase(WifiP2pConfig config) { + if (config != null && Environment.isSdkAtLeastB() + && config.getPairingBootstrappingConfig() != null) { + int pairingBootstrappingMethod = + config.getPairingBootstrappingConfig().getPairingBootstrappingMethod(); + if (pairingBootstrappingMethod == WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_DISPLAY_PINCODE || pairingBootstrappingMethod + == WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_DISPLAY_PASSPHRASE) { + return true; + } + } + return false; + } + + /** + * Check the configuration is for forming or joining a P2P version 2 group with pairing + * bootstrapping method: Keypad pin-code or Keypad passphrase. + * + * @param config config to be checked for bootstrapping method. + * @return true if the pairing bootstrapping method is + * {@link WifiP2pPairingBootstrappingConfig#PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PINCODE} or + * {@link WifiP2pPairingBootstrappingConfig#PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PASSPHRASE}, + * false otherwise. + */ + @SuppressLint("NewApi") + private boolean isConfigForBootstrappingMethodKeypadPinOrPassphrase(WifiP2pConfig config) { + if (config != null && Environment.isSdkAtLeastB() + && config.getPairingBootstrappingConfig() != null) { + int pairingBootstrappingMethod = + config.getPairingBootstrappingConfig().getPairingBootstrappingMethod(); + if (pairingBootstrappingMethod == WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PINCODE || pairingBootstrappingMethod + == WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PASSPHRASE) { + return true; + } + } + return false; + } + + /** + * Check the configuration is for forming or joining a P2P version 2 group with pairing + * bootstrapping method done out of band. + * + * @param config config to be checked for bootstrapping method. + * @return true if the pairing bootstrapping method is + * {@link WifiP2pPairingBootstrappingConfig#PAIRING_BOOTSTRAPPING_METHOD_OUT_OF_BAND}, + * false otherwise. + */ + @SuppressLint("NewApi") + private boolean isConfigForBootstrappingMethodOutOfBand(WifiP2pConfig config) { + if (config != null && Environment.isSdkAtLeastB() + && config.getPairingBootstrappingConfig() != null) { + if (config.getPairingBootstrappingConfig().getPairingBootstrappingMethod() + == WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_OUT_OF_BAND) { + return true; + } + } + return false; + } + + @SuppressLint("NewApi") + private boolean processProvDiscPairingBootstrappingOpportunisticRequestEvent( + WifiP2pProvDiscEvent provDisc) { + if (!isWifiDirect2Enabled()) { + return false; + } + if (provDisc == null) { + Log.e(TAG, "provDisc is null"); + return false; + } + WifiP2pDevice device = provDisc.device; + if (device == null) { + loge("Device entry is null"); + return false; + } + if (TextUtils.isEmpty(device.deviceAddress)) { + loge("Device address is empty"); + return false; + } + + WifiP2pPairingBootstrappingConfig pairingBootstrappingConfig = + new WifiP2pPairingBootstrappingConfig( + WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_OPPORTUNISTIC, + ""); + mSavedPeerConfig = new WifiP2pConfig.Builder() + .setDeviceAddress(MacAddress.fromString(device.deviceAddress)) + .setPairingBootstrappingConfig(pairingBootstrappingConfig) + .setGroupClientIpProvisioningMode( + GROUP_CLIENT_IP_PROVISIONING_MODE_IPV6_LINK_LOCAL) + .build(); + mPeers.updateStatus(device.deviceAddress, WifiP2pDevice.INVITED); + sendPeersChangedBroadcast(); + return true; + } + + @SuppressLint("NewApi") + private boolean processProvDiscEnterPairingBootstrappingPinOrPassphraseEvent( + WifiP2pProvDiscEvent provDisc) { + if (!isWifiDirect2Enabled()) { + return false; + } + if (provDisc == null) { + Log.e(TAG, "provDisc is null"); + return false; + } + WifiP2pDevice device = provDisc.device; + if (device == null) { + loge("Device entry is null"); + return false; + } + if (TextUtils.isEmpty(device.deviceAddress)) { + loge("Device address is empty"); + return false; + } + + int pairingBootstrappingMethod = + convertWifiP2pProvDiscEventToPairingBootstrappingMethod( + provDisc.event); + if (pairingBootstrappingMethod == WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PINCODE + || pairingBootstrappingMethod == WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PASSPHRASE) { + WifiP2pPairingBootstrappingConfig pairingBootstrappingConfig = + new WifiP2pPairingBootstrappingConfig( + pairingBootstrappingMethod, ""); + mSavedPeerConfig = new WifiP2pConfig.Builder() + .setDeviceAddress(MacAddress.fromString(device.deviceAddress)) + .setPairingBootstrappingConfig(pairingBootstrappingConfig) + .setGroupClientIpProvisioningMode( + GROUP_CLIENT_IP_PROVISIONING_MODE_IPV6_LINK_LOCAL) + .build(); + mPeers.updateStatus(device.deviceAddress, WifiP2pDevice.INVITED); + sendPeersChangedBroadcast(); + return true; + } else { + loge("Error in mapping pairingBootstrappingMethod"); + return false; + } + } + + @SuppressLint("NewApi") + private boolean processProvDiscShowPairingBootstrappingPinOrPassphraseEvent( + WifiP2pProvDiscEvent provDisc) { + if (!isWifiDirect2Enabled()) { + return false; + } + if (provDisc == null) { + Log.e(TAG, "provDisc is null"); + return false; + } + WifiP2pDevice device = provDisc.device; + if (device == null) { + loge("Device entry is null"); + return false; + } + if (TextUtils.isEmpty(device.deviceAddress)) { + loge("Device address is empty"); + return false; + } + + int pairingBootstrappingMethod = + convertWifiP2pProvDiscEventToPairingBootstrappingMethod( + provDisc.event); + if (pairingBootstrappingMethod == WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_DISPLAY_PINCODE + || pairingBootstrappingMethod == WifiP2pPairingBootstrappingConfig + .PAIRING_BOOTSTRAPPING_METHOD_DISPLAY_PASSPHRASE) { + WifiP2pPairingBootstrappingConfig pairingBootstrappingConfig = + new WifiP2pPairingBootstrappingConfig( + pairingBootstrappingMethod, + provDisc.pairingPinOrPassphrase); + mSavedPeerConfig = new WifiP2pConfig.Builder() + .setDeviceAddress(MacAddress.fromString(device.deviceAddress)) + .setPairingBootstrappingConfig(pairingBootstrappingConfig) + .setGroupClientIpProvisioningMode( + GROUP_CLIENT_IP_PROVISIONING_MODE_IPV6_LINK_LOCAL) + .build(); + if (provDisc.getVendorData() != null) { + mSavedPeerConfig.setVendorData(provDisc.getVendorData()); + } + mPeers.updateStatus(device.deviceAddress, WifiP2pDevice.INVITED); + sendPeersChangedBroadcast(); + return true; + } else { + loge("Error in mapping pairingBootstrappingMethod"); + return false; + } + } + + @SuppressLint("NewApi") + private boolean processProvDiscEventOnGroupOwner( + WifiP2pProvDiscEvent provDisc) { + if (!isWifiDirect2Enabled()) { + return false; + } + if (provDisc == null) { + Log.e(TAG, "provDisc is null"); + return false; + } + WifiP2pDevice device = provDisc.device; + if (device == null) { + loge("Device entry is null"); + return false; + } + if (TextUtils.isEmpty(device.deviceAddress)) { + loge("Device address is empty"); + return false; + } + + + WifiP2pConfig newPeerConfig = null; + int pairingBootstrappingMethod = + convertWifiP2pProvDiscEventToPairingBootstrappingMethod( + provDisc.event); + if (pairingBootstrappingMethod > 0) { + WifiP2pPairingBootstrappingConfig pairingBootstrappingConfig = + new WifiP2pPairingBootstrappingConfig( + pairingBootstrappingMethod, + provDisc.pairingPinOrPassphrase); + newPeerConfig = new WifiP2pConfig.Builder() + .setDeviceAddress(MacAddress.fromString( + provDisc.device.deviceAddress)) + .setPairingBootstrappingConfig(pairingBootstrappingConfig) + .setAuthorizeConnectionFromPeerEnabled(true) + .build(); + if (provDisc.getVendorData() != null) { + newPeerConfig.setVendorData(provDisc.getVendorData()); + } + } else { + loge("Error in mapping pairingBootstrappingMethod"); + return false; + } + if (isPeerAuthorizing(newPeerConfig.deviceAddress)) { + Log.i(TAG, "Ignore duplicate provision discovery request from " + + newPeerConfig.deviceAddress); + return false; + } + mSavedPeerConfig = newPeerConfig; + mPeerAuthorizingTimestamp.put(mSavedPeerConfig.deviceAddress, + mClock.getElapsedSinceBootMillis()); + return true; + } + + @SuppressLint("NewApi") + private void setPinOrPassphraseInSavedPeerConfig(String pairingPinOrPassphrase) { + mSavedPeerConfig.getPairingBootstrappingConfig() + .setPairingBootstrappingPassword(pairingPinOrPassphrase); + } + private WifiP2pDevice fetchCurrentDeviceDetails(WifiP2pConfig config) { if (config == null) return null; // Fetch & update group capability from supplicant on the device @@ -7240,9 +7261,12 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { String pin = mWifiNative.p2pConnect(config, action); try { - Integer.parseInt(pin); - mSavedPeerConfig.wps.pin = pin; - notifyInvitationSent(pin, config.deviceAddress); + // TODO check this logic in detail + if (mSavedPeerConfig.wps.setup == WpsInfo.DISPLAY) { + Integer.parseInt(pin); + mSavedPeerConfig.wps.pin = pin; + notifyInvitationSent(pin, config.deviceAddress); + } } catch (NumberFormatException ignore) { // do nothing if p2pConnect did not return a pin } @@ -7282,7 +7306,8 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { int netId = mGroups.getNetworkId(dev.deviceAddress, ssid); if (netId >= 0) { // Skip WPS and start 4way handshake immediately. - return mWifiNative.p2pGroupAdd(netId); + // TODO Add support P2P2 + return mWifiNative.p2pGroupAdd(netId, false); } else { loge("The Network: " + ssid + " is not found in the persistent group list"); } @@ -7444,11 +7469,6 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { mWifiP2pInfo.groupFormed = true; mWifiP2pInfo.isGroupOwner = mGroup.isGroupOwner(); mWifiP2pInfo.groupOwnerAddress = serverAddress; - WifiP2pGroupInfo groupInfo = mOwnershipMap.putIfAbsent( - mConnectionPkgName, new WifiP2pGroupInfo(null, mWifiP2pInfo)); - if (groupInfo != null) { - groupInfo.p2pInfo = mWifiP2pInfo; - } } private void resetWifiP2pInfo() { @@ -7659,7 +7679,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { mWifiNative.p2pFlush(); mWifiNative.p2pServiceFlush(); mServiceTransactionId = 0; - mServiceDiscReqId = null; + mServiceDiscoveryInfo.invalidate(); if (null != mThisDevice.wfdInfo) { setWfdInfo(mThisDevice.wfdInfo); @@ -7730,7 +7750,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { } mPeersLostDuringConnection.clear(); - mServiceDiscReqId = null; + mServiceDiscoveryInfo.invalidate(); Bundle extras = new Bundle(); extras.putBoolean(WifiP2pManager.EXTRA_PARAM_KEY_INTERNAL_MESSAGE, true); @@ -7743,7 +7763,7 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { sendDisconnectWifiRequest(false); } - private void handleGroupRemoved(String packageName) { + private void handleGroupRemoved() { if (mGroup.isGroupOwner()) { // {@link com.android.server.connectivity.Tethering} listens to // {@link WifiP2pManager#WIFI_P2P_CONNECTION_CHANGED_ACTION} @@ -7781,15 +7801,9 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { sendPeersChangedBroadcast(); } - if (mOwnershipMap.containsKey(packageName)) { - mOwnershipMap.remove(packageName); - } else { - mOwnershipMap.remove(SHARED_PKG_NAME); - } - mGroup = null; mPeersLostDuringConnection.clear(); - mServiceDiscReqId = null; + mServiceDiscoveryInfo.invalidate(); sendDisconnectWifiRequest(false); } @@ -7859,11 +7873,97 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { } /** - * Update service discovery request to wpa_supplicant. + * Process USD based service discovery request message. + */ + private void processGasFrameBasedServiceDiscoveryRequestMessage(@NonNull Message message) { + if (!updateSupplicantGasFrameBasedServiceRequest()) { + replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED, + WifiP2pManager.NO_SERVICE_REQUESTS); + return; + } + if (p2pFind(DISCOVER_TIMEOUT_S)) { + sendP2pDiscoveryChangedBroadcast(true); + mWifiP2pMetrics.incrementServiceScans(); + replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_SUCCEEDED); + } else { + replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED, + WifiP2pManager.ERROR); + } + } + /** + * Process USD based service discovery request message. + */ + @SuppressLint("NewApi") + private void processUsdFrameBasedServiceDiscoveryRequestMessage(@NonNull Message message) { + if (!isWifiDirect2Enabled()) { + Log.e(TAG, "Wi-Fi Direct R2 is not supported"); + replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED, + WifiP2pManager.ERROR); + return; + } + Messenger m = message.replyTo; + Bundle extras = message.getData() + .getBundle(WifiP2pManager.EXTRA_PARAM_KEY_BUNDLE); + if (m == null || extras == null) { + Log.e(TAG, "Illegal argument(s)"); + replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED, + WifiP2pManager.ERROR); + return; + } + WifiP2pUsdBasedServiceDiscoveryConfig serviceDiscoveryConfig = + extras.getParcelable(WifiP2pManager + .EXTRA_PARAM_KEY_USD_BASED_SERVICE_DISCOVERY_CONFIG); + if (serviceDiscoveryConfig == null) { + Log.e(TAG, "Service discovery config null!"); + replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED, + WifiP2pManager.ERROR); + return; + } + + ClientInfo clientInfo = getClientInfo(m, false); + if (clientInfo == null) { + Log.e(TAG, "Client doesn't exist"); + replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED, + WifiP2pManager.NO_SERVICE_REQUESTS); + return; + } + + if (mServiceDiscoveryInfo.isValid() && mServiceDiscoveryInfo.getServiceDiscoveryType() + == WifiP2pOngoingServiceDiscoveryRequestInfo + .WIFI_P2P_USD_FRAME_BASED_SERVICE_DISCOVERY) { + mWifiNative.stopUsdBasedServiceDiscovery(mServiceDiscoveryInfo.getSessionIdInt()); + } + + // TODO This loop runs only once as add service request allow to add only one service + // per client. Keeping this loop to expand in the future to service multiple services. + for (WifiP2pServiceRequest serviceRequest: clientInfo.mUsdServiceDiscoverList) { + int sessionId = mWifiNative.startUsdBasedServiceDiscovery( + serviceRequest.getWifiP2pUsdBasedServiceConfig(), serviceDiscoveryConfig, + USD_BASED_SERVICE_ADVERTISEMENT_DISCOVERY_TIMEOUT_S); + if (sessionId > 0) { + serviceRequest.setUsdSessionId(sessionId); + mServiceDiscoveryInfo.update(WifiP2pOngoingServiceDiscoveryRequestInfo + .WIFI_P2P_USD_FRAME_BASED_SERVICE_DISCOVERY, sessionId); + sendP2pDiscoveryChangedBroadcast(true); + replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_SUCCEEDED); + } else { + replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED, + WifiP2pManager.ERROR); + } + return; + } + + Log.e(TAG, "No service requests added"); + replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED, + WifiP2pManager.NO_SERVICE_REQUESTS); + } + + /** + * Update GAS frame based service discovery request to wpa_supplicant. */ - private boolean updateSupplicantServiceRequest() { - clearSupplicantServiceRequest(); - StringBuffer sb = new StringBuffer(); + private boolean updateSupplicantGasFrameBasedServiceRequest() { + clearGasFrameBasedServiceDiscoveryRequests(); + StringBuilder sb = new StringBuilder(); for (ClientInfo c: mClientInfoList.values()) { WifiP2pServiceRequest req; for (int i = 0; i < c.mReqList.size(); i++) { @@ -7877,27 +7977,42 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { return false; } - mServiceDiscReqId = mWifiNative.p2pServDiscReq("00:00:00:00:00:00", sb.toString()); - return mServiceDiscReqId != null; + String serviceDiscReqId = mWifiNative.p2pServDiscReq("00:00:00:00:00:00", + sb.toString()); + if (!TextUtils.isEmpty(serviceDiscReqId)) { + mServiceDiscoveryInfo.update(WifiP2pOngoingServiceDiscoveryRequestInfo + .WIFI_P2P_GAS_FRAME_BASED_SERVICE_DISCOVERY, serviceDiscReqId); + return true; + } else { + return false; + } } /** - * Clear service discovery request in wpa_supplicant + * Clear GAS frame based service discovery requests in wpa_supplicant */ - private void clearSupplicantServiceRequest() { - if (mServiceDiscReqId == null) return; - - mWifiNative.p2pServDiscCancelReq(mServiceDiscReqId); - mServiceDiscReqId = null; + private void clearGasFrameBasedServiceDiscoveryRequests() { + if (!mServiceDiscoveryInfo.isValid()) { + return; + } + if (mServiceDiscoveryInfo.getServiceDiscoveryType() + != WifiP2pOngoingServiceDiscoveryRequestInfo + .WIFI_P2P_GAS_FRAME_BASED_SERVICE_DISCOVERY) { + return; + } + mWifiNative.p2pServDiscCancelReq(mServiceDiscoveryInfo.getSessionId()); + mServiceDiscoveryInfo.invalidate(); } - private boolean addServiceRequest(Messenger m, WifiP2pServiceRequest req) { + @SuppressLint("NewApi") + private boolean addServiceRequest(@NonNull Message message, AtomicInteger errorCode) { + Messenger m = message.replyTo; + WifiP2pServiceRequest req = (WifiP2pServiceRequest) message.obj; if (m == null || req == null) { Log.e(TAG, "Illegal argument(s)"); + errorCode.set(WifiP2pManager.ERROR); return false; } - // TODO: We could track individual service adds separately and avoid - // having to do update all service requests on every new request clearClientDeadChannels(); ClientInfo clientInfo = getClientInfo(m, false); @@ -7905,20 +8020,49 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { return false; } + if (Environment.isSdkAtLeastB() && req.getWifiP2pUsdBasedServiceConfig() != null) { + if (isWifiDirect2Enabled()) { + if (mVerboseLoggingEnabled) { + logd(getName() + " Add USD service config: " + + req.getWifiP2pUsdBasedServiceConfig().toString()); + } + if (clientInfo.mUsdServiceDiscoverList.isEmpty()) { + clientInfo.mUsdServiceDiscoverList.add(req); + return true; + } else { + loge(getName() + "This application already has a service added"); + errorCode.set(WifiP2pManager.BUSY); + return false; + } + } else { + loge(getName() + "Device doesn't support Wi-Fi Direct R2"); + errorCode.set(WifiP2pManager.ERROR); + return false; + } + } + + // Add GAS frame based service discovery request. + + // TODO: We could track individual service adds separately and avoid + // having to do update all service requests on every new request ++mServiceTransactionId; // The Wi-Fi p2p spec says transaction id should be 1 byte and non-zero. if (mServiceTransactionId == 256) mServiceTransactionId = 1; req.setTransactionId((mServiceTransactionId)); clientInfo.mReqList.put(mServiceTransactionId, req); - if (mServiceDiscReqId == null) { + if (!mServiceDiscoveryInfo.isValid() || mServiceDiscoveryInfo + .getServiceDiscoveryType() == WifiP2pOngoingServiceDiscoveryRequestInfo + .WIFI_P2P_USD_FRAME_BASED_SERVICE_DISCOVERY) { return true; } - return updateSupplicantServiceRequest(); + return updateSupplicantGasFrameBasedServiceRequest(); } + @SuppressLint("NewApi") private void removeServiceRequest(Messenger m, WifiP2pServiceRequest req) { if (m == null || req == null) { Log.e(TAG, "Illegal argument(s)"); + return; } ClientInfo clientInfo = getClientInfo(m, false); @@ -7929,21 +8073,37 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { // Application does not have transaction id information // go through stored requests to remove boolean removed = false; - for (int i = 0; i < clientInfo.mReqList.size(); i++) { - if (req.equals(clientInfo.mReqList.valueAt(i))) { - removed = true; - clientInfo.mReqList.removeAt(i); - break; + if (Environment.isSdkAtLeastB() && req.getWifiP2pUsdBasedServiceConfig() != null) { + for (WifiP2pServiceRequest usdServRequest : clientInfo.mUsdServiceDiscoverList) { + if (req.equals(usdServRequest)) { + int sessionId = usdServRequest.getUsdSessionId(); + if (sessionId > 0) { + mWifiNative.stopUsdBasedServiceDiscovery(sessionId); + } + clientInfo.mUsdServiceDiscoverList.remove(usdServRequest); + removed = true; + break; + } + } + } else { + for (int i = 0; i < clientInfo.mReqList.size(); i++) { + if (req.equals(clientInfo.mReqList.valueAt(i))) { + removed = true; + clientInfo.mReqList.removeAt(i); + break; + } } } if (!removed) return; - if (mServiceDiscReqId == null) { + if (!mServiceDiscoveryInfo.isValid() || mServiceDiscoveryInfo + .getServiceDiscoveryType() == WifiP2pOngoingServiceDiscoveryRequestInfo + .WIFI_P2P_USD_FRAME_BASED_SERVICE_DISCOVERY) { return; } - updateSupplicantServiceRequest(); + updateSupplicantGasFrameBasedServiceRequest(); } private void clearServiceRequests(Messenger m) { @@ -7957,20 +8117,37 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { return; } - if (clientInfo.mReqList.size() == 0) { + if (clientInfo.mReqList.size() == 0 + && clientInfo.mUsdServiceDiscoverList.size() == 0) { return; } clientInfo.mReqList.clear(); - if (mServiceDiscReqId == null) { + for (WifiP2pServiceRequest usdServRequest : clientInfo.mUsdServiceDiscoverList) { + int sessionId = usdServRequest.getUsdSessionId(); + if (sessionId > 0) { + mWifiNative.stopUsdBasedServiceDiscovery(sessionId); + } + } + clientInfo.mUsdServiceDiscoverList.clear(); + + if (!mServiceDiscoveryInfo.isValid() || mServiceDiscoveryInfo + .getServiceDiscoveryType() == WifiP2pOngoingServiceDiscoveryRequestInfo + .WIFI_P2P_USD_FRAME_BASED_SERVICE_DISCOVERY) { return; } - updateSupplicantServiceRequest(); + updateSupplicantGasFrameBasedServiceRequest(); } - private boolean addLocalService(Messenger m, WifiP2pServiceInfo servInfo) { + @SuppressLint("NewApi") + private boolean addLocalService(@NonNull Message message) { + Messenger m = message.replyTo; + Bundle extras = message.getData() + .getBundle(WifiP2pManager.EXTRA_PARAM_KEY_BUNDLE); + WifiP2pServiceInfo servInfo = + extras.getParcelable(WifiP2pManager.EXTRA_PARAM_KEY_SERVICE_INFO); if (m == null || servInfo == null) { Log.e(TAG, "Illegal arguments"); return false; @@ -7984,18 +8161,46 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { return false; } - if (!clientInfo.mServList.add(servInfo)) { - return false; - } + int addLocalServiceType = message.arg1; + if (addLocalServiceType + == WifiP2pManager.WIFI_P2P_USD_BASED_ADD_LOCAL_SERVICE) { + if (!isWifiDirect2Enabled()) { + return false; + } + int sessionId = 0; + if (Environment.isSdkAtLeastB() + && servInfo.getWifiP2pUsdBasedServiceConfig() != null) { + WifiP2pUsdBasedServiceConfig usdServiceConfig = + servInfo.getWifiP2pUsdBasedServiceConfig(); + WifiP2pUsdBasedLocalServiceAdvertisementConfig advertisementConfig = + extras.getParcelable(WifiP2pManager + .EXTRA_PARAM_KEY_USD_BASED_LOCAL_SERVICE_ADVERTISEMENT_CONFIG); + sessionId = mWifiNative.startUsdBasedServiceAdvertisement(usdServiceConfig, + advertisementConfig, + USD_BASED_SERVICE_ADVERTISEMENT_DISCOVERY_TIMEOUT_S); - if (!mWifiNative.p2pServiceAdd(servInfo)) { - clientInfo.mServList.remove(servInfo); - return false; + } + if (sessionId > 0) { + servInfo.setUsdSessionId(sessionId); + clientInfo.mUsdServiceAdvertiseList.add(servInfo); + sendP2pListenChangedBroadcast(true); + } else { + return false; + } + } else { + if (!clientInfo.mServList.add(servInfo)) { + return false; + } + if (!mWifiNative.p2pServiceAdd(servInfo)) { + clientInfo.mServList.remove(servInfo); + return false; + } } return true; } + @SuppressLint("NewApi") private void removeLocalService(Messenger m, WifiP2pServiceInfo servInfo) { if (m == null || servInfo == null) { Log.e(TAG, "Illegal arguments"); @@ -8007,8 +8212,21 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { return; } - mWifiNative.p2pServiceDel(servInfo); - clientInfo.mServList.remove(servInfo); + if (Environment.isSdkAtLeastB() && servInfo.getWifiP2pUsdBasedServiceConfig() != null) { + for (WifiP2pServiceInfo savedServInfo: clientInfo.mUsdServiceAdvertiseList) { + if (servInfo.equals(savedServInfo)) { + int sessionId = savedServInfo.getUsdSessionId(); + if (sessionId > 0) { + mWifiNative.stopUsdBasedServiceAdvertisement(sessionId); + } + clientInfo.mUsdServiceAdvertiseList.remove(servInfo); + break; + } + } + } else { + mWifiNative.p2pServiceDel(servInfo); + clientInfo.mServList.remove(servInfo); + } } private void clearLocalServices(Messenger m) { @@ -8022,6 +8240,15 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { return; } + for (WifiP2pServiceInfo savedServInfo: clientInfo.mUsdServiceAdvertiseList) { + int sessionId = savedServInfo.getUsdSessionId(); + if (sessionId > 0) { + mWifiNative.stopUsdBasedServiceAdvertisement(sessionId); + } + } + + clientInfo.mUsdServiceAdvertiseList.clear(); + for (WifiP2pServiceInfo servInfo: clientInfo.mServList) { mWifiNative.p2pServiceDel(servInfo); } @@ -8042,33 +8269,52 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { /** * Send the service response to the WifiP2pManager.Channel. - * @param WifiP2pServiceResponse response to service discovery + * @param resp {@link WifiP2pServiceResponse} response to service discovery */ private void sendServiceResponse(WifiP2pServiceResponse resp) { + WifiP2pServiceRequest req = null; + ClientInfo client = null; if (resp == null) { Log.e(TAG, "sendServiceResponse with null response"); return; } for (ClientInfo c : mClientInfoList.values()) { - WifiP2pServiceRequest req = c.mReqList.get(resp.getTransactionId()); - if (req != null) { - Message msg = Message.obtain(); - msg.what = WifiP2pManager.RESPONSE_SERVICE; - msg.arg1 = 0; - msg.arg2 = 0; - msg.obj = resp; - if (c.mMessenger == null) { - continue; + if (Environment.isSdkAtLeastB() + && resp.getWifiP2pUsdBasedServiceResponse() != null) { + for (WifiP2pServiceRequest serviceRequest : c.mUsdServiceDiscoverList) { + if (serviceRequest.getUsdSessionId() == resp.getUsdSessionId()) { + client = c; + req = serviceRequest; + break; + } + } - try { - c.mMessenger.send(msg); - } catch (RemoteException e) { - if (mVerboseLoggingEnabled) logd("detect dead channel"); - clearClientInfo(c.mMessenger); - return; + } else { + WifiP2pServiceRequest serviceRequest = c.mReqList.get(resp.getTransactionId()); + if (serviceRequest != null) { + client = c; + req = serviceRequest; + break; } } } + + if (req != null) { + Message msg = Message.obtain(); + msg.what = WifiP2pManager.RESPONSE_SERVICE; + msg.arg1 = 0; + msg.arg2 = 0; + msg.obj = resp; + if (client.mMessenger == null) { + return; + } + try { + client.mMessenger.send(msg); + } catch (RemoteException e) { + if (mVerboseLoggingEnabled) logd("detect dead channel"); + clearClientInfo(client.mMessenger); + } + } } /** @@ -8578,12 +8824,18 @@ public class WifiP2pServiceImpl extends IWifiP2pManager.Stub { private String mPackageName; @Nullable private String mFeatureId; - // A service discovery request list. + // A service discovery request list using GAS frames. private final SparseArray<WifiP2pServiceRequest> mReqList = new SparseArray<>(); // A local service information list. private final List<WifiP2pServiceInfo> mServList = new ArrayList<>(); + // A service discovery request list using USD frames. + private final List<WifiP2pServiceRequest> mUsdServiceDiscoverList = new ArrayList<>(); + + // A service advertise list using USD frames. + private final List<WifiP2pServiceInfo> mUsdServiceAdvertiseList = new ArrayList<>(); + private ClientInfo(Messenger m) { mMessenger = m; mPackageName = null; diff --git a/service/java/com/android/server/wifi/rtt/RttServiceImpl.java b/service/java/com/android/server/wifi/rtt/RttServiceImpl.java index 85e5e65c50..930368e1b0 100644 --- a/service/java/com/android/server/wifi/rtt/RttServiceImpl.java +++ b/service/java/com/android/server/wifi/rtt/RttServiceImpl.java @@ -21,7 +21,10 @@ import static android.net.wifi.rtt.WifiRttManager.CHARACTERISTICS_KEY_BOOLEAN_LC import static android.net.wifi.rtt.WifiRttManager.CHARACTERISTICS_KEY_BOOLEAN_LCR; import static android.net.wifi.rtt.WifiRttManager.CHARACTERISTICS_KEY_BOOLEAN_NTB_INITIATOR; import static android.net.wifi.rtt.WifiRttManager.CHARACTERISTICS_KEY_BOOLEAN_ONE_SIDED_RTT; +import static android.net.wifi.rtt.WifiRttManager.CHARACTERISTICS_KEY_BOOLEAN_RANGING_FRAME_PROTECTION_SUPPORTED; +import static android.net.wifi.rtt.WifiRttManager.CHARACTERISTICS_KEY_BOOLEAN_SECURE_HE_LTF_SUPPORTED; import static android.net.wifi.rtt.WifiRttManager.CHARACTERISTICS_KEY_BOOLEAN_STA_RESPONDER; +import static android.net.wifi.rtt.WifiRttManager.CHARACTERISTICS_KEY_INT_MAX_SUPPORTED_SECURE_HE_LTF_PROTO_VERSION; import static com.android.server.wifi.WifiSettingsConfigStore.WIFI_VERBOSE_LOGGING_ENABLED; @@ -240,6 +243,9 @@ public class RttServiceImpl extends IWifiRttManager.Stub { j.put("mcVersion", mCapabilities.mcVersion); j.put("ntbInitiatorSupported", mCapabilities.ntbInitiatorSupported); j.put("ntbResponderSupported", mCapabilities.ntbResponderSupported); + j.put("secureHeLtfSupported", mCapabilities.secureHeLtfSupported); + j.put("rangingFrameProtectionSupported", + mCapabilities.rangingFrameProtectionSupported); } catch (JSONException e) { Log.e(TAG, "onCommand: get_capabilities e=" + e); } @@ -346,7 +352,7 @@ public class RttServiceImpl extends IWifiRttManager.Stub { intentFilter = new IntentFilter(); intentFilter.addAction(LocationManager.MODE_CHANGED_ACTION); - mContext.registerReceiver(new BroadcastReceiver() { + mContext.registerReceiverForAllUsers(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (mVerboseLoggingEnabled) { @@ -358,7 +364,7 @@ public class RttServiceImpl extends IWifiRttManager.Stub { disable(); } } - }, intentFilter); + }, intentFilter, null, mRttServiceSynchronized.mHandler); mHalDeviceManager.initialize(); mHalDeviceManager.registerStatusListener(() -> { @@ -483,6 +489,12 @@ public class RttServiceImpl extends IWifiRttManager.Stub { capabilities.responderSupported); characteristics.putBoolean(CHARACTERISTICS_KEY_BOOLEAN_NTB_INITIATOR, capabilities.ntbInitiatorSupported); + characteristics.putBoolean(CHARACTERISTICS_KEY_BOOLEAN_SECURE_HE_LTF_SUPPORTED, + capabilities.secureHeLtfSupported); + characteristics.putBoolean(CHARACTERISTICS_KEY_BOOLEAN_RANGING_FRAME_PROTECTION_SUPPORTED, + capabilities.rangingFrameProtectionSupported); + characteristics.putInt(CHARACTERISTICS_KEY_INT_MAX_SUPPORTED_SECURE_HE_LTF_PROTO_VERSION, + capabilities.maxSupportedSecureHeLtfProtocolVersion); return characteristics; } diff --git a/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java b/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java index 120de838d6..5392d1a0fa 100644 --- a/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java +++ b/service/java/com/android/server/wifi/scanner/WifiScanningServiceImpl.java @@ -244,7 +244,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { return; } final ExternalClientInfo client = new ExternalClientInfo(uid, packageName, - listener); + listener, featureId); client.register(); localLog("register scan listener: " + client + " AttributionTag " + featureId); logScanRequest("registerScanListener", client, null, null, null); @@ -267,13 +267,13 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { notifyFailure(listener, WifiScanner.REASON_NOT_AUTHORIZED, "Not authorized"); return; } - ExternalClientInfo client = (ExternalClientInfo) mClients.get(listener); - if (client == null) { - logw("no client registered: " + uid + ", listener=" + listener - + " AttributionTag " + featureId); - return; - } mWifiThreadRunner.post(() -> { + ExternalClientInfo client = (ExternalClientInfo) mClients.get(listener); + if (client == null) { + logw("no client registered: " + uid + ", listener=" + listener + + " AttributionTag " + featureId); + return; + } logScanRequest("deregisterScanListener", client, null, null, null); mSingleScanListeners.removeRequest(client); client.cleanup(); @@ -298,13 +298,13 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { mWifiThreadRunner.post(() -> { ExternalClientInfo client = (ExternalClientInfo) mClients.get(listener); if (client == null) { - client = new ExternalClientInfo(uid, packageName, listener); + client = new ExternalClientInfo(uid, packageName, listener, featureId); client.register(); } localLog("start background scan: " + client + " package " + packageName); Message msg = Message.obtain(); msg.what = WifiScanner.CMD_START_BACKGROUND_SCAN; - msg.obj = new ScanParams(listener, settings, workSource); + msg.obj = new ScanParams(listener, settings, workSource, featureId); msg.sendingUid = uid; mBackgroundScanStateMachine.sendMessage(msg); }, TAG + "#startBackgroundScan"); @@ -332,7 +332,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { localLog("stop background scan: " + client); Message msg = Message.obtain(); msg.what = WifiScanner.CMD_STOP_BACKGROUND_SCAN; - msg.obj = new ScanParams(listener, null, null); + msg.obj = new ScanParams(listener, null, null, featureId); msg.sendingUid = uid; mBackgroundScanStateMachine.sendMessage(msg); } @@ -363,8 +363,8 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { public String featureId; ScanParams(IWifiScannerListener listener, WifiScanner.ScanSettings settings, - WorkSource workSource) { - this(listener, settings, null, workSource, null, null); + WorkSource workSource, String featureId) { + this(listener, settings, null, workSource, null, featureId); } ScanParams(IWifiScannerListener listener, WifiScanner.ScanSettings settings, @@ -399,14 +399,14 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { mWifiThreadRunner.post(() -> { ExternalClientInfo client = (ExternalClientInfo) mClients.get(listener); if (client == null) { - client = new ExternalClientInfo(uid, packageName, listener); + client = new ExternalClientInfo(uid, packageName, listener, featureId); client.register(); } localLog("start scan: " + client + " package " + packageName + " AttributionTag " + featureId); Message msg = Message.obtain(); msg.what = WifiScanner.CMD_START_SINGLE_SCAN; - msg.obj = new ScanParams(listener, settings, workSource); + msg.obj = new ScanParams(listener, settings, workSource, featureId); msg.sendingUid = uid; mSingleScanStateMachine.sendMessage(msg); }, TAG + "#startScan"); @@ -434,7 +434,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { localLog("stop scan: " + client + " AttributionTag " + featureId); Message msg = Message.obtain(); msg.what = WifiScanner.CMD_STOP_SINGLE_SCAN; - msg.obj = new ScanParams(listener, null, null); + msg.obj = new ScanParams(listener, null, null, featureId); msg.sendingUid = uid; mSingleScanStateMachine.sendMessage(msg); }, TAG + "#stopScan"); @@ -504,7 +504,8 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { localLog("start pno scan: " + clientInfoLog + " AttributionTag " + featureId); Message msg = Message.obtain(); msg.what = WifiScanner.CMD_START_PNO_SCAN; - msg.obj = new ScanParams(listener, scanSettings, pnoSettings, null, packageName, null); + msg.obj = new ScanParams(listener, scanSettings, pnoSettings, null, packageName, + featureId); msg.sendingUid = uid; mPnoScanStateMachine.sendMessage(msg); }, TAG + "#startPnoScan"); @@ -531,7 +532,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { localLog("stop pno scan: " + packageName + " AttributionTag " + featureId); Message msg = Message.obtain(); msg.what = WifiScanner.CMD_STOP_PNO_SCAN; - msg.obj = new ScanParams(listener, null, null); + msg.obj = new ScanParams(listener, null, null, featureId); msg.sendingUid = uid; mPnoScanStateMachine.sendMessage(msg); }, TAG + "#stopPnoScan"); @@ -889,6 +890,70 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { } } + private String getWhatToStringInternal(int what) { + switch (what) { + case WifiScanner.CMD_START_BACKGROUND_SCAN: + return "WifiScanner.CMD_START_BACKGROUND_SCAN"; + case WifiScanner.CMD_STOP_BACKGROUND_SCAN: + return "WifiScanner.CMD_STOP_BACKGROUND_SCAN"; + case WifiScanner.CMD_GET_SCAN_RESULTS: + return "WifiScanner.CMD_GET_SCAN_RESULTS"; + case WifiScanner.CMD_SCAN_RESULT: + return "WifiScanner.CMD_SCAN_RESULT"; + case WifiScanner.CMD_CACHED_SCAN_DATA: + return "WifiScanner.CMD_CACHED_SCAN_DATA"; + case WifiScanner.CMD_OP_SUCCEEDED: + return "WifiScanner.CMD_OP_SUCCEEDED"; + case WifiScanner.CMD_OP_FAILED: + return "WifiScanner.CMD_OP_FAILED"; + case WifiScanner.CMD_FULL_SCAN_RESULT: + return "WifiScanner.CMD_FULL_SCAN_RESULT"; + case WifiScanner.CMD_START_SINGLE_SCAN: + return "WifiScanner.CMD_START_SINGLE_SCAN"; + case WifiScanner.CMD_STOP_SINGLE_SCAN: + return "WifiScanner.CMD_STOP_SINGLE_SCAN"; + case WifiScanner.CMD_SINGLE_SCAN_COMPLETED: + return "WifiScanner.CMD_SINGLE_SCAN_COMPLETED"; + case WifiScanner.CMD_START_PNO_SCAN: + return "WifiScanner.CMD_START_PNO_SCAN"; + case WifiScanner.CMD_STOP_PNO_SCAN: + return "WifiScanner.CMD_STOP_PNO_SCAN"; + case WifiScanner.CMD_PNO_NETWORK_FOUND: + return "WifiScanner.CMD_PNO_NETWORK_FOUND"; + case WifiScanner.CMD_REGISTER_SCAN_LISTENER: + return "WifiScanner.CMD_REGISTER_SCAN_LISTENER"; + case WifiScanner.CMD_DEREGISTER_SCAN_LISTENER: + return "WifiScanner.CMD_DEREGISTER_SCAN_LISTENER"; + case WifiScanner.CMD_GET_SINGLE_SCAN_RESULTS: + return "WifiScanner.CMD_GET_SINGLE_SCAN_RESULTS"; + case WifiScanner.CMD_ENABLE: + return "WifiScanner.CMD_ENABLE"; + case WifiScanner.CMD_DISABLE: + return "WifiScanner.CMD_DISABLE"; + case CMD_SCAN_RESULTS_AVAILABLE: + return "CMD_SCAN_RESULTS_AVAILABLE"; + case CMD_FULL_SCAN_SINGLE_RESULT: + return "CMD_FULL_SCAN_SINGLE_RESULT"; + case CMD_FULL_SCAN_ALL_RESULTS: + return "CMD_FULL_SCAN_ALL_RESULTS"; + case CMD_SCAN_PAUSED: + return "CMD_SCAN_PAUSED"; + case CMD_SCAN_RESTARTED: + return "CMD_SCAN_RESTARTED"; + case CMD_SCAN_FAILED: + return "CMD_SCAN_FAILED"; + case CMD_PNO_NETWORK_FOUND: + return "CMD_PNO_NETWORK_FOUND"; + case CMD_PNO_SCAN_FAILED: + return "CMD_PNO_SCAN_FAILED"; + case CMD_SW_PNO_SCAN: + return "CMD_SW_PNO_SCAN"; + default: + return "what:" + what; + } + } + + /** * State machine that holds the state of single scans. Scans should only be active in the * ScanningState. The pending scans and active scans maps are swapped when entering @@ -954,6 +1019,30 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { } /** + * @return the string for msg.what + */ + @Override + protected String getWhatToString(int what) { + return getWhatToStringInternal(what); + } + + /** + * Return the additional string to be logged by LogRec, default + * + * @param msg that was processed + * @return information to be logged as a String + */ + @Override + protected String getLogRecString(Message msg) { + StringBuilder sb = new StringBuilder(); + sb.append(" "); + sb.append(Integer.toString(msg.arg1)); + sb.append(" "); + sb.append(Integer.toString(msg.arg2)); + return sb.toString(); + } + + /** * Tracks a single scan request across all the available scanner impls. * * a) Initiates the scan using the same ScanSettings across all the available impls. @@ -1696,7 +1785,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { } for (RequestInfo<Void> entry : mSingleScanListeners) { - logCallback("singleScanResults", entry.clientInfo, + logCallback("singleScanResults listener", entry.clientInfo, describeForLog(allResults)); entry.clientInfo.reportEvent((listener) -> { try { @@ -2338,6 +2427,30 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { setInitialState(mDefaultState); } + /** + * @return the string for msg.what + */ + @Override + protected String getWhatToString(int what) { + return getWhatToStringInternal(what); + } + + /** + * Return the additional string to be logged by LogRec, default + * + * @param msg that was processed + * @return information to be logged as a String + */ + @Override + protected String getLogRecString(Message msg) { + StringBuilder sb = new StringBuilder(); + sb.append(" "); + sb.append(Integer.toString(msg.arg1)); + sb.append(" "); + sb.append(Integer.toString(msg.arg2)); + return sb.toString(); + } + public void removePnoSettings(ClientInfo ci) { mActivePnoScans.removeAllForClient(ci); } @@ -2543,7 +2656,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { ClientInfo ci = mClients.get(scanParams.listener); if (ci == null) { ci = new ExternalClientInfo(msg.sendingUid, scanParams.packageName, - scanParams.listener); + scanParams.listener, scanParams.featureId); ci.register(); } if (scanParams.pnoSettings == null || scanParams.settings == null) { @@ -2603,7 +2716,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { ClientInfo ci = mClients.get(scanParams.listener); if (ci == null) { ci = new ExternalClientInfo(msg.sendingUid, scanParams.packageName, - scanParams.listener); + scanParams.listener, scanParams.featureId); ci.register(); } if (scanParams.pnoSettings == null || scanParams.settings == null) { @@ -2917,7 +3030,8 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { ClientInfo clientInfo = mClients.get(scanParams.listener); if (clientInfo == null) { clientInfo = new ExternalClientInfo(msg.sendingUid, - scanParams.packageName, scanParams.listener); + scanParams.packageName, scanParams.listener, + scanParams.featureId); clientInfo.register(); } @@ -3056,7 +3170,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { private void addInternalClient(ClientInfo ci) { if (mInternalClientInfo == null) { mInternalClientInfo = new InternalClientInfo(ci.getUid(), "internal", - new InternalListener()); + new InternalListener(), ci.mAttributionTag); mInternalClientInfo.register(); } else { Log.w(TAG, "Internal client for PNO already exists"); @@ -3141,7 +3255,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { Message msg = Message.obtain(); msg.what = WifiScanner.CMD_START_SINGLE_SCAN; msg.obj = new ScanParams(mInternalClientInfo.mListener, settings, - ClientModeImpl.WIFI_WORK_SOURCE); + ClientModeImpl.WIFI_WORK_SOURCE, "WIFI_INTERNAL"); mSingleScanStateMachine.sendMessage(msg); } mWifiMetrics.getScanMetrics().setWorkSource(ClientModeImpl.WIFI_WORK_SOURCE); @@ -3170,6 +3284,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { private abstract class ClientInfo { private final int mUid; private final String mPackageName; + private final String mAttributionTag; private final WorkSource mWorkSource; private boolean mScanWorkReported = false; protected final IWifiScannerListener mListener; @@ -3186,11 +3301,13 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { } }; - ClientInfo(int uid, String packageName, IWifiScannerListener listener) { + ClientInfo(int uid, String packageName, IWifiScannerListener listener, + String attributionTag) { mUid = uid; mPackageName = packageName; mListener = listener; mWorkSource = new WorkSource(uid); + mAttributionTag = attributionTag; } /** @@ -3312,8 +3429,8 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { @Override public String toString() { - return "ClientInfo[uid=" + mUid + ", package=" + mPackageName + ", " + mListener - + "]"; + return "ClientInfo[uid=" + mUid + ", package=" + mPackageName + ", attributionTag=" + + mAttributionTag + ", " + mListener + "]"; } } @@ -3327,8 +3444,9 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { */ private boolean mDisconnected = false; - ExternalClientInfo(int uid, String packageName, IWifiScannerListener listener) { - super(uid, packageName, listener); + ExternalClientInfo(int uid, String packageName, IWifiScannerListener listener, + String attributionTag) { + super(uid, packageName, listener, attributionTag); if (DBG) localLog("New client, listener: " + listener); try { listener.asBinder().linkToDeath(mDeathRecipient, 0); @@ -3361,8 +3479,9 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { /** * The UID here is used to proxy the original external requester UID. */ - InternalClientInfo(int requesterUid, String packageName, IWifiScannerListener listener) { - super(requesterUid, packageName, listener); + InternalClientInfo(int requesterUid, String packageName, IWifiScannerListener listener, + String attributionTag) { + super(requesterUid, packageName, listener, attributionTag); } @Override diff --git a/service/java/com/android/server/wifi/usd/UsdNativeManager.java b/service/java/com/android/server/wifi/usd/UsdNativeManager.java new file mode 100644 index 0000000000..5c0e3dc0b6 --- /dev/null +++ b/service/java/com/android/server/wifi/usd/UsdNativeManager.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2024 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.usd; + +import android.net.MacAddress; +import android.net.wifi.usd.PublishConfig; +import android.net.wifi.usd.PublishSessionCallback; +import android.net.wifi.usd.SessionCallback; +import android.net.wifi.usd.SubscribeConfig; +import android.net.wifi.usd.SubscribeSessionCallback; + +import com.android.server.wifi.SupplicantStaIfaceHal; +import com.android.server.wifi.WifiNative; + +import java.util.concurrent.Executor; + +/** + * Manages the interface to the HAL. + */ +public class UsdNativeManager { + private final WifiNative mWifiNative; + + /** + * USD Events from HAL. + */ + public interface UsdEventsCallback { + /** + * Called when publisher is started. + */ + void onUsdPublishStarted(int cmdId, int publishId); + + /** + * Called when subscribe is started. + */ + void onUsdSubscribeStarted(int cmdId, int subscribeId); + + /** + * Called when publisher is failed to start. + */ + void onUsdPublishConfigFailed(int cmdId, @SessionCallback.FailureCode int errorCode); + + /** + * Called when subscriber is failed to start. + */ + void onUsdSubscribeConfigFailed(int cmdId, @SessionCallback.FailureCode int errorCode); + + /** + * Called when publish session is terminated. + */ + void onUsdPublishTerminated(int publishId, + @SessionCallback.TerminationReasonCode int reasonCode); + + /** + * Called when subscribe session is terminated. + */ + void onUsdSubscribeTerminated(int subscribeId, + @SessionCallback.TerminationReasonCode int reasonCode); + + /** + * Called for each Publish replied event. + */ + void onUsdPublishReplied( + UsdRequestManager.UsdHalDiscoveryInfo usdHalDiscoveryInfo); + + /** + * Called when the subscriber discovers publisher. + */ + void onUsdServiceDiscovered( + UsdRequestManager.UsdHalDiscoveryInfo usdHalDiscoveryInfo); + + /** + * Called when a message is received. + */ + void onUsdMessageReceived(int ownId, int peerId, MacAddress peerMacAddress, + byte[] message); + } + + /** + * Constructor + */ + public UsdNativeManager(WifiNative wifiNative) { + mWifiNative = wifiNative; + } + + /** + * Register USD events. + */ + public void registerUsdEventsCallback( + UsdRequestManager.UsdNativeEventsCallback usdNativeEventsCallback) { + mWifiNative.registerUsdEventsCallback(usdNativeEventsCallback); + } + + /** + * Gets USD capabilities. + */ + public SupplicantStaIfaceHal.UsdCapabilitiesInternal getUsdCapabilities() { + return mWifiNative.getUsdCapabilities(); + } + + /** + * See {@link android.net.wifi.usd.UsdManager#publish(PublishConfig, Executor, + * PublishSessionCallback)} + */ + public boolean publish(String interfaceName, int cmdId, PublishConfig publishConfig) { + return mWifiNative.startUsdPublish(interfaceName, cmdId, publishConfig); + } + + /** + * See {@link android.net.wifi.usd.UsdManager#subscribe(SubscribeConfig, Executor, + * SubscribeSessionCallback)} + */ + public boolean subscribe(String interfaceName, int cmdId, SubscribeConfig subscribeConfig) { + return mWifiNative.startUsdSubscribe(interfaceName, cmdId, subscribeConfig); + } + + /** + * Update publish. + */ + public void updatePublish(String interfaceName, int publishId, byte[] ssi) { + mWifiNative.updateUsdPublish(interfaceName, publishId, ssi); + } + + /** + * Cancels publish session identified by publishId. + */ + public void cancelPublish(String interfaceName, int publishId) { + mWifiNative.cancelUsdPublish(interfaceName, publishId); + } + + /** + * Cancels subscribe identified by subscribeId + */ + public void cancelSubscribe(String interfaceName, int subscribeId) { + mWifiNative.cancelUsdSubscribe(interfaceName, subscribeId); + } + + /** + * Send a message to the peer identified by the peerId and the peerMacAddress. + */ + public boolean sendMessage(String interfaceName, int ownId, int peerId, + MacAddress peerMacAddress, byte[] message) { + return mWifiNative.sendUsdMessage(interfaceName, ownId, peerId, peerMacAddress, message); + } +} diff --git a/service/java/com/android/server/wifi/usd/UsdRequestManager.java b/service/java/com/android/server/wifi/usd/UsdRequestManager.java new file mode 100644 index 0000000000..896d386099 --- /dev/null +++ b/service/java/com/android/server/wifi/usd/UsdRequestManager.java @@ -0,0 +1,845 @@ +/* + * Copyright (C) 2024 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.usd; + +import android.annotation.NonNull; +import android.annotation.SuppressLint; +import android.app.AlarmManager; +import android.net.MacAddress; +import android.net.wifi.IBooleanListener; +import android.net.wifi.usd.Characteristics; +import android.net.wifi.usd.Config; +import android.net.wifi.usd.IPublishSessionCallback; +import android.net.wifi.usd.ISubscribeSessionCallback; +import android.net.wifi.usd.PublishConfig; +import android.net.wifi.usd.PublishSession; +import android.net.wifi.usd.PublishSessionCallback; +import android.net.wifi.usd.SessionCallback; +import android.net.wifi.usd.SubscribeConfig; +import android.net.wifi.usd.SubscribeSession; +import android.net.wifi.usd.SubscribeSessionCallback; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; +import android.util.SparseArray; + +import com.android.server.wifi.Clock; +import com.android.server.wifi.SupplicantStaIfaceHal; +import com.android.server.wifi.WifiThreadRunner; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.Executor; +import java.util.function.Consumer; + +import javax.annotation.concurrent.NotThreadSafe; + +/** + * This class UsdRequestManager acts as central point for handling various USD requests from + * applications such as publish, subscribe, send message, etc. It sends the command to HAL to + * carry out these actions and expect for callbacks from HAL on various events such as susbcribe/ + * publish started, service discovered, received a message from the peer, etc. + * + * <p>Here is how it works, + * <ul> + * <li>Role: The UsdRequestManager can act as either a publisher or subscriber + * <li>Request handling: It manages incoming requests and ensures the new commands are not accepted + * while a previous subscribe or publish is still awaiting for the response from HAL. + * <li>Session Management: USD session are organized and tracked using unique session IDs. Each + * session maintains a collection of USD discovery results which are indexed by the USD peer. + * <li>USD Peer: A peer is created for discover and also created a unique id (hash) which maps to + * local session id, remote session id and remote mac address. Applications are given this unique + * id (hash) on various indications. + * + * <p>Essentially, this class streamlines USD communication by managing requests, organizing + * sessions, and maintaining information about discovered peers. It also enforces a sequential + * processing of requests to prevent conflicts and ensure reliable communication with HAL. + * </ul> + */ +@NotThreadSafe +@SuppressLint("NewApi") +public class UsdRequestManager { + public static final String TAG = "UsdRequestManager"; + private static final int DEFAULT_COMMAND_ID = 100; + private static final int USD_TEMP_SESSION_ID = 255; + private static final int INVALID_ID = -1; + private static final String USD_REQUEST_MANAGER_ALARM_TAG = "UsdRequestManagerAlarmTag"; + + /** + * A unique peer hash (a unique peer id) generator. Application will get the peer hash as the + * identifier of the peer. Also peer hash is globally mapped to a peer (defined by ownId, + * peerId and peer mac address). + */ + private static int sNextPeerHash = 100; + private final UsdNativeManager mUsdNativeManager; + /** + * A reference count to capture subscriber role is disabled. The role can be disabled due to + * multiple reasons, publisher is running, concurrency ..etc. + */ + private int mSubscriberDisabledCount = 0; + /** + * A reference count to capture publisher role is disabled. The role can be disabled due to + * multiple reasons, subscriber is running, concurrency, overlay config ..etc. + */ + private int mPublisherDisabledCount = 0; + private final String mInterfaceName; + private final SupplicantStaIfaceHal.UsdCapabilitiesInternal mUsdCapabilities; + private final WifiThreadRunner mWifiThreadRunner; + private final Clock mClock; + private enum Role { + NONE, PUBLISHER, SUBSCRIBER + } + private Role mRequesterRole; + private final AlarmManager mAlarmManager; + private final AlarmManager.OnAlarmListener mTimeoutListener = () -> { + startCleaningUpExpiredSessions(); + }; + private static final int TEMP_SESSION_TIMEOUT_MILLIS = 1000; + private static final int TTL_GAP_MILLIS = 1000; + + private void startCleaningUpExpiredSessions() { + long current = mClock.getElapsedSinceBootMillis(); + long nextSchedule = Long.MAX_VALUE; + long age; + List<Integer> sessionsToDelete = new ArrayList<>(); + + // Cleanup sessions which crossed the TTL. + for (int i = 0; i < mUsdSessions.size(); i++) { + UsdSession usdSession = mUsdSessions.valueAt(i); + int sessionId = mUsdSessions.keyAt(i); + int ttlMillis = TEMP_SESSION_TIMEOUT_MILLIS; + if (sessionId != USD_TEMP_SESSION_ID) { + ttlMillis = ((usdSession.getRole() == Role.PUBLISHER) + ? usdSession.mPublishConfig.getTtlSeconds() + : usdSession.mSubscribeConfig.getTtlSeconds()) * 1000 + TTL_GAP_MILLIS; + } + age = current - usdSession.mCreationTimeMillis; + if (age >= ttlMillis) { + sessionsToDelete.add(sessionId); + } else { + nextSchedule = Math.min(ttlMillis - age, nextSchedule); + } + } + + for (int sessionId : sessionsToDelete) { + mUsdSessions.get(sessionId).sessionCleanup(); + mUsdSessions.remove(sessionId); + } + + // Reschedule if necessary. + if (mUsdSessions.size() > 0 && nextSchedule < Long.MAX_VALUE) { + mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, + mClock.getElapsedSinceBootMillis() + nextSchedule, + USD_REQUEST_MANAGER_ALARM_TAG, mTimeoutListener, + mWifiThreadRunner.getHandler()); + } + } + + private void stopCleaningUpExpiredSessions() { + mAlarmManager.cancel(mTimeoutListener); + } + + /** + * A class to represent USD peer. A combination of ownId, peerId and peerMacAddress define a + * unique peer. + */ + public static final class UsdPeer { + public final int ownId; + public final int peerId; + public final MacAddress peerMacAddress; + + public UsdPeer(int ownId, int peerId, MacAddress peerMacAddress) { + this.ownId = ownId; + this.peerId = peerId; + this.peerMacAddress = peerMacAddress; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof UsdPeer peer)) return false; + return ownId == peer.ownId && peerId == peer.peerId && peerMacAddress.equals( + peer.peerMacAddress); + } + + @Override + public int hashCode() { + return Objects.hash(ownId, peerId, peerMacAddress); + } + } + + /** + * A class representing USD session. + */ + private final class UsdSession implements IBinder.DeathRecipient { + private int mId = INVALID_ID; + private Role mSessionRole = Role.NONE; + private PublishConfig mPublishConfig; + private IPublishSessionCallback mIPublishSessionCallback; + private SubscribeConfig mSubscribeConfig; + private ISubscribeSessionCallback mISubscribeSessionCallback; + private final long mCreationTimeMillis; + /** + * Maps peer to peer hash (a unique identifier to the peer). + */ + private final HashMap<UsdPeer, Integer> mSessionPeers = new HashMap<>(); + + /** + * Get Role of the session. See {@link Role} for different roles. + */ + public Role getRole() { + return mSessionRole; + } + + /** + * Set session id for this session. + */ + public void setSessionId(int sessionId) { + mId = sessionId; + } + + /** + * Adds a peer to the session if not already there. It creates a unique id (key) and add the + * peer to a map. + */ + public void addPeerOnce(UsdPeer peer) { + if (mSessionPeers.containsKey(peer)) return; + int peerHash = sNextPeerHash++; + mSessionPeers.put(peer, peerHash); + addPeerToGlobalMap(peerHash, peer); + } + + /** + * Get unique hash (a unique id) for a peer. + */ + public int getPeerHash(UsdPeer peer) { + return mSessionPeers.getOrDefault(peer, INVALID_ID); + } + + /** + * Clear all peers for this session. + */ + public void releasePeers() { + // Release all peers associated to this session from global map. + for (int peerHash : mSessionPeers.values()) { + removePeerFromGlobalMap(peerHash); + } + mSessionPeers.clear(); + } + + /** + * A constructor for publisher session. + */ + UsdSession(PublishConfig publishConfig, IPublishSessionCallback callback) { + mSessionRole = Role.PUBLISHER; + mPublishConfig = publishConfig; + mIPublishSessionCallback = callback; + // Register the recipient for a notification if this binder goes away. + try { + callback.asBinder().linkToDeath(this, 0); + } catch (RemoteException e) { + Log.e(TAG, "UsdSession linkToDeath " + e); + } + mCreationTimeMillis = mClock.getElapsedSinceBootMillis(); + // Disable Subscriber operation + mSubscriberDisabledCount++; + } + + /** + * A constructor for subscriber session. + */ + UsdSession(SubscribeConfig subscribeConfig, ISubscribeSessionCallback callback) { + mSessionRole = Role.SUBSCRIBER; + mSubscribeConfig = subscribeConfig; + mISubscribeSessionCallback = callback; + // Register the recipient for a notification if this binder goes away. + try { + callback.asBinder().linkToDeath(this, 0); + } catch (RemoteException e) { + Log.e(TAG, "UsdSession linkToDeath " + e); + } + mCreationTimeMillis = mClock.getElapsedSinceBootMillis(); + // Disable Publisher operation + mPublisherDisabledCount++; + + } + + @Override + public void binderDied() { + mWifiThreadRunner.post(() -> sessionCleanup()); + } + + /** + * A sessionCleanup function for the USD session. + */ + public void sessionCleanup() { + releasePeers(); + if (mSessionRole == Role.PUBLISHER) { + mIPublishSessionCallback.asBinder().unlinkToDeath(this, 0); + mSubscriberDisabledCount--; + } else { + mISubscribeSessionCallback.asBinder().unlinkToDeath(this, 0); + mPublisherDisabledCount--; + } + if (isSingleSession()) { + mRequesterRole = Role.NONE; + stopCleaningUpExpiredSessions(); + } + mUsdSessions.remove(mId); + mSessionRole = Role.NONE; + } + } + + /** + * A class for USD discovery info from HAL. + */ + public static final class UsdHalDiscoveryInfo { + public final int ownId; + public final int peerId; + public MacAddress peerMacAddress; + public final byte[] serviceSpecificInfo; + @Config.ServiceProtoType + public final int serviceProtoType; + public final boolean isFsdEnabled; + public final byte[] matchFilter; + + public UsdHalDiscoveryInfo(int ownId, int peerId, MacAddress peerMacAddress, + byte[] serviceSpecificInfo, int serviceProtoType, boolean isFsdEnabled, + byte[] matchFilter) { + this.ownId = ownId; + this.peerId = peerId; + this.peerMacAddress = peerMacAddress; + this.serviceSpecificInfo = serviceSpecificInfo; + this.serviceProtoType = serviceProtoType; + this.isFsdEnabled = isFsdEnabled; + this.matchFilter = matchFilter; + } + } + + private final SparseArray<UsdSession> mUsdSessions = new SparseArray<>(); + private final SparseArray<UsdPeer> mGlobalPeerMap = new SparseArray<>(); + + private boolean isSingleSession() { + return mUsdSessions.size() == 1; + } + + /** + * Add peer to the global peer map. + */ + private void addPeerToGlobalMap(int peerHash, UsdPeer peer) { + mGlobalPeerMap.put(peerHash, peer); + } + + /** + * Checks whether peer existing in the global peer map. + */ + private boolean doesPeerExistInGlobalMap(int peerHash) { + return mGlobalPeerMap.contains(peerHash); + } + + /** + * Gets peer from the global peer map. Returns null if peer does not exist. + */ + private UsdPeer getPeerFromGlobalMap(int peerHash) { + return mGlobalPeerMap.get(peerHash); + } + + /** + * Removes peer from global peer map. + */ + private void removePeerFromGlobalMap(int peerHash) { + mGlobalPeerMap.remove(peerHash); + } + + /** + * Constructor. + */ + public UsdRequestManager(UsdNativeManager usdNativeManager, WifiThreadRunner wifiThreadRunner, + String interfaceName, Clock clock, AlarmManager alarmManager) { + mUsdNativeManager = usdNativeManager; + mInterfaceName = interfaceName; + SupplicantStaIfaceHal.UsdCapabilitiesInternal usdCapabilities = + mUsdNativeManager.getUsdCapabilities(); + if (usdCapabilities == null) { + usdCapabilities = new SupplicantStaIfaceHal.UsdCapabilitiesInternal(); + } + mUsdCapabilities = usdCapabilities; + mWifiThreadRunner = wifiThreadRunner; + registerUsdEventsCallback(new UsdNativeEventsCallback()); + mClock = clock; + mAlarmManager = alarmManager; + mRequesterRole = Role.NONE; + } + + /** + * Get USD characteristics. + */ + public Characteristics getCharacteristics() { + Bundle bundle = new Bundle(); + if (mUsdCapabilities != null) { + bundle.putInt(Characteristics.KEY_MAX_NUM_SUBSCRIBE_SESSIONS, + mUsdCapabilities.maxNumSubscribeSessions); + bundle.putInt(Characteristics.KEY_MAX_NUM_PUBLISH_SESSIONS, + mUsdCapabilities.maxNumPublishSessions); + bundle.putInt(Characteristics.KEY_MAX_SERVICE_SPECIFIC_INFO_LENGTH, + mUsdCapabilities.maxLocalSsiLengthBytes); + bundle.putInt(Characteristics.KEY_MAX_MATCH_FILTER_LENGTH, + mUsdCapabilities.maxMatchFilterLengthBytes); + bundle.putInt(Characteristics.KEY_MAX_SERVICE_NAME_LENGTH, + mUsdCapabilities.maxServiceNameLengthBytes); + } + return new Characteristics(bundle); + } + + /** + * Whether subscriber is available. + */ + public boolean isSubscriberAvailable() { + return mPublisherDisabledCount == 0; + } + + /** + * Whether publisher is available. + */ + public boolean isPublisherAvailable() { + return mSubscriberDisabledCount == 0; + } + + private void notifyStatus(IBooleanListener listener, String errMsg, boolean isSuccess) { + if (!isSuccess) { + Log.e(TAG, "notifyStatus: " + errMsg); + } + try { + listener.onResult(isSuccess); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } + + /** + * See {@link SubscribeSession#sendMessage(int, byte[], Executor, Consumer)} and + * {@link PublishSession#sendMessage(int, byte[], Executor, Consumer)} + */ + public void sendMessage(int sessionId, int peerHash, @NonNull byte[] message, + @NonNull IBooleanListener listener) { + if (!isUsdAvailable()) { + notifyStatus(listener, "USD is not available", false); + return; + } + if (!mUsdSessions.contains(sessionId)) { + notifyStatus(listener, "Session does not exist. Session id = " + sessionId, false); + return; + } + if (message.length > mUsdCapabilities.maxLocalSsiLengthBytes) { + notifyStatus(listener, "longer message than supported. Max len supported = " + + mUsdCapabilities.maxLocalSsiLengthBytes + " len = " + message.length, false); + return; + } + if (!doesPeerExistInGlobalMap(peerHash)) { + notifyStatus(listener, "Invalid peer hash = " + peerHash, false); + return; + } + UsdPeer peer = getPeerFromGlobalMap(peerHash); + if (mUsdNativeManager.sendMessage(mInterfaceName, sessionId, peer.peerId, + peer.peerMacAddress, message)) { + notifyStatus(listener, "", true); + } else { + notifyStatus(listener, "sendMessage failed", false); + } + } + + private boolean isUsdAvailable() { + if (mRequesterRole == Role.PUBLISHER) { + return isPublisherAvailable(); + } else if (mRequesterRole == Role.SUBSCRIBER) { + return isSubscriberAvailable(); + } + return false; + } + + /** + * See {@link SubscribeSession#cancel()} + */ + public void cancelSubscribe(int sessionId) { + if (mRequesterRole == Role.SUBSCRIBER && mUsdSessions.contains(sessionId)) { + mUsdNativeManager.cancelSubscribe(mInterfaceName, sessionId); + } + } + + /** + * See {@link PublishSession#cancel()} + */ + public void cancelPublish(int sessionId) { + if (mRequesterRole == Role.PUBLISHER && mUsdSessions.contains(sessionId)) { + mUsdNativeManager.cancelPublish(mInterfaceName, sessionId); + } + } + + /** + * See {@link PublishSession#updatePublish(byte[])} + */ + public void updatePublish(int sessionId, byte[] ssi) { + if (mRequesterRole == Role.PUBLISHER && mUsdSessions.contains(sessionId) + && isPublisherAvailable()) { + mUsdNativeManager.updatePublish(mInterfaceName, sessionId, ssi); + } + } + + private void notifyPublishFailure(IPublishSessionCallback callback, int reasonCode, + String reason) { + try { + Log.w(TAG, reason); + callback.onPublishFailed(reasonCode); + } catch (RemoteException e) { + Log.e(TAG, "publish: " + e); + } + } + + /** + * See {@link android.net.wifi.usd.UsdManager#publish(PublishConfig, Executor, + * PublishSessionCallback)} + */ + public void publish(PublishConfig publishConfig, IPublishSessionCallback callback) { + if (!isPublisherAvailable()) { + notifyPublishFailure(callback, SessionCallback.FAILURE_NOT_AVAILABLE, "Not available"); + return; + } + // Check if the Role is already taken. + if (mRequesterRole == Role.SUBSCRIBER) { + notifyPublishFailure(callback, SessionCallback.FAILURE_NOT_AVAILABLE, + "Subscriber is running"); + return; + } + if (sessionCreationInProgress()) { + notifyPublishFailure(callback, SessionCallback.FAILURE_NOT_AVAILABLE, + "Publish session creation in progress"); + return; + } + // Check if maximum sessions reached + if (mUsdSessions.size() >= mUsdCapabilities.maxNumPublishSessions) { + notifyPublishFailure(callback, SessionCallback.FAILURE_MAX_SESSIONS_REACHED, + "Maximum number of publish sessions reached, num of sessions = " + + mUsdSessions.size()); + return; + } + // publish + if (mUsdNativeManager.publish(mInterfaceName, DEFAULT_COMMAND_ID, publishConfig)) { + createPublishSession(publishConfig, callback); + // Next: onUsdPublishStarted or onUsdPublishConfigFailed + } else { + notifyPublishFailure(callback, SessionCallback.FAILURE_NOT_AVAILABLE, "Failed"); + } + } + + private boolean sessionCreationInProgress() { + return mUsdSessions.contains(USD_TEMP_SESSION_ID); + } + + private void notifySubscribeFailure(ISubscribeSessionCallback callback, int reasonCode, + String reason) { + try { + Log.w(TAG, reason); + callback.onSubscribeFailed(reasonCode); + } catch (RemoteException e) { + Log.e(TAG, "subscribe: " + e); + } + } + + private void createPublishSession(PublishConfig config, IPublishSessionCallback callback) { + UsdSession usdSession = new UsdSession(config, callback); + // Use a temp session id. Will get updated in onPublisherStarted. + usdSession.setSessionId(USD_TEMP_SESSION_ID); + mUsdSessions.put(USD_TEMP_SESSION_ID, usdSession); + if (isSingleSession()) { + mRequesterRole = Role.PUBLISHER; + startCleaningUpExpiredSessions(); + } + } + + private void createSubscribeSession(SubscribeConfig config, + ISubscribeSessionCallback callback) { + UsdSession usdSession = new UsdSession(config, callback); + // Use a temp session id. Will get updated in onSubscriberStarted. + usdSession.setSessionId(USD_TEMP_SESSION_ID); + mUsdSessions.put(USD_TEMP_SESSION_ID, usdSession); + if (isSingleSession()) { + mRequesterRole = Role.SUBSCRIBER; + startCleaningUpExpiredSessions(); + } + } + + /** + * See {@link android.net.wifi.usd.UsdManager#subscribe(SubscribeConfig, Executor, + * SubscribeSessionCallback)} + */ + public void subscribe(SubscribeConfig subscribeConfig, ISubscribeSessionCallback callback) { + if (!isSubscriberAvailable()) { + notifySubscribeFailure(callback, SessionCallback.FAILURE_NOT_AVAILABLE, + "Not available"); + return; + } + // Check if the Role is already taken. + if (mRequesterRole == Role.PUBLISHER) { + notifySubscribeFailure(callback, SessionCallback.FAILURE_NOT_AVAILABLE, + "Publisher is running"); + return; + } + if (sessionCreationInProgress()) { + notifySubscribeFailure(callback, SessionCallback.FAILURE_NOT_AVAILABLE, + "Subscribe session creation in progress"); + return; + } + // Check if maximum sessions reached + if (mUsdSessions.size() >= mUsdCapabilities.maxNumSubscribeSessions) { + notifySubscribeFailure(callback, SessionCallback.FAILURE_MAX_SESSIONS_REACHED, + "Maximum number of subscribe sessions reached"); + return; + } + // subscribe + if (mUsdNativeManager.subscribe(mInterfaceName, DEFAULT_COMMAND_ID, subscribeConfig)) { + createSubscribeSession(subscribeConfig, callback); + // Next: onUsdSubscribeStarted or onUsdSubscribeConfigFailed + } else { + notifySubscribeFailure(callback, SessionCallback.FAILURE_NOT_AVAILABLE, "Failed"); + } + } + + + /** + * Register USD events from HAL. + */ + public void registerUsdEventsCallback(UsdNativeEventsCallback usdNativeEventsCallback) { + mUsdNativeManager.registerUsdEventsCallback(usdNativeEventsCallback); + } + + /** + * Implementation of USD callbacks. All callbacks are posted to Wi-Fi thread from + * SupplicantStaIfaceCallbackAidlImpl. + */ + public class UsdNativeEventsCallback implements UsdNativeManager.UsdEventsCallback { + @Override + public void onUsdPublishStarted(int cmdId, int publishId) { + if (cmdId != DEFAULT_COMMAND_ID) { + Log.e(TAG, "onUsdPublishStarted: Invalid command id = " + cmdId); + return; + } + UsdSession usdSession = mUsdSessions.get(USD_TEMP_SESSION_ID); + if (usdSession == null) { + Log.e(TAG, "onUsdPublishStarted: session does not exist. Publish Id = " + + publishId); + return; + } + if (usdSession.getRole() != Role.PUBLISHER) return; + mUsdSessions.put(publishId, usdSession); + usdSession.setSessionId(publishId); + mUsdSessions.remove(USD_TEMP_SESSION_ID); + try { + usdSession.mIPublishSessionCallback.onPublishStarted(publishId); + } catch (RemoteException e) { + Log.e(TAG, "onUsdPublishStarted " + e); + } + // Next: onUsdPublishReplied or onUsdPublishTerminated + } + + @Override + public void onUsdSubscribeStarted(int cmdId, int subscribeId) { + if (cmdId != DEFAULT_COMMAND_ID) { + Log.e(TAG, "onUsdSubscribeStarted: Invalid command id = " + cmdId); + return; + } + UsdSession usdSession = mUsdSessions.get(USD_TEMP_SESSION_ID); + if (usdSession == null) { + Log.e(TAG, "onUsdSubscribeStarted: session does not exist. Subscribe Id = " + + subscribeId); + return; + } + if (usdSession.getRole() != Role.SUBSCRIBER) return; + mUsdSessions.put(subscribeId, usdSession); + usdSession.setSessionId(subscribeId); + mUsdSessions.remove(USD_TEMP_SESSION_ID); + try { + usdSession.mISubscribeSessionCallback.onSubscribeStarted(subscribeId); + } catch (RemoteException e) { + Log.e(TAG, "onUsdSubscribeStarted " + e); + } + // Next: onUsdServiceDiscovered or onUsdSubscribeTerminated + } + + @Override + public void onUsdPublishConfigFailed(int cmdId, + @SessionCallback.FailureCode int errorCode) { + if (cmdId != DEFAULT_COMMAND_ID) { + Log.e(TAG, "onUsdPublishConfigFailed: Invalid command id = " + cmdId); + return; + } + UsdSession usdSession = mUsdSessions.get(USD_TEMP_SESSION_ID); + if (usdSession.getRole() != Role.PUBLISHER) return; + usdSession.sessionCleanup(); + try { + usdSession.mIPublishSessionCallback.onPublishFailed(errorCode); + } catch (RemoteException e) { + Log.e(TAG, "onUsdPublishConfigFailed " + e); + } + } + + @Override + public void onUsdSubscribeConfigFailed(int cmdId, + @SessionCallback.FailureCode int errorCode) { + if (cmdId != DEFAULT_COMMAND_ID) { + Log.e(TAG, "onUsdSubscribeConfigFailed: Invalid command id = " + cmdId); + return; + } + UsdSession usdSession = mUsdSessions.get(USD_TEMP_SESSION_ID); + if (usdSession.getRole() != Role.SUBSCRIBER) return; + usdSession.sessionCleanup(); + try { + usdSession.mISubscribeSessionCallback.onSubscribeFailed(errorCode); + } catch (RemoteException e) { + Log.e(TAG, "onUsdSubscribeConfigFailed " + e); + } + } + + @Override + public void onUsdPublishTerminated(int publishId, int reasonCode) { + if (!mUsdSessions.contains(publishId)) { + return; + } + UsdSession usdSession = mUsdSessions.get(publishId); + try { + usdSession.mIPublishSessionCallback.onPublishSessionTerminated(reasonCode); + } catch (RemoteException e) { + Log.e(TAG, "onUsdPublishTerminated " + e); + } + usdSession.sessionCleanup(); + } + + @Override + public void onUsdSubscribeTerminated(int subscribeId, int reasonCode) { + if (!mUsdSessions.contains(subscribeId)) { + return; + } + UsdSession usdSession = mUsdSessions.get(subscribeId); + try { + usdSession.mISubscribeSessionCallback.onSubscribeSessionTerminated(reasonCode); + } catch (RemoteException e) { + Log.e(TAG, "onUsdSubscribeTerminated " + e); + } + usdSession.sessionCleanup(); + } + + @Override + public void onUsdPublishReplied(UsdHalDiscoveryInfo info) { + // Check whether session matches. + if (!mUsdSessions.contains(info.ownId)) { + return; + } + // Check whether events are enabled for the publisher. + UsdSession usdSession = mUsdSessions.get(info.ownId); + if (!usdSession.mPublishConfig.isEventsEnabled()) return; + // Add the peer to the session if not already present. + UsdPeer peer = new UsdPeer(info.ownId, info.peerId, info.peerMacAddress); + usdSession.addPeerOnce(peer); + try { + // Pass unique peer hash to the application. When the application gives back the + // peer hash, it'll be used to retrieve the peer. + usdSession.mIPublishSessionCallback.onPublishReplied(usdSession.getPeerHash(peer), + info.serviceSpecificInfo, info.serviceProtoType, info.isFsdEnabled); + } catch (RemoteException e) { + Log.e(TAG, "onUsdPublishReplied " + e); + } + } + + @Override + public void onUsdServiceDiscovered(UsdHalDiscoveryInfo info) { + // Check whether session matches. + if (!mUsdSessions.contains(info.ownId)) { + return; + } + // Add the peer to the session if not already present. + UsdPeer peer = new UsdPeer(info.ownId, info.peerId, info.peerMacAddress); + UsdSession usdSession = mUsdSessions.get(info.ownId); + usdSession.addPeerOnce(peer); + try { + // Pass unique peer hash to the application. When the application gives back the + // peer hash, it'll be used to retrieve the peer. + usdSession.mISubscribeSessionCallback.onSubscribeDiscovered( + usdSession.getPeerHash(peer), info.serviceSpecificInfo, + info.serviceProtoType, info.isFsdEnabled); + } catch (RemoteException e) { + Log.e(TAG, "onUsdServiceDiscovered " + e); + } + } + + @Override + public void onUsdMessageReceived(int ownId, int peerId, MacAddress peerMacAddress, + byte[] message) { + // Check whether session matches. + if (!mUsdSessions.contains(ownId)) { + return; + } + // Add the peer to the session if not already present. + UsdPeer peer = new UsdPeer(ownId, peerId, peerMacAddress); + UsdSession usdSession = mUsdSessions.get(ownId); + usdSession.addPeerOnce(peer); + try { + // Pass unique peer hash to the application. When the application gives back the + // peer hash, it'll be used to retrieve the peer. + if (mRequesterRole == Role.SUBSCRIBER) { + usdSession.mISubscribeSessionCallback.onMessageReceived( + usdSession.getPeerHash(peer), message); + } else { + usdSession.mIPublishSessionCallback.onMessageReceived( + usdSession.getPeerHash(peer), message); + } + } catch (RemoteException e) { + Log.e(TAG, "onUsdMessageReceived " + e); + } + } + } + + /** + * Register for publisher status listener. + */ + public void registerPublisherStatusListener(IBooleanListener listener) { + // TODO: Implement the status listener (b/384504293) + } + + /** + * Unregister previously registered publisher status listener. + */ + public void unregisterPublisherStatusListener(IBooleanListener listener) { + // TODO: Implement the status listener (b/384504293) + } + + /** + * Register for subscriber status listener. + */ + public void registerSubscriberStatusListener(IBooleanListener listener) { + // TODO: Implement the status listener (b/384504293) + } + + + /** + * Unregister previously registered subscriber status listener. + */ + public void unregisterSubscriberStatusListener(IBooleanListener listener) { + // TODO: Implement the status listener (b/384504293) + } +} diff --git a/service/java/com/android/server/wifi/usd/UsdService.java b/service/java/com/android/server/wifi/usd/UsdService.java new file mode 100644 index 0000000000..6b3aeaa6d2 --- /dev/null +++ b/service/java/com/android/server/wifi/usd/UsdService.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2024 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.usd; + +import android.content.Context; +import android.net.wifi.WifiContext; +import android.net.wifi.util.Environment; +import android.util.Log; + +import androidx.annotation.NonNull; + +import com.android.server.SystemService; +import com.android.server.wifi.WifiInjector; + +/** + * Service implementing USD functionality. Delegates actual interface implementation to + * {@link UsdServiceImpl}. + */ +public class UsdService extends SystemService { + private static final String TAG = UsdService.class.getName(); + final UsdServiceImpl mUsdServiceImpl; + + public UsdService(@NonNull Context context) { + super(new WifiContext(context)); + mUsdServiceImpl = new UsdServiceImpl(getContext()); + } + + @Override + public void onStart() { + if (!Environment.isSdkAtLeastB()) { + return; + } + Log.i(TAG, "Registering " + Context.WIFI_USD_SERVICE); + publishBinderService(Context.WIFI_USD_SERVICE, mUsdServiceImpl); + } + + @Override + public void onBootPhase(int phase) { + if (!Environment.isSdkAtLeastB()) { + return; + } + if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { + WifiInjector wifiInjector = WifiInjector.getInstance(); + if (wifiInjector == null) { + Log.e(TAG, "onBootPhase(PHASE_SYSTEM_SERVICES_READY): NULL injector!"); + return; + } + mUsdServiceImpl.start(wifiInjector); + } else if (phase == SystemService.PHASE_BOOT_COMPLETED) { + mUsdServiceImpl.startLate(); + } + } +} diff --git a/service/java/com/android/server/wifi/usd/UsdServiceImpl.java b/service/java/com/android/server/wifi/usd/UsdServiceImpl.java new file mode 100644 index 0000000000..67c9ee3f63 --- /dev/null +++ b/service/java/com/android/server/wifi/usd/UsdServiceImpl.java @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2024 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.usd; + +import android.annotation.NonNull; +import android.content.Context; +import android.net.wifi.IBooleanListener; +import android.net.wifi.usd.Characteristics; +import android.net.wifi.usd.IPublishSessionCallback; +import android.net.wifi.usd.ISubscribeSessionCallback; +import android.net.wifi.usd.IUsdManager; +import android.net.wifi.usd.PublishConfig; +import android.net.wifi.usd.PublishSession; +import android.net.wifi.usd.PublishSessionCallback; +import android.net.wifi.usd.SubscribeConfig; +import android.net.wifi.usd.SubscribeSession; +import android.net.wifi.usd.SubscribeSessionCallback; +import android.net.wifi.usd.UsdManager; +import android.os.Binder; +import android.util.Log; + +import com.android.server.wifi.WifiInjector; +import com.android.server.wifi.WifiThreadRunner; +import com.android.server.wifi.util.WifiPermissionsUtil; + +import java.util.Objects; +import java.util.concurrent.Executor; +import java.util.function.Consumer; + +/** + * Implementation of the IUsdManager. + */ +public class UsdServiceImpl extends IUsdManager.Stub { + private static final String TAG = UsdServiceImpl.class.getName(); + private final Context mContext; + private WifiThreadRunner mWifiThreadRunner; + private WifiInjector mWifiInjector; + private WifiPermissionsUtil mWifiPermissionsUtil; + private UsdRequestManager mUsdRequestManager; + private UsdNativeManager mUsdNativeManager; + + /** + * Constructor + */ + public UsdServiceImpl(Context context) { + mContext = context; + } + + /** + * Start the service + */ + public void start(@NonNull WifiInjector wifiInjector) { + mWifiInjector = wifiInjector; + mWifiPermissionsUtil = mWifiInjector.getWifiPermissionsUtil(); + mUsdNativeManager = new UsdNativeManager(mWifiInjector.getWifiNative()); + mUsdRequestManager = new UsdRequestManager(mUsdNativeManager, + mWifiInjector.getWifiThreadRunner(), + mWifiInjector.getActiveModeWarden().getPrimaryClientModeManager() + .getInterfaceName(), + mWifiInjector.getClock(), mWifiInjector.getAlarmManager()); + mWifiThreadRunner = mWifiInjector.getWifiThreadRunner(); + Log.i(TAG, "start"); + } + + /** + * Start/initialize portions of the service which require the boot stage to be complete. + */ + public void startLate() { + Log.i(TAG, "startLate"); + } + + /** + * Proxy for the final native call of the parent class. Enables mocking of + * the function. + */ + public int getMockableCallingUid() { + return Binder.getCallingUid(); + } + + /** + * See {@link UsdManager#getCharacteristics()} + */ + @Override + public Characteristics getCharacteristics() { + int uid = getMockableCallingUid(); + if (!mWifiPermissionsUtil.checkManageWifiNetworkSelectionPermission(uid)) { + throw new SecurityException("App not allowed to use USD (uid = " + uid + ")"); + } + return mUsdRequestManager.getCharacteristics(); + } + + /** + * See {@link SubscribeSession#sendMessage(int, byte[], Executor, Consumer)} + */ + public void sendMessage(int sessionId, int peerId, @NonNull byte[] message, + @NonNull IBooleanListener listener) { + Objects.requireNonNull(message, "message must not be null"); + Objects.requireNonNull(listener, "listener must not be null"); + int uid = getMockableCallingUid(); + if (!mWifiPermissionsUtil.checkManageWifiNetworkSelectionPermission(uid)) { + throw new SecurityException("App not allowed to use USD (uid = " + uid + ")"); + } + Log.i(TAG, "sendMessage ( peerId = " + peerId + " , message length = " + message.length + + " )"); + mWifiThreadRunner.post(() -> mUsdRequestManager.sendMessage(sessionId, peerId, message, + listener)); + } + + /** + * See {@link SubscribeSession#cancel()} + */ + public void cancelSubscribe(int sessionId) { + int uid = getMockableCallingUid(); + if (!mWifiPermissionsUtil.checkManageWifiNetworkSelectionPermission(uid)) { + throw new SecurityException("App not allowed to use USD (uid = " + uid + ")"); + } + Log.i(TAG, "cancelSubscribe: ( sessionId = " + sessionId + " )"); + mWifiThreadRunner.post(() -> mUsdRequestManager.cancelSubscribe(sessionId)); + } + + /** + * See {@link PublishSession#cancel()} + */ + public void cancelPublish(int sessionId) { + int uid = getMockableCallingUid(); + if (!mWifiPermissionsUtil.checkManageWifiNetworkSelectionPermission(uid)) { + throw new SecurityException("App not allowed to use USD (uid = " + uid + ")"); + } + Log.i(TAG, "cancelPublish: ( sessionId = " + sessionId + " )"); + mWifiThreadRunner.post(() -> mUsdRequestManager.cancelPublish(sessionId)); + } + + /** + * See {@link PublishSession#updatePublish(byte[])} + */ + public void updatePublish(int sessionId, @NonNull byte[] ssi) { + Objects.requireNonNull(ssi, "Service specific info must not be null"); + int uid = getMockableCallingUid(); + if (!mWifiPermissionsUtil.checkManageWifiNetworkSelectionPermission(uid)) { + throw new SecurityException("App not allowed to use USD (uid = " + uid + ")"); + } + Log.i(TAG, "updatePublish: ( sessionId = " + sessionId + " )"); + mWifiThreadRunner.post(() -> mUsdRequestManager.updatePublish(sessionId, ssi)); + } + + /** + * See {@link UsdManager#publish(PublishConfig, Executor, PublishSessionCallback)} + */ + @Override + public void publish(PublishConfig publishConfig, IPublishSessionCallback callback) { + Objects.requireNonNull(publishConfig, "publishConfig must not be null"); + Objects.requireNonNull(callback, "callback must not be null"); + int uid = getMockableCallingUid(); + if (!mWifiPermissionsUtil.checkManageWifiNetworkSelectionPermission(uid)) { + throw new SecurityException("App not allowed to use USD (uid = " + uid + ")"); + } + // TODO: validate config + Log.i(TAG, "publish " + publishConfig); + mWifiThreadRunner.post(() -> mUsdRequestManager.publish(publishConfig, callback)); + } + + /** + * See {@link UsdManager#subscribe(SubscribeConfig, Executor, SubscribeSessionCallback)} + */ + @Override + public void subscribe(SubscribeConfig subscribeConfig, ISubscribeSessionCallback callback) { + Objects.requireNonNull(subscribeConfig, "subscribeConfig must not be null"); + Objects.requireNonNull(callback, "callback must not be null"); + int uid = getMockableCallingUid(); + if (!mWifiPermissionsUtil.checkManageWifiNetworkSelectionPermission(uid)) { + throw new SecurityException("App not allowed to use USD (uid = " + uid + ")"); + } + // TODO: validate config + Log.i(TAG, "subscribe " + subscribeConfig); + mWifiThreadRunner.post(() -> mUsdRequestManager.subscribe(subscribeConfig, callback)); + } + + /** + * See {@link UsdManager#registerPublisherStatusListener(Executor, Consumer)} + */ + public void registerPublisherStatusListener(@NonNull IBooleanListener listener) { + int uid = getMockableCallingUid(); + if (!mWifiPermissionsUtil.checkManageWifiNetworkSelectionPermission(uid)) { + throw new SecurityException("App not allowed to use USD (uid = " + uid + ")"); + } + mWifiThreadRunner.post(() -> mUsdRequestManager.registerPublisherStatusListener(listener)); + } + + /** + * See {@link UsdManager#unregisterPublisherStatusListener(Consumer)} + */ + public void unregisterPublisherStatusListener(@NonNull IBooleanListener listener) { + int uid = getMockableCallingUid(); + if (!mWifiPermissionsUtil.checkManageWifiNetworkSelectionPermission(uid)) { + throw new SecurityException("App not allowed to use USD (uid = " + uid + ")"); + } + mWifiThreadRunner.post( + () -> mUsdRequestManager.unregisterPublisherStatusListener(listener)); + } + + /** + * See {@link UsdManager#registerSubscriberStatusListener(Executor, Consumer)} + */ + public void registerSubscriberStatusListener(@NonNull IBooleanListener listener) { + int uid = getMockableCallingUid(); + if (!mWifiPermissionsUtil.checkManageWifiNetworkSelectionPermission(uid)) { + throw new SecurityException("App not allowed to use USD (uid = " + uid + ")"); + } + mWifiThreadRunner.post(() -> mUsdRequestManager.registerSubscriberStatusListener(listener)); + } + + /** + * See {@link UsdManager#unregisterSubscriberStatusListener(Consumer)} + */ + public void unregisterSubscriberStatusListener(@NonNull IBooleanListener listener) { + int uid = getMockableCallingUid(); + if (!mWifiPermissionsUtil.checkManageWifiNetworkSelectionPermission(uid)) { + throw new SecurityException("App not allowed to use USD (uid = " + uid + ")"); + } + mWifiThreadRunner.post( + () -> mUsdRequestManager.unregisterSubscriberStatusListener(listener)); + } +} diff --git a/service/java/com/android/server/wifi/util/ApConfigUtil.java b/service/java/com/android/server/wifi/util/ApConfigUtil.java index 3da0bf1586..c60a9f4f35 100644 --- a/service/java/com/android/server/wifi/util/ApConfigUtil.java +++ b/service/java/com/android/server/wifi/util/ApConfigUtil.java @@ -36,6 +36,8 @@ import static com.android.server.wifi.HalDeviceManager.HDM_CREATE_IFACE_STA; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.Context; +import android.content.res.Resources; import android.net.wifi.CoexUnsafeChannel; import android.net.wifi.ScanResult; import android.net.wifi.SoftApCapability; @@ -55,12 +57,16 @@ import android.util.Log; import android.util.SparseArray; import android.util.SparseIntArray; +import androidx.annotation.Keep; + import com.android.internal.annotations.VisibleForTesting; import com.android.modules.utils.build.SdkLevel; import com.android.server.wifi.SoftApManager; +import com.android.server.wifi.WifiInjector; import com.android.server.wifi.WifiNative; import com.android.server.wifi.WifiSettingsConfigStore; import com.android.server.wifi.coex.CoexManager; +import com.android.wifi.flags.Flags; import com.android.wifi.resources.R; import java.util.ArrayList; @@ -464,16 +470,20 @@ public class ApConfigUtil { * * @param band to get channels for * @param wifiNative reference used to get regulatory restrictions. - * @param resources used to get OEM restrictions + * @param resources used to get OEM restrictions. * @param inFrequencyMHz true to convert channel to frequency. * @return A list of frequencies that are allowed, null on error. + * TODO(b/380087289): Resources will be removed in the future together with the @keep annotation */ + @Keep public static List<Integer> getAvailableChannelFreqsForBand( - @BandType int band, WifiNative wifiNative, WifiResourceCache resources, + @BandType int band, WifiNative wifiNative, Resources resources, boolean inFrequencyMHz) { if (!isBandValid(band) || isMultiband(band)) { return null; } + WifiResourceCache resourceCache = WifiInjector.getInstance().getContext() + .getResourceCache(); int scannerBand = apConfig2wifiScannerBand(band); List<Integer> regulatoryList = null; @@ -488,7 +498,7 @@ public class ApConfigUtil { // HAL is not started, return null return null; } - regulatoryList = getHalAvailableChannelsForBand(scannerBand, wifiNative, resources, + regulatoryList = getHalAvailableChannelsForBand(scannerBand, wifiNative, resourceCache, inFrequencyMHz); if (regulatoryList == null) { // HAL API not supported by HAL, fallback to wificond @@ -496,10 +506,10 @@ public class ApConfigUtil { } } if (useWifiCond) { - regulatoryList = getWifiCondAvailableChannelsForBand(scannerBand, wifiNative, resources, - inFrequencyMHz); + regulatoryList = getWifiCondAvailableChannelsForBand(scannerBand, wifiNative, + resourceCache, inFrequencyMHz); } - List<Integer> configuredList = getConfiguredChannelList(resources, band); + List<Integer> configuredList = getConfiguredChannelList(resourceCache, band); if (configuredList == null || configuredList.isEmpty() || regulatoryList == null) { return regulatoryList; } @@ -894,32 +904,88 @@ public class ApConfigUtil { * IEEE80211BE & single link MLO in bridged mode from the resource file. * @param config The current {@link SoftApConfiguration}. * @param isBridgedMode true if bridged mode is enabled, false otherwise. + * @param maximumSupportedMLD maximum number of supported MLD on SoftAp. + * @param currentExistingMLD number of existing 11BE SoftApManager. + * @param isMLDApSupportMLO true if the chip reports the support multiple links + * on a single MLD AP. * * @return true if IEEE80211BE is allowed for the given configuration, false otherwise. */ public static boolean is11beAllowedForThisConfiguration(DeviceWiphyCapabilities capabilities, @NonNull WifiContext context, SoftApConfiguration config, - boolean isBridgedMode) { + boolean isBridgedMode, int maximumSupportedMLD, int currentExistingMLD, + boolean isMLDApSupportMLO) { if (!ApConfigUtil.isIeee80211beSupported(context)) { return false; } - if (capabilities == null || !capabilities.isWifiStandardSupported( - ScanResult.WIFI_STANDARD_11BE)) { + if (!isMLDApSupportMLO) { + // For non-MLO case, check capabilities + if (capabilities == null || !capabilities.isWifiStandardSupported( + ScanResult.WIFI_STANDARD_11BE)) { + return false; + } + } + if (Flags.mloSap()) { + if (!hasAvailableMLD(context, isBridgedMode, maximumSupportedMLD, + currentExistingMLD, isMLDApSupportMLO)) { + Log.i(TAG, "No available MLD, hence downgrading from 11be. currentExistingMLD = " + + currentExistingMLD + ", isMLDApSupportMLO = " + isMLDApSupportMLO); + return false; + } + } else { + if (isBridgedMode + && !context.getResourceCache().getBoolean( + R.bool.config_wifiSoftApSingleLinkMloInBridgedModeSupported)) { + return false; + } + } + if (is11beDisabledForSecurityType(config.getSecurityType())) { return false; } - if (isBridgedMode - && !context.getResourceCache().getBoolean( - R.bool.config_wifiSoftApSingleLinkMloInBridgedModeSupported)) { + return true; + } + + private static boolean hasAvailableMLD(@NonNull WifiContext context, + boolean isBridgedMode, int maximumSupportedMLD, int currentExistingMLD, + boolean isMLDApSupportMLO) { + int numberOfMLDStillAllowed = + maximumSupportedMLD - currentExistingMLD; + if (numberOfMLDStillAllowed < 1) { return false; } - if (is11beDisabledForSecurityType(config.getSecurityType())) { + if (isBridgedMode && !isMLDApSupportMLO && numberOfMLDStillAllowed < 2) { + // For non multilink MLO bridged mode, it requires two 11be instances. return false; } return true; } /** + * Returns maximum number of supported MLD on SoftAp. + * + * @param context The caller context used to get the OEM configuration from resource file. + * @param chipSupportsMultipleMld whether Chip supports multiple mld on SoftAp. + */ + public static int getMaximumSupportedMLD(@NonNull WifiContext context, + boolean chipSupportsMultipleMld) { + int numberOfMLDSupported = context.getResourceCache() + .getInteger(R.integer.config_wifiSoftApMaxNumberMLDSupported); + if (numberOfMLDSupported > 0) { + if (Flags.multipleMldOnSapSupported() && !chipSupportsMultipleMld) { + // Chip doesn't support multiple mld on SoftAp + return 1; + } + return numberOfMLDSupported; + } + if (context.getResourceCache().getBoolean( + R.bool.config_wifiSoftApSingleLinkMloInBridgedModeSupported)) { + return 2; + } + return 1; + } + + /** * Update AP band and channel based on the provided country code and band. * This will also set * @param wifiNative reference to WifiNative @@ -1044,7 +1110,14 @@ public class ApConfigUtil { * @return SoftApCapability which updated the feature support or not from resource. */ @NonNull - public static SoftApCapability updateCapabilityFromResource(@NonNull WifiContext context) { + @Keep + public static SoftApCapability updateCapabilityFromResource(@NonNull Context contextIn) { + WifiContext context; + if (contextIn instanceof WifiContext) { + context = (WifiContext) contextIn; + } else { + context = new WifiContext(contextIn); + } long features = 0; if (isAcsSupported(context)) { Log.d(TAG, "Update Softap capability, add acs feature support"); @@ -1246,7 +1319,14 @@ public class ApConfigUtil { * @param context the caller context used to get value from resource file. * @return true if supported, false otherwise. */ - public static boolean isWpa3SaeSupported(@NonNull WifiContext context) { + @Keep + public static boolean isWpa3SaeSupported(@NonNull Context contextIn) { + WifiContext context; + if (contextIn instanceof WifiContext) { + context = (WifiContext) contextIn; + } else { + context = new WifiContext(contextIn); + } return context.getResourceCache().getBoolean( R.bool.config_wifi_softap_sae_supported); } @@ -1280,9 +1360,15 @@ public class ApConfigUtil { * @param band the band soft AP to operate on. * @return true if supported, false otherwise. */ - public static boolean isSoftApBandSupported(@NonNull WifiContext context, + @Keep + public static boolean isSoftApBandSupported(@NonNull Context contextIn, @BandType int band) { - + WifiContext context; + if (contextIn instanceof WifiContext) { + context = (WifiContext) contextIn; + } else { + context = new WifiContext(contextIn); + } switch (band) { case SoftApConfiguration.BAND_2GHZ: return context.getResourceCache().getBoolean(R.bool.config_wifi24ghzSupport) @@ -1681,7 +1767,7 @@ public class ApConfigUtil { for (int band : SoftApConfiguration.BAND_TYPES) { if (isSoftApBandSupported(context, band)) { supportedChannelList = getAvailableChannelFreqsForBand( - band, wifiNative, context.getResourceCache(), false); + band, wifiNative, null, false); if (supportedChannelList != null) { newSoftApCapability.setSupportedChannelList( band, diff --git a/service/java/com/android/server/wifi/util/FeatureBitsetUtils.java b/service/java/com/android/server/wifi/util/FeatureBitsetUtils.java new file mode 100644 index 0000000000..73ad5c867a --- /dev/null +++ b/service/java/com/android/server/wifi/util/FeatureBitsetUtils.java @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2024 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.util; + +import android.net.wifi.WifiManager; +import android.util.SparseArray; + +import java.util.BitSet; + +/** + * Utilities for formatting the WifiManager.FEATURE_ BitSet as a String. + */ +public class FeatureBitsetUtils { + // All WifiManager.WIFI_FEATURE_ values should be added to the ALL_FEATURES map below + // to keep the feature logging up to date. + protected static final SparseArray ALL_FEATURES = new SparseArray() { + { + append(WifiManager.WIFI_FEATURE_INFRA, "WIFI_FEATURE_INFRA"); + append(WifiManager.WIFI_FEATURE_PASSPOINT, "WIFI_FEATURE_PASSPOINT"); + append(WifiManager.WIFI_FEATURE_P2P, "WIFI_FEATURE_P2P"); + append(WifiManager.WIFI_FEATURE_MOBILE_HOTSPOT, "WIFI_FEATURE_MOBILE_HOTSPOT"); + append(WifiManager.WIFI_FEATURE_SCANNER, "WIFI_FEATURE_SCANNER"); + append(WifiManager.WIFI_FEATURE_AWARE, "WIFI_FEATURE_AWARE"); + append(WifiManager.WIFI_FEATURE_D2D_RTT, "WIFI_FEATURE_D2D_RTT"); + append(WifiManager.WIFI_FEATURE_D2AP_RTT, "WIFI_FEATURE_D2AP_RTT"); + append(WifiManager.WIFI_FEATURE_PNO, "WIFI_FEATURE_PNO"); + append(WifiManager.WIFI_FEATURE_TDLS, "WIFI_FEATURE_TDLS"); + append(WifiManager.WIFI_FEATURE_TDLS_OFFCHANNEL, "WIFI_FEATURE_TDLS_OFFCHANNEL"); + append(WifiManager.WIFI_FEATURE_AP_STA, "WIFI_FEATURE_AP_STA"); + append(WifiManager.WIFI_FEATURE_LINK_LAYER_STATS, "WIFI_FEATURE_LINK_LAYER_STATS"); + append(WifiManager.WIFI_FEATURE_LOGGER, "WIFI_FEATURE_LOGGER"); + append(WifiManager.WIFI_FEATURE_RSSI_MONITOR, "WIFI_FEATURE_RSSI_MONITOR"); + append(WifiManager.WIFI_FEATURE_MKEEP_ALIVE, "WIFI_FEATURE_MKEEP_ALIVE"); + append(WifiManager.WIFI_FEATURE_CONFIG_NDO, "WIFI_FEATURE_CONFIG_NDO"); + append(WifiManager.WIFI_FEATURE_CONTROL_ROAMING, "WIFI_FEATURE_CONTROL_ROAMING"); + append(WifiManager.WIFI_FEATURE_IE_WHITELIST, "WIFI_FEATURE_IE_WHITELIST"); + append(WifiManager.WIFI_FEATURE_SCAN_RAND, "WIFI_FEATURE_SCAN_RAND"); + append(WifiManager.WIFI_FEATURE_TX_POWER_LIMIT, "WIFI_FEATURE_TX_POWER_LIMIT"); + append(WifiManager.WIFI_FEATURE_WPA3_SAE, "WIFI_FEATURE_WPA3_SAE"); + append(WifiManager.WIFI_FEATURE_WPA3_SUITE_B, "WIFI_FEATURE_WPA3_SUITE_B"); + append(WifiManager.WIFI_FEATURE_OWE, "WIFI_FEATURE_OWE"); + append(WifiManager.WIFI_FEATURE_LOW_LATENCY, "WIFI_FEATURE_LOW_LATENCY"); + append(WifiManager.WIFI_FEATURE_DPP, "WIFI_FEATURE_DPP"); + append(WifiManager.WIFI_FEATURE_P2P_RAND_MAC, "WIFI_FEATURE_P2P_RAND_MAC"); + append(WifiManager.WIFI_FEATURE_CONNECTED_RAND_MAC, "WIFI_FEATURE_CONNECTED_RAND_MAC"); + append(WifiManager.WIFI_FEATURE_AP_RAND_MAC, "WIFI_FEATURE_AP_RAND_MAC"); + append(WifiManager.WIFI_FEATURE_MBO, "WIFI_FEATURE_MBO"); + append(WifiManager.WIFI_FEATURE_OCE, "WIFI_FEATURE_OCE"); + append(WifiManager.WIFI_FEATURE_WAPI, "WIFI_FEATURE_WAPI"); + append(WifiManager.WIFI_FEATURE_FILS_SHA256, "WIFI_FEATURE_FILS_SHA256"); + append(WifiManager.WIFI_FEATURE_FILS_SHA384, "WIFI_FEATURE_FILS_SHA384"); + append(WifiManager.WIFI_FEATURE_SAE_PK, "WIFI_FEATURE_SAE_PK"); + append(WifiManager.WIFI_FEATURE_STA_BRIDGED_AP, "WIFI_FEATURE_STA_BRIDGED_AP"); + append(WifiManager.WIFI_FEATURE_BRIDGED_AP, "WIFI_FEATURE_BRIDGED_AP"); + append(WifiManager.WIFI_FEATURE_INFRA_60G, "WIFI_FEATURE_INFRA_60G"); + append(WifiManager.WIFI_FEATURE_ADDITIONAL_STA_LOCAL_ONLY, + "WIFI_FEATURE_ADDITIONAL_STA_LOCAL_ONLY"); + append(WifiManager.WIFI_FEATURE_ADDITIONAL_STA_MBB, "WIFI_FEATURE_ADDITIONAL_STA_MBB"); + append(WifiManager.WIFI_FEATURE_ADDITIONAL_STA_RESTRICTED, + "WIFI_FEATURE_ADDITIONAL_STA_RESTRICTED"); + append(WifiManager.WIFI_FEATURE_DPP_ENROLLEE_RESPONDER, + "WIFI_FEATURE_DPP_ENROLLEE_RESPONDER"); + append(WifiManager.WIFI_FEATURE_PASSPOINT_TERMS_AND_CONDITIONS, + "WIFI_FEATURE_PASSPOINT_TERMS_AND_CONDITIONS"); + append(WifiManager.WIFI_FEATURE_SAE_H2E, "WIFI_FEATURE_SAE_H2E"); + append(WifiManager.WIFI_FEATURE_WFD_R2, "WIFI_FEATURE_WFD_R2"); + append(WifiManager.WIFI_FEATURE_DECORATED_IDENTITY, "WIFI_FEATURE_DECORATED_IDENTITY"); + append(WifiManager.WIFI_FEATURE_TRUST_ON_FIRST_USE, "WIFI_FEATURE_TRUST_ON_FIRST_USE"); + append(WifiManager.WIFI_FEATURE_ADDITIONAL_STA_MULTI_INTERNET, + "WIFI_FEATURE_ADDITIONAL_STA_MULTI_INTERNET"); + append(WifiManager.WIFI_FEATURE_DPP_AKM, "WIFI_FEATURE_DPP_AKM"); + append(WifiManager.WIFI_FEATURE_SET_TLS_MINIMUM_VERSION, + "WIFI_FEATURE_SET_TLS_MINIMUM_VERSION"); + append(WifiManager.WIFI_FEATURE_TLS_V1_3, "WIFI_FEATURE_TLS_V1_3"); + append(WifiManager.WIFI_FEATURE_DUAL_BAND_SIMULTANEOUS, + "WIFI_FEATURE_DUAL_BAND_SIMULTANEOUS"); + append(WifiManager.WIFI_FEATURE_T2LM_NEGOTIATION, "WIFI_FEATURE_T2LM_NEGOTIATION"); + append(WifiManager.WIFI_FEATURE_WEP, "WIFI_FEATURE_WEP"); + append(WifiManager.WIFI_FEATURE_WPA_PERSONAL, "WIFI_FEATURE_WPA_PERSONAL"); + append(WifiManager.WIFI_FEATURE_AGGRESSIVE_ROAMING_MODE_SUPPORT, + "WIFI_FEATURE_AGGRESSIVE_ROAMING_MODE_SUPPORT"); + append(WifiManager.WIFI_FEATURE_D2D_WHEN_INFRA_STA_DISABLED, + "WIFI_FEATURE_D2D_WHEN_INFRA_STA_DISABLED"); + append(WifiManager.WIFI_FEATURE_SOFTAP_MLO, "WIFI_FEATURE_SOFTAP_MLO"); + append(WifiManager.WIFI_FEATURE_MULTIPLE_MLD_ON_SAP, + "WIFI_FEATURE_MULTIPLE_MLD_ON_SAP"); + } + }; + + // Index of the newest available feature. This will be calculated automatically + // in the static block below. + protected static final int NEWEST_FEATURE_INDEX; + static { + int newestFeatureIndex = 0; + for (int i = 0; i < ALL_FEATURES.size(); i++) { + newestFeatureIndex = Math.max(ALL_FEATURES.keyAt(i), newestFeatureIndex); + } + NEWEST_FEATURE_INDEX = newestFeatureIndex; + } + + /** + * Format a BitSet of WifiManager.WIFI_FEATURE_ features as a String. + */ + public static String formatSupportedFeatures(BitSet supportedFeatures) { + if (supportedFeatures == null || supportedFeatures.isEmpty()) return "[]"; + StringBuilder formatted = new StringBuilder("["); + for (int i = 0; i < ALL_FEATURES.size(); i++) { + int capabilityIndex = ALL_FEATURES.keyAt(i); + if (supportedFeatures.get(capabilityIndex)) { + String capabilityName = (String) ALL_FEATURES.valueAt(i); + formatted.append(capabilityName); + formatted.append(", "); + } + } + + // Include a warning if an unrecognized feature is supported. It may have been added + // to WifiManager without updating this file. + if (supportedFeatures.length() > NEWEST_FEATURE_INDEX + 1) { + formatted.append("+ UNRECOGNIZED FEATURE(S)"); + } else { + // Otherwise, trim the last 2 characters (", ") from the string + formatted.setLength(formatted.length() - 2); + } + formatted.append("]"); + return formatted.toString(); + } +} diff --git a/service/java/com/android/server/wifi/util/InformationElementUtil.java b/service/java/com/android/server/wifi/util/InformationElementUtil.java index ea499578df..37a07a5ca8 100644 --- a/service/java/com/android/server/wifi/util/InformationElementUtil.java +++ b/service/java/com/android/server/wifi/util/InformationElementUtil.java @@ -1870,6 +1870,8 @@ public class InformationElementUtil { private static final int RSN_AKM_PSK = 0x02ac0f00; private static final int RSN_AKM_FT_EAP = 0x03ac0f00; private static final int RSN_AKM_FT_PSK = 0x04ac0f00; + private static final int RSN_AKM_FT_PSK_SHA384 = 0x13ac0f00; + private static final int RSN_AKM_EAP_FT_SHA384 = 0x0dac0f00; private static final int RSN_AKM_EAP_SHA256 = 0x05ac0f00; private static final int RSN_AKM_PSK_SHA256 = 0x06ac0f00; private static final int RSN_AKM_SAE = 0x08ac0f00; @@ -1882,6 +1884,7 @@ public class InformationElementUtil { private static final int RSN_AKM_SAE_EXT_KEY = 0x18ac0f00; private static final int RSN_AKM_FT_SAE_EXT_KEY = 0x19ac0f00; private static final int RSN_AKM_DPP = 0x029a6f50; + private static final int RSN_AKM_PASN = 0x15ac0f00; private static final int WPA_CIPHER_NONE = 0x00f25000; private static final int WPA_CIPHER_TKIP = 0x02f25000; @@ -2024,6 +2027,15 @@ public class InformationElementUtil { case RSN_AKM_DPP: rsnKeyManagement.add(ScanResult.KEY_MGMT_DPP); break; + case RSN_AKM_FT_PSK_SHA384: + rsnKeyManagement.add(ScanResult.KEY_MGMT_FT_PSK_SHA384); + break; + case RSN_AKM_EAP_FT_SHA384: + rsnKeyManagement.add(ScanResult.KEY_MGMT_EAP_FT_SHA384); + break; + case RSN_AKM_PASN: + rsnKeyManagement.add(ScanResult.KEY_MGMT_PASN); + break; default: { int akmScheme = getScanResultAkmSchemeOfUnknownAkmIfConfigured( @@ -2380,6 +2392,12 @@ public class InformationElementUtil { return ScanResult.KEY_MGMT_FILS_SHA384; case RSN_AKM_DPP: return ScanResult.KEY_MGMT_DPP; + case RSN_AKM_FT_PSK_SHA384: + return ScanResult.KEY_MGMT_FT_PSK_SHA384; + case RSN_AKM_EAP_FT_SHA384: + return ScanResult.KEY_MGMT_EAP_FT_SHA384; + case RSN_AKM_PASN: + return ScanResult.KEY_MGMT_PASN; default: return ScanResult.KEY_MGMT_UNKNOWN; } @@ -2455,6 +2473,12 @@ public class InformationElementUtil { return "EAP-FILS-SHA384"; case ScanResult.KEY_MGMT_DPP: return "DPP"; + case ScanResult.KEY_MGMT_FT_PSK_SHA384: + return "FT/PSK-SHA384"; + case ScanResult.KEY_MGMT_EAP_FT_SHA384: + return "EAP-FT-SHA384"; + case ScanResult.KEY_MGMT_PASN: + return "PASN"; default: return "?"; } @@ -2853,4 +2877,49 @@ public class InformationElementUtil { return mCountryCode; } } + + /* + * RSNXE (Robust Security Network Extended) Field + * + * RSNXE is a field within Wi-Fi beacon frames that provides extra information about the + * access point's security capabilities, going beyond the basics of RSN (Robust Security + * Network). + */ + public static class Rsnxe { + private static final int SECURE_HE_LTF_SUPPORT_BIT = 8; + private static final int URNM_MFPR_BIT = 15; + private boolean mIsSecureHeLtfSupported; + private boolean mIsRangingFrameProtectionRequired; + + /** + * Parse RSN extension element + * @param ie Information element + */ + public void from(InformationElement ie) { + if (ie == null || ie.id != InformationElement.EID_RSN_EXTENSION) return; + BitSet rsnxBitset = BitSet.valueOf( + ByteBuffer.wrap(ie.bytes).order(ByteOrder.LITTLE_ENDIAN)); + mIsSecureHeLtfSupported = rsnxBitset.get(SECURE_HE_LTF_SUPPORT_BIT); + mIsRangingFrameProtectionRequired = rsnxBitset.get(URNM_MFPR_BIT); + } + + /** + * The secure HE-LTF is a security enhancement for ranging measurements where the HE-LTF + * sequence is randomized using cryptographic keys derived from the security association. + * + * @return Whether the secure HE-LTF is supported or not. + */ + public boolean isSecureHeLtfSupported() { + return mIsSecureHeLtfSupported; + } + + /** + * A security policy that specifies whether ranging frames are required to be protected + * without association. + * @return Whether ranging frames are required to be protected or not. + */ + public boolean isRangingFrameProtectionRequired() { + return mIsRangingFrameProtectionRequired; + } + } } diff --git a/service/java/com/android/server/wifi/util/XmlUtil.java b/service/java/com/android/server/wifi/util/XmlUtil.java index 0f70340d84..3c07365957 100644 --- a/service/java/com/android/server/wifi/util/XmlUtil.java +++ b/service/java/com/android/server/wifi/util/XmlUtil.java @@ -20,6 +20,7 @@ import static com.android.wifi.flags.Flags.softapConfigStoreMaxChannelWidth; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.app.compat.CompatChanges; import android.net.InetAddresses; import android.net.IpConfiguration; @@ -41,6 +42,7 @@ import android.net.wifi.WifiEnterpriseConfig; import android.net.wifi.WifiManager; import android.net.wifi.WifiMigration; import android.net.wifi.WifiSsid; +import android.net.wifi.util.Environment; import android.os.ParcelUuid; import android.os.PersistableBundle; import android.text.TextUtils; @@ -49,6 +51,7 @@ import android.util.Pair; import android.util.SparseIntArray; import com.android.modules.utils.build.SdkLevel; +import com.android.wifi.flags.Flags; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -1975,6 +1978,7 @@ public class XmlUtil { public static final String XML_TAG_PERSISTENT_RANDOMIZED_MAC_ADDRESS = "PersistentRandomizedMacAddress"; public static final String XML_TAG_MAX_CHANNEL_WIDTH = "MaxChannelWidth"; + public static final String XML_TAG_CLIENT_ISOLATION = "ClientIsolation"; /** @@ -2129,6 +2133,7 @@ public class XmlUtil { * @param out XmlSerializer instance pointing to the XML stream. * @param softApConfig configuration of the Soft AP. */ + @SuppressLint("NewApi") public static void writeSoftApConfigurationToXml(@NonNull XmlSerializer out, @NonNull SoftApConfiguration softApConfig, WifiConfigStoreEncryptionUtil encryptionUtil) @@ -2206,6 +2211,10 @@ public class XmlUtil { if (SdkLevel.isAtLeastV()) { writeVendorDataListToXml(out, softApConfig.getVendorData()); } + if (Flags.apIsolate() && Environment.isSdkAtLeastB()) { + XmlUtil.writeNextValue(out, XML_TAG_CLIENT_ISOLATION, + softApConfig.isClientIsolationEnabled()); + } } // End of writeSoftApConfigurationToXml /** @@ -2215,6 +2224,7 @@ public class XmlUtil { * @param outerTagDepth depth of the outer tag in the XML document. * @param settingsMigrationDataHolder the class instance of SettingsMigrationDataHolder */ + @SuppressLint("NewApi") @Nullable public static SoftApConfiguration parseFromXml(XmlPullParser in, int outerTagDepth, SettingsMigrationDataHolder settingsMigrationDataHolder, @@ -2361,6 +2371,11 @@ public class XmlUtil { softApConfigBuilder.setMaxChannelBandwidth((int) value); } break; + case XML_TAG_CLIENT_ISOLATION: + if (Flags.apIsolate() && Environment.isSdkAtLeastB()) { + softApConfigBuilder.setClientIsolationEnabled((boolean) value); + } + break; default: Log.w(TAG, "Ignoring unknown value name " + valueName[0]); break; diff --git a/service/proto/src/metrics.proto b/service/proto/src/metrics.proto index a27864fb87..225cad81fb 100644 --- a/service/proto/src/metrics.proto +++ b/service/proto/src/metrics.proto @@ -774,6 +774,12 @@ message WifiLog { // and telephony. // Bucket value is capped to WifiMetrics.MAX_COUNTRY_CODE_COUNT. repeated Int32Count country_code_scan_histogram = 219; + + // Each WifiUsabilityStatsTraining instance contains a list of WifiUsabilityStatsEntry objects, + // representing a time series of WiFi usability statistics recorded within a specific data + // capture period. It also includes information about the type of data capture and the duration + // of the capture period. + repeated WifiUsabilityStatsTraining wifi_usability_stats_training = 220; } // Information that gets logged for every WiFi connection. @@ -1466,6 +1472,15 @@ message StaEvent { // Wi-Fi 7 support for this network has been enabled or disabled. DISCONNECT_NETWORK_WIFI7_TOGGLED = 17; + + // IP configuration is lost. Triggered by connectivity + DISCONNECT_IP_CONFIGURATION_LOST = 18; + + // IP reachability is lost. Triggered by connectivity + DISCONNECT_IP_REACHABILITY_LOST = 19; + + // No credentials + DISCONNECT_NO_CREDENTIALS = 20; } // Authentication Failure reasons as reported through the API. @@ -2520,6 +2535,8 @@ message WifiLinkLayerUsageStats { repeated RadioStats radio_stats = 11; } +// WifiUsabilityStatsEntry will only be uploaded when its timestamp is within the data capture +// period message WifiUsabilityStatsEntry { // Status codes for link probe status enum LinkProbeStatus { @@ -2563,6 +2580,32 @@ message WifiUsabilityStatsEntry { NETWORK_TYPE_NR = 7; } + enum CaptureEventType { + // Unknown event + CAPTURE_EVENT_TYPE_UNKNOWN = 0; + + // This is a synchronous event + CAPTURE_EVENT_TYPE_SYNCHRONOUS = 1; + + // RSSI polling turned on + CAPTURE_EVENT_TYPE_RSSI_POLLING_ENABLED = 2; + + // RSSI polling turned off + CAPTURE_EVENT_TYPE_RSSI_POLLING_DISABLED = 3; + + // Data sample came from CMD_ONESHOT_RSSI_POLL + CAPTURE_EVENT_TYPE_ONESHOT_RSSI_POLL = 4; + + // IP reachability lost + CAPTURE_EVENT_TYPE_IP_REACHABILITY_LOST = 5; + + // IP reachability failure + CAPTURE_EVENT_TYPE_IP_REACHABILITY_FAILURE = 6; + + // Firmware alert + CAPTURE_EVENT_TYPE_FIRMWARE_ALERT = 7; + } + // Absolute milliseconds from device boot when these stats were sampled optional int64 time_stamp_ms = 1; @@ -2709,6 +2752,79 @@ message WifiUsabilityStatsEntry { // Link layer radio stats for all the radios repeated RadioStats radio_stats = 44; + + // Number of WiFi links. + optional int32 wifi_link_count = 45; + + // Link layer radio stats for all WiFi links + repeated LinkStats link_stats = 46; + + // The MLO mode + // See details in @WifiManager.MloMode + optional int32 mlo_mode = 47; + + // The number of tx bytes transmitted on current interface + optional int64 tx_transmitted_bytes = 48; + + // The number of rx bytes transmitted on current interface + optional int64 rx_transmitted_bytes = 49; + + // The total number of LABEL_BAD event happens + optional int32 label_bad_event_count = 50; + + // Current WiFi state in framework + optional int32 wifi_framework_state = 51; + + // Downstream throughput estimation provided by Network Capabilities + optional int32 is_network_capabilities_downstream_sufficient = 52; + + // Upstream throughput estimation provided by Network Capabilities + optional int32 is_network_capabilities_upstream_sufficient = 53; + + // Downstream throughput estimation used in Network Selection + optional int32 is_throughput_predictor_downstream_sufficient = 54; + + // Upstream throughput estimation provided by Network Capabilities + optional int32 is_throughput_predictor_upstream_sufficient = 55; + + // If bluetooth is connected + optional bool is_bluetooth_connected = 56; + + // State of UWB adapter. Refers to + // UwbManager#registerAdapterStateCallback(Executor, AdapterStateCallback) + optional int32 uwb_adapter_state = 57; + + // Wifi Low Latency mode state + optional bool is_low_latency_activated = 58; + + // Maximum supported tx link speed in Mbps + optional int32 max_supported_tx_linkspeed = 59; + + // Maximum supported rx link speed in Mbps + optional int32 max_supported_rx_linkspeed = 60; + + // Wifi Voip mode state + optional int32 voip_mode = 61; + + // Device Role of thread. Refers to ThreadNetworkController@DeviceRole + optional int32 thread_device_role = 62; + + // Capture buffer event type + optional CaptureEventType capture_event_type = 63; + + // Some capture event types (eg. ip reachability lost) have a code associated + // with them. This code is stored here. + optional int32 capture_event_type_subcode = 64; + + optional int32 status_data_stall = 65; + + // If the full data capture is being stored (field isFullCapture is true in the function call + // WifiManager.storeCapturedData), this field will represent the time offset between this sample + // and the first sample in the capture buffer. i.e. this field will have the value 0 for the + // first sample in the capture buffer. + // If isFullCapture is false, then this field will be the time offset between the capture start + // time and the timestamp of this sample. + optional int64 timestamp_offset_ms = 66; } message ContentionTimeStats { @@ -2742,6 +2858,37 @@ message ContentionTimeStats { optional int64 contention_num_samples = 5; } +message PacketStats { + enum AccessCategory { + // WME Best Effort Access Category + WME_ACCESS_CATEGORY_BE = 0; + + // WME Background Access Category + WME_ACCESS_CATEGORY_BK = 1; + + // WME Video Access Category + WME_ACCESS_CATEGORY_VI = 2; + + // WME Voice Access Category + WME_ACCESS_CATEGORY_VO = 3; + } + + // WME access category + optional AccessCategory access_category = 1; + + // The number of tx success counted from the last radio chip reset + optional int64 tx_success = 2; + + // The number of MPDU data packet retries counted from the last radio chip reset + optional int64 tx_retries = 3; + + // The number of tx bad counted from the last radio chip reset + optional int64 tx_bad = 4; + + // The number of rx success counted from the last radio chip reset + optional int64 rx_success = 5; +} + message RateStats { enum WifiPreambleType { // Preamble type for IEEE 802.11a/g, IEEE Std 802.11-2020, Section 17 @@ -2786,6 +2933,8 @@ message RateStats { WIFI_BANDWIDTH_5_MHZ = 5; // Channel bandwidth: 10MHz WIFI_BANDWIDTH_10_MHZ = 6; + // Channel bandwidth: 320MHz + WIFI_BANDWIDTH_320_MHZ = 7; // Invalid channel bandwidth WIFI_BANDWIDTH_INVALID = -1; } @@ -2849,6 +2998,123 @@ message RadioStats { // The total time spent on hotspot2.0 scans and GAS exchange in ms counted from the last radio // chip reset optional int64 total_hotspot_2_scan_time_ms = 10; + + // Time for which the radio is in active tranmission per tx level + repeated int32 tx_time_ms_per_level = 11; +} + +message LinkStats { + enum LinkState { + // Chip does not support reporting the state of the link + LINK_STATE_UNKNOWN = 0; + + // Link has not been in use since last report. It is placed in power save + LINK_STATE_NOT_IN_USE = 1; + + // Link is in use. In presence of traffic, it is set to be power active. + LINK_STATE_IN_USE = 2; + } + + enum WifiChannelBandwidth { + // Channel bandwidth: 20MHz + WIFI_BANDWIDTH_20_MHZ = 0; + // Channel bandwidth: 40MHz + WIFI_BANDWIDTH_40_MHZ = 1; + // Channel bandwidth: 80MHz + WIFI_BANDWIDTH_80_MHZ = 2; + // Channel bandwidth: 160MHz + WIFI_BANDWIDTH_160_MHZ = 3; + // Channel bandwidth: 80MHz + 80MHz + WIFI_BANDWIDTH_80P80_MHZ = 4; + // Channel bandwidth: 5MHz + WIFI_BANDWIDTH_5_MHZ = 5; + // Channel bandwidth: 10MHz + WIFI_BANDWIDTH_10_MHZ = 6; + // Channel bandwidth: 320MHz + WIFI_BANDWIDTH_320_MHZ = 7; + // Invalid channel bandwidth + WIFI_BANDWIDTH_INVALID = -1; + } + + // The Link ID + optional int32 link_id = 1; + + // Link state + optional LinkState state = 2; + + // Identifier of the radio on which link is currently operating + optional int32 radio_id = 3; + + // Frequency of the link in MHz + optional int32 frequency_mhz = 4; + + // Number of beacons received from our own AP + optional int32 beacon_rx = 5; + + // RSSI of management frames + optional int32 rssi_mgmt = 6; + + // Duty cycle of the link. + optional int32 time_slice_duty_cycle_in_percent = 7; + + // Overall RSSI from wpa_supplicant signal_poll + optional int32 rssi = 8; + + // channel width of WiFi link. + optional WifiChannelBandwidth channel_width = 9; + + // Center frequency (MHz) of first segment. + optional int32 center_freq_first_seg = 10; + + // Center frequency (MHz) of second segment. + optional int32 center_freq_second_seg = 11; + + // Total time for which the radio is awake on this channel. + optional int64 on_time_in_ms = 12; + + // Total time for which CCA is held busy on this channel. + optional int64 cca_busy_time_in_ms = 13; + + // WME data packet contention time statistics for all four categories: BE, BK, VI, VO + repeated ContentionTimeStats contention_time_stats = 14; + + // Packet statistics for all four categories: BE, BK, VI, VO + repeated PacketStats packet_stats = 15; + + // Peer statistics on this WiFi link + repeated PeerInfo peer_info = 16; + + // List of scan results who have the same freq with current WiFi link + repeated ScanResultWithSameFreq scan_result_with_same_freq = 17; + + // TX linkspeed in this WiFi link + optional int32 tx_linkspeed = 18; + + // RX linkspeed in this WiFi link + optional int32 rx_linkspeed = 19; +} + +message PeerInfo { + // Station count. + optional int32 sta_count = 1; + + // Channel utilization. + optional int32 chan_util = 2; + + // Rate statistics, including number of successful packets, retries, etc., + // indexed by preamble, bandwidth, number of spatial streams, MCS. + repeated RateStats rate_stats = 3; +} + +message ScanResultWithSameFreq { + // timestamp in microseconds (since boot) when this result was last seen. + optional int64 scan_result_timestamp_micros = 1; + + // The detected signal level in dBm + optional int32 rssi = 2; + + // The center frequency of the primary 20 MHz frequency (in MHz) of the channel + optional int32 frequency_mhz = 3; } message WifiUsabilityStats { @@ -2900,6 +3166,28 @@ message WifiUsabilityStats { optional int64 time_stamp_ms = 5; } +message TrainingData { + // The list of timestamped wifi usability stats + repeated WifiUsabilityStatsEntry stats = 1; +} + +message WifiUsabilityStatsTraining { + // Data capture type + optional int32 data_capture_type = 1; + + // Capture period start timestamp floored to the nearest hour. + optional int64 capture_start_timestamp_secs = 2; + + optional TrainingData training_data = 3; + + // If isFullCapture is true in the WifiManager.storeCaptureData call, this represents the time + // offset between the last sample in the capture buffer and the time the capture buffer was + // stored. If ring buffer is empty (no last sample), we set store_time_offset_ms to 0. + // If isFullCapture is false, this represents the time between 'capture period stop time' and the + // time the capture buffer was stored. + optional int64 store_time_offset_ms = 4; +} + message DeviceMobilityStatePnoScanStats { // see WifiManager.DEVICE_MOBILITY_STATE_* constants enum DeviceMobilityState { diff --git a/service/tests/wifitests/Android.bp b/service/tests/wifitests/Android.bp index 98974c26c7..f0f9102a76 100644 --- a/service/tests/wifitests/Android.bp +++ b/service/tests/wifitests/Android.bp @@ -503,6 +503,9 @@ android_test { "com.android.server.wifi.WakeupOnboarding", "com.android.server.wifi.WakeupOnboarding$*", "com.android.server.wifi.WakeupOnboarding.**", + "com.android.server.wifi.WepNetworkUsageController", + "com.android.server.wifi.WepNetworkUsageController$*", + "com.android.server.wifi.WepNetworkUsageController.**", "com.android.server.wifi.WifiApConfigStore", "com.android.server.wifi.WifiApConfigStore$*", "com.android.server.wifi.WifiApConfigStore.**", @@ -1085,6 +1088,12 @@ android_test { "com.android.server.wifi.hotspot2.soap.command.SppCommand", "com.android.server.wifi.hotspot2.soap.command.SppCommand$*", "com.android.server.wifi.hotspot2.soap.command.SppCommand.**", + "com.android.server.wifi.mainline_supplicant.MainlineSupplicant", + "com.android.server.wifi.mainline_supplicant.MainlineSupplicant$*", + "com.android.server.wifi.mainline_supplicant.MainlineSupplicant.**", + "com.android.server.wifi.mainline_supplicant.ServiceManagerWrapper", + "com.android.server.wifi.mainline_supplicant.ServiceManagerWrapper$*", + "com.android.server.wifi.mainline_supplicant.ServiceManagerWrapper.**", "com.android.server.wifi.mockwifi.MockSupplicantManager", "com.android.server.wifi.mockwifi.MockSupplicantManager$*", "com.android.server.wifi.mockwifi.MockSupplicantManager.**", @@ -1094,6 +1103,18 @@ android_test { "com.android.server.wifi.mockwifi.MockWifiServiceUtil", "com.android.server.wifi.mockwifi.MockWifiServiceUtil$*", "com.android.server.wifi.mockwifi.MockWifiServiceUtil.**", + "com.android.server.wifi.nl80211.GenericNetlinkMsg", + "com.android.server.wifi.nl80211.GenericNetlinkMsg$*", + "com.android.server.wifi.nl80211.GenericNetlinkMsg.**", + "com.android.server.wifi.nl80211.NetlinkConstants", + "com.android.server.wifi.nl80211.NetlinkConstants$*", + "com.android.server.wifi.nl80211.NetlinkConstants.**", + "com.android.server.wifi.nl80211.Nl80211Proxy", + "com.android.server.wifi.nl80211.Nl80211Proxy$*", + "com.android.server.wifi.nl80211.Nl80211Proxy.**", + "com.android.server.wifi.nl80211.StructGenNlMsgHdr", + "com.android.server.wifi.nl80211.StructGenNlMsgHdr$*", + "com.android.server.wifi.nl80211.StructGenNlMsgHdr.**", "com.android.server.wifi.p2p.ExternalApproverManager", "com.android.server.wifi.p2p.ExternalApproverManager$*", "com.android.server.wifi.p2p.ExternalApproverManager.**", @@ -1181,6 +1202,18 @@ android_test { "com.android.server.wifi.scanner.WificondScannerImpl", "com.android.server.wifi.scanner.WificondScannerImpl$*", "com.android.server.wifi.scanner.WificondScannerImpl.**", + "com.android.server.wifi.usd.UsdNativeManager", + "com.android.server.wifi.usd.UsdNativeManager$*", + "com.android.server.wifi.usd.UsdNativeManager.**", + "com.android.server.wifi.usd.UsdRequestManager", + "com.android.server.wifi.usd.UsdRequestManager$*", + "com.android.server.wifi.usd.UsdRequestManager.**", + "com.android.server.wifi.usd.UsdService", + "com.android.server.wifi.usd.UsdService$*", + "com.android.server.wifi.usd.UsdService.**", + "com.android.server.wifi.usd.UsdServiceImpl", + "com.android.server.wifi.usd.UsdServiceImpl$*", + "com.android.server.wifi.usd.UsdServiceImpl.**", "com.android.server.wifi.util.ActionListenerWrapper", "com.android.server.wifi.util.ActionListenerWrapper$*", "com.android.server.wifi.util.ActionListenerWrapper.**", @@ -1202,6 +1235,9 @@ android_test { "com.android.server.wifi.util.EncryptedData", "com.android.server.wifi.util.EncryptedData$*", "com.android.server.wifi.util.EncryptedData.**", + "com.android.server.wifi.util.FeatureBitsetUtils", + "com.android.server.wifi.util.FeatureBitsetUtils$*", + "com.android.server.wifi.util.FeatureBitsetUtils.**", "com.android.server.wifi.util.FileUtils", "com.android.server.wifi.util.FileUtils$*", "com.android.server.wifi.util.FileUtils.**", diff --git a/service/tests/wifitests/src/com/android/server/wifi/ActiveModeWardenTest.java b/service/tests/wifitests/src/com/android/server/wifi/ActiveModeWardenTest.java index 18d1fbe2f3..780fdbed07 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/ActiveModeWardenTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/ActiveModeWardenTest.java @@ -18,9 +18,11 @@ package com.android.server.wifi; import static android.net.wifi.WifiManager.SAP_START_FAILURE_GENERAL; import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED; +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 com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_LOCAL_ONLY; import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_PRIMARY; import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_SCAN_ONLY; @@ -29,6 +31,9 @@ import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_SECONDARY_TR import static com.android.server.wifi.ActiveModeManager.ROLE_SOFTAP_LOCAL_ONLY; import static com.android.server.wifi.ActiveModeManager.ROLE_SOFTAP_TETHERED; import static com.android.server.wifi.ActiveModeWarden.INTERNAL_REQUESTOR_WS; +import static com.android.server.wifi.TestUtil.addCapabilitiesToBitset; +import static com.android.server.wifi.TestUtil.combineBitsets; +import static com.android.server.wifi.TestUtil.createCapabilityBitset; import static com.android.server.wifi.WifiSettingsConfigStore.WIFI_NATIVE_SUPPORTED_STA_BANDS; import static com.google.common.truth.Truth.assertThat; @@ -51,6 +56,7 @@ import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.isNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockingDetails; import static org.mockito.Mockito.never; @@ -71,6 +77,7 @@ import android.net.Network; import android.net.wifi.ISubsystemRestartCallback; import android.net.wifi.IWifiConnectedNetworkScorer; import android.net.wifi.IWifiNetworkStateChangedListener; +import android.net.wifi.IWifiStateChangedListener; import android.net.wifi.SoftApCapability; import android.net.wifi.SoftApConfiguration; import android.net.wifi.SoftApConfiguration.Builder; @@ -84,6 +91,7 @@ import android.net.wifi.WifiScanner; import android.net.wifi.util.WifiResourceCache; import android.os.BatteryStatsManager; import android.os.Build; +import android.os.Handler; import android.os.IBinder; import android.os.Process; import android.os.RemoteException; @@ -96,6 +104,7 @@ import android.util.Log; import androidx.test.filters.SmallTest; +import com.android.dx.mockito.inline.extended.StaticMockitoSession; import com.android.modules.utils.build.SdkLevel; import com.android.server.wifi.ActiveModeManager.ClientConnectivityRole; import com.android.server.wifi.ActiveModeManager.Listener; @@ -104,6 +113,7 @@ import com.android.server.wifi.ActiveModeWarden.ExternalClientModeManagerRequest import com.android.server.wifi.util.GeneralUtil.Mutable; import com.android.server.wifi.util.LastCallerInfoManager; import com.android.server.wifi.util.WifiPermissionsUtil; +import com.android.wifi.flags.FeatureFlags; import com.android.wifi.resources.R; import org.junit.After; @@ -119,6 +129,8 @@ import org.mockito.stubbing.Answer; import java.io.ByteArrayOutputStream; import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.BitSet; import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -148,7 +160,9 @@ public class ActiveModeWardenTest extends WifiBaseTest { private static final int TEST_AP_FREQUENCY = 2412; private static final int TEST_AP_BANDWIDTH = SoftApInfo.CHANNEL_WIDTH_20MHZ; private static final int TEST_UID = 435546654; - private static final long TEST_FEATURE_SET = 0xAB3DEF; + private static final BitSet TEST_FEATURE_SET = createCapabilityBitset( + WifiManager.WIFI_FEATURE_P2P, WifiManager.WIFI_FEATURE_PNO, + WifiManager.WIFI_FEATURE_OWE, WifiManager.WIFI_FEATURE_DPP); private static final String TEST_PACKAGE = "com.test"; private static final String TEST_COUNTRYCODE = "US"; private static final WorkSource TEST_WORKSOURCE = new WorkSource(TEST_UID, TEST_PACKAGE); @@ -190,6 +204,9 @@ public class ActiveModeWardenTest extends WifiBaseTest { @Mock WifiGlobals mWifiGlobals; @Mock WifiConnectivityManager mWifiConnectivityManager; @Mock WifiConfigManager mWifiConfigManager; + @Mock WakeupController mWakeupController; + @Mock DeviceConfigFacade mDeviceConfigFacade; + @Mock FeatureFlags mFeatureFlags; Listener<ConcreteClientModeManager> mClientListener; Listener<SoftApManager> mSoftApListener; @@ -206,6 +223,7 @@ public class ActiveModeWardenTest extends WifiBaseTest { private BroadcastReceiver mEmergencyCallbackModeChangedBr; private BroadcastReceiver mEmergencyCallStateChangedBr; + private StaticMockitoSession mStaticMockSession; /** * Set up the test environment. @@ -215,8 +233,12 @@ public class ActiveModeWardenTest extends WifiBaseTest { Log.d(TAG, "Setting up ..."); MockitoAnnotations.initMocks(this); + mStaticMockSession = mockitoSession() + .mockStatic(WifiInjector.class) + .startMocking(); mLooper = new TestLooper(); + when(WifiInjector.getInstance()).thenReturn(mWifiInjector); when(mWifiInjector.getScanRequestProxy()).thenReturn(mScanRequestProxy); when(mWifiInjector.getSarManager()).thenReturn(mSarManager); when(mWifiInjector.getHalDeviceManager()).thenReturn(mHalDeviceManager); @@ -224,10 +246,13 @@ public class ActiveModeWardenTest extends WifiBaseTest { when(mWifiInjector.getWifiHandlerLocalLog()).thenReturn(mLocalLog); when(mWifiInjector.getWifiConnectivityManager()).thenReturn(mWifiConnectivityManager); when(mWifiInjector.getWifiConfigManager()).thenReturn(mWifiConfigManager); + when(mWifiInjector.getWakeupController()).thenReturn(mWakeupController); when(mClientModeManager.getRole()).thenReturn(ROLE_CLIENT_PRIMARY); when(mClientModeManager.getInterfaceName()).thenReturn(WIFI_IFACE_NAME); when(mContext.getResourceCache()).thenReturn(mWifiResourceCache); when(mSoftApManager.getRole()).thenReturn(ROLE_SOFTAP_TETHERED); + when(mWifiInjector.getDeviceConfigFacade()).thenReturn(mDeviceConfigFacade); + when(mDeviceConfigFacade.getFeatureFlags()).thenReturn(mFeatureFlags); when(mWifiResourceCache.getString(R.string.wifi_localhotspot_configure_ssid_default)) .thenReturn("AndroidShare"); @@ -273,6 +298,8 @@ public class ActiveModeWardenTest extends WifiBaseTest { any(WifiServiceImpl.SoftApCallbackInternal.class), any(), any(), any(), anyBoolean()); when(mWifiNative.initialize()).thenReturn(true); + when(mWifiNative.getSupportedFeatureSet(isNull())).thenReturn(new BitSet()); + when(mWifiNative.getSupportedFeatureSet(anyString())).thenReturn(new BitSet()); when(mWifiPermissionsUtil.isSystem(TEST_PACKAGE, TEST_UID)).thenReturn(true); mActiveModeWarden = createActiveModeWarden(); @@ -280,6 +307,7 @@ public class ActiveModeWardenTest extends WifiBaseTest { mLooper.dispatchAll(); verify(mWifiMetrics).noteWifiEnabledDuringBoot(false); + verify(mWifiMetrics, never()).reportWifiStateChanged(eq(true), anyBoolean(), eq(false)); verify(mWifiGlobals).setD2dStaConcurrencySupported(false); verify(mWifiNative).registerStatusListener(mStatusListenerCaptor.capture()); verify(mWifiNative).initialize(); @@ -341,6 +369,7 @@ public class ActiveModeWardenTest extends WifiBaseTest { @After public void cleanUp() throws Exception { mActiveModeWarden = null; + mStaticMockSession.finishMocking(); mLooper.dispatchAll(); } @@ -375,7 +404,7 @@ public class ActiveModeWardenTest extends WifiBaseTest { * @param isClientModeSwitch true if switching from another mode, false if creating a new one * @param testFeatureSet a customized feature set to test */ - private void enterClientModeActiveState(boolean isClientModeSwitch, long testFeatureSet) + private void enterClientModeActiveState(boolean isClientModeSwitch, BitSet testFeatureSet) throws Exception { String fromState = mActiveModeWarden.getCurrentMode(); when(mSettingsStore.isWifiToggleEnabled()).thenReturn(true); @@ -385,7 +414,8 @@ public class ActiveModeWardenTest extends WifiBaseTest { when(mClientModeManager.getRole()).thenReturn(ROLE_CLIENT_PRIMARY); when(mClientModeManager.getCurrentNetwork()).thenReturn(mNetwork); - when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn(testFeatureSet); + when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)) + .thenReturn(testFeatureSet); // ClientModeManager starts in SCAN_ONLY role. mClientListener.onRoleChanged(mClientModeManager); mLooper.dispatchAll(); @@ -406,7 +436,7 @@ public class ActiveModeWardenTest extends WifiBaseTest { } verify(mClientModeManager, atLeastOnce()).getInterfaceName(); verify(mWifiNative, atLeastOnce()).getSupportedFeatureSet(WIFI_IFACE_NAME); - assertEquals(testFeatureSet, mActiveModeWarden.getSupportedFeatureSet()); + assertTrue(testFeatureSet.equals(mActiveModeWarden.getSupportedFeatureSet())); verify(mScanRequestProxy, times(4)).enableScanning(true, true); assertEquals(mClientModeManager, mActiveModeWarden.getPrimaryClientModeManager()); verify(mModeChangeCallback).onActiveModeManagerRoleChanged(mClientModeManager); @@ -448,7 +478,7 @@ public class ActiveModeWardenTest extends WifiBaseTest { verify(mModeChangeCallback, times(2)) .onActiveModeManagerRoleChanged(mClientModeManager); verify(mWifiNative, atLeastOnce()).getSupportedFeatureSet(null); - assertEquals(TEST_FEATURE_SET, mActiveModeWarden.getSupportedFeatureSet()); + assertTrue(TEST_FEATURE_SET.equals(mActiveModeWarden.getSupportedFeatureSet())); } assertInEnabledState(); verify(mScanRequestProxy).enableScanning(true, false); @@ -489,7 +519,7 @@ public class ActiveModeWardenTest extends WifiBaseTest { any(), any(), any(), eq(TEST_WORKSOURCE), eq(softApRole), anyBoolean()); mTimesCreatedSoftApManager++; if (fromState.equals(DISABLED_STATE_STRING)) { - verify(mBatteryStats).reportWifiOn(); + verify(mBatteryStats, atLeastOnce()).reportWifiOn(); } if (softApRole == ROLE_SOFTAP_TETHERED) { assertEquals(mSoftApManager, mActiveModeWarden.getTetheredSoftApManager()); @@ -588,6 +618,7 @@ public class ActiveModeWardenTest extends WifiBaseTest { // take snapshot of ActiveModeManagers Collection<ActiveModeManager> activeModeManagers = mActiveModeWarden.getActiveModeManagers(); + ClientModeManager primaryCmm = mActiveModeWarden.getPrimaryClientModeManagerNullable(); List<Integer> expectedStopInvocationCounts = activeModeManagers .stream() @@ -595,6 +626,9 @@ public class ActiveModeWardenTest extends WifiBaseTest { .collect(Collectors.toList()); r.run(); + if (times > 0 && primaryCmm != null) { + assertEquals(WIFI_STATE_DISABLING, mActiveModeWarden.getWifiState()); + } List<Integer> actualStopInvocationCounts = activeModeManagers .stream() @@ -865,6 +899,30 @@ public class ActiveModeWardenTest extends WifiBaseTest { } @Test + public void testClientModeChangeRoleDuringTransition() throws Exception { + enterClientModeActiveState(); + verify(mWifiInjector).makeClientModeManager( + any(), eq(TEST_WORKSOURCE), eq(ROLE_CLIENT_PRIMARY), anyBoolean()); + + // Simulate the primary not fully started by making the role null and targetRole primary. + when(mClientModeManager.getRole()).thenReturn(null); + when(mClientModeManager.getTargetRole()).thenReturn(ROLE_CLIENT_PRIMARY); + List<ClientModeManager> currentCMMs = mActiveModeWarden.getClientModeManagers(); + assertEquals(1, currentCMMs.size()); + ConcreteClientModeManager currentCmm = (ConcreteClientModeManager) currentCMMs.get(0); + assertTrue(currentCmm.getTargetRole() == ROLE_CLIENT_PRIMARY); + + // toggle wifi off while wifi scanning is on + when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(true); + when(mSettingsStore.isWifiToggleEnabled()).thenReturn(false); + mActiveModeWarden.wifiToggled(TEST_WORKSOURCE); + mLooper.dispatchAll(); + + // expect transition to scan only mode + verify(mClientModeManager).setRole(eq(ROLE_CLIENT_SCAN_ONLY), any()); + } + + @Test public void testPrimaryNotCreatedTwice() throws Exception { enterClientModeActiveState(); verify(mWifiInjector).makeClientModeManager( @@ -1063,6 +1121,21 @@ public class ActiveModeWardenTest extends WifiBaseTest { } /** + * Verifies that ClientsDisconnected event is being passed from SoftApManager + * to WifiServiceImpl. + */ + @Test + public void callsWifiServiceCallbackOnSoftApClientsDisconnected() throws Exception { + List<WifiClient> testClients = new ArrayList<>(); + enterSoftApActiveMode(); + mSoftApManagerCallback.onClientsDisconnected(mTestSoftApInfo, testClients); + mLooper.dispatchAll(); + + verify(mSoftApStateMachineCallback).onClientsDisconnected( + mTestSoftApInfo, testClients); + } + + /** * Test that we remain in the active state when we get a state change update that scan mode is * active. */ @@ -1315,7 +1388,7 @@ public class ActiveModeWardenTest extends WifiBaseTest { new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null, mSoftApCapability, TEST_COUNTRYCODE, null); SoftApConfiguration lohsConfigWC = mWifiApConfigStore.generateLocalOnlyHotspotConfig( - mContext, null, mSoftApCapability); + mContext, null, mSoftApCapability, false); SoftApModeConfiguration lohsConfig = new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_LOCAL_ONLY, lohsConfigWC, mSoftApCapability, TEST_COUNTRYCODE, null); @@ -1458,6 +1531,7 @@ public class ActiveModeWardenTest extends WifiBaseTest { mLooper.dispatchAll(); verify(mWifiMetrics).noteWifiEnabledDuringBoot(true); + verify(mWifiMetrics).reportWifiStateChanged(eq(true), anyBoolean(), eq(false)); assertInEnabledState(); @@ -1505,9 +1579,13 @@ public class ActiveModeWardenTest extends WifiBaseTest { ArgumentCaptor<BroadcastReceiver> bcastRxCaptor = ArgumentCaptor.forClass(BroadcastReceiver.class); - verify(mContext).registerReceiver( + // Note: Ignore lint warning UnspecifiedRegisterReceiverFlag since here is using + // to test receiving for system broadcasts. The lint warning is a false alarm since + // here is using argThat and hasAction. + verify(mContext).registerReceiverForAllUsers( bcastRxCaptor.capture(), - argThat(filter -> filter.hasAction(LocationManager.MODE_CHANGED_ACTION))); + argThat(filter -> filter.hasAction(LocationManager.MODE_CHANGED_ACTION)), + eq(null), any(Handler.class)); BroadcastReceiver broadcastReceiver = bcastRxCaptor.getValue(); assertInDisabledState(); @@ -1526,6 +1604,21 @@ public class ActiveModeWardenTest extends WifiBaseTest { */ @Test public void testWifiStateUnaffectedByAirplaneMode() throws Exception { + when(mFeatureFlags.monitorIntentForAllUsers()).thenReturn(false); + verifyWifiStateUnaffectedByAirplaneMode(false); + } + + /** + * Same as #testWifiStateUnaffectedByAirplaneMode but monitoring intent by RegisterForAllUsers. + */ + @Test + public void testWifiStateUnaffectedByAirplaneModeWithRegisterForAllUsers() throws Exception { + when(mFeatureFlags.monitorIntentForAllUsers()).thenReturn(true); + verifyWifiStateUnaffectedByAirplaneMode(true); + } + + private void verifyWifiStateUnaffectedByAirplaneMode(boolean isMonitorIntentForAllUsersEnabled) + throws Exception { assumeTrue(SdkLevel.isAtLeastT()); when(mUserManager.hasUserRestrictionForUser(eq(UserManager.DISALLOW_CHANGE_WIFI_STATE), any())).thenReturn(true); @@ -1539,9 +1632,16 @@ public class ActiveModeWardenTest extends WifiBaseTest { ArgumentCaptor<BroadcastReceiver> bcastRxCaptor = ArgumentCaptor.forClass(BroadcastReceiver.class); - verify(mContext).registerReceiver( - bcastRxCaptor.capture(), - argThat(filter -> filter.hasAction(Intent.ACTION_AIRPLANE_MODE_CHANGED))); + if (isMonitorIntentForAllUsersEnabled) { + verify(mContext).registerReceiverForAllUsers( + bcastRxCaptor.capture(), + argThat(filter -> filter.hasAction(Intent.ACTION_AIRPLANE_MODE_CHANGED)), + eq(null), any(Handler.class)); + } else { + verify(mContext).registerReceiver( + bcastRxCaptor.capture(), + argThat(filter -> filter.hasAction(Intent.ACTION_AIRPLANE_MODE_CHANGED))); + } BroadcastReceiver broadcastReceiver = bcastRxCaptor.getValue(); Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); @@ -1656,9 +1756,10 @@ public class ActiveModeWardenTest extends WifiBaseTest { ArgumentCaptor<BroadcastReceiver> bcastRxCaptor = ArgumentCaptor.forClass(BroadcastReceiver.class); - verify(mContext).registerReceiver( + verify(mContext).registerReceiverForAllUsers( bcastRxCaptor.capture(), - argThat(filter -> filter.hasAction(LocationManager.MODE_CHANGED_ACTION))); + argThat(filter -> filter.hasAction(LocationManager.MODE_CHANGED_ACTION)), + eq(null), any(Handler.class)); BroadcastReceiver broadcastReceiver = bcastRxCaptor.getValue(); assertInEnabledState(); @@ -1678,6 +1779,37 @@ public class ActiveModeWardenTest extends WifiBaseTest { * When in Client mode, make sure ECM triggers wifi shutdown. */ @Test + public void testEcmReceiverFromClientModeWithRegisterForAllUsers() + throws Exception { + when(mFeatureFlags.monitorIntentForAllUsers()).thenReturn(true); + ArgumentCaptor<BroadcastReceiver> bcastRxCaptor = + ArgumentCaptor.forClass(BroadcastReceiver.class); + mActiveModeWarden = createActiveModeWarden(); + mActiveModeWarden.start(); + mLooper.dispatchAll(); + verify(mContext).registerReceiverForAllUsers( + bcastRxCaptor.capture(), + argThat(filter -> + filter.hasAction(TelephonyManager.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)), + eq(null), any(Handler.class)); + mEmergencyCallbackModeChangedBr = bcastRxCaptor.getValue(); + when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(false); + enableWifi(); + + // Test with WifiDisableInECBM turned on: + when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); + + assertWifiShutDown(() -> { + // test ecm changed + emergencyCallbackModeChanged(true); + mLooper.dispatchAll(); + }); + } + + /** + * When in Client mode, make sure ECM triggers wifi shutdown. + */ + @Test public void testEcmOnFromClientMode() throws Exception { when(mSettingsStore.isScanAlwaysAvailable()).thenReturn(false); enableWifi(); @@ -1903,6 +2035,45 @@ public class ActiveModeWardenTest extends WifiBaseTest { }); } + /** + * Updates about call state change also trigger entry of ECM mode. + */ + @Test + public void testEnterEcmOnEmergencyCallStateChangeWithRegisterForAllUsers() + throws Exception { + when(mFeatureFlags.monitorIntentForAllUsers()).thenReturn(true); + ArgumentCaptor<BroadcastReceiver> bcastRxCaptor = + ArgumentCaptor.forClass(BroadcastReceiver.class); + mActiveModeWarden = createActiveModeWarden(); + mActiveModeWarden.start(); + mLooper.dispatchAll(); + verify(mContext).registerReceiverForAllUsers( + bcastRxCaptor.capture(), + argThat(filter -> + filter.hasAction(TelephonyManager.ACTION_EMERGENCY_CALL_STATE_CHANGED)), + eq(null), any(Handler.class)); + mEmergencyCallStateChangedBr = bcastRxCaptor.getValue(); + assertInDisabledState(); + + enableWifi(); + assertInEnabledState(); + + // Test with WifiDisableInECBM turned on: + when(mFacade.getConfigWiFiDisableInECBM(mContext)).thenReturn(true); + + assertEnteredEcmMode(() -> { + // test call state changed + emergencyCallStateChanged(true); + mLooper.dispatchAll(); + mClientListener.onStopped(mClientModeManager); + mLooper.dispatchAll(); + }); + + emergencyCallStateChanged(false); + mLooper.dispatchAll(); + + assertInEnabledState(); + } /** * Updates about call state change also trigger entry of ECM mode. @@ -2661,6 +2832,52 @@ public class ActiveModeWardenTest extends WifiBaseTest { } /** + * The command to trigger WiFi restart on Bootup. + * WiFi is in connect mode, calls to reset the wifi stack due to connection failures + * should trigger a supplicant stop, and subsequently, a driver reload. (Reboot) + * Create and start WifiController in EnabledState, start softAP and then + * send command to restart WiFi + * <p> + * Expected: Wi-Fi should be restarted successfully on bootup. + */ + @Test + public void testRestartWifiStackInStaConnectEnabledStatewithSap() throws Exception { + enableWifi(); + assertInEnabledState(); + verify(mWifiInjector).makeClientModeManager( + any(), eq(TEST_WORKSOURCE), eq(ROLE_CLIENT_PRIMARY), anyBoolean()); + + assertWifiShutDown(() -> { + mActiveModeWarden.recoveryRestartWifi(SelfRecovery.REASON_WIFINATIVE_FAILURE, + true); + mLooper.dispatchAll(); + // Complete the stop + mClientListener.onStopped(mClientModeManager); + mLooper.dispatchAll(); + }); + + verify(mModeChangeCallback).onActiveModeManagerRemoved(mClientModeManager); + + // still only started once + verify(mWifiInjector).makeClientModeManager( + any(), eq(TEST_WORKSOURCE), eq(ROLE_CLIENT_PRIMARY), anyBoolean()); + + // start softAp + enterSoftApActiveMode(); + assertInEnabledState(); + + mLooper.moveTimeForward(TEST_WIFI_RECOVERY_DELAY_MS); + mLooper.dispatchAll(); + + // started again + verify(mWifiInjector, times(2)).makeClientModeManager(any(), any(), any(), anyBoolean()); + assertInEnabledState(); + + verify(mSubsystemRestartCallback).onSubsystemRestarting(); + verify(mSubsystemRestartCallback).onSubsystemRestarted(); + } + + /** * The command to trigger a WiFi reset should not trigger a reset when in ECM mode. * Enable wifi and enter ECM state, send command to restart wifi. * <p> @@ -3004,13 +3221,13 @@ public class ActiveModeWardenTest extends WifiBaseTest { enterClientModeActiveState(); when(mWifiNative.isStaApConcurrencySupported()).thenReturn(false); mClientListener.onStarted(mClientModeManager); - assertEquals(0L, - mActiveModeWarden.getSupportedFeatureSet() & WifiManager.WIFI_FEATURE_AP_STA); + assertFalse(mActiveModeWarden.getSupportedFeatureSet() + .get(WifiManager.WIFI_FEATURE_AP_STA)); when(mWifiNative.isStaApConcurrencySupported()).thenReturn(true); mClientListener.onStarted(mClientModeManager); - assertEquals(WifiManager.WIFI_FEATURE_AP_STA, - mActiveModeWarden.getSupportedFeatureSet() & WifiManager.WIFI_FEATURE_AP_STA); + assertTrue(mActiveModeWarden.getSupportedFeatureSet() + .get(WifiManager.WIFI_FEATURE_AP_STA)); } @Test @@ -3072,7 +3289,7 @@ public class ActiveModeWardenTest extends WifiBaseTest { // request for ssid2/bssid2 if (additionaClientModeManagerRole == ROLE_CLIENT_LOCAL_ONLY) { mActiveModeWarden.requestLocalOnlyClientModeManager( - externalRequestListener, TEST_WORKSOURCE, ssid, bssid, false); + externalRequestListener, TEST_WORKSOURCE, ssid, bssid, false, false); } else if (additionaClientModeManagerRole == ROLE_CLIENT_SECONDARY_LONG_LIVED) { mActiveModeWarden.requestSecondaryLongLivedClientModeManager( externalRequestListener, TEST_WORKSOURCE, ssid, bssid); @@ -3159,7 +3376,7 @@ public class ActiveModeWardenTest extends WifiBaseTest { private void requestRemoveAdditionalClientModeManagerWhenNotAllowed( ClientConnectivityRole role, boolean clientIsExpected, - long featureSet) throws Exception { + BitSet featureSet) throws Exception { enterClientModeActiveState(false, featureSet); // Connected to ssid1/bssid1 @@ -3187,7 +3404,8 @@ public class ActiveModeWardenTest extends WifiBaseTest { // request for ssid2/bssid2 if (role == ROLE_CLIENT_LOCAL_ONLY) { mActiveModeWarden.requestLocalOnlyClientModeManager( - externalRequestListener, TEST_WORKSOURCE, TEST_SSID_2, TEST_BSSID_2, false); + externalRequestListener, TEST_WORKSOURCE, TEST_SSID_2, TEST_BSSID_2, false, + false); } else if (role == ROLE_CLIENT_SECONDARY_LONG_LIVED) { mActiveModeWarden.requestSecondaryLongLivedClientModeManager( externalRequestListener, TEST_WORKSOURCE, TEST_SSID_2, TEST_BSSID_2); @@ -3218,7 +3436,8 @@ public class ActiveModeWardenTest extends WifiBaseTest { ExternalClientModeManagerRequestListener.class); if (role == ROLE_CLIENT_LOCAL_ONLY) { mActiveModeWarden.requestLocalOnlyClientModeManager( - externalRequestListener, TEST_WORKSOURCE, TEST_SSID_1, TEST_BSSID_1, false); + externalRequestListener, TEST_WORKSOURCE, TEST_SSID_1, TEST_BSSID_1, false, + false); } else if (role == ROLE_CLIENT_SECONDARY_LONG_LIVED) { mActiveModeWarden.requestSecondaryLongLivedClientModeManager( externalRequestListener, TEST_WORKSOURCE, TEST_SSID_1, TEST_BSSID_1); @@ -3250,7 +3469,8 @@ public class ActiveModeWardenTest extends WifiBaseTest { // request for one more CMM (returns the existing one). if (role == ROLE_CLIENT_LOCAL_ONLY) { mActiveModeWarden.requestLocalOnlyClientModeManager( - externalRequestListener, TEST_WORKSOURCE, TEST_SSID_3, TEST_BSSID_3, false); + externalRequestListener, TEST_WORKSOURCE, TEST_SSID_3, TEST_BSSID_3, false, + false); } else if (role == ROLE_CLIENT_SECONDARY_LONG_LIVED) { mActiveModeWarden.requestSecondaryLongLivedClientModeManager( externalRequestListener, TEST_WORKSOURCE, TEST_SSID_3, TEST_BSSID_3); @@ -3293,7 +3513,8 @@ public class ActiveModeWardenTest extends WifiBaseTest { // request for the same SSID/BSSID and expect the existing CMM to get returned twice. if (role == ROLE_CLIENT_LOCAL_ONLY) { mActiveModeWarden.requestLocalOnlyClientModeManager( - externalRequestListener, TEST_WORKSOURCE, TEST_SSID_2, TEST_BSSID_2, false); + externalRequestListener, TEST_WORKSOURCE, TEST_SSID_2, TEST_BSSID_2, false, + false); } else if (role == ROLE_CLIENT_SECONDARY_LONG_LIVED) { mActiveModeWarden.requestSecondaryLongLivedClientModeManager( externalRequestListener, TEST_WORKSOURCE, TEST_SSID_2, TEST_BSSID_2); @@ -3340,7 +3561,8 @@ public class ActiveModeWardenTest extends WifiBaseTest { // request for same ssid1/bssid1 if (role == ROLE_CLIENT_LOCAL_ONLY) { mActiveModeWarden.requestLocalOnlyClientModeManager( - externalRequestListener, TEST_WORKSOURCE, TEST_SSID_1, TEST_BSSID_1, false); + externalRequestListener, TEST_WORKSOURCE, TEST_SSID_1, TEST_BSSID_1, false, + false); } else if (role == ROLE_CLIENT_SECONDARY_LONG_LIVED) { mActiveModeWarden.requestSecondaryLongLivedClientModeManager( externalRequestListener, TEST_WORKSOURCE, TEST_SSID_1, TEST_BSSID_1); @@ -3406,7 +3628,7 @@ public class ActiveModeWardenTest extends WifiBaseTest { ExternalClientModeManagerRequestListener externalRequestListener = mock( ExternalClientModeManagerRequestListener.class); mActiveModeWarden.requestLocalOnlyClientModeManager( - externalRequestListener, TEST_WORKSOURCE, TEST_SSID_1, TEST_BSSID_1, false); + externalRequestListener, TEST_WORKSOURCE, TEST_SSID_1, TEST_BSSID_1, false, false); mLooper.dispatchAll(); verify(externalRequestListener).onAnswer(null); @@ -3514,8 +3736,10 @@ public class ActiveModeWardenTest extends WifiBaseTest { .thenReturn(true); assertFalse(mActiveModeWarden.canRequestMoreClientModeManagersInRole( TEST_WORKSOURCE, ROLE_CLIENT_LOCAL_ONLY, false)); + BitSet expectedFeatureSet = addCapabilitiesToBitset( + TEST_FEATURE_SET, WifiManager.WIFI_FEATURE_ADDITIONAL_STA_LOCAL_ONLY); requestRemoveAdditionalClientModeManagerWhenNotAllowed(ROLE_CLIENT_LOCAL_ONLY, - true, TEST_FEATURE_SET | WifiManager.WIFI_FEATURE_ADDITIONAL_STA_LOCAL_ONLY); + true, expectedFeatureSet); } private void testLoFallbackAboveAndroidS(boolean isStaStaSupported) throws Exception { @@ -3529,9 +3753,9 @@ public class ActiveModeWardenTest extends WifiBaseTest { .thenReturn(false); assertFalse(mActiveModeWarden.canRequestMoreClientModeManagersInRole( TEST_WORKSOURCE, ROLE_CLIENT_LOCAL_ONLY, false)); - long expectedFeatureSet = TEST_FEATURE_SET; + BitSet expectedFeatureSet = (BitSet) TEST_FEATURE_SET.clone(); if (isStaStaSupported) { - expectedFeatureSet |= WifiManager.WIFI_FEATURE_ADDITIONAL_STA_LOCAL_ONLY; + expectedFeatureSet.set(WifiManager.WIFI_FEATURE_ADDITIONAL_STA_LOCAL_ONLY); } requestRemoveAdditionalClientModeManagerWhenNotAllowed(ROLE_CLIENT_LOCAL_ONLY, @@ -3779,7 +4003,7 @@ public class ActiveModeWardenTest extends WifiBaseTest { ExternalClientModeManagerRequestListener.class); // request for ssid2/bssid2 mActiveModeWarden.requestLocalOnlyClientModeManager( - externalRequestListener, TEST_WORKSOURCE, TEST_SSID_2, TEST_BSSID_2, false); + externalRequestListener, TEST_WORKSOURCE, TEST_SSID_2, TEST_BSSID_2, false, false); mLooper.dispatchAll(); verify(mWifiInjector).makeClientModeManager( any(), eq(TEST_WORKSOURCE), eq(ROLE_CLIENT_LOCAL_ONLY), anyBoolean()); @@ -3864,7 +4088,7 @@ public class ActiveModeWardenTest extends WifiBaseTest { ExternalClientModeManagerRequestListener.class); // request for ssid2/bssid2 mActiveModeWarden.requestLocalOnlyClientModeManager( - externalRequestListener, TEST_WORKSOURCE, TEST_SSID_2, TEST_BSSID_2, false); + externalRequestListener, TEST_WORKSOURCE, TEST_SSID_2, TEST_BSSID_2, false, false); mLooper.dispatchAll(); verify(mWifiInjector).makeClientModeManager( any(), eq(TEST_WORKSOURCE), eq(ROLE_CLIENT_LOCAL_ONLY), anyBoolean()); @@ -4002,7 +4226,7 @@ public class ActiveModeWardenTest extends WifiBaseTest { // mock requesting local only secondary mActiveModeWarden.requestLocalOnlyClientModeManager( - externalRequestListener, TEST_WORKSOURCE, TEST_SSID_2, TEST_BSSID_2, false); + externalRequestListener, TEST_WORKSOURCE, TEST_SSID_2, TEST_BSSID_2, false, false); mLooper.dispatchAll(); // Verify the primary is given to the externalRequestListener verify(externalRequestListener).onAnswer(requestedClientModeManager.capture()); @@ -4010,20 +4234,18 @@ public class ActiveModeWardenTest extends WifiBaseTest { any(), any(), eq(ROLE_CLIENT_LOCAL_ONLY), anyBoolean()); assertEquals(ROLE_CLIENT_PRIMARY, requestedClientModeManager.getValue().getRole()); - // Request for non local-only STA and verify the secondary STA is provided instead. - when(additionalClientModeManager.getRole()).thenReturn(ROLE_CLIENT_SECONDARY_LONG_LIVED); - mActiveModeWarden.requestSecondaryLongLivedClientModeManager( - externalRequestListener, TEST_WORKSOURCE, TEST_SSID_2, TEST_BSSID_2); + // mock requesting local only secondary, but with preference for secondary STA. + // This should bypass the enterCarMode permission check and still give secondary STA. + mActiveModeWarden.requestLocalOnlyClientModeManager( + externalRequestListener, TEST_WORKSOURCE, TEST_SSID_2, TEST_BSSID_2, false, true); mLooper.dispatchAll(); - verify(mWifiInjector).makeClientModeManager(any(), any(), - eq(ROLE_CLIENT_SECONDARY_LONG_LIVED), anyBoolean()); - additionalClientListener.value.onStarted(additionalClientModeManager); mLooper.dispatchAll(); - verify(externalRequestListener, times(2)).onAnswer( - requestedClientModeManager.capture()); - assertEquals(ROLE_CLIENT_SECONDARY_LONG_LIVED, - requestedClientModeManager.getValue().getRole()); + // Verify secondary is given to the externalRequestListener + verify(externalRequestListener, times(2)).onAnswer(requestedClientModeManager.capture()); + verify(mWifiInjector).makeClientModeManager( + any(), any(), eq(ROLE_CLIENT_LOCAL_ONLY), anyBoolean()); + assertEquals(ROLE_CLIENT_LOCAL_ONLY, requestedClientModeManager.getValue().getRole()); } @Test @@ -4060,10 +4282,10 @@ public class ActiveModeWardenTest extends WifiBaseTest { when(additionalClientModeManager.getInterfaceName()).thenReturn(WIFI_IFACE_NAME_1); when(additionalClientModeManager.getRole()).thenReturn(ROLE_CLIENT_LOCAL_ONLY); - // Request will shell uid for local-only STA and verify the secondary is provided instead. + // Request with shell uid for local-only STA and verify the secondary is provided instead. WorkSource shellWs = new WorkSource(0, "shell"); mActiveModeWarden.requestLocalOnlyClientModeManager( - externalRequestListener, shellWs, TEST_SSID_2, TEST_BSSID_2, false); + externalRequestListener, shellWs, TEST_SSID_2, TEST_BSSID_2, false, false); mLooper.dispatchAll(); verify(mWifiInjector).makeClientModeManager(any(), any(), eq(ROLE_CLIENT_LOCAL_ONLY), anyBoolean()); @@ -4445,7 +4667,7 @@ public class ActiveModeWardenTest extends WifiBaseTest { mActiveModeWarden.requestLocalOnlyClientModeManager( mock(ExternalClientModeManagerRequestListener.class), - TEST_WORKSOURCE, TEST_SSID_2, TEST_BSSID_2, false); + TEST_WORKSOURCE, TEST_SSID_2, TEST_BSSID_2, false, false); mLooper.dispatchAll(); // No role set, should be ignored. @@ -4776,7 +4998,7 @@ public class ActiveModeWardenTest extends WifiBaseTest { // mock requesting local only secondary mActiveModeWarden.requestLocalOnlyClientModeManager( - externalRequestListener, TEST_WORKSOURCE, TEST_SSID_2, TEST_BSSID_2, true); + externalRequestListener, TEST_WORKSOURCE, TEST_SSID_2, TEST_BSSID_2, true, false); mLooper.dispatchAll(); // Verify the primary is given to the externalRequestListener verify(externalRequestListener).onAnswer(requestedClientModeManager.capture()); @@ -4834,7 +5056,7 @@ public class ActiveModeWardenTest extends WifiBaseTest { // mock requesting local only secondary mActiveModeWarden.requestLocalOnlyClientModeManager( - externalRequestListener, TEST_WORKSOURCE, TEST_SSID_2, TEST_BSSID_2, true); + externalRequestListener, TEST_WORKSOURCE, TEST_SSID_2, TEST_BSSID_2, true, false); mLooper.dispatchAll(); WorkSource ws = new WorkSource(TEST_WORKSOURCE); ws.add(SETTINGS_WORKSOURCE); @@ -4864,20 +5086,21 @@ public class ActiveModeWardenTest extends WifiBaseTest { @Test public void testGetSupportedFeaturesForStaApConcurrency() throws Exception { enterScanOnlyModeActiveState(); - long supportedFeaturesFromWifiNative = WifiManager.WIFI_FEATURE_OWE; - when(mWifiNative.getSupportedFeatureSet(null)).thenReturn( - supportedFeaturesFromWifiNative); + BitSet supportedFeaturesFromWifiNative = + createCapabilityBitset(WifiManager.WIFI_FEATURE_OWE); + when(mWifiNative.getSupportedFeatureSet(null)).thenReturn(supportedFeaturesFromWifiNative); when(mWifiNative.isStaApConcurrencySupported()).thenReturn(false); mClientListener.onStarted(mClientModeManager); - assertEquals(supportedFeaturesFromWifiNative, - mActiveModeWarden.getSupportedFeatureSet()); + assertTrue(supportedFeaturesFromWifiNative + .equals(mActiveModeWarden.getSupportedFeatureSet())); when(mWifiNative.isStaApConcurrencySupported()).thenReturn(true); mClientListener.onStarted(mClientModeManager); - assertEquals(supportedFeaturesFromWifiNative | WifiManager.WIFI_FEATURE_AP_STA, - mActiveModeWarden.getSupportedFeatureSet()); + assertTrue(addCapabilitiesToBitset( + supportedFeaturesFromWifiNative, WifiManager.WIFI_FEATURE_AP_STA) + .equals(mActiveModeWarden.getSupportedFeatureSet())); } /** @@ -4888,45 +5111,47 @@ public class ActiveModeWardenTest extends WifiBaseTest { public void testGetSupportedFeaturesForStaStaConcurrency() throws Exception { assumeTrue(SdkLevel.isAtLeastS()); enterScanOnlyModeActiveState(); - long supportedFeaturesFromWifiNative = WifiManager.WIFI_FEATURE_OWE; + BitSet supportedFeaturesFromWifiNative = + createCapabilityBitset(WifiManager.WIFI_FEATURE_OWE); when(mWifiNative.getSupportedFeatureSet(null)).thenReturn( supportedFeaturesFromWifiNative); mClientListener.onStarted(mClientModeManager); - assertEquals(supportedFeaturesFromWifiNative, mActiveModeWarden.getSupportedFeatureSet()); + assertTrue(supportedFeaturesFromWifiNative + .equals(mActiveModeWarden.getSupportedFeatureSet())); when(mWifiNative.isStaStaConcurrencySupported()).thenReturn(true); when(mWifiResourceCache.getBoolean(R.bool.config_wifiMultiStaLocalOnlyConcurrencyEnabled)) .thenReturn(true); mClientListener.onStarted(mClientModeManager); - assertEquals(supportedFeaturesFromWifiNative - | WifiManager.WIFI_FEATURE_ADDITIONAL_STA_LOCAL_ONLY, - mActiveModeWarden.getSupportedFeatureSet()); + assertTrue(addCapabilitiesToBitset(supportedFeaturesFromWifiNative, + WifiManager.WIFI_FEATURE_ADDITIONAL_STA_LOCAL_ONLY) + .equals(mActiveModeWarden.getSupportedFeatureSet())); when(mWifiResourceCache.getBoolean( R.bool.config_wifiMultiStaNetworkSwitchingMakeBeforeBreakEnabled)) .thenReturn(true); mClientListener.onStarted(mClientModeManager); - assertEquals(supportedFeaturesFromWifiNative - | WifiManager.WIFI_FEATURE_ADDITIONAL_STA_LOCAL_ONLY - | WifiManager.WIFI_FEATURE_ADDITIONAL_STA_MBB, - mActiveModeWarden.getSupportedFeatureSet()); + assertTrue(addCapabilitiesToBitset(supportedFeaturesFromWifiNative, + WifiManager.WIFI_FEATURE_ADDITIONAL_STA_LOCAL_ONLY, + WifiManager.WIFI_FEATURE_ADDITIONAL_STA_MBB) + .equals(mActiveModeWarden.getSupportedFeatureSet())); when(mWifiResourceCache.getBoolean(R.bool.config_wifiMultiStaRestrictedConcurrencyEnabled)) .thenReturn(true); when(mWifiResourceCache.getBoolean( R.bool.config_wifiMultiStaMultiInternetConcurrencyEnabled)).thenReturn(true); mClientListener.onStarted(mClientModeManager); - assertEquals(supportedFeaturesFromWifiNative - | WifiManager.WIFI_FEATURE_ADDITIONAL_STA_LOCAL_ONLY - | WifiManager.WIFI_FEATURE_ADDITIONAL_STA_MBB - | WifiManager.WIFI_FEATURE_ADDITIONAL_STA_RESTRICTED - | WifiManager.WIFI_FEATURE_ADDITIONAL_STA_MULTI_INTERNET, - mActiveModeWarden.getSupportedFeatureSet()); + assertTrue(addCapabilitiesToBitset(supportedFeaturesFromWifiNative, + WifiManager.WIFI_FEATURE_ADDITIONAL_STA_LOCAL_ONLY, + WifiManager.WIFI_FEATURE_ADDITIONAL_STA_MBB, + WifiManager.WIFI_FEATURE_ADDITIONAL_STA_RESTRICTED, + WifiManager.WIFI_FEATURE_ADDITIONAL_STA_MULTI_INTERNET) + .equals(mActiveModeWarden.getSupportedFeatureSet())); } - private long testGetSupportedFeaturesCaseForMacRandomization( - long supportedFeaturesFromWifiNative, boolean apMacRandomizationEnabled, + private BitSet testGetSupportedFeaturesCaseForMacRandomization( + BitSet supportedFeaturesFromWifiNative, boolean apMacRandomizationEnabled, boolean staConnectedMacRandomizationEnabled, boolean p2pMacRandomizationEnabled) { when(mWifiResourceCache.getBoolean( R.bool.config_wifi_connected_mac_randomization_supported)) @@ -4947,28 +5172,29 @@ public class ActiveModeWardenTest extends WifiBaseTest { /** Verifies that syncGetSupportedFeatures() masks out capabilities based on system flags. */ @Test public void syncGetSupportedFeaturesForMacRandomization() throws Exception { - final long featureStaConnectedMacRandomization = - WifiManager.WIFI_FEATURE_CONNECTED_RAND_MAC; - final long featureApMacRandomization = - WifiManager.WIFI_FEATURE_AP_RAND_MAC; - final long featureP2pMacRandomization = - WifiManager.WIFI_FEATURE_CONNECTED_RAND_MAC; + final BitSet featureStaConnectedMacRandomization = + createCapabilityBitset(WifiManager.WIFI_FEATURE_CONNECTED_RAND_MAC); + final BitSet featureApMacRandomization = + createCapabilityBitset(WifiManager.WIFI_FEATURE_AP_RAND_MAC); + final BitSet featureP2pMacRandomization = + createCapabilityBitset(WifiManager.WIFI_FEATURE_CONNECTED_RAND_MAC); enterClientModeActiveState(); - assertEquals(featureStaConnectedMacRandomization | featureApMacRandomization - | featureP2pMacRandomization, - testGetSupportedFeaturesCaseForMacRandomization( - featureP2pMacRandomization, true, true, true)); + assertTrue(combineBitsets(featureStaConnectedMacRandomization, featureApMacRandomization, + featureP2pMacRandomization) + .equals(testGetSupportedFeaturesCaseForMacRandomization( + featureP2pMacRandomization, true, true, true))); // p2p supported by HAL, but disabled by overlay. - assertEquals(featureStaConnectedMacRandomization | featureApMacRandomization, - testGetSupportedFeaturesCaseForMacRandomization( - featureP2pMacRandomization, true, true, false)); - assertEquals(featureStaConnectedMacRandomization | featureApMacRandomization, - testGetSupportedFeaturesCaseForMacRandomization(0, true, true, false)); + assertTrue(combineBitsets(featureStaConnectedMacRandomization, featureApMacRandomization) + .equals(testGetSupportedFeaturesCaseForMacRandomization( + featureP2pMacRandomization, true, true, false))); + assertTrue(combineBitsets(featureStaConnectedMacRandomization, featureApMacRandomization) + .equals(testGetSupportedFeaturesCaseForMacRandomization( + new BitSet(), true, true, false))); } - private long testGetSupportedFeaturesCaseForRtt( - long supportedFeaturesFromWifiNative, boolean rttDisabled) { + private BitSet testGetSupportedFeaturesCaseForRtt( + BitSet supportedFeaturesFromWifiNative, boolean rttDisabled) { when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI_RTT)).thenReturn( !rttDisabled); when(mWifiNative.getSupportedFeatureSet(anyString())).thenReturn( @@ -4981,39 +5207,41 @@ public class ActiveModeWardenTest extends WifiBaseTest { /** Verifies that syncGetSupportedFeatures() masks out capabilities based on system flags. */ @Test public void syncGetSupportedFeaturesForRtt() throws Exception { - final long featureAware = WifiManager.WIFI_FEATURE_AWARE; - final long featureInfra = WifiManager.WIFI_FEATURE_INFRA; - final long featureD2dRtt = WifiManager.WIFI_FEATURE_D2D_RTT; - final long featureD2apRtt = WifiManager.WIFI_FEATURE_D2AP_RTT; - final long featureLongBits = 0x1000000000L; + final BitSet featureAware = createCapabilityBitset(WifiManager.WIFI_FEATURE_AWARE); + final BitSet featureInfra = createCapabilityBitset(WifiManager.WIFI_FEATURE_INFRA); + final BitSet featureD2dRtt = createCapabilityBitset(WifiManager.WIFI_FEATURE_D2D_RTT); + final BitSet featureD2apRtt = createCapabilityBitset(WifiManager.WIFI_FEATURE_D2AP_RTT); + enterClientModeActiveState(); - assertEquals(0, testGetSupportedFeaturesCaseForRtt(0, false)); - assertEquals(0, testGetSupportedFeaturesCaseForRtt(0, true)); - assertEquals(featureAware | featureInfra, - testGetSupportedFeaturesCaseForRtt(featureAware | featureInfra, false)); - assertEquals(featureAware | featureInfra, - testGetSupportedFeaturesCaseForRtt(featureAware | featureInfra, true)); - assertEquals(featureInfra | featureD2dRtt, - testGetSupportedFeaturesCaseForRtt(featureInfra | featureD2dRtt, false)); - assertEquals(featureInfra, - testGetSupportedFeaturesCaseForRtt(featureInfra | featureD2dRtt, true)); - assertEquals(featureInfra | featureD2apRtt, - testGetSupportedFeaturesCaseForRtt(featureInfra | featureD2apRtt, false)); - assertEquals(featureInfra, - testGetSupportedFeaturesCaseForRtt(featureInfra | featureD2apRtt, true)); - assertEquals(featureInfra | featureD2dRtt | featureD2apRtt, - testGetSupportedFeaturesCaseForRtt( - featureInfra | featureD2dRtt | featureD2apRtt, false)); - assertEquals(featureInfra, - testGetSupportedFeaturesCaseForRtt( - featureInfra | featureD2dRtt | featureD2apRtt, true)); - assertEquals(featureLongBits | featureInfra | featureD2dRtt | featureD2apRtt, + assertTrue(testGetSupportedFeaturesCaseForRtt(new BitSet(), false).equals(new BitSet())); + assertTrue(testGetSupportedFeaturesCaseForRtt(new BitSet(), true).equals(new BitSet())); + assertTrue(combineBitsets(featureAware, featureInfra).equals( + testGetSupportedFeaturesCaseForRtt(combineBitsets(featureAware, featureInfra), + false))); + assertTrue(combineBitsets(featureAware, featureInfra).equals( + testGetSupportedFeaturesCaseForRtt(combineBitsets(featureAware, featureInfra), + true))); + assertTrue(combineBitsets(featureInfra, featureD2dRtt).equals( + testGetSupportedFeaturesCaseForRtt(combineBitsets(featureInfra, featureD2dRtt), + false))); + assertTrue(featureInfra.equals( + testGetSupportedFeaturesCaseForRtt(combineBitsets(featureInfra, featureD2dRtt), + true))); + assertTrue(combineBitsets(featureInfra, featureD2apRtt).equals( + testGetSupportedFeaturesCaseForRtt(combineBitsets(featureInfra, featureD2apRtt), + false))); + assertTrue(featureInfra.equals( + testGetSupportedFeaturesCaseForRtt(combineBitsets(featureInfra, featureD2apRtt), + true))); + assertTrue(combineBitsets(featureInfra, featureD2dRtt, featureD2apRtt).equals( testGetSupportedFeaturesCaseForRtt( - featureLongBits | featureInfra | featureD2dRtt | featureD2apRtt, false)); - assertEquals(featureLongBits | featureInfra, + combineBitsets(featureInfra, featureD2dRtt, featureD2apRtt), + false))); + assertTrue(featureInfra.equals( testGetSupportedFeaturesCaseForRtt( - featureLongBits | featureInfra | featureD2dRtt | featureD2apRtt, true)); + combineBitsets(featureInfra, featureD2dRtt, featureD2apRtt), + true))); } @Test @@ -5345,9 +5573,10 @@ public class ActiveModeWardenTest extends WifiBaseTest { // Location state changes ArgumentCaptor<BroadcastReceiver> bcastRxCaptor = ArgumentCaptor.forClass(BroadcastReceiver.class); - verify(mContext).registerReceiver( + verify(mContext).registerReceiverForAllUsers( bcastRxCaptor.capture(), - argThat(filter -> filter.hasAction(LocationManager.MODE_CHANGED_ACTION))); + argThat(filter -> filter.hasAction(LocationManager.MODE_CHANGED_ACTION)), + eq(null), any(Handler.class)); BroadcastReceiver broadcastReceiver = bcastRxCaptor.getValue(); when(mWifiPermissionsUtil.isLocationModeEnabled()).thenReturn(true); @@ -5373,13 +5602,17 @@ public class ActiveModeWardenTest extends WifiBaseTest { @Test public void testWepNotDeprecated() throws Exception { when(mWifiGlobals.isWepSupported()).thenReturn(true); - enterClientModeActiveState(false, TEST_FEATURE_SET | WifiManager.WIFI_FEATURE_WEP); + BitSet featureSet = + addCapabilitiesToBitset(TEST_FEATURE_SET, WifiManager.WIFI_FEATURE_WEP); + enterClientModeActiveState(false, featureSet); } @Test public void testWpaPersonalNotDeprecated() throws Exception { when(mWifiGlobals.isWpaPersonalDeprecated()).thenReturn(false); - enterClientModeActiveState(false, TEST_FEATURE_SET | WifiManager.WIFI_FEATURE_WPA_PERSONAL); + BitSet featureSet = + addCapabilitiesToBitset(TEST_FEATURE_SET, WifiManager.WIFI_FEATURE_WPA_PERSONAL); + enterClientModeActiveState(false, featureSet); } @Test @@ -5405,4 +5638,82 @@ public class ActiveModeWardenTest extends WifiBaseTest { verify(mWifiGlobals).setD2dStaConcurrencySupported(true); verify(mWifiGlobals, atLeastOnce()).isD2dSupportedWhenInfraStaDisabled(); } + + @Test + public void testGetNumberOf11beSoftApManager() throws Exception { + assumeTrue(SdkLevel.isAtLeastT()); + enterSoftApActiveMode(); + when(mSoftApManager.isStarted()).thenReturn(true); + SoftApModeConfiguration mockSoftApModeConfiguration = mock(SoftApModeConfiguration.class); + SoftApConfiguration mockSoftApConfiguration = mock(SoftApConfiguration.class); + when(mockSoftApConfiguration.isIeee80211beEnabled()).thenReturn(true); + when(mockSoftApModeConfiguration.getSoftApConfiguration()) + .thenReturn(mockSoftApConfiguration); + when(mSoftApManager.getSoftApModeConfiguration()).thenReturn(mockSoftApModeConfiguration); + assertEquals(1, mActiveModeWarden.getCurrentMLDAp()); + when(mSoftApManager.isBridgedMode()).thenReturn(true); + when(mSoftApManager.isUsingMlo()).thenReturn(false); + assertEquals(2, mActiveModeWarden.getCurrentMLDAp()); + when(mSoftApManager.isUsingMlo()).thenReturn(true); + assertEquals(1, mActiveModeWarden.getCurrentMLDAp()); + when(mockSoftApConfiguration.isIeee80211beEnabled()).thenReturn(false); + assertEquals(0, mActiveModeWarden.getCurrentMLDAp()); + } + + /** + * Verifies that registered remote WifiStateChangedListeners are notified when the Wifi state + * changes. + */ + @Test + public void testRegisteredWifiStateChangedListenerIsNotifiedWhenWifiStateChanges() + throws RemoteException { + // Start off ENABLED + mActiveModeWarden.setWifiStateForApiCalls(WIFI_STATE_ENABLED); + + // Registering should give the current state of ENABLED. + IWifiStateChangedListener remoteCallback1 = mock(IWifiStateChangedListener.class); + when(remoteCallback1.asBinder()).thenReturn(mock(IBinder.class)); + IWifiStateChangedListener remoteCallback2 = mock(IWifiStateChangedListener.class); + when(remoteCallback2.asBinder()).thenReturn(mock(IBinder.class)); + mActiveModeWarden.addWifiStateChangedListener(remoteCallback1); + mActiveModeWarden.addWifiStateChangedListener(remoteCallback2); + + // Change the state to DISABLED and verify the listeners were called. + final int newState = WIFI_STATE_DISABLED; + mActiveModeWarden.setWifiStateForApiCalls(newState); + + verify(remoteCallback1, times(1)).onWifiStateChanged(); + verify(remoteCallback2, times(1)).onWifiStateChanged(); + + // Duplicate wifi state should not notify the callbacks again. + mActiveModeWarden.setWifiStateForApiCalls(newState); + mActiveModeWarden.setWifiStateForApiCalls(newState); + mActiveModeWarden.setWifiStateForApiCalls(newState); + + verify(remoteCallback1, times(1)).onWifiStateChanged(); + verify(remoteCallback2, times(1)).onWifiStateChanged(); + } + + /** + * Verifies that unregistered remote WifiStateChangedListeners are not notified when the Wifi + * state changes. + */ + @Test + public void testUnregisteredWifiStateChangedListenerIsNotNotifiedWhenWifiStateChanges() + throws RemoteException { + IWifiStateChangedListener remoteCallback1 = mock(IWifiStateChangedListener.class); + when(remoteCallback1.asBinder()).thenReturn(mock(IBinder.class)); + IWifiStateChangedListener remoteCallback2 = mock(IWifiStateChangedListener.class); + when(remoteCallback2.asBinder()).thenReturn(mock(IBinder.class)); + mActiveModeWarden.addWifiStateChangedListener(remoteCallback1); + mActiveModeWarden.addWifiStateChangedListener(remoteCallback2); + mActiveModeWarden.removeWifiStateChangedListener(remoteCallback1); + mActiveModeWarden.removeWifiStateChangedListener(remoteCallback2); + + final int newState = WIFI_STATE_ENABLED; + mActiveModeWarden.setWifiStateForApiCalls(newState); + + verify(remoteCallback1, never()).onWifiStateChanged(); + verify(remoteCallback2, never()).onWifiStateChanged(); + } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java b/service/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java index 2d114d20c4..915c9a49b0 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java @@ -44,8 +44,10 @@ import static com.android.server.wifi.ClientModeImpl.CMD_PRE_DHCP_ACTION; import static com.android.server.wifi.ClientModeImpl.CMD_PRE_DHCP_ACTION_COMPLETE; import static com.android.server.wifi.ClientModeImpl.CMD_UNWANTED_NETWORK; import static com.android.server.wifi.ClientModeImpl.WIFI_WORK_SOURCE; +import static com.android.server.wifi.WifiBlocklistMonitor.REASON_APP_DISALLOW; import static com.android.server.wifi.WifiSettingsConfigStore.SECONDARY_WIFI_STA_FACTORY_MAC_ADDRESS; import static com.android.server.wifi.WifiSettingsConfigStore.WIFI_STA_FACTORY_MAC_ADDRESS; +import static com.android.server.wifi.TestUtil.createCapabilityBitset; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -123,6 +125,7 @@ import android.net.networkstack.aidl.ip.ReachabilityLossInfoParcelable; import android.net.networkstack.aidl.ip.ReachabilityLossReason; import android.net.vcn.VcnManager; import android.net.vcn.VcnNetworkPolicyResult; +import android.net.wifi.BlockingOption; import android.net.wifi.IActionListener; import android.net.wifi.MloLink; import android.net.wifi.ScanResult; @@ -186,7 +189,7 @@ import com.android.server.wifi.p2p.WifiP2pServiceImpl; import com.android.server.wifi.proto.nano.WifiMetricsProto; import com.android.server.wifi.proto.nano.WifiMetricsProto.StaEvent; import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiIsUnusableEvent; -import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiUsabilityStats; +import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiUsabilityStatsEntry; import com.android.server.wifi.util.ActionListenerWrapper; import com.android.server.wifi.util.NativeUtil; import com.android.server.wifi.util.RssiUtilTest; @@ -641,6 +644,7 @@ public class ClientModeImplTest extends WifiBaseTest { when(mWifiNative.connectToNetwork(any(), any())).thenReturn(true); when(mWifiNative.getApfCapabilities(anyString())).thenReturn(APF_CAP); when(mWifiNative.isQosPolicyFeatureEnabled()).thenReturn(true); + when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn(new BitSet()); } /** Reset verify() counters on WifiNative, and restore when() mocks on mWifiNative */ @@ -732,8 +736,9 @@ public class ClientModeImplTest extends WifiBaseTest { when(mWifiInjector.getActiveModeWarden()).thenReturn(mActiveModeWarden); when(mActiveModeWarden.getPrimaryClientModeManager()).thenReturn(mPrimaryClientModeManager); - when(mPrimaryClientModeManager.getSupportedFeatures()).thenReturn( - WifiManager.WIFI_FEATURE_WPA3_SAE | WifiManager.WIFI_FEATURE_OWE); + when(mPrimaryClientModeManager.getSupportedFeaturesBitSet()).thenReturn( + createCapabilityBitset( + WifiManager.WIFI_FEATURE_WPA3_SAE, WifiManager.WIFI_FEATURE_OWE)); when(mWifiInjector.getWifiGlobals()).thenReturn(mWifiGlobals); when(mWifiInjector.getWifiHandlerThread()).thenReturn(mWifiHandlerThread); when(mWifiInjector.getSsidTranslator()).thenReturn(mSsidTranslator); @@ -852,6 +857,7 @@ public class ClientModeImplTest extends WifiBaseTest { assertNotNull(mOffloadDisabledListenerArgumentCaptor.getValue()); mCmi.enableVerboseLogging(true); + mCmi.disconnect(); mLooper.dispatchAll(); verify(mWifiLastResortWatchdog, atLeastOnce()).clearAllFailureCounts(); @@ -3347,7 +3353,7 @@ public class ClientModeImplTest extends WifiBaseTest { // "normal" commands. mCmi.sendMessage(ClientModeImpl.CMD_DISCONNECT); mLooper.dispatchAll(); - assertEquals(1, mCmi.copyLogRecs() + assertEquals(2, mCmi.copyLogRecs() .stream() .filter(logRec -> logRec.getWhat() == ClientModeImpl.CMD_DISCONNECT) .count()); @@ -4023,7 +4029,7 @@ public class ClientModeImplTest extends WifiBaseTest { WifiSignalPollResults signalPollResults = new WifiSignalPollResults(); signalPollResults.addEntry(0, -42, 65, 54, sFreq); when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn( - WifiManager.WIFI_FEATURE_LINK_LAYER_STATS); + createCapabilityBitset(WifiManager.WIFI_FEATURE_LINK_LAYER_STATS)); when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(llStats); when(mWifiNative.signalPoll(any())).thenReturn(signalPollResults); when(mClock.getWallClockMillis()).thenReturn(startMillis + 0); @@ -4058,7 +4064,7 @@ public class ClientModeImplTest extends WifiBaseTest { WifiSignalPollResults signalPollResults = new WifiSignalPollResults(); signalPollResults.addEntry(0, -42, 65, 54, sFreq); when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn( - WifiManager.WIFI_FEATURE_LINK_LAYER_STATS); + createCapabilityBitset(WifiManager.WIFI_FEATURE_LINK_LAYER_STATS)); when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(llStats); when(mWifiNative.signalPoll(any())).thenReturn(signalPollResults); when(mClock.getWallClockMillis()).thenReturn(startMillis + 0); @@ -4112,7 +4118,7 @@ public class ClientModeImplTest extends WifiBaseTest { WifiSignalPollResults signalPollResults = new WifiSignalPollResults(); signalPollResults.addEntry(0, -42, 65, 54, sFreq); when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn( - WifiManager.WIFI_FEATURE_LINK_LAYER_STATS); + createCapabilityBitset(WifiManager.WIFI_FEATURE_LINK_LAYER_STATS)); when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(llStats); when(mWifiNative.signalPoll(any())).thenReturn(signalPollResults); when(mClock.getWallClockMillis()).thenReturn(startMillis + 0); @@ -4139,6 +4145,76 @@ public class ClientModeImplTest extends WifiBaseTest { } /** + * Verify that RSSI polling starts/ stops/ one-offs are properly recorded in the capture buffer. + */ + @Test + public void testCaptureBufferRssiOnOff() throws Exception { + // Log should indicate RSSI polling turned on. + mCmi.enableRssiPolling(true); + connect(); + verify(mWifiMetrics).logAsynchronousEvent( + eq(WIFI_IFACE_NAME), + eq(WifiUsabilityStatsEntry.CAPTURE_EVENT_TYPE_RSSI_POLLING_ENABLED)); + reset(mWifiMetrics); + + // Normal RSSI polling loop. No events should be logged. + mLooper.moveTimeForward(3000); + mLooper.dispatchAll(); + verify(mWifiMetrics, never()).logAsynchronousEvent(anyString(), anyInt()); + verify(mWifiMetrics, never()).logAsynchronousEvent(anyString(), anyInt(), anyInt()); + reset(mWifiMetrics); + + // Normal RSSI polling loop. No events should be logged here either. + mLooper.moveTimeForward(3000); + mLooper.dispatchAll(); + verify(mWifiMetrics, never()).logAsynchronousEvent(anyString(), anyInt()); + verify(mWifiMetrics, never()).logAsynchronousEvent(anyString(), anyInt(), anyInt()); + reset(mWifiMetrics); + + // Turn off RSSI polling. This should show up in the capture buffer. + mCmi.enableRssiPolling(false); + mLooper.moveTimeForward(3000); + mLooper.dispatchAll(); + verify(mWifiMetrics).logAsynchronousEvent( + eq(WIFI_IFACE_NAME), + eq(WifiUsabilityStatsEntry.CAPTURE_EVENT_TYPE_RSSI_POLLING_DISABLED)); + reset(mWifiMetrics); + } + + /** + * Verify that IP reachability problems are recorded in the capture buffer. + */ + @Test + public void testCaptureBufferReachabilityLost() throws Exception { + // Log should indicate RSSI polling turned on. + connect(); + verify(mWifiMetrics).logAsynchronousEvent( + eq(WIFI_IFACE_NAME), + eq(WifiUsabilityStatsEntry.CAPTURE_EVENT_TYPE_RSSI_POLLING_ENABLED)); + reset(mWifiMetrics); + + // Simulate an IP_REACHABILITY_LOST event. + mIpClientCallback.onReachabilityLost("CMD_IP_REACHABILITY_LOST"); + mLooper.dispatchAll(); + verify(mWifiMetrics).logAsynchronousEvent( + eq(WIFI_IFACE_NAME), + eq(WifiUsabilityStatsEntry.CAPTURE_EVENT_TYPE_IP_REACHABILITY_LOST), + eq(-1)); + reset(mWifiMetrics); + + // Simulate an IP_REACHABILITY_FAILURE event. + ReachabilityLossInfoParcelable lossInfo = + new ReachabilityLossInfoParcelable("", ReachabilityLossReason.CONFIRM); + mIpClientCallback.onReachabilityFailure(lossInfo); + mLooper.dispatchAll(); + verify(mWifiMetrics).logAsynchronousEvent( + eq(WIFI_IFACE_NAME), + eq(WifiUsabilityStatsEntry.CAPTURE_EVENT_TYPE_IP_REACHABILITY_FAILURE), + eq(ReachabilityLossReason.CONFIRM)); + reset(mWifiMetrics); + } + + /** * Verify link bandwidth update in connected mode */ @Test @@ -4172,7 +4248,7 @@ public class ClientModeImplTest extends WifiBaseTest { WifiSignalPollResults signalPollResults = new WifiSignalPollResults(); signalPollResults.addEntry(0, -42, 65, 54, sFreq); when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn( - WifiManager.WIFI_FEATURE_LINK_LAYER_STATS); + createCapabilityBitset(WifiManager.WIFI_FEATURE_LINK_LAYER_STATS)); when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(llStats); when(mWifiNative.signalPoll(any())).thenReturn(signalPollResults); when(mClock.getWallClockMillis()).thenReturn(startMillis + 0); @@ -4713,7 +4789,7 @@ public class ClientModeImplTest extends WifiBaseTest { WifiSignalPollResults signalPollResults = new WifiSignalPollResults(); signalPollResults.addEntry(0, TEST_RSSI, 65, 54, sFreq); when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn( - WifiManager.WIFI_FEATURE_LINK_LAYER_STATS); + createCapabilityBitset(WifiManager.WIFI_FEATURE_LINK_LAYER_STATS)); when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(llStats); when(mWifiNative.signalPoll(any())).thenReturn(signalPollResults); @@ -4740,7 +4816,7 @@ public class ClientModeImplTest extends WifiBaseTest { WifiSignalPollResults signalPollResults = new WifiSignalPollResults(); signalPollResults.addEntry(0, TEST_RSSI, 65, 54, sFreq); when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn( - WifiManager.WIFI_FEATURE_LINK_LAYER_STATS); + createCapabilityBitset(WifiManager.WIFI_FEATURE_LINK_LAYER_STATS)); when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(llStats); when(mWifiNative.signalPoll(any())).thenReturn(signalPollResults); @@ -5286,6 +5362,19 @@ public class ClientModeImplTest extends WifiBaseTest { // BSSID different, record this connection. verify(mWifiMetrics).incrementNumBssidDifferentSelectionBetweenFrameworkAndFirmware(); verifyConnectionEventTimeoutDoesNotOccur(); + + // Disconnect now should not trigger handleConnectionAttemptEnded + DisconnectEventInfo disconnectEventInfo = + new DisconnectEventInfo(TEST_SSID, TEST_BSSID_STR, 0, false); + mCmi.sendMessage(WifiMonitor.NETWORK_DISCONNECTION_EVENT, disconnectEventInfo); + mLooper.dispatchAll(); + + verify(mWifiMetrics, never()).endConnectionEvent( + any(), eq(WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION), + anyInt(), anyInt(), anyInt(), anyInt()); + verify(mWifiConnectivityManager, never()).handleConnectionAttemptEnded( + any(), eq(WifiMetrics.ConnectionEvent.FAILURE_NETWORK_DISCONNECTION), + anyInt(), any(), any()); } /** @@ -5460,7 +5549,7 @@ public class ClientModeImplTest extends WifiBaseTest { WifiSignalPollResults signalPollResults = new WifiSignalPollResults(); signalPollResults.addEntry(0, RSSI_THRESHOLD_BREACH_MIN, 65, 54, sFreq); when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn( - WifiManager.WIFI_FEATURE_LINK_LAYER_STATS); + createCapabilityBitset(WifiManager.WIFI_FEATURE_LINK_LAYER_STATS)); when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(llStats); when(mWifiNative.signalPoll(any())).thenReturn(signalPollResults); @@ -6081,7 +6170,7 @@ public class ClientModeImplTest extends WifiBaseTest { failOnRssiChangeBroadcast(); when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn( - WifiManager.WIFI_FEATURE_LINK_LAYER_STATS); + createCapabilityBitset(WifiManager.WIFI_FEATURE_LINK_LAYER_STATS)); WifiLinkLayerStats oldLLStats = new WifiLinkLayerStats(); when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(oldLLStats); mCmi.sendMessage(ClientModeImpl.CMD_RSSI_POLL, 1); @@ -6108,7 +6197,7 @@ public class ClientModeImplTest extends WifiBaseTest { failOnRssiChangeBroadcast(); when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn( - WifiManager.WIFI_FEATURE_LINK_LAYER_STATS); + createCapabilityBitset(WifiManager.WIFI_FEATURE_LINK_LAYER_STATS)); WifiLinkLayerStats stats = new WifiLinkLayerStats(); when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(stats); when(mWifiDataStall.checkDataStallAndThroughputSufficiency(any(), @@ -6116,9 +6205,8 @@ public class ClientModeImplTest extends WifiBaseTest { .thenReturn(WifiIsUnusableEvent.TYPE_UNKNOWN); mCmi.sendMessage(ClientModeImpl.CMD_RSSI_POLL, 1); mLooper.dispatchAll(); - verify(mWifiMetrics).updateWifiUsabilityStatsEntries(any(), any(), eq(stats)); - verify(mWifiMetrics, never()).addToWifiUsabilityStatsList(any(), - WifiUsabilityStats.LABEL_BAD, eq(anyInt()), eq(-1)); + verify(mWifiMetrics).updateWifiUsabilityStatsEntries(any(), any(), eq(stats), eq(false), + anyInt()); when(mWifiDataStall.checkDataStallAndThroughputSufficiency(any(), any(), any(), any(), any(), anyLong(), anyLong())) @@ -6126,13 +6214,8 @@ public class ClientModeImplTest extends WifiBaseTest { when(mClock.getElapsedSinceBootMillis()).thenReturn(10L); mCmi.sendMessage(ClientModeImpl.CMD_RSSI_POLL, 1); mLooper.dispatchAll(); - verify(mWifiMetrics, times(2)).updateWifiUsabilityStatsEntries(any(), any(), eq(stats)); - when(mClock.getElapsedSinceBootMillis()) - .thenReturn(10L + ClientModeImpl.DURATION_TO_WAIT_ADD_STATS_AFTER_DATA_STALL_MS); - mCmi.sendMessage(ClientModeImpl.CMD_RSSI_POLL, 1); - mLooper.dispatchAll(); - verify(mWifiMetrics).addToWifiUsabilityStatsList(WIFI_IFACE_NAME, - WifiUsabilityStats.LABEL_BAD, WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX, -1); + verify(mWifiMetrics, times(2)).updateWifiUsabilityStatsEntries(any(), any(), eq(stats), + eq(false), anyInt()); } /** @@ -6530,9 +6613,6 @@ public class ClientModeImplTest extends WifiBaseTest { mLooper.dispatchAll(); verify(mWifiMetrics).logWifiIsUnusableEvent(WIFI_IFACE_NAME, WifiIsUnusableEvent.TYPE_IP_REACHABILITY_LOST); - verify(mWifiMetrics).addToWifiUsabilityStatsList(WIFI_IFACE_NAME, - WifiUsabilityStats.LABEL_BAD, - WifiUsabilityStats.TYPE_IP_REACHABILITY_LOST, -1); } /** @@ -6551,9 +6631,6 @@ public class ClientModeImplTest extends WifiBaseTest { mLooper.dispatchAll(); verify(mWifiMetrics).logWifiIsUnusableEvent(WIFI_IFACE_NAME, WifiIsUnusableEvent.TYPE_IP_REACHABILITY_LOST); - verify(mWifiMetrics).addToWifiUsabilityStatsList(WIFI_IFACE_NAME, - WifiUsabilityStats.LABEL_BAD, - WifiUsabilityStats.TYPE_IP_REACHABILITY_LOST, -1); } /** @@ -6575,9 +6652,6 @@ public class ClientModeImplTest extends WifiBaseTest { mLooper.dispatchAll(); verify(mWifiMetrics, never()).logWifiIsUnusableEvent(WIFI_IFACE_NAME, WifiIsUnusableEvent.TYPE_IP_REACHABILITY_LOST); - verify(mWifiMetrics, never()).addToWifiUsabilityStatsList(WIFI_IFACE_NAME, - WifiUsabilityStats.LABEL_BAD, - WifiUsabilityStats.TYPE_IP_REACHABILITY_LOST, -1); } /** @@ -6671,6 +6745,8 @@ public class ClientModeImplTest extends WifiBaseTest { public void testOnNetworkPermanentlyDisabled() throws Exception { connect(); + // Verify connection failure related disable reason should not trigger disconnect because + // this could be from another STA WifiConfiguration disabledNetwork = new WifiConfiguration(); disabledNetwork.networkId = FRAMEWORK_NETWORK_ID; for (WifiConfigManager.OnNetworkUpdateListener listener : mConfigUpdateListenerCaptor @@ -6679,7 +6755,15 @@ public class ClientModeImplTest extends WifiBaseTest { WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD); } mLooper.dispatchAll(); + verify(mWifiNative, never()).disconnect(WIFI_IFACE_NAME); + // Verify that the network is disconnect if the profile is disabled by wifi API call + for (WifiConfigManager.OnNetworkUpdateListener listener : mConfigUpdateListenerCaptor + .getAllValues()) { + listener.onNetworkPermanentlyDisabled(disabledNetwork, + WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WIFI_MANAGER); + } + mLooper.dispatchAll(); verify(mWifiNative).disconnect(WIFI_IFACE_NAME); } @@ -6944,9 +7028,10 @@ public class ClientModeImplTest extends WifiBaseTest { when(mWifiConfigManager.getConfiguredNetworkWithoutMasking(anyInt())).thenReturn(config); if (isDriverSupportFils) { when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn( - WifiManager.WIFI_FEATURE_FILS_SHA256 | WifiManager.WIFI_FEATURE_FILS_SHA384); + createCapabilityBitset(WifiManager.WIFI_FEATURE_FILS_SHA256, + WifiManager.WIFI_FEATURE_FILS_SHA384)); } else { - when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn((long) 0); + when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn(new BitSet()); } return config; @@ -8038,7 +8123,7 @@ public class ClientModeImplTest extends WifiBaseTest { @Test public void testWifiScoreReportDump() throws Exception { when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn( - WifiManager.WIFI_FEATURE_LINK_LAYER_STATS); + createCapabilityBitset(WifiManager.WIFI_FEATURE_LINK_LAYER_STATS)); InOrder inOrder = inOrder(mWifiNative, mWifiScoreReport); inOrder.verify(mWifiNative, never()).getWifiLinkLayerStats(any()); connect(); @@ -9051,7 +9136,7 @@ public class ClientModeImplTest extends WifiBaseTest { clearInvocations(mWifiNative, mWifiMetrics, mWifiDataStall); when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn( - WifiManager.WIFI_FEATURE_LINK_LAYER_STATS); + createCapabilityBitset(WifiManager.WIFI_FEATURE_LINK_LAYER_STATS)); WifiLinkLayerStats oldLLStats = new WifiLinkLayerStats(); when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(oldLLStats); mLooper.moveTimeForward(mWifiGlobals.getPollRssiIntervalMillis()); @@ -9095,7 +9180,7 @@ public class ClientModeImplTest extends WifiBaseTest { verifyNoMoreInteractions(mWifiNative, mWifiMetrics, mWifiDataStall); when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn( - WifiManager.WIFI_FEATURE_LINK_LAYER_STATS); + createCapabilityBitset(WifiManager.WIFI_FEATURE_LINK_LAYER_STATS)); when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(new WifiLinkLayerStats()); // No link layer stats collection on secondary CMM. @@ -9114,7 +9199,7 @@ public class ClientModeImplTest extends WifiBaseTest { clearInvocations(mWifiNative, mWifiMetrics, mWifiDataStall); when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn( - WifiManager.WIFI_FEATURE_LINK_LAYER_STATS); + createCapabilityBitset(WifiManager.WIFI_FEATURE_LINK_LAYER_STATS)); when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(new WifiLinkLayerStats()); // No link layer stats collection on secondary CMM. @@ -9147,7 +9232,7 @@ public class ClientModeImplTest extends WifiBaseTest { // RSSI polling is enabled on primary. when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn( - WifiManager.WIFI_FEATURE_LINK_LAYER_STATS); + createCapabilityBitset(WifiManager.WIFI_FEATURE_LINK_LAYER_STATS)); WifiLinkLayerStats oldLLStats = new WifiLinkLayerStats(); when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(oldLLStats); mLooper.moveTimeForward(mWifiGlobals.getPollRssiIntervalMillis()); @@ -9176,7 +9261,7 @@ public class ClientModeImplTest extends WifiBaseTest { connect(); clearInvocations(mWifiNative, mWifiMetrics, mWifiDataStall); - when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn(0L); + when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn(new BitSet()); WifiLinkLayerStats stats = new WifiLinkLayerStats(); when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(stats); mLooper.moveTimeForward(mWifiGlobals.getPollRssiIntervalMillis()); @@ -9799,7 +9884,7 @@ public class ClientModeImplTest extends WifiBaseTest { throws Exception { if (isTrustOnFirstUseSupported) { when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn( - WifiManager.WIFI_FEATURE_TRUST_ON_FIRST_USE); + createCapabilityBitset(WifiManager.WIFI_FEATURE_TRUST_ON_FIRST_USE)); } mCmi.mInsecureEapNetworkHandler = mInsecureEapNetworkHandler; @@ -10876,7 +10961,7 @@ public class ClientModeImplTest extends WifiBaseTest { connect(); when(mWifiNative.getMaxSupportedConcurrentTdlsSessions(WIFI_IFACE_NAME)).thenReturn(1); when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)) - .thenReturn(WifiManager.WIFI_FEATURE_TDLS); + .thenReturn(createCapabilityBitset(WifiManager.WIFI_FEATURE_TDLS)); when(mWifiNative.startTdls(eq(WIFI_IFACE_NAME), eq(TEST_TDLS_PEER_ADDR_STR), anyBoolean())) .thenReturn(true); assertEquals(1, mCmi.getMaxSupportedConcurrentTdlsSessions()); @@ -10970,7 +11055,7 @@ public class ClientModeImplTest extends WifiBaseTest { WifiSignalPollResults signalPollResults = new WifiSignalPollResults(); signalPollResults.addEntry(TEST_MLO_LINK_ID, -42, 65, 54, sFreq); when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn( - WifiManager.WIFI_FEATURE_LINK_LAYER_STATS); + createCapabilityBitset(WifiManager.WIFI_FEATURE_LINK_LAYER_STATS)); when(mWifiNative.getWifiLinkLayerStats(any())).thenReturn(llStats); when(mWifiNative.signalPoll(any())).thenReturn(signalPollResults); when(mClock.getWallClockMillis()).thenReturn(startMillis + 0); @@ -11210,7 +11295,7 @@ public class ClientModeImplTest extends WifiBaseTest { public void testSaeH2eSetEvenThoughConfigForSaeH2eIsNotTrue() throws Exception { when(mWifiNative.isSupplicantAidlServiceVersionAtLeast(3)).thenReturn(true); when(mWifiNative.getSupportedFeatureSet(WIFI_IFACE_NAME)).thenReturn( - WifiManager.WIFI_FEATURE_WPA3_SAE); + createCapabilityBitset(WifiManager.WIFI_FEATURE_WPA3_SAE)); initializeCmi(); if (SdkLevel.isAtLeastV()) { verify(mWifiGlobals).enableWpa3SaeH2eSupport(); @@ -11218,4 +11303,15 @@ public class ClientModeImplTest extends WifiBaseTest { verify(mWifiGlobals, never()).enableWpa3SaeH2eSupport(); } } + + @Test + public void testBlockNetwork() throws Exception { + connect(); + BlockingOption option = new BlockingOption.Builder(100) + .setBlockingBssidOnly(true).build(); + mCmi.blockNetwork(option); + verify(mWifiBlocklistMonitor).blockBssidForDurationMs(eq(TEST_BSSID_STR), any(), + eq(100 * 1000L), eq(REASON_APP_DISALLOW), eq(0)); + verify(mWifiBlocklistMonitor).updateAndGetBssidBlocklistForSsids(any()); + } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/ConcreteClientModeManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/ConcreteClientModeManagerTest.java index e30e8f8262..cfa03ff236 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/ConcreteClientModeManagerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/ConcreteClientModeManagerTest.java @@ -187,13 +187,15 @@ public class ConcreteClientModeManagerTest extends WifiBaseTest { * * no deferring time for wifi off */ mStaticMockSession = mockitoSession() - .mockStatic(ImsMmTelManager.class) - .mockStatic(SubscriptionManager.class) - .startMocking(); + .mockStatic(ImsMmTelManager.class) + .mockStatic(SubscriptionManager.class) + .mockStatic(WifiInjector.class) + .startMocking(); lenient().when(ImsMmTelManager.createForSubscriptionId(eq(TEST_ACTIVE_SUBSCRIPTION_ID))) .thenReturn(mImsMmTelManager); lenient().when(SubscriptionManager.isValidSubscriptionId(eq(TEST_ACTIVE_SUBSCRIPTION_ID))) .thenReturn(true); + when(WifiInjector.getInstance()).thenReturn(mWifiInjector); doAnswer(new AnswerWithArguments() { public void answer(Executor executor, RegistrationManager.RegistrationCallback c) { mImsMmTelManagerRegistrationCallback = c; @@ -498,6 +500,7 @@ public class ConcreteClientModeManagerTest extends WifiBaseTest { .thenReturn(false); InOrder inOrder = inOrder(mContext); mClientModeManager.setRole(ROLE_CLIENT_SCAN_ONLY, TEST_WORKSOURCE); + verify(mActiveModeWarden).setWifiStateForApiCalls(WIFI_STATE_DISABLING); mLooper.dispatchAll(); // Verify callback received for failed transition diff --git a/service/tests/wifitests/src/com/android/server/wifi/ConfigurationMapTest.java b/service/tests/wifitests/src/com/android/server/wifi/ConfigurationMapTest.java index dc8f7f676e..5225d518db 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/ConfigurationMapTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/ConfigurationMapTest.java @@ -17,6 +17,7 @@ package com.android.server.wifi; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; +import static com.android.server.wifi.TestUtil.createCapabilityBitset; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -110,8 +111,9 @@ public class ConfigurationMapTest extends WifiBaseTest { when(mWifiInjector.getActiveModeWarden()).thenReturn(mActiveModeWarden); when(mWifiInjector.getWifiGlobals()).thenReturn(mWifiGlobals); when(mActiveModeWarden.getPrimaryClientModeManager()).thenReturn(mPrimaryClientModeManager); - when(mPrimaryClientModeManager.getSupportedFeatures()).thenReturn( - WifiManager.WIFI_FEATURE_WPA3_SAE | WifiManager.WIFI_FEATURE_OWE); + when(mPrimaryClientModeManager.getSupportedFeaturesBitSet()).thenReturn( + createCapabilityBitset( + WifiManager.WIFI_FEATURE_WPA3_SAE, WifiManager.WIFI_FEATURE_OWE)); when(mWifiGlobals.isWpa3SaeUpgradeEnabled()).thenReturn(true); when(mWifiGlobals.isOweUpgradeEnabled()).thenReturn(true); diff --git a/service/tests/wifitests/src/com/android/server/wifi/DppManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/DppManagerTest.java index 1870690353..bda9d6e362 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/DppManagerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/DppManagerTest.java @@ -59,6 +59,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.anyString; @@ -623,7 +624,7 @@ public class DppManagerTest extends WifiBaseTest { verify(mDppMetrics).updateDppEnrolleeSuccess(); verify(mDppMetrics).updateDppOperationTime(anyInt()); verifyNoMoreInteractions(mDppMetrics); - verifyCleanUpResources(DppManager.DPP_AUTH_ROLE_INITIATOR); + verifyCleanUpResources(DppManager.DPP_AUTH_ROLE_INITIATOR, 1); assertFalse(mDppManager.isSessionInProgress()); } @@ -674,7 +675,7 @@ public class DppManagerTest extends WifiBaseTest { .EASY_CONNECT_EVENT_FAILURE_AUTHENTICATION)); verify(mDppMetrics).updateDppOperationTime(anyInt()); verifyNoMoreInteractions(mDppMetrics); - verifyCleanUpResources(DppManager.DPP_AUTH_ROLE_INITIATOR); + verifyCleanUpResources(DppManager.DPP_AUTH_ROLE_INITIATOR, 1); assertFalse(mDppManager.isSessionInProgress()); } @@ -724,7 +725,7 @@ public class DppManagerTest extends WifiBaseTest { .EASY_CONNECT_EVENT_FAILURE_AUTHENTICATION)); verify(mDppMetrics).updateDppOperationTime(anyInt()); verifyNoMoreInteractions(mDppMetrics); - verifyCleanUpResources(DppManager.DPP_AUTH_ROLE_INITIATOR); + verifyCleanUpResources(DppManager.DPP_AUTH_ROLE_INITIATOR, 1); assertFalse(mDppManager.isSessionInProgress()); } @@ -807,15 +808,15 @@ public class DppManagerTest extends WifiBaseTest { // Check that WifiNative is called to stop the DPP session mDppManager.stopDppSession(10); verify(mWifiNative).stopDppInitiator(eq(TEST_INTERFACE_NAME)); - verifyCleanUpResources(DppManager.DPP_AUTH_ROLE_INITIATOR); + verifyCleanUpResources(DppManager.DPP_AUTH_ROLE_INITIATOR, 1); assertFalse(mDppManager.isSessionInProgress()); } - private void verifyCleanUpResources(int authRole) { + private void verifyCleanUpResources(int authRole, int expectedNumOfCancelTimeOuts) { if (authRole == DppManager.DPP_AUTH_ROLE_INITIATOR) { verify(mWifiNative).removeDppUri(eq(TEST_INTERFACE_NAME), anyInt()); } - verify(mWakeupMessage).cancel(); + verify(mWakeupMessage, times(expectedNumOfCancelTimeOuts)).cancel(); } @Test @@ -1061,7 +1062,7 @@ public class DppManagerTest extends WifiBaseTest { verify(mDppMetrics, times(1)).updateDppR2EnrolleeResponderIncompatibleConfiguration(); } verifyNoMoreInteractions(mDppMetrics); - verifyCleanUpResources(DppManager.DPP_AUTH_ROLE_INITIATOR); + verifyCleanUpResources(DppManager.DPP_AUTH_ROLE_INITIATOR, 1); assertFalse(mDppManager.isSessionInProgress()); } @@ -1203,7 +1204,7 @@ public class DppManagerTest extends WifiBaseTest { verify(mDppMetrics).updateDppEnrolleeResponderSuccess(); verify(mDppMetrics).updateDppOperationTime(anyInt()); verifyNoMoreInteractions(mDppMetrics); - verifyCleanUpResources(DppManager.DPP_AUTH_ROLE_RESPONDER); + verifyCleanUpResources(DppManager.DPP_AUTH_ROLE_RESPONDER, 1); assertFalse(mDppManager.isSessionInProgress()); } @@ -1244,7 +1245,7 @@ public class DppManagerTest extends WifiBaseTest { .EASY_CONNECT_EVENT_FAILURE_AUTHENTICATION)); verify(mDppMetrics).updateDppOperationTime(anyInt()); verifyNoMoreInteractions(mDppMetrics); - verifyCleanUpResources(DppManager.DPP_AUTH_ROLE_RESPONDER); + verifyCleanUpResources(DppManager.DPP_AUTH_ROLE_RESPONDER, 1); assertFalse(mDppManager.isSessionInProgress()); } @@ -1273,7 +1274,7 @@ public class DppManagerTest extends WifiBaseTest { verify(mWifiNative).stopDppResponder(eq(TEST_INTERFACE_NAME), eq(TEST_BOOTSTRAP_ID)); verify(mDppMetrics).updateDppEnrolleeResponderRequests(); verifyNoMoreInteractions(mDppMetrics); - verifyCleanUpResources(DppManager.DPP_AUTH_ROLE_RESPONDER); + verifyCleanUpResources(DppManager.DPP_AUTH_ROLE_RESPONDER, 1); assertFalse(mDppManager.isSessionInProgress()); } @@ -1315,7 +1316,8 @@ public class DppManagerTest extends WifiBaseTest { // and check that DPP session is cleaned up & session is not in progress anymore. dppEventCallback.onConnectionStatusResultSent(0); mLooper.dispatchAll(); - verifyCleanUpResources(DppManager.DPP_AUTH_ROLE_RESPONDER); + verify(mWakeupMessage, times(2)).schedule(anyLong()); + verifyCleanUpResources(DppManager.DPP_AUTH_ROLE_RESPONDER, 2); assertFalse(mDppManager.isSessionInProgress()); } @@ -1356,7 +1358,8 @@ public class DppManagerTest extends WifiBaseTest { // and check that DPP session is cleaned up & session is not in progress anymore. dppEventCallback.onConnectionStatusResultSent(0); mLooper.dispatchAll(); - verifyCleanUpResources(DppManager.DPP_AUTH_ROLE_INITIATOR); + verify(mWakeupMessage, times(2)).schedule(anyLong()); + verifyCleanUpResources(DppManager.DPP_AUTH_ROLE_INITIATOR, 2); assertFalse(mDppManager.isSessionInProgress()); } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java index f4fe2814b8..40fd2597c6 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java @@ -23,7 +23,7 @@ import static com.android.server.wifi.HalDeviceManager.HDM_CREATE_IFACE_AP_BRIDG import static com.android.server.wifi.HalDeviceManager.HDM_CREATE_IFACE_NAN; import static com.android.server.wifi.HalDeviceManager.HDM_CREATE_IFACE_P2P; import static com.android.server.wifi.HalDeviceManager.HDM_CREATE_IFACE_STA; -import static com.android.server.wifi.util.GeneralUtil.longToBitset; +import static com.android.server.wifi.TestUtil.createCapabilityBitset; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; @@ -38,6 +38,7 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; @@ -1022,6 +1023,24 @@ public class HalDeviceManagerTest extends WifiBaseTest { HDM_CREATE_IFACE_AP, TEST_WORKSOURCE_1)); } + /** + * Test that BitSet comparisons are performed correctly in + * {@link HalDeviceManager#areChipCapabilitiesSupported(BitSet, BitSet)} + */ + @Test + public void testRequiredCapabilitiesComparison() { + BitSet capabilitySubset = createCapabilityBitset(WifiManager.WIFI_FEATURE_INFRA_60G); + BitSet capabilitySuperset = (BitSet) capabilitySubset.clone(); + capabilitySuperset.set(WifiManager.WIFI_FEATURE_BRIDGED_AP); + + assertTrue(HalDeviceManager.areChipCapabilitiesSupported( + capabilitySuperset /* current caps */, new BitSet() /* no desired caps */)); + assertTrue(HalDeviceManager.areChipCapabilitiesSupported( + capabilitySuperset /* current caps */, capabilitySubset /* desired caps */)); + assertFalse(HalDeviceManager.areChipCapabilitiesSupported( + capabilitySubset /* current caps */, capabilitySuperset /* desired caps */)); + } + ////////////////////////////////////////////////////////////////////////////////////// // Chip Specific Tests - but should work on all chips! // (i.e. add copies for each test chip) @@ -1787,7 +1806,6 @@ public class HalDeviceManagerTest extends WifiBaseTest { "[" + " {" + " \"chipId\": 10," - + " \"chipCapabilities\": -1," + " \"availableModes\": [" + " {" + " \"id\": 0," @@ -1863,6 +1881,92 @@ public class HalDeviceManagerTest extends WifiBaseTest { })); } + /** + * Validates that {@link HalDeviceManager#canDeviceSupportCreateTypeCombo(SparseArray)} with + * a faulty stored static chip info will be updated once we load the chip info when the driver + * is up. + */ + @Test + public void testCanDeviceSupportCreateTypeComboChipV1WithFaultyStoredStaticChipInfo() + throws Exception { + TestChipV1 chipMock = new TestChipV1(); + chipMock.initialize(); + mInOrder = inOrder(mWifiMock, chipMock.chip, mManagerStatusListenerMock); + + // Try to query iface support before starting the HAL. Should return false with the outdated + // stored static chip info that's missing AP capabilities. + String faultyStaticChipInfo = + "[" + + " {" + + " \"chipId\": 10," + + " \"chipCapabilities\": -1," + + " \"availableModes\": [" + + " {" + + " \"id\": 0," + + " \"availableCombinations\": [" + + " {" + + " \"limits\": [" + + " {" + + " \"maxIfaces\": 1," + + " \"types\": [0]" + + " }," + + " {" + + " \"maxIfaces\": 1," + + " \"types\": [3, 4]"; + when(mWifiMock.isStarted()).thenReturn(false); + when(mWifiSettingsConfigStore.get(WifiSettingsConfigStore.WIFI_STATIC_CHIP_INFO)) + .thenReturn(faultyStaticChipInfo); + + // Can't create anything since the stored static chip info is faulty. + assertFalse( + mDut.canDeviceSupportCreateTypeCombo( + new SparseArray<Integer>() { + { + put(WifiChip.IFACE_CONCURRENCY_TYPE_NAN, 1); + } + })); + assertFalse( + mDut.canDeviceSupportCreateTypeCombo( + new SparseArray<Integer>() { + { + put(WifiChip.IFACE_CONCURRENCY_TYPE_AP, 1); + } + })); + + verify(mWifiMock, never()).getChipIds(); + when(mWifiMock.isStarted()).thenReturn(true); + executeAndValidateStartupSequence(); + clearInvocations(mWifiMock); + + // Create a STA to get the static chip info from driver and save it to store. + validateInterfaceSequence( + chipMock, + false, // chipModeValid + -1000, // chipModeId (only used if chipModeValid is true) + HDM_CREATE_IFACE_STA, // ifaceTypeToCreate + "wlan0", // ifaceName + TestChipV1.STA_CHIP_MODE_ID, // finalChipMode + null, // tearDownList + mock(InterfaceDestroyedListener.class), // destroyedListener + TEST_WORKSOURCE_0 // requestorWs + ); + + // Verify that the latest static chip info is saved to store. + verify(mWifiSettingsConfigStore) + .put( + eq(WifiSettingsConfigStore.WIFI_STATIC_CHIP_INFO), + eq(new JSONArray(TestChipV1.STATIC_CHIP_INFO_JSON_STRING).toString())); + + // Now we can create an AP. + assertTrue( + mDut.canDeviceSupportCreateTypeCombo( + new SparseArray<Integer>() { + { + put(WifiChip.IFACE_CONCURRENCY_TYPE_AP, 1); + } + })); + } + @Test public void testIsItPossibleToCreateIfaceTestChipV1() throws Exception { assumeTrue(SdkLevel.isAtLeastS()); @@ -3288,7 +3392,8 @@ public class HalDeviceManagerTest extends WifiBaseTest { public void verify60GhzIfaceCreation( ChipMockBase chipMock, int chipModeId, int finalChipModeId, boolean isWigigSupported) throws Exception { - long requiredChipCapabilities = WifiManager.WIFI_FEATURE_INFRA_60G; + BitSet requiredChipCapabilities = + createCapabilityBitset(WifiManager.WIFI_FEATURE_INFRA_60G); chipMock.initialize(); mInOrder = inOrder(mWifiMock, chipMock.chip, mManagerStatusListenerMock); executeAndValidateStartupSequence(); @@ -3350,7 +3455,7 @@ public class HalDeviceManagerTest extends WifiBaseTest { when(mWorkSourceHelper1.getRequestorWsPriority()) .thenReturn(WorkSourceHelper.PRIORITY_PRIVILEGED); assertThat(mDut.isItPossibleToCreateIface(HDM_CREATE_IFACE_P2P, - WifiManager.WIFI_FEATURE_INFRA_60G, + createCapabilityBitset(WifiManager.WIFI_FEATURE_INFRA_60G), TEST_WORKSOURCE_1), is(isWigigSupported)); } } @@ -4117,6 +4222,157 @@ public class HalDeviceManagerTest extends WifiBaseTest { collector.checkThat("interface was null", p2pIface, IsNull.notNullValue()); } + /** + * Validate an AP request from requestors not on the AP/NAN concurrency allowlist. + */ + @Test + public void testApNanConcurrencyNotAllowedTestChipV11() + throws Exception { + when(mResources.getStringArray(R.array.config_wifiSoftApAwareConcurrencyAllowlist)) + .thenReturn(new String[]{"Some other package"}); + + TestChipV11 chipMock = new TestChipV11(); + chipMock.initialize(); + mInOrder = inOrder(mWifiMock, chipMock.chip, mManagerStatusListenerMock); + executeAndValidateStartupSequence(); + + InterfaceDestroyedListener idl = mock(InterfaceDestroyedListener.class); + + // Create a NAN + WifiInterface nanIface = validateInterfaceSequence(chipMock, + false, // chipModeValid + -1000, // chipModeId (only used if chipModeValid is true) + HDM_CREATE_IFACE_NAN, + "wlan0", + TestChipV11.CHIP_MODE_ID, + null, // tearDownList + idl, // destroyedListener + TEST_WORKSOURCE_0 // requestorWs + ); + collector.checkThat("interface was null", nanIface, IsNull.notNullValue()); + + // AP request from Worksource 1 (lower priority) should be blocked. + when(mWorkSourceHelper1.getRequestorWsPriority()) + .thenReturn(WorkSourceHelper.PRIORITY_FG_APP); + List<Pair<Integer, WorkSource>> apDetails = mDut.reportImpactToCreateIface( + HDM_CREATE_IFACE_AP, true, TEST_WORKSOURCE_1); + if (SdkLevel.isAtLeastS()) { + assertNull("Should not be able to create this AP", apDetails); + } else { + // Pre-S lets AP/NAN destroy each other. + assertEquals(apDetails.get(0).second, TEST_WORKSOURCE_0); + } + + // Bridged AP request should also be blocked + apDetails = mDut.reportImpactToCreateIface( + HDM_CREATE_IFACE_AP_BRIDGE, true, TEST_WORKSOURCE_1); + if (SdkLevel.isAtLeastS()) { + assertNull("Should not be able to create this Bridged AP", apDetails); + } else { + // Pre-S lets AP/NAN destroy each other. + assertEquals(apDetails.get(0).second, TEST_WORKSOURCE_0); + } + + // AP request from Worksource 2 (same privileged priority) should tear down NAN. + WifiInterface ApIface = validateInterfaceSequence(chipMock, + true, // chipModeValid + TestChipV11.CHIP_MODE_ID, // chipModeId (only used if chipModeValid is true) + HDM_CREATE_IFACE_AP, + "wlan1", + TestChipV11.CHIP_MODE_ID, + new WifiInterface[]{nanIface}, // tearDownList + idl, // destroyedListener + TEST_WORKSOURCE_2 // requestorWs + ); + collector.checkThat("interface was null", ApIface, IsNull.notNullValue()); + } + + /** + * Validate an AP request from a requestor on the AP/NAN concurrency allowlist. + */ + @Test + public void testSingleApNanConcurrencyAllowedTestChipV11() + throws Exception { + when(mResources.getStringArray(R.array.config_wifiSoftApAwareConcurrencyAllowlist)) + .thenReturn(new String[]{TEST_WORKSOURCE_1.getPackageName(0)}); + + TestChipV11 chipMock = new TestChipV11(); + chipMock.initialize(); + mInOrder = inOrder(mWifiMock, chipMock.chip, mManagerStatusListenerMock); + executeAndValidateStartupSequence(); + + InterfaceDestroyedListener idl = mock(InterfaceDestroyedListener.class); + + // Create a NAN + WifiInterface nanIface = validateInterfaceSequence(chipMock, + false, // chipModeValid + -1000, // chipModeId (only used if chipModeValid is true) + HDM_CREATE_IFACE_NAN, + "wlan0", + TestChipV11.CHIP_MODE_ID, + null, // tearDownList + idl, // destroyedListener + TEST_WORKSOURCE_0 // requestorWs + ); + collector.checkThat("interface was null", nanIface, IsNull.notNullValue()); + + // AP request from Worksource 1 (on allowlist) should be allowed. + WifiInterface apIface = validateInterfaceSequence(chipMock, + true, // chipModeValid + TestChipV11.CHIP_MODE_ID, // chipModeId (only used if chipModeValid is true) + HDM_CREATE_IFACE_AP, + "wlan1", + TestChipV11.CHIP_MODE_ID, + null, // tearDownList + idl, // destroyedListener + TEST_WORKSOURCE_1 // requestorWs + ); + collector.checkThat("interface was null", apIface, IsNull.notNullValue()); + } + + /** + * Validate a Bridged AP request from a requestor on the AP/NAN concurrency allowlist. + */ + @Test + public void testBridgedApNanConcurrencyAllowedTestChipV11() + throws Exception { + when(mResources.getStringArray(R.array.config_wifiSoftApAwareConcurrencyAllowlist)) + .thenReturn(new String[]{TEST_WORKSOURCE_1.getPackageName(0)}); + + TestChipV11 chipMock = new TestChipV11(); + chipMock.initialize(); + mInOrder = inOrder(mWifiMock, chipMock.chip, mManagerStatusListenerMock); + executeAndValidateStartupSequence(); + + InterfaceDestroyedListener idl = mock(InterfaceDestroyedListener.class); + + // Create a NAN + WifiInterface nanIface = validateInterfaceSequence(chipMock, + false, // chipModeValid + -1000, // chipModeId (only used if chipModeValid is true) + HDM_CREATE_IFACE_NAN, + "wlan0", + TestChipV11.CHIP_MODE_ID, + null, // tearDownList + idl, // destroyedListener + TEST_WORKSOURCE_0 // requestorWs + ); + collector.checkThat("interface was null", nanIface, IsNull.notNullValue()); + + // Bridged AP request from Worksource 1 (on allowlist) should be allowed. + WifiInterface apIface = validateInterfaceSequence(chipMock, + true, // chipModeValid + TestChipV11.CHIP_MODE_ID, // chipModeId (only used if chipModeValid is true) + HDM_CREATE_IFACE_AP_BRIDGE, + "wlan1", + TestChipV11.CHIP_MODE_ID, + null, // tearDownList + idl, // destroyedListener + TEST_WORKSOURCE_1 // requestorWs + ); + collector.checkThat("interface was null", apIface, IsNull.notNullValue()); + } + /////////////////////////////////////////////////////////////////////////////////////// // utilities /////////////////////////////////////////////////////////////////////////////////////// @@ -4294,7 +4550,7 @@ public class HalDeviceManagerTest extends WifiBaseTest { private WifiInterface validateInterfaceSequence(ChipMockBase chipMock, boolean chipModeValid, int chipModeId, int createIfaceType, String ifaceName, int finalChipMode, - long requiredChipCapabilities, + BitSet requiredChipCapabilities, WifiInterface[] tearDownList, InterfaceDestroyedListener destroyedListener, WorkSource requestorWs, @@ -4338,8 +4594,8 @@ public class HalDeviceManagerTest extends WifiBaseTest { } doAnswer(new CreateApIfaceAnswer(chipMock, true, iface)) .when(chipMock.chip).createApIface(anyList()); - doAnswer(new CreateApIfaceAnswer(chipMock, true, iface)) - .when(chipMock.chip).createBridgedApIface(anyList()); + doAnswer(new CreateBridgedApIfaceAnswer(chipMock, true, iface)) + .when(chipMock.chip).createBridgedApIface(anyList(), anyBoolean()); mDut.createApIface(requiredChipCapabilities, destroyedListener, mHandler, requestorWs, createIfaceType == HDM_CREATE_IFACE_AP_BRIDGE, mSoftApManager, @@ -4395,7 +4651,7 @@ public class HalDeviceManagerTest extends WifiBaseTest { mInOrder.verify(chipMock.chip).createStaIface(); break; case HDM_CREATE_IFACE_AP_BRIDGE: - mInOrder.verify(chipMock.chip).createBridgedApIface(anyList()); + mInOrder.verify(chipMock.chip).createBridgedApIface(anyList(), anyBoolean()); break; case HDM_CREATE_IFACE_AP: mInOrder.verify(chipMock.chip).createApIface(anyList()); @@ -4493,7 +4749,7 @@ public class HalDeviceManagerTest extends WifiBaseTest { public WifiChip.Response<BitSet> answer() { WifiChip.Response<BitSet> response = - new WifiChip.Response<>(longToBitset(mChipMockBase.chipCapabilities)); + new WifiChip.Response<>(mChipMockBase.chipCapabilities); response.setStatusCode(mChipMockBase.allowGetCapsBeforeIfaceCreated ? WifiHal.WIFI_STATUS_SUCCESS : WifiHal.WIFI_STATUS_ERROR_UNKNOWN); return response; @@ -4669,6 +4925,20 @@ public class HalDeviceManagerTest extends WifiBaseTest { } } + private class CreateBridgedApIfaceAnswer extends CreateXxxIfaceAnswer { + CreateBridgedApIfaceAnswer(ChipMockBase chipMockBase, boolean success, + WifiInterface wifiIface) { + super(chipMockBase, success, wifiIface, WifiChip.IFACE_TYPE_AP); + } + + @SuppressWarnings("unused") + public WifiApIface answer(@NonNull List<OuiKeyedData> vendorData, + boolean isUsingMultiLinkOperation) { + addInterfaceInfo(); + return mSuccess ? (WifiApIface) mWifiIface : null; + } + } + private class CreateP2pIfaceAnswer extends CreateXxxIfaceAnswer { CreateP2pIfaceAnswer(ChipMockBase chipMockBase, boolean success, WifiInterface wifiIface) { super(chipMockBase, success, wifiIface, WifiChip.IFACE_TYPE_P2P); @@ -4809,7 +5079,7 @@ public class HalDeviceManagerTest extends WifiBaseTest { public boolean chipModeValid = false; public int chipModeId = -1000; public int chipModeIdValidForRtt = -1; // single chip mode ID where RTT can be created - public long chipCapabilities = 0L; + public BitSet chipCapabilities = new BitSet(); public boolean allowGetCapsBeforeIfaceCreated = true; public List<WifiChip.WifiRadioCombination> chipSupportedRadioCombinations = null; public Map<Integer, ArrayList<String>> interfaceNames = new HashMap<>(); @@ -4886,7 +5156,6 @@ public class HalDeviceManagerTest extends WifiBaseTest { static final String STATIC_CHIP_INFO_JSON_STRING = "[" + " {" + " \"chipId\": 10," - + " \"chipCapabilities\": 0," + " \"availableModes\": [" + " {" + " \"id\": 0," @@ -4963,7 +5232,6 @@ public class HalDeviceManagerTest extends WifiBaseTest { static final String STATIC_CHIP_INFO_JSON_STRING = "[" + " {" + " \"chipId\": 12," - + " \"chipCapabilities\": 0," + " \"availableModes\": [" + " {" + " \"id\": 5," @@ -5112,7 +5380,7 @@ public class HalDeviceManagerTest extends WifiBaseTest { super.initialize(); chipMockId = CHIP_MOCK_V5; - chipCapabilities |= WifiManager.WIFI_FEATURE_INFRA_60G; + chipCapabilities.set(WifiManager.WIFI_FEATURE_INFRA_60G); // chip Id configuration ArrayList<Integer> chipIds; @@ -5169,7 +5437,6 @@ public class HalDeviceManagerTest extends WifiBaseTest { static final String STATIC_CHIP_INFO_JSON_STRING = "[" + " {" + " \"chipId\": 6," - + " \"chipCapabilities\": 0," + " \"availableModes\": [" + " {" + " \"id\": 60," @@ -5352,7 +5619,6 @@ public class HalDeviceManagerTest extends WifiBaseTest { static final String STATIC_CHIP_INFO_JSON_STRING = "[" + " {" + " \"chipId\": 10," - + " \"chipCapabilities\": -1," + " \"availableModes\": [" + " {" + " \"id\": 100," @@ -5418,7 +5684,7 @@ public class HalDeviceManagerTest extends WifiBaseTest { // Test chip configuration V11 for P2P/NAN concurrency // mode: - // (STA + AP + NAN + P2P) + // (STA + AP + AP_BRIDGE + NAN + P2P) private class TestChipV11 extends ChipMockBase { static final int CHIP_MODE_ID = 90; @@ -5440,6 +5706,7 @@ public class HalDeviceManagerTest extends WifiBaseTest { WifiChip.ChipConcurrencyCombination combo1 = createConcurrencyCombo( createConcurrencyComboLimit(1, WifiChip.IFACE_CONCURRENCY_TYPE_STA), createConcurrencyComboLimit(1, WifiChip.IFACE_CONCURRENCY_TYPE_AP), + createConcurrencyComboLimit(1, WifiChip.IFACE_CONCURRENCY_TYPE_AP_BRIDGED), createConcurrencyComboLimit(1, WifiChip.IFACE_CONCURRENCY_TYPE_P2P), createConcurrencyComboLimit(1, WifiChip.IFACE_CONCURRENCY_TYPE_NAN)); availableModes.add(createChipMode(CHIP_MODE_ID, combo1)); diff --git a/service/tests/wifitests/src/com/android/server/wifi/HostapdHalAidlImpTest.java b/service/tests/wifitests/src/com/android/server/wifi/HostapdHalAidlImpTest.java index 0d54c8114f..ffc9446327 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/HostapdHalAidlImpTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/HostapdHalAidlImpTest.java @@ -34,8 +34,10 @@ import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; +import static org.mockito.Mockito.validateMockitoUsage; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.mockito.Mockito.withSettings; import android.app.test.MockAnswerUtil; import android.hardware.wifi.hostapd.ApInfo; @@ -53,11 +55,13 @@ import android.hardware.wifi.hostapd.Ieee80211ReasonCode; import android.hardware.wifi.hostapd.IfaceParams; import android.hardware.wifi.hostapd.NetworkParams; import android.net.MacAddress; +import android.net.wifi.DeauthenticationReasonCode; import android.net.wifi.OuiKeyedData; import android.net.wifi.SoftApConfiguration; import android.net.wifi.SoftApConfiguration.Builder; import android.net.wifi.WifiContext; import android.net.wifi.WifiManager; +import android.net.wifi.util.Environment; import android.net.wifi.util.PersistableBundleUtils; import android.net.wifi.util.WifiResourceCache; import android.os.Binder; @@ -72,19 +76,24 @@ import android.util.SparseIntArray; import androidx.test.filters.SmallTest; +import com.android.dx.mockito.inline.extended.ExtendedMockito; import com.android.modules.utils.build.SdkLevel; import com.android.server.wifi.util.ApConfigUtil; import com.android.server.wifi.util.NativeUtil; +import com.android.wifi.flags.Flags; import com.android.wifi.resources.R; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.MockitoSession; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; /** * Unit tests for HostapdHal @@ -98,10 +107,15 @@ public class HostapdHalAidlImpTest extends WifiBaseTest { private static final String TEST_CLIENT_MAC = "11:22:33:44:55:66"; private static final String TEST_AP_INSTANCE = "instance-wlan0"; private static final String TEST_AP_INSTANCE_2 = "instance-wlan1"; + private static final String TEST_MLD_MAC = "aa:bb:cc:dd:ee:ff"; private static final int TEST_FREQ_24G = 2412; private static final int TEST_FREQ_5G = 5745; private static final int TEST_BANDWIDTH = ChannelBandwidth.BANDWIDTH_20; private static final int TEST_GENERATION = Generation.WIFI_STANDARD_11N; + private static final int TEST_HAL_DEAUTHENTICATION_REASON_CODE = + android.hardware.wifi.common.DeauthenticationReasonCode.GROUP_CIPHER_NOT_VALID; + private static final int DEFAULT_DISCONNECT_REASON = + DeauthenticationReasonCode.REASON_UNKNOWN; private final int mBand256G = SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ | SoftApConfiguration.BAND_6GHZ; @@ -115,6 +129,7 @@ public class HostapdHalAidlImpTest extends WifiBaseTest { private IHostapdCallback mIHostapdCallback; private MockResources mResources; + private MockitoSession mSession; private TestLooper mLooper = new TestLooper(); private HostapdHalAidlImp mHostapdHal; @@ -153,7 +168,7 @@ public class HostapdHalAidlImpTest extends WifiBaseTest { private void mockApInfoChangedAndVerify(String ifaceName, int numOfApInfo, IHostapdCallback mockHostapdCallback, - WifiNative.SoftApHalCallback mockSoftApHalCallback) throws Exception { + WifiNative.SoftApHalCallback mockSoftApHalCallback, boolean isMLD) throws Exception { // Trigger on info changed. ApInfo apInfo = new ApInfo(); apInfo.ifaceName = ifaceName; @@ -162,12 +177,16 @@ public class HostapdHalAidlImpTest extends WifiBaseTest { apInfo.channelBandwidth = TEST_BANDWIDTH; apInfo.generation = TEST_GENERATION; apInfo.apIfaceInstanceMacAddress = MacAddress.fromString(TEST_CLIENT_MAC).toByteArray(); + if (isMLD) { + apInfo.mldMacAddress = MacAddress.fromString(TEST_MLD_MAC).toByteArray(); + } if (numOfApInfo == 1) { mockHostapdCallback.onApInstanceInfoChanged(apInfo); verify(mockSoftApHalCallback).onInfoChanged(eq(TEST_AP_INSTANCE), eq(TEST_FREQ_24G), eq(mHostapdHal.mapHalChannelBandwidthToSoftApInfo(TEST_BANDWIDTH)), eq(mHostapdHal.mapHalGenerationToWifiStandard(TEST_GENERATION)), - eq(MacAddress.fromString(TEST_CLIENT_MAC)), anyList()); + eq(MacAddress.fromString(TEST_CLIENT_MAC)), + isMLD ? eq(MacAddress.fromString(TEST_MLD_MAC)) : eq(null), anyList()); } else if (numOfApInfo == 2) { apInfo.apIfaceInstance = TEST_AP_INSTANCE_2; apInfo.freqMhz = TEST_FREQ_5G; @@ -175,13 +194,17 @@ public class HostapdHalAidlImpTest extends WifiBaseTest { verify(mockSoftApHalCallback).onInfoChanged(eq(TEST_AP_INSTANCE_2), eq(TEST_FREQ_5G), eq(mHostapdHal.mapHalChannelBandwidthToSoftApInfo(TEST_BANDWIDTH)), eq(mHostapdHal.mapHalGenerationToWifiStandard(TEST_GENERATION)), - eq(MacAddress.fromString(TEST_CLIENT_MAC)), anyList()); + eq(MacAddress.fromString(TEST_CLIENT_MAC)), + isMLD ? eq(MacAddress.fromString(TEST_MLD_MAC)) : eq(null), anyList()); } } @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + mSession = ExtendedMockito.mockitoSession() + .mockStatic(Flags.class, withSettings().lenient()) + .startMocking(); mResources = new MockResources(); mResources.setBoolean(R.bool.config_wifi_softap_acs_supported, false); mResources.setBoolean(R.bool.config_wifi_softap_ieee80211ac_supported, false); @@ -191,7 +214,7 @@ public class HostapdHalAidlImpTest extends WifiBaseTest { mResources.setString(R.string.config_wifiSoftap2gChannelList, ""); mResources.setString(R.string.config_wifiSoftap5gChannelList, ""); mResources.setString(R.string.config_wifiSoftap6gChannelList, ""); - + when(Flags.mloSap()).thenReturn(true); when(mContext.getResources()).thenReturn(mResources); when(mContext.getResourceCache()).thenReturn(new WifiResourceCache(mContext)); doNothing().when(mIHostapdMock).addAccessPoint( @@ -201,6 +224,17 @@ public class HostapdHalAidlImpTest extends WifiBaseTest { } /** + * Called after each test + */ + @After + public void cleanup() { + validateMockitoUsage(); + if (mSession != null) { + mSession.finishMocking(); + } + } + + /** * Sunny day scenario for HostapdHal initialization * Asserts successful initialization */ @@ -267,7 +301,7 @@ public class HostapdHalAidlImpTest extends WifiBaseTest { configurationBuilder.setChannel(apChannel, SoftApConfiguration.BAND_2GHZ); assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), true, + configurationBuilder.build(), true, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMock).addAccessPoint(any(), any()); @@ -300,7 +334,7 @@ public class HostapdHalAidlImpTest extends WifiBaseTest { configurationBuilder.setChannel(apChannel, SoftApConfiguration.BAND_5GHZ); assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), true, + configurationBuilder.build(), true, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMock).addAccessPoint(any(), any()); @@ -335,7 +369,7 @@ public class HostapdHalAidlImpTest extends WifiBaseTest { configurationBuilder.setChannel(apChannel, SoftApConfiguration.BAND_5GHZ); assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), true, + configurationBuilder.build(), true, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMock).addAccessPoint(any(), any()); @@ -374,7 +408,7 @@ public class HostapdHalAidlImpTest extends WifiBaseTest { configurationBuilder.setChannel(apChannel, SoftApConfiguration.BAND_2GHZ); assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), true, + configurationBuilder.build(), true, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMock).addAccessPoint(any(), any()); @@ -414,7 +448,7 @@ public class HostapdHalAidlImpTest extends WifiBaseTest { configurationBuilder.setChannel(apChannel, SoftApConfiguration.BAND_2GHZ); assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), true, + configurationBuilder.build(), true, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMock).addAccessPoint(any(), any()); @@ -452,7 +486,7 @@ public class HostapdHalAidlImpTest extends WifiBaseTest { configurationBuilder.setBand(mBand256G); assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), true, + configurationBuilder.build(), true, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMock).addAccessPoint(any(), any()); @@ -489,7 +523,7 @@ public class HostapdHalAidlImpTest extends WifiBaseTest { configurationBuilder.setBand(mBand256G); assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), true, + configurationBuilder.build(), true, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMock).addAccessPoint(any(), any()); @@ -518,7 +552,7 @@ public class HostapdHalAidlImpTest extends WifiBaseTest { configurationBuilder.setChannel(6, SoftApConfiguration.BAND_2GHZ); assertFalse(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), true, + configurationBuilder.build(), true, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMock).addAccessPoint(any(), any()); } @@ -537,7 +571,7 @@ public class HostapdHalAidlImpTest extends WifiBaseTest { configurationBuilder.setChannel(6, SoftApConfiguration.BAND_2GHZ); assertFalse(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), true, + configurationBuilder.build(), true, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMock).addAccessPoint(any(), any()); } @@ -590,7 +624,7 @@ public class HostapdHalAidlImpTest extends WifiBaseTest { configurationBuilder.setBand(SoftApConfiguration.BAND_2GHZ); assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), true, + configurationBuilder.build(), true, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMock).addAccessPoint(any(), any()); @@ -769,7 +803,7 @@ public class HostapdHalAidlImpTest extends WifiBaseTest { mIfaceParamsCaptor.capture(), mNetworkParamsCaptor.capture()); assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), true, + configurationBuilder.build(), true, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMock).addAccessPoint(any(), any()); @@ -808,7 +842,7 @@ public class HostapdHalAidlImpTest extends WifiBaseTest { mIfaceParamsCaptor.capture(), mNetworkParamsCaptor.capture()); assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), true, + configurationBuilder.build(), true, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMock).addAccessPoint(any(), any()); @@ -845,7 +879,7 @@ public class HostapdHalAidlImpTest extends WifiBaseTest { configurationBuilder.setBand(mBand256G); assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), true, + configurationBuilder.build(), true, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMock).addAccessPoint(any(), any()); @@ -869,6 +903,7 @@ public class HostapdHalAidlImpTest extends WifiBaseTest { */ @Test public void testHostapdCallbackEvent() throws Exception { + when(mIHostapdMock.getInterfaceVersion()).thenReturn(3); executeAndValidateInitializationSequence(true); Builder configurationBuilder = new SoftApConfiguration.Builder(); configurationBuilder.setSsid(NETWORK_SSID); @@ -876,7 +911,7 @@ public class HostapdHalAidlImpTest extends WifiBaseTest { doNothing().when(mIHostapdMock).addAccessPoint(any(), any()); assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), true, + configurationBuilder.build(), true, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMock).addAccessPoint(any(), any()); // Register SoftApManager callback @@ -884,7 +919,7 @@ public class HostapdHalAidlImpTest extends WifiBaseTest { // Add second AP to test that the callbacks are triggered for the correct iface. assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME_1, - configurationBuilder.build(), true, + configurationBuilder.build(), true, false, Collections.emptyList(), () -> mSoftApHalCallback1.onFailure())); mHostapdHal.registerApCallback(IFACE_NAME_1, mSoftApHalCallback1); @@ -894,9 +929,9 @@ public class HostapdHalAidlImpTest extends WifiBaseTest { verify(mSoftApHalCallback1, never()).onFailure(); // Trigger on info changed and verify. - mockApInfoChangedAndVerify(IFACE_NAME, 1, mIHostapdCallback, mSoftApHalCallback); + mockApInfoChangedAndVerify(IFACE_NAME, 1, mIHostapdCallback, mSoftApHalCallback, false); verify(mSoftApHalCallback1, never()).onInfoChanged(anyString(), anyInt(), anyInt(), - anyInt(), any(), anyList()); + anyInt(), any(), any(), anyList()); // Trigger on client connected. ClientInfo clientInfo = new ClientInfo(); @@ -906,16 +941,32 @@ public class HostapdHalAidlImpTest extends WifiBaseTest { clientInfo.isConnected = true; mIHostapdCallback.onConnectedClientsChanged(clientInfo); verify(mSoftApHalCallback).onConnectedClientsChanged(eq(TEST_AP_INSTANCE), - eq(MacAddress.fromString(TEST_CLIENT_MAC)), eq(true)); + eq(MacAddress.fromString(TEST_CLIENT_MAC)), eq(true), + eq(DEFAULT_DISCONNECT_REASON)); verify(mSoftApHalCallback1, never()).onConnectedClientsChanged( - anyString(), any(), anyBoolean()); + anyString(), any(), anyBoolean(), anyInt()); + + // Trigger client disconnect + clientInfo = new ClientInfo(); + clientInfo.ifaceName = IFACE_NAME; + clientInfo.apIfaceInstance = TEST_AP_INSTANCE; + clientInfo.clientAddress = MacAddress.fromString(TEST_CLIENT_MAC).toByteArray(); + clientInfo.isConnected = false; + clientInfo.disconnectReasonCode = TEST_HAL_DEAUTHENTICATION_REASON_CODE; + mIHostapdCallback.onConnectedClientsChanged(clientInfo); + verify(mSoftApHalCallback).onConnectedClientsChanged(eq(TEST_AP_INSTANCE), + eq(MacAddress.fromString(TEST_CLIENT_MAC)), eq(false), + eq(mHostapdHal.mapHalToFrameworkDeauthenticationReasonCode( + TEST_HAL_DEAUTHENTICATION_REASON_CODE))); + verify(mSoftApHalCallback1, never()).onConnectedClientsChanged( + anyString(), any(), anyBoolean(), anyInt()); } /** - * Verifies the successful addition of access point with SAE with metered indication. + * Verifies the successful addition of access point with SAE with metered. */ @Test - public void testAddAccessPointSuccess_WithMeteredSAE() throws Exception { + public void testAddSAEAccessPointSuccess_WithMetered() throws Exception { boolean isMetered = true; mResources.setBoolean(R.bool.config_wifi_softap_acs_supported, true); mHostapdHal = new HostapdHalSpy(); @@ -933,7 +984,7 @@ public class HostapdHalAidlImpTest extends WifiBaseTest { mIfaceParamsCaptor.capture(), mNetworkParamsCaptor.capture()); assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), isMetered, + configurationBuilder.build(), isMetered, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMock).addAccessPoint(any(), any()); @@ -973,7 +1024,7 @@ public class HostapdHalAidlImpTest extends WifiBaseTest { mIfaceParamsCaptor.capture(), mNetworkParamsCaptor.capture()); assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), isMetered, + configurationBuilder.build(), isMetered, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMock).addAccessPoint(any(), any()); @@ -1043,7 +1094,7 @@ public class HostapdHalAidlImpTest extends WifiBaseTest { mIfaceParamsCaptor.capture(), mNetworkParamsCaptor.capture()); assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), true, + configurationBuilder.build(), true, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMock).addAccessPoint(any(), any()); @@ -1109,7 +1160,7 @@ public class HostapdHalAidlImpTest extends WifiBaseTest { mIfaceParamsCaptor.capture(), mNetworkParamsCaptor.capture()); assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), isMetered, + configurationBuilder.build(), isMetered, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMock).addAccessPoint(any(), any()); @@ -1155,7 +1206,7 @@ public class HostapdHalAidlImpTest extends WifiBaseTest { mIfaceParamsCaptor.capture(), mNetworkParamsCaptor.capture()); assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), isMetered, + configurationBuilder.build(), isMetered, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMock).addAccessPoint(any(), any()); @@ -1178,7 +1229,7 @@ public class HostapdHalAidlImpTest extends WifiBaseTest { * Verifies the onFailure event in bridged mode. */ @Test - public void testHostapdCallbackOnFailureEventInBridgedMode() throws Exception { + public void testHostapdCallbackOnFailureEventInMldBridgedMode() throws Exception { assumeTrue(SdkLevel.isAtLeastT()); executeAndValidateInitializationSequence(true); Builder configurationBuilder = new SoftApConfiguration.Builder(); @@ -1188,7 +1239,7 @@ public class HostapdHalAidlImpTest extends WifiBaseTest { doNothing().when(mIHostapdMock).addAccessPoint(any(), any()); assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), true, + configurationBuilder.build(), true, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMock).addAccessPoint(any(), any()); @@ -1196,8 +1247,8 @@ public class HostapdHalAidlImpTest extends WifiBaseTest { mHostapdHal.registerApCallback(IFACE_NAME, mSoftApHalCallback); // Trigger on info changed and verify. - mockApInfoChangedAndVerify(IFACE_NAME, 1, mIHostapdCallback, mSoftApHalCallback); - mockApInfoChangedAndVerify(IFACE_NAME, 2, mIHostapdCallback, mSoftApHalCallback); + mockApInfoChangedAndVerify(IFACE_NAME, 1, mIHostapdCallback, mSoftApHalCallback, true); + mockApInfoChangedAndVerify(IFACE_NAME, 2, mIHostapdCallback, mSoftApHalCallback, true); // Trigger on instance failure from first instance. mIHostapdCallback.onFailure(IFACE_NAME, TEST_AP_INSTANCE); @@ -1222,7 +1273,7 @@ public class HostapdHalAidlImpTest extends WifiBaseTest { doNothing().when(mIHostapdMock).addAccessPoint(any(), any()); assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), true, + configurationBuilder.build(), true, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMock).addAccessPoint(any(), any()); @@ -1230,7 +1281,7 @@ public class HostapdHalAidlImpTest extends WifiBaseTest { mHostapdHal.registerApCallback(IFACE_NAME, mSoftApHalCallback); // Trigger on info changed and verify. - mockApInfoChangedAndVerify(IFACE_NAME, 1, mIHostapdCallback, mSoftApHalCallback); + mockApInfoChangedAndVerify(IFACE_NAME, 1, mIHostapdCallback, mSoftApHalCallback, false); // Trigger on failure from first instance. mIHostapdCallback.onFailure(IFACE_NAME, TEST_AP_INSTANCE); @@ -1246,7 +1297,11 @@ public class HostapdHalAidlImpTest extends WifiBaseTest { /** * Verifies that SoftApConfigurations containing OEM-specific vendor data * are handled currently in addAccessPoint. + * + * SuppressWarnings DirectInvocationOnMock for "mSoftApHalCallback.onFailure()" + * since it is a lambda callback implementation. Not a really function call. */ + @SuppressWarnings("DirectInvocationOnMock") @Test public void testAddAccessPointWithVendorData() throws Exception { assumeTrue(SdkLevel.isAtLeastV()); @@ -1259,7 +1314,7 @@ public class HostapdHalAidlImpTest extends WifiBaseTest { // SoftApConfig does not contain vendor data. assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), true, + configurationBuilder.build(), true, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMock).addAccessPoint(any(), any()); assertNull(mIfaceParamsCaptor.getValue().vendorData); @@ -1272,7 +1327,7 @@ public class HostapdHalAidlImpTest extends WifiBaseTest { // SoftApConfig contains vendor data. assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), true, + configurationBuilder.build(), true, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMock, times(2)).addAccessPoint(any(), any()); android.hardware.wifi.common.OuiKeyedData[] halDataList = @@ -1281,4 +1336,81 @@ public class HostapdHalAidlImpTest extends WifiBaseTest { assertEquals(oui, halDataList[0].oui); assertTrue(PersistableBundleUtils.isEqual(bundle, halDataList[0].vendorData)); } + + /** + * Verifies that MLO AP is handled currently in addAccessPoint. + * + * SuppressWarnings DirectInvocationOnMock for "mSoftApHalCallback.onFailure()" + * since it is a lambda callback implementation. Not a really function call. + */ + @SuppressWarnings("DirectInvocationOnMock") + @Test + public void testAddAccessPointWithMLOConfiguration() throws Exception { + assumeTrue(SdkLevel.isAtLeastT()); + when(mIHostapdMock.getInterfaceVersion()).thenReturn(3); + executeAndValidateInitializationSequence(true); + Builder configurationBuilder = new SoftApConfiguration.Builder(); + configurationBuilder.setSsid(NETWORK_SSID); + configurationBuilder.setBands(new int[] {SoftApConfiguration.BAND_2GHZ, + SoftApConfiguration.BAND_5GHZ}); + doNothing().when(mIHostapdMock).addAccessPoint(mIfaceParamsCaptor.capture(), any()); + assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, + configurationBuilder.build(), true, true, Arrays.asList(new String[] {"1", "2"}), + () -> mSoftApHalCallback.onFailure())); + verify(mIHostapdMock).addAccessPoint(any(), any()); + assertTrue(mIfaceParamsCaptor.getValue().usesMlo); + assertTrue(Arrays.equals(new String[] {"1", "2"}, + mIfaceParamsCaptor.getValue().instanceIdentities)); + } + + /* + * Verifies the successful addition of access point with SAE with + * client isolation indication. + * + * SuppressWarnings DirectInvocationOnMock for "mSoftApHalCallback.onFailure()" + * since it is a lambda callback implementation. Not a really function call. + */ + @SuppressWarnings("DirectInvocationOnMock") + @Test + public void testAddSAEAccessPointSuccess_WithClientIsolationAndNullBridgedInstances() + throws Exception { + assumeTrue(Environment.isSdkAtLeastB()); + mResources.setBoolean(R.bool.config_wifi_softap_acs_supported, true); + when(Flags.apIsolate()).thenReturn(true); + when(mIHostapdMock.getInterfaceVersion()).thenReturn(3); + mHostapdHal = new HostapdHalSpy(); + + executeAndValidateInitializationSequence(true); + + Builder configurationBuilder = new SoftApConfiguration.Builder(); + configurationBuilder.setSsid(NETWORK_SSID); + configurationBuilder.setClientIsolationEnabled(true); + configurationBuilder.setHiddenSsid(false); + configurationBuilder.setPassphrase(NETWORK_PSK, + SoftApConfiguration.SECURITY_TYPE_WPA3_SAE); + configurationBuilder.setBand(mBand256G); + + doNothing().when(mIHostapdMock).addAccessPoint( + mIfaceParamsCaptor.capture(), mNetworkParamsCaptor.capture()); + + // Null instanceIdentities won't cause crash. + assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, + configurationBuilder.build(), true, false, null /* instanceIdentities */, + () -> mSoftApHalCallback.onFailure())); + verify(mIHostapdMock).addAccessPoint(any(), any()); + + assertEquals(IFACE_NAME, mIfaceParamsCaptor.getValue().name); + assertTrue(mIfaceParamsCaptor.getValue().hwModeParams.enable80211N); + assertFalse(mIfaceParamsCaptor.getValue().hwModeParams.enable80211AC); + assertTrue(mIfaceParamsCaptor.getValue().channelParams[0].enableAcs); + + assertEquals(NETWORK_SSID, + NativeUtil.stringFromByteArray(mNetworkParamsCaptor.getValue().ssid)); + assertTrue(mNetworkParamsCaptor.getValue().isClientIsolationEnabled); + assertFalse(mNetworkParamsCaptor.getValue().isHidden); + assertEquals(EncryptionType.WPA3_SAE, + mNetworkParamsCaptor.getValue().encryptionType); + assertEquals(NETWORK_PSK, mNetworkParamsCaptor.getValue().passphrase); + assertTrue(mNetworkParamsCaptor.getValue().isMetered); + } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/HostapdHalHidlImpTest.java b/service/tests/wifitests/src/com/android/server/wifi/HostapdHalHidlImpTest.java index a6a9010a18..ab45a35d76 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/HostapdHalHidlImpTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/HostapdHalHidlImpTest.java @@ -49,6 +49,7 @@ import android.hardware.wifi.hostapd.V1_3.Generation; import android.hidl.manager.V1_0.IServiceManager; import android.hidl.manager.V1_0.IServiceNotification; import android.net.MacAddress; +import android.net.wifi.DeauthenticationReasonCode; import android.net.wifi.SoftApConfiguration; import android.net.wifi.SoftApConfiguration.Builder; import android.net.wifi.WifiContext; @@ -74,6 +75,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.ArrayList; +import java.util.Collections; /** * Unit tests for HostapdHal @@ -86,6 +88,8 @@ public class HostapdHalHidlImpTest extends WifiBaseTest { private static final String NETWORK_PSK = "test-psk"; private static final String TEST_CLIENT_MAC = "11:22:33:44:55:66"; private static final String TEST_AP_INSTANCE = "instance-wlan0"; + private static final int DEFAULT_DISCONNECT_REASON = + DeauthenticationReasonCode.REASON_UNKNOWN; private final int mBand256G = SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ | SoftApConfiguration.BAND_6GHZ; @@ -300,7 +304,7 @@ public class HostapdHalHidlImpTest extends WifiBaseTest { configurationBuilder.setChannel(apChannel, SoftApConfiguration.BAND_2GHZ); assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), true, + configurationBuilder.build(), true, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMock).addAccessPoint(any(), any()); @@ -333,7 +337,7 @@ public class HostapdHalHidlImpTest extends WifiBaseTest { configurationBuilder.setChannel(apChannel, SoftApConfiguration.BAND_5GHZ); assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), true, + configurationBuilder.build(), true, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMock).addAccessPoint(any(), any()); @@ -368,7 +372,7 @@ public class HostapdHalHidlImpTest extends WifiBaseTest { configurationBuilder.setChannel(apChannel, SoftApConfiguration.BAND_5GHZ); assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), true, + configurationBuilder.build(), true, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMock).addAccessPoint(any(), any()); @@ -407,7 +411,7 @@ public class HostapdHalHidlImpTest extends WifiBaseTest { configurationBuilder.setChannel(apChannel, SoftApConfiguration.BAND_2GHZ); assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), true, + configurationBuilder.build(), true, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMock).addAccessPoint(any(), any()); @@ -447,7 +451,7 @@ public class HostapdHalHidlImpTest extends WifiBaseTest { configurationBuilder.setChannel(apChannel, SoftApConfiguration.BAND_2GHZ); assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), true, + configurationBuilder.build(), true, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMock).addAccessPoint(any(), any()); @@ -485,7 +489,7 @@ public class HostapdHalHidlImpTest extends WifiBaseTest { configurationBuilder.setBand(mBand256G); assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), true, + configurationBuilder.build(), true, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMock).addAccessPoint(any(), any()); @@ -522,7 +526,7 @@ public class HostapdHalHidlImpTest extends WifiBaseTest { configurationBuilder.setBand(mBand256G); assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), true, + configurationBuilder.build(), true, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMock).addAccessPoint(any(), any()); @@ -591,7 +595,7 @@ public class HostapdHalHidlImpTest extends WifiBaseTest { configurationBuilder.setBand(mBand256G); assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), true, + configurationBuilder.build(), true, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMockV11).addAccessPoint_1_1(any(), any()); @@ -626,7 +630,7 @@ public class HostapdHalHidlImpTest extends WifiBaseTest { configurationBuilder.setChannel(6, SoftApConfiguration.BAND_2GHZ); assertFalse(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), true, + configurationBuilder.build(), true, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMock).addAccessPoint(any(), any()); } @@ -645,7 +649,7 @@ public class HostapdHalHidlImpTest extends WifiBaseTest { configurationBuilder.setChannel(6, SoftApConfiguration.BAND_2GHZ); assertFalse(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), true, + configurationBuilder.build(), true, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMock).addAccessPoint(any(), any()); } @@ -704,7 +708,7 @@ public class HostapdHalHidlImpTest extends WifiBaseTest { configurationBuilder.setBand(SoftApConfiguration.BAND_2GHZ); assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), true, + configurationBuilder.build(), true, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMockV11).addAccessPoint_1_1(any(), any()); @@ -1146,7 +1150,7 @@ public class HostapdHalHidlImpTest extends WifiBaseTest { assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), true, + configurationBuilder.build(), true, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMockV13).addAccessPoint_1_3(any(), any()); @@ -1193,7 +1197,7 @@ public class HostapdHalHidlImpTest extends WifiBaseTest { assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), true, + configurationBuilder.build(), true, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMockV13).addAccessPoint_1_3(any(), any()); @@ -1237,7 +1241,7 @@ public class HostapdHalHidlImpTest extends WifiBaseTest { configurationBuilder.setBand(mBand256G); assertFalse(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), true, + configurationBuilder.build(), true, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); } @@ -1261,7 +1265,7 @@ public class HostapdHalHidlImpTest extends WifiBaseTest { configurationBuilder.setBand(mBand256G); assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), true, + configurationBuilder.build(), true, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMock).addAccessPoint(any(), any()); @@ -1309,7 +1313,7 @@ public class HostapdHalHidlImpTest extends WifiBaseTest { when(mIHostapdMockV13.addAccessPoint_1_3(any(), any())) .thenReturn(mStatusSuccess12); assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), true, + configurationBuilder.build(), true, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMockV13).addAccessPoint_1_3(any(), any()); // Register SoftApManager callback @@ -1317,7 +1321,7 @@ public class HostapdHalHidlImpTest extends WifiBaseTest { // Add second AP to test that the callbacks are triggered for the correct iface. assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME_1, - configurationBuilder.build(), true, + configurationBuilder.build(), true, false, Collections.emptyList(), () -> mSoftApHalCallback1.onFailure())); mHostapdHal.registerApCallback(IFACE_NAME_1, mSoftApHalCallback1); @@ -1336,17 +1340,18 @@ public class HostapdHalHidlImpTest extends WifiBaseTest { verify(mSoftApHalCallback).onInfoChanged(eq(TEST_AP_INSTANCE), eq(testFreq), eq(mHostapdHal.mapHalBandwidthToSoftApInfo(testBandwidth)), eq(mHostapdHal.mapHalGenerationToWifiStandard(testGeneration)), - eq(MacAddress.fromString(TEST_CLIENT_MAC)), anyList()); + eq(MacAddress.fromString(TEST_CLIENT_MAC)), eq(null), anyList()); verify(mSoftApHalCallback1, never()).onInfoChanged(anyString(), anyInt(), anyInt(), - anyInt(), any(), anyList()); + anyInt(), any(), any(), anyList()); // Trigger on client connected. mIHostapdCallback13.onConnectedClientsChanged(IFACE_NAME, TEST_AP_INSTANCE, MacAddress.fromString(TEST_CLIENT_MAC).toByteArray(), true); verify(mSoftApHalCallback).onConnectedClientsChanged(eq(TEST_AP_INSTANCE), - eq(MacAddress.fromString(TEST_CLIENT_MAC)), eq(true)); + eq(MacAddress.fromString(TEST_CLIENT_MAC)), eq(true), + eq(DEFAULT_DISCONNECT_REASON)); verify(mSoftApHalCallback1, never()).onConnectedClientsChanged(anyString(), any(), - anyBoolean()); + anyBoolean(), anyInt()); } /** @@ -1378,7 +1383,7 @@ public class HostapdHalHidlImpTest extends WifiBaseTest { assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), isMetered, + configurationBuilder.build(), isMetered, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMockV13).addAccessPoint_1_3(any(), any()); @@ -1426,7 +1431,7 @@ public class HostapdHalHidlImpTest extends WifiBaseTest { assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), isMetered, + configurationBuilder.build(), isMetered, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMockV13).addAccessPoint_1_3(any(), any()); @@ -1508,7 +1513,7 @@ public class HostapdHalHidlImpTest extends WifiBaseTest { assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), true, + configurationBuilder.build(), true, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMockV13).addAccessPoint_1_3(any(), any()); @@ -1573,7 +1578,7 @@ public class HostapdHalHidlImpTest extends WifiBaseTest { assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), isMetered, + configurationBuilder.build(), isMetered, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMockV13).addAccessPoint_1_3(any(), any()); @@ -1627,7 +1632,7 @@ public class HostapdHalHidlImpTest extends WifiBaseTest { assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, - configurationBuilder.build(), isMetered, + configurationBuilder.build(), isMetered, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdMockV13).addAccessPoint_1_3(any(), any()); diff --git a/service/tests/wifitests/src/com/android/server/wifi/HostapdHalTest.java b/service/tests/wifitests/src/com/android/server/wifi/HostapdHalTest.java index a8ec7da515..37634c5a3f 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/HostapdHalTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/HostapdHalTest.java @@ -23,6 +23,7 @@ import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.anyList; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; @@ -43,6 +44,8 @@ import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.Collections; + /** * Unit tests for HostapdHal */ @@ -181,21 +184,25 @@ public class HostapdHalTest extends WifiBaseTest { /** * Check that HostapdHal.addAccessPoint() returns the implementation's result - * and that the implementation receives the expected arguments + * and that the implementation receives the expected arguments. + * + * SuppressWarnings DirectInvocationOnMock for "mSoftApHalCallback.onFailure()" + * since it is a lambda callback implementation. Not a really function call. */ + @SuppressWarnings("DirectInvocationOnMock") @Test public void testAddAccessPoint() { initializeWithAidlImp(true); when(mIHostapdAidlMock.addAccessPoint(anyString(), any(SoftApConfiguration.class), - anyBoolean(), any(Runnable.class))) + anyBoolean(), anyBoolean(), anyList(), any(Runnable.class))) .thenReturn(true); boolean isMetered = true; Builder configurationBuilder = new SoftApConfiguration.Builder(); SoftApConfiguration config = configurationBuilder.build(); assertTrue(mHostapdHal.addAccessPoint(IFACE_NAME, config, - isMetered, () -> mSoftApHalCallback.onFailure())); + isMetered, false, Collections.emptyList(), () -> mSoftApHalCallback.onFailure())); verify(mIHostapdAidlMock).addAccessPoint(eq(IFACE_NAME), eq(config), - eq(isMetered), any(Runnable.class)); + eq(isMetered), eq(false), eq(Collections.emptyList()), any(Runnable.class)); } /** diff --git a/service/tests/wifitests/src/com/android/server/wifi/MboOceControllerTest.java b/service/tests/wifitests/src/com/android/server/wifi/MboOceControllerTest.java index f7a801b32e..dba2ba6ff7 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/MboOceControllerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/MboOceControllerTest.java @@ -34,6 +34,7 @@ import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.BitSet; /** * unit tests for {@link com.android.server.wifi.MboOceController}. @@ -75,16 +76,16 @@ public class MboOceControllerTest extends WifiBaseTest { * Helper function to initialize mboOceController */ private PhoneStateListener enableMboOceController(boolean isMboEnabled, boolean isOceEnabled) { - long featureSet = 0; + BitSet featureSet = new BitSet(); PhoneStateListener dataConnectionStateListener = null; if (isMboEnabled) { - featureSet |= WifiManager.WIFI_FEATURE_MBO; + featureSet.set(WifiManager.WIFI_FEATURE_MBO); } if (isOceEnabled) { - featureSet |= WifiManager.WIFI_FEATURE_OCE; + featureSet.set(WifiManager.WIFI_FEATURE_OCE); } - when(mClientModeManager.getSupportedFeatures()).thenReturn(featureSet); + when(mClientModeManager.getSupportedFeaturesBitSet()).thenReturn(featureSet); mMboOceController.enable(); diff --git a/service/tests/wifitests/src/com/android/server/wifi/SavedNetworkNominatorTest.java b/service/tests/wifitests/src/com/android/server/wifi/SavedNetworkNominatorTest.java index 850f8a7a82..f1b4f2de26 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/SavedNetworkNominatorTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/SavedNetworkNominatorTest.java @@ -19,6 +19,7 @@ package com.android.server.wifi; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.android.server.wifi.WifiConfigurationTestUtil.SECURITY_NONE; import static com.android.server.wifi.WifiConfigurationTestUtil.SECURITY_PSK; +import static com.android.server.wifi.TestUtil.createCapabilityBitset; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.*; @@ -67,8 +68,9 @@ public class SavedNetworkNominatorTest extends WifiBaseTest { when(mWifiInjector.getActiveModeWarden()).thenReturn(mActiveModeWarden); when(mWifiInjector.getWifiGlobals()).thenReturn(mWifiGlobals); when(mActiveModeWarden.getPrimaryClientModeManager()).thenReturn(mPrimaryClientModeManager); - when(mPrimaryClientModeManager.getSupportedFeatures()).thenReturn( - WifiManager.WIFI_FEATURE_WPA3_SAE | WifiManager.WIFI_FEATURE_OWE); + when(mPrimaryClientModeManager.getSupportedFeaturesBitSet()).thenReturn( + createCapabilityBitset( + WifiManager.WIFI_FEATURE_WPA3_SAE, WifiManager.WIFI_FEATURE_OWE)); when(mWifiGlobals.isWpa3SaeUpgradeEnabled()).thenReturn(true); when(mWifiGlobals.isOweUpgradeEnabled()).thenReturn(true); diff --git a/service/tests/wifitests/src/com/android/server/wifi/ScanResultMatchInfoTest.java b/service/tests/wifitests/src/com/android/server/wifi/ScanResultMatchInfoTest.java index eb38f5a4a2..6b1fa2ea52 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/ScanResultMatchInfoTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/ScanResultMatchInfoTest.java @@ -18,6 +18,8 @@ package com.android.server.wifi; import static android.net.wifi.WifiManager.WIFI_FEATURE_OWE; import static android.net.wifi.WifiManager.WIFI_FEATURE_WPA3_SAE; +import static com.android.server.wifi.TestUtil.createCapabilityBitset; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -72,8 +74,8 @@ public class ScanResultMatchInfoTest extends WifiBaseTest { when(mWifiInjector.getWifiGlobals()).thenReturn(mWifiGlobals); when(mWifiInjector.getActiveModeWarden()).thenReturn(mActiveModeWarden); when(mActiveModeWarden.getPrimaryClientModeManager()).thenReturn(mClientModeManager); - when(mClientModeManager.getSupportedFeatures()).thenReturn( - WIFI_FEATURE_OWE | WIFI_FEATURE_WPA3_SAE); + when(mClientModeManager.getSupportedFeaturesBitSet()).thenReturn( + createCapabilityBitset(WIFI_FEATURE_OWE, WIFI_FEATURE_WPA3_SAE)); when(mWifiGlobals.isWpa3SaeUpgradeEnabled()).thenReturn(true); when(mWifiGlobals.isOweUpgradeEnabled()).thenReturn(true); } diff --git a/service/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java index 5ba7e3ae7b..86a4a48c1c 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java @@ -29,6 +29,7 @@ import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED; import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING; import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.android.server.wifi.ActiveModeManager.ROLE_SOFTAP_LOCAL_ONLY; import static com.android.server.wifi.ActiveModeManager.ROLE_SOFTAP_TETHERED; import static com.android.server.wifi.HalDeviceManager.HDM_CREATE_IFACE_AP_BRIDGE; @@ -64,6 +65,7 @@ import android.content.IntentFilter; import android.net.MacAddress; import android.net.TetheringManager; import android.net.wifi.CoexUnsafeChannel; +import android.net.wifi.DeauthenticationReasonCode; import android.net.wifi.OuiKeyedData; import android.net.wifi.ScanResult; import android.net.wifi.SoftApCapability; @@ -92,20 +94,24 @@ import android.util.SparseIntArray; import androidx.test.filters.SmallTest; +import com.android.dx.mockito.inline.extended.StaticMockitoSession; import com.android.internal.util.StateMachine; import com.android.internal.util.WakeupMessage; import com.android.modules.utils.build.SdkLevel; import com.android.server.wifi.coex.CoexManager; +import com.android.wifi.flags.Flags; import com.android.wifi.resources.R; import com.google.common.collect.ImmutableList; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.quality.Strictness; import java.util.ArrayList; import java.util.Arrays; @@ -131,6 +137,7 @@ public class SoftApManagerTest extends WifiBaseTest { private static final String TEST_SECOND_INSTANCE_NAME = "testif2"; private static final String OTHER_INTERFACE_NAME = "otherif"; private static final String TEST_STA_INTERFACE_NAME = "testif0sta"; + private static final long TEST_START_TIME_MILLIS = 1234567890; private static final long TEST_DEFAULT_SHUTDOWN_TIMEOUT_MILLIS = 600_000; private static final long TEST_DEFAULT_SHUTDOWN_IDLE_INSTANCE_IN_BRIDGED_MODE_TIMEOUT_MILLIS = 300_000; @@ -159,13 +166,27 @@ public class SoftApManagerTest extends WifiBaseTest { private static final int TEST_AP_BANDWIDTH_FROM_IFACE_CALLBACK = SoftApInfo.CHANNEL_WIDTH_20MHZ_NOHT; private static final int TEST_AP_BANDWIDTH_IN_SOFTAPINFO = SoftApInfo.CHANNEL_WIDTH_20MHZ_NOHT; + private static final int TEST_DISCONNECT_REASON = + DeauthenticationReasonCode.REASON_UNKNOWN; + private static final WifiClient TEST_DISCONNECTED_CLIENT = + new WifiClient(TEST_CLIENT_MAC_ADDRESS, TEST_INTERFACE_NAME, + TEST_DISCONNECT_REASON); + private static final WifiClient TEST_DISCONNECTED_CLIENT_ON_FIRST_IFACE = + new WifiClient(TEST_CLIENT_MAC_ADDRESS, TEST_FIRST_INSTANCE_NAME, + TEST_DISCONNECT_REASON); + private static final WifiClient TEST_DISCONNECTED_CLIENT_2_ON_FIRST_IFACE = + new WifiClient(TEST_CLIENT_MAC_ADDRESS_2, TEST_FIRST_INSTANCE_NAME, + TEST_DISCONNECT_REASON); + private static final WifiClient TEST_DISCONNECTED_CLIENT_2_ON_SECOND_IFACE = + new WifiClient(TEST_CLIENT_MAC_ADDRESS_2, TEST_SECOND_INSTANCE_NAME, + TEST_DISCONNECT_REASON); private static final int[] EMPTY_CHANNEL_ARRAY = {}; private static final int[] ALLOWED_2G_FREQS = {2462}; //ch# 11 private static final int[] ALLOWED_5G_FREQS = {5745, 5765}; //ch# 149, 153 private static final int[] ALLOWED_6G_FREQS = {5945, 5965}; private static final int[] ALLOWED_60G_FREQS = {58320, 60480}; // ch# 1, 2 private static final WorkSource TEST_WORKSOURCE = new WorkSource(); - private SoftApConfiguration mDefaultApConfig = createDefaultApConfig(); + private SoftApConfiguration mPersistentApConfig; private static final TetheringManager.TetheringRequest TEST_TETHERING_REQUEST = new TetheringManager.TetheringRequest.Builder(TetheringManager.TETHERING_WIFI).build(); @@ -218,6 +239,7 @@ public class SoftApManagerTest extends WifiBaseTest { @Mock InterfaceConflictManager mInterfaceConflictManager; @Mock WifiInjector mWifiInjector; @Mock WifiCountryCode mWifiCountryCode; + @Mock Clock mClock; @Mock LocalLog mLocalLog; @Mock DeviceWiphyCapabilities mDeviceWiphyCapabilities; @@ -238,11 +260,12 @@ public class SoftApManagerTest extends WifiBaseTest { ArgumentCaptor.forClass(BroadcastReceiver.class); SoftApManager mSoftApManager; + private StaticMockitoSession mStaticMockSession; /** Old callback event from wificond */ private void mockChannelSwitchEvent(int frequency, int bandwidth) { mSoftApHalCallbackCaptor.getValue().onInfoChanged( - TEST_INTERFACE_NAME, frequency, bandwidth, 0, null, Collections.emptyList()); + TEST_INTERFACE_NAME, frequency, bandwidth, 0, null, null, Collections.emptyList()); } /** New callback event from hostapd */ @@ -251,7 +274,8 @@ public class SoftApManagerTest extends WifiBaseTest { ? apInfo.getVendorData() : Collections.emptyList(); mSoftApHalCallbackCaptor.getValue().onInfoChanged( apInfo.getApInstanceIdentifier(), apInfo.getFrequency(), apInfo.getBandwidth(), - apInfo.getWifiStandardInternal(), apInfo.getBssidInternal(), vendorData); + apInfo.getWifiStandardInternal(), apInfo.getBssidInternal(), + apInfo.getMldAddress(), vendorData); mTestSoftApInfoMap.put(apInfo.getApInstanceIdentifier(), apInfo); mTestWifiClientsMap.put(apInfo.getApInstanceIdentifier(), new ArrayList<WifiClient>()); } @@ -259,9 +283,9 @@ public class SoftApManagerTest extends WifiBaseTest { private void mockClientConnectedEvent(MacAddress mac, boolean isConnected, String apIfaceInstance, boolean updateTheTestMap) { mSoftApHalCallbackCaptor.getValue().onConnectedClientsChanged( - apIfaceInstance, mac, isConnected); + apIfaceInstance, mac, isConnected, TEST_DISCONNECT_REASON); if (mac == null || !updateTheTestMap) return; - WifiClient client = new WifiClient(mac, apIfaceInstance); + WifiClient client = new WifiClient(mac, apIfaceInstance, TEST_DISCONNECT_REASON); List<WifiClient> targetList = mTempConnectedClientListMap.get(apIfaceInstance); if (isConnected) { targetList.add(client); @@ -323,17 +347,24 @@ public class SoftApManagerTest extends WifiBaseTest { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + mStaticMockSession = mockitoSession() + .mockStatic(WifiInjector.class) + .mockStatic(Flags.class) + .strictness(Strictness.LENIENT) + .startMocking(); mLooper = new TestLooper(); + when(WifiInjector.getInstance()).thenReturn(mWifiInjector); + when(mWifiInjector.getContext()).thenReturn(mContext); when(mWifiNative.isItPossibleToCreateApIface(any())).thenReturn(true); when(mWifiNative.isItPossibleToCreateBridgedApIface(any())).thenReturn(true); when(mWifiNative.isApSetMacAddressSupported(any())).thenReturn(true); when(mWifiNative.setApMacAddress(any(), any())).thenReturn(true); when(mWifiNative.startSoftAp(eq(TEST_INTERFACE_NAME), any(), anyBoolean(), - any(WifiNative.SoftApHalCallback.class))) + any(WifiNative.SoftApHalCallback.class), anyBoolean())) .thenReturn(SoftApManager.START_RESULT_SUCCESS); when(mWifiNative.setupInterfaceForSoftApMode(any(), any(), anyInt(), anyBoolean(), - any(), anyList())) + any(), anyList(), anyBoolean())) .thenReturn(TEST_INTERFACE_NAME); when(mFrameworkFacade.getIntegerSetting( mContext, Settings.Global.SOFT_AP_TIMEOUT_ENABLED, 1)).thenReturn(1); @@ -408,6 +439,8 @@ public class SoftApManagerTest extends WifiBaseTest { when(mWifiNative.forceClientDisconnect(any(), any(), anyInt())).thenReturn(true); when(mWifiInjector.getWifiHandlerLocalLog()).thenReturn(mLocalLog); when(mWifiInjector.getWifiCountryCode()).thenReturn(mWifiCountryCode); + when(mWifiInjector.getClock()).thenReturn(mClock); + when(mClock.getWallClockMillis()).thenReturn(TEST_START_TIME_MILLIS); when(mWifiNative.getDeviceWiphyCapabilities(any(), anyBoolean())).thenReturn( mDeviceWiphyCapabilities); when(mDeviceWiphyCapabilities.isWifiStandardSupported(ScanResult.WIFI_STANDARD_11BE)) @@ -445,7 +478,8 @@ public class SoftApManagerTest extends WifiBaseTest { mTestSoftApCapability.setSupportedChannelList( SoftApConfiguration.BAND_5GHZ, TEST_SUPPORTED_5G_CHANNELS); mTestSoftApCapability.setCountryCode(TEST_COUNTRY_CODE); - when(mWifiApConfigStore.getApConfiguration()).thenReturn(mDefaultApConfig); + mPersistentApConfig = createDefaultApConfig(); + when(mWifiApConfigStore.getApConfiguration()).thenReturn(mPersistentApConfig); when(mWifiNative.isHalStarted()).thenReturn(true); mTestSoftApInfoMap.clear(); @@ -453,6 +487,12 @@ public class SoftApManagerTest extends WifiBaseTest { mTempConnectedClientListMap.forEach((key, value) -> value.clear()); } + @After + public void cleanUp() throws Exception { + mStaticMockSession.finishMocking(); + } + + private SoftApConfiguration createDefaultApConfig() { Builder defaultConfigBuilder = new SoftApConfiguration.Builder(); defaultConfigBuilder.setSsid(DEFAULT_SSID); @@ -479,6 +519,7 @@ public class SoftApManagerTest extends WifiBaseTest { TEST_WORKSOURCE, role, false); + verify(mWifiNative).isMLDApSupportMLO(); mLooper.dispatchAll(); return newSoftApManager; @@ -626,7 +667,8 @@ public class SoftApManagerTest extends WifiBaseTest { public void testSetupForSoftApModeNullApInterfaceNameFailureIncrementsMetrics() throws Exception { when(mWifiNative.setupInterfaceForSoftApMode( - any(), any(), anyInt(), anyBoolean(), any(), anyList())).thenReturn(null); + any(), any(), anyInt(), anyBoolean(), any(), anyList(), anyBoolean())) + .thenReturn(null); when(mWifiApConfigStore.getApConfiguration()).thenReturn(null); SoftApModeConfiguration nullApConfig = new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null, @@ -664,7 +706,8 @@ public class SoftApManagerTest extends WifiBaseTest { public void testStartSoftApNotPossibleToCreateApInterfaceIncrementsMetrics() throws Exception { when(mWifiNative.setupInterfaceForSoftApMode( - any(), any(), anyInt(), anyBoolean(), any(), anyList())).thenReturn(null); + any(), any(), anyInt(), anyBoolean(), any(), anyList(), anyBoolean())) + .thenReturn(null); when(mWifiNative.isItPossibleToCreateApIface(any())).thenReturn(false); Builder configBuilder = new SoftApConfiguration.Builder(); configBuilder.setBand(SoftApConfiguration.BAND_2GHZ); @@ -706,7 +749,8 @@ public class SoftApManagerTest extends WifiBaseTest { public void testSetupForSoftApModeEmptyInterfaceNameFailureIncrementsMetrics() throws Exception { when(mWifiNative.setupInterfaceForSoftApMode( - any(), any(), anyInt(), anyBoolean(), any(), anyList())).thenReturn(""); + any(), any(), anyInt(), anyBoolean(), any(), anyList(), anyBoolean())) + .thenReturn(""); SoftApModeConfiguration nullApConfig = new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null, mTestSoftApCapability, TEST_COUNTRY_CODE, TEST_TETHERING_REQUEST); @@ -995,11 +1039,11 @@ public class SoftApManagerTest extends WifiBaseTest { @Test public void startSoftApApInterfaceFailedToStart() throws Exception { when(mWifiNative.startSoftAp(eq(TEST_INTERFACE_NAME), any(), anyBoolean(), - any(WifiNative.SoftApHalCallback.class))).thenReturn( + any(WifiNative.SoftApHalCallback.class), anyBoolean())).thenReturn( SoftApManager.START_RESULT_FAILURE_ADD_AP_HOSTAPD); SoftApModeConfiguration softApModeConfig = - new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, mDefaultApConfig, + new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, mPersistentApConfig, mTestSoftApCapability, TEST_COUNTRY_CODE, TEST_TETHERING_REQUEST); mSoftApManager = createSoftApManager( @@ -1028,9 +1072,13 @@ public class SoftApManagerTest extends WifiBaseTest { // reset to clear verified Intents for ap state change updates reset(mContext); + when(mContext.getResourceCache()).thenReturn(mResourceCache); InOrder order = inOrder(mCallback, mListener, mContext); + int sessionDurationSeconds = 3000; + when(mClock.getWallClockMillis()).thenReturn( + TEST_START_TIME_MILLIS + (sessionDurationSeconds * 1000)); mSoftApManager.stop(); mLooper.dispatchAll(); @@ -1066,6 +1114,10 @@ public class SoftApManagerTest extends WifiBaseTest { softApModeConfig.getTargetMode()); order.verify(mListener).onStopped(mSoftApManager); verify(mCmiMonitor).unregisterListener(mCmiListenerCaptor.getValue()); + verify(mWifiMetrics).writeSoftApStoppedEvent(eq(SoftApManager.STOP_EVENT_STOPPED), + any(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean(), anyInt(), anyBoolean(), + eq(sessionDurationSeconds), anyInt(), anyInt(), anyInt(), anyBoolean(), anyInt(), + anyInt(), any()); } /** @@ -1289,7 +1341,7 @@ public class SoftApManagerTest extends WifiBaseTest { mLooper.dispatchAll(); // Verify the remove correct iface and instance verify(mWifiNative).removeIfaceInstanceFromBridgedApIface(eq(TEST_INTERFACE_NAME), - eq(TEST_SECOND_INSTANCE_NAME)); + eq(TEST_SECOND_INSTANCE_NAME), eq(false)); mLooper.dispatchAll(); mTestSoftApInfoMap.clear(); mTestWifiClientsMap.clear(); @@ -1324,7 +1376,7 @@ public class SoftApManagerTest extends WifiBaseTest { mLooper.dispatchAll(); // Verify the remove correct iface and instance verify(mWifiNative).removeIfaceInstanceFromBridgedApIface(eq(TEST_INTERFACE_NAME), - eq(TEST_SECOND_INSTANCE_NAME)); + eq(TEST_SECOND_INSTANCE_NAME), eq(false)); mLooper.dispatchAll(); mTestSoftApInfoMap.clear(); mTestWifiClientsMap.clear(); @@ -1369,7 +1421,7 @@ public class SoftApManagerTest extends WifiBaseTest { mLooper.dispatchAll(); // Verify the remove correct iface and instance but SAP off since it can't get instances. verify(mWifiNative).removeIfaceInstanceFromBridgedApIface(eq(TEST_INTERFACE_NAME), - eq(TEST_SECOND_INSTANCE_NAME)); + eq(TEST_SECOND_INSTANCE_NAME), eq(false)); mLooper.dispatchAll(); mTestSoftApInfoMap.clear(); mTestWifiClientsMap.clear(); @@ -1398,7 +1450,7 @@ public class SoftApManagerTest extends WifiBaseTest { mLooper.dispatchAll(); // Verify AP remains up while waiting for the second instance. verify(mWifiNative).removeIfaceInstanceFromBridgedApIface(eq(TEST_INTERFACE_NAME), - eq(TEST_FIRST_INSTANCE_NAME)); + eq(TEST_FIRST_INSTANCE_NAME), eq(false)); verify(mWifiNative, never()).teardownInterface(TEST_INTERFACE_NAME); } @@ -1591,6 +1643,7 @@ public class SoftApManagerTest extends WifiBaseTest { */ @Test public void testDoesNotTriggerCallbackForSameClients() throws Exception { + when(Flags.softapDisconnectReason()).thenReturn(true); SoftApModeConfiguration apConfig = new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, null, mTestSoftApCapability, TEST_COUNTRY_CODE, TEST_TETHERING_REQUEST); @@ -1618,7 +1671,12 @@ public class SoftApManagerTest extends WifiBaseTest { // Should just trigger 1 time callback, the first time will be happen when softap enable verify(mCallback, times(3)).onConnectedClientsOrInfoChanged(mTestSoftApInfoMap, mTestWifiClientsMap, false); + // onClientsDisconnected should trigger 1 time from the update to zero client. + verify(mCallback).onClientsDisconnected(eq(mTestSoftApInfo), + eq(ImmutableList.of(TEST_DISCONNECTED_CLIENT))); + verify(mWifiMetrics).reportOnClientsDisconnected( + eq(TEST_DISCONNECT_REASON), eq(TEST_WORKSOURCE)); verify(mWifiMetrics) .addSoftApNumAssociatedStationsChangedEvent(0, 0, apConfig.getTargetMode(), mTestSoftApInfo); @@ -1640,11 +1698,25 @@ public class SoftApManagerTest extends WifiBaseTest { verify(mWifiMetrics).addSoftApNumAssociatedStationsChangedEvent(1, 1, apConfig.getTargetMode(), mTestSoftApInfo); + mockClientConnectedEvent(TEST_CLIENT_MAC_ADDRESS_2, true, TEST_INTERFACE_NAME, true); + mLooper.dispatchAll(); + order.verify(mCallback).onConnectedClientsOrInfoChanged(mTestSoftApInfoMap, + mTestWifiClientsMap, false); + verify(mWifiMetrics).addSoftApNumAssociatedStationsChangedEvent(2, 2, + apConfig.getTargetMode(), mTestSoftApInfo); + mSoftApManager.stop(); mLooper.dispatchAll(); verify(mWifiNative).forceClientDisconnect(TEST_INTERFACE_NAME, TEST_CLIENT_MAC_ADDRESS, SAP_CLIENT_DISCONNECT_REASON_CODE_UNSPECIFIED); + verify(mWifiNative).forceClientDisconnect(TEST_INTERFACE_NAME, TEST_CLIENT_MAC_ADDRESS_2, + SAP_CLIENT_DISCONNECT_REASON_CODE_UNSPECIFIED); + + verify(mWifiMetrics).writeSoftApStoppedEvent(eq(SoftApManager.STOP_EVENT_STOPPED), + any(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean(), anyInt(), anyBoolean(), + anyInt(), anyInt(), anyInt(), eq(2), anyBoolean(), + anyInt(), anyInt(), any()); } @Test @@ -2106,7 +2178,7 @@ public class SoftApManagerTest extends WifiBaseTest { mockSoftApInfoUpdateAndVerifyAfterSapStarted(false, true); - SoftApConfiguration newConfig = new SoftApConfiguration.Builder(mDefaultApConfig) + SoftApConfiguration newConfig = new SoftApConfiguration.Builder(mPersistentApConfig) .setAutoShutdownEnabled(false) .build(); mSoftApManager.updateConfiguration(newConfig); @@ -2120,7 +2192,7 @@ public class SoftApManagerTest extends WifiBaseTest { @Test public void schedulesTimeoutTimerOnTimeoutToggleChangeWhenNoClients() throws Exception { // start with timeout toggle disabled - mDefaultApConfig = new SoftApConfiguration.Builder(mDefaultApConfig) + mPersistentApConfig = new SoftApConfiguration.Builder(mPersistentApConfig) .setAutoShutdownEnabled(false) .build(); SoftApModeConfiguration apConfig = @@ -2142,7 +2214,7 @@ public class SoftApManagerTest extends WifiBaseTest { verify(mAlarmManager.getAlarmManager(), never()).setExact(anyInt(), anyLong(), any(), any(), any()); - SoftApConfiguration newConfig = new SoftApConfiguration.Builder(mDefaultApConfig) + SoftApConfiguration newConfig = new SoftApConfiguration.Builder(mPersistentApConfig) .setAutoShutdownEnabled(true) .build(); mSoftApManager.updateConfiguration(newConfig); @@ -2157,7 +2229,7 @@ public class SoftApManagerTest extends WifiBaseTest { @Test public void doesNotScheduleTimeoutTimerOnStartWhenTimeoutIsDisabled() throws Exception { // start with timeout toggle disabled - mDefaultApConfig = new SoftApConfiguration.Builder(mDefaultApConfig) + mPersistentApConfig = new SoftApConfiguration.Builder(mPersistentApConfig) .setAutoShutdownEnabled(false) .build(); SoftApModeConfiguration apConfig = @@ -2180,7 +2252,7 @@ public class SoftApManagerTest extends WifiBaseTest { public void doesNotScheduleTimeoutTimerWhenAllClientsDisconnectButTimeoutIsDisabled() throws Exception { // start with timeout toggle disabled - mDefaultApConfig = new SoftApConfiguration.Builder(mDefaultApConfig) + mPersistentApConfig = new SoftApConfiguration.Builder(mPersistentApConfig) .setAutoShutdownEnabled(false) .build(); SoftApModeConfiguration apConfig = @@ -2323,7 +2395,7 @@ public class SoftApManagerTest extends WifiBaseTest { @Test public void setMacFailureWhenRandomMac() throws Exception { SoftApConfiguration.Builder randomizedBssidConfigBuilder = - new SoftApConfiguration.Builder(mDefaultApConfig) + new SoftApConfiguration.Builder(mPersistentApConfig) .setBssid(TEST_CLIENT_MAC_ADDRESS); if (SdkLevel.isAtLeastS()) { randomizedBssidConfigBuilder.setMacRandomizationSetting( @@ -2435,7 +2507,7 @@ public class SoftApManagerTest extends WifiBaseTest { int[] dual_bands = {SoftApConfiguration.BAND_2GHZ , SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ}; Builder configBuilder = new SoftApConfiguration.Builder( - config != null ? config : mDefaultApConfig); + config != null ? config : mPersistentApConfig); configBuilder.setBands(dual_bands); return configBuilder.build(); } @@ -2452,20 +2524,28 @@ public class SoftApManagerTest extends WifiBaseTest { startSoftApAndVerifyEnabled(softApConfig, null, true); } - /** Starts soft AP and verifies that it is enabled successfully. */ + /** Starts soft AP with non MLO and verifies that it is enabled successfully. */ protected void startSoftApAndVerifyEnabled( SoftApModeConfiguration softApConfig, SoftApConfiguration expectedConfig, boolean userApprovalNeeded) throws Exception { + startSoftApAndVerifyEnabled(softApConfig, expectedConfig, userApprovalNeeded, false); + } + + /** Starts soft AP and verifies that it is enabled successfully. */ + protected void startSoftApAndVerifyEnabled( + SoftApModeConfiguration softApConfig, + SoftApConfiguration expectedConfig, boolean userApprovalNeeded, boolean isUsingMlo) + throws Exception { // The config which base on mDefaultApConfig and generate ramdonized mac address SoftApConfiguration randomizedBssidConfig = null; InOrder order = inOrder(mCallback, mWifiNative); - SoftApConfiguration config = softApConfig.getSoftApConfiguration(); + final SoftApConfiguration config = softApConfig.getSoftApConfiguration(); if (expectedConfig == null) { if (config == null) { // Only generate randomized mac for default config since test case doesn't care it. SoftApConfiguration.Builder randomizedBssidConfigBuilder = - new SoftApConfiguration.Builder(mDefaultApConfig) + new SoftApConfiguration.Builder(mPersistentApConfig) .setBssid(TEST_INTERFACE_MAC_ADDRESS); if (SdkLevel.isAtLeastS()) { randomizedBssidConfigBuilder.setMacRandomizationSetting( @@ -2518,7 +2598,7 @@ public class SoftApManagerTest extends WifiBaseTest { verify(mWifiNative, never()).setupInterfaceForSoftApMode( mWifiNativeInterfaceCallbackCaptor.capture(), eq(TEST_WORKSOURCE), eq(expectedConfig.getBand()), eq(expectedConfig.getBands().length > 1), - eq(mSoftApManager), anyList()); + eq(mSoftApManager), anyList(), anyBoolean()); // Simulate user approval ArgumentCaptor<StateMachine> stateMachineCaptor = ArgumentCaptor.forClass(StateMachine.class); @@ -2544,7 +2624,8 @@ public class SoftApManagerTest extends WifiBaseTest { softApConfig.getCountryCode(), softApConfig.getCapability().getCountryCode())) { // Don't start SoftAP before driver country code change. - verify(mWifiNative, never()).startSoftAp(any(), any(), anyBoolean(), any()); + verify(mWifiNative, never()).startSoftAp(any(), any(), anyBoolean(), any(), + anyBoolean()); ArgumentCaptor<WifiCountryCode.ChangeListener> changeListenerCaptor = ArgumentCaptor.forClass(WifiCountryCode.ChangeListener.class); @@ -2554,7 +2635,8 @@ public class SoftApManagerTest extends WifiBaseTest { mLooper.dispatchAll(); // Ignore country code changes that don't match what we set. - verify(mWifiNative, never()).startSoftAp(any(), any(), anyBoolean(), any()); + verify(mWifiNative, never()).startSoftAp(any(), any(), anyBoolean(), any(), + anyBoolean()); // Now notify the correct country code. changeListenerCaptor.getValue() @@ -2566,7 +2648,8 @@ public class SoftApManagerTest extends WifiBaseTest { } else if (TextUtils.isEmpty(softApConfig.getCountryCode()) && mIsDriverSupportedRegChangedEvent && expectedConfig.getBands().length == 1) { // Don't start SoftAP before driver country code change. - verify(mWifiNative, never()).startSoftAp(any(), any(), anyBoolean(), any()); + verify(mWifiNative, never()).startSoftAp(any(), any(), anyBoolean(), any(), + anyBoolean()); ArgumentCaptor<WifiCountryCode.ChangeListener> changeListenerCaptor = ArgumentCaptor.forClass(WifiCountryCode.ChangeListener.class); @@ -2581,14 +2664,14 @@ public class SoftApManagerTest extends WifiBaseTest { order.verify(mWifiNative).setupInterfaceForSoftApMode( mWifiNativeInterfaceCallbackCaptor.capture(), eq(TEST_WORKSOURCE), eq(expectedConfig.getBand()), eq(expectedConfig.getBands().length > 1), - eq(mSoftApManager), anyList()); + eq(mSoftApManager), anyList(), anyBoolean()); order.verify(mCallback).onStateChanged(eq(new SoftApState( WifiManager.WIFI_AP_STATE_ENABLING, 0, softApConfig.getTetheringRequest(), TEST_INTERFACE_NAME))); order.verify(mWifiNative).startSoftAp(eq(TEST_INTERFACE_NAME), configCaptor.capture(), eq(softApConfig.getTargetMode() == WifiManager.IFACE_IP_MODE_TETHERED), - mSoftApHalCallbackCaptor.capture()); + mSoftApHalCallbackCaptor.capture(), eq(isUsingMlo)); assertThat(configCaptor.getValue()).isEqualTo(expectedConfigWithFrameworkACS != null ? expectedConfigWithFrameworkACS : expectedConfig); mWifiNativeInterfaceCallbackCaptor.getValue().onUp(TEST_INTERFACE_NAME); @@ -2628,7 +2711,7 @@ public class SoftApManagerTest extends WifiBaseTest { // Verify the bands we get from getSoftApModeConfiguration() match the original bands // we passed in. assertThat(mSoftApManager.getSoftApModeConfiguration().getSoftApConfiguration().getBands()) - .isEqualTo(config != null ? config.getBands() : mDefaultApConfig.getBands()); + .isEqualTo(config != null ? config.getBands() : mPersistentApConfig.getBands()); if (SdkLevel.isAtLeastS()) { SparseIntArray actualChannels = mSoftApManager @@ -2636,7 +2719,7 @@ public class SoftApManagerTest extends WifiBaseTest { .getSoftApConfiguration() .getChannels(); SparseIntArray expectedChannels = - config != null ? config.getChannels() : mDefaultApConfig.getChannels(); + config != null ? config.getChannels() : mPersistentApConfig.getChannels(); assertThat(actualChannels.size()).isEqualTo(expectedChannels.size()); for (int band : actualChannels.copyKeys()) { assertThat(actualChannels.get(band)).isEqualTo(actualChannels.get(band)); @@ -2705,7 +2788,7 @@ public class SoftApManagerTest extends WifiBaseTest { noClientControlCapability.setMaxSupportedClients(1); noClientControlCapability.setCountryCode(TEST_COUNTRY_CODE); SoftApConfiguration softApConfig = new SoftApConfiguration.Builder( - mDefaultApConfig).setMaxNumberOfClients(1).build(); + mPersistentApConfig).setMaxNumberOfClients(1).build(); SoftApModeConfiguration apConfig = new SoftApModeConfiguration(WifiManager.IFACE_IP_MODE_TETHERED, softApConfig, @@ -2734,7 +2817,7 @@ public class SoftApManagerTest extends WifiBaseTest { SoftApCapability noSaeCapability = new SoftApCapability(testSoftApFeature); noSaeCapability.setCountryCode(TEST_COUNTRY_CODE); SoftApConfiguration softApConfig = new SoftApConfiguration.Builder( - mDefaultApConfig).setPassphrase(TEST_PASSWORD, + mPersistentApConfig).setPassphrase(TEST_PASSWORD, SoftApConfiguration.SECURITY_TYPE_WPA3_SAE).build(); SoftApModeConfiguration apConfig = @@ -2807,7 +2890,7 @@ public class SoftApManagerTest extends WifiBaseTest { SoftApConfiguration.BAND_2GHZ, TEST_SUPPORTED_24G_CHANNELS); testCapability.setSupportedChannelList( SoftApConfiguration.BAND_5GHZ, TEST_SUPPORTED_5G_CHANNELS); - SoftApConfiguration softApConfig = new SoftApConfiguration.Builder(mDefaultApConfig) + SoftApConfiguration softApConfig = new SoftApConfiguration.Builder(mPersistentApConfig) .setChannels(dual_channels) .build(); SoftApModeConfiguration apConfig = new SoftApModeConfiguration( @@ -2993,7 +3076,7 @@ public class SoftApManagerTest extends WifiBaseTest { public void testBssidUpdatedWhenSoftApInfoUpdate() throws Exception { MacAddress testBssid = MacAddress.fromString("aa:bb:cc:11:22:33"); SoftApConfiguration.Builder customizedBssidConfigBuilder = new SoftApConfiguration - .Builder(mDefaultApConfig).setBssid(testBssid); + .Builder(mPersistentApConfig).setBssid(testBssid); if (SdkLevel.isAtLeastS()) { customizedBssidConfigBuilder.setMacRandomizationSetting( SoftApConfiguration.RANDOMIZATION_NONE); @@ -3152,7 +3235,7 @@ public class SoftApManagerTest extends WifiBaseTest { mSoftApManager = createSoftApManager(dualBandConfig, ROLE_SOFTAP_TETHERED); verify(mWifiNative).setupInterfaceForSoftApMode( any(), any(), eq(SoftApConfiguration.BAND_2GHZ), eq(true), eq(mSoftApManager), - anyList()); + anyList(), anyBoolean()); } @Test @@ -3195,7 +3278,7 @@ public class SoftApManagerTest extends WifiBaseTest { mLooper.dispatchAll(); // Verify the remove correct iface and instance verify(mWifiNative).removeIfaceInstanceFromBridgedApIface(eq(TEST_INTERFACE_NAME), - eq(TEST_SECOND_INSTANCE_NAME)); + eq(TEST_SECOND_INSTANCE_NAME), eq(false)); mLooper.dispatchAll(); mTestSoftApInfoMap.clear(); mTestWifiClientsMap.clear(); @@ -3212,6 +3295,7 @@ public class SoftApManagerTest extends WifiBaseTest { @Test public void schedulesTimeoutTimerWorkFlowInBridgedMode() throws Exception { assumeTrue(SdkLevel.isAtLeastS()); + when(Flags.softapDisconnectReason()).thenReturn(true); SoftApModeConfiguration apConfig = new SoftApModeConfiguration( WifiManager.IFACE_IP_MODE_TETHERED, generateBridgedModeSoftApConfig(null), mTestSoftApCapability, TEST_COUNTRY_CODE, TEST_TETHERING_REQUEST); @@ -3259,6 +3343,8 @@ public class SoftApManagerTest extends WifiBaseTest { mLooper.dispatchAll(); verify(mCallback).onConnectedClientsOrInfoChanged( mTestSoftApInfoMap, mTestWifiClientsMap, true); + verify(mCallback).onClientsDisconnected(eq(mTestSoftApInfoOnFirstInstance), + eq(ImmutableList.of(TEST_DISCONNECTED_CLIENT_2_ON_FIRST_IFACE))); reset(mCallback); mockClientConnectedEvent(TEST_CLIENT_MAC_ADDRESS_2, true, TEST_SECOND_INSTANCE_NAME, true); mLooper.dispatchAll(); @@ -3274,6 +3360,8 @@ public class SoftApManagerTest extends WifiBaseTest { mLooper.dispatchAll(); verify(mCallback).onConnectedClientsOrInfoChanged( mTestSoftApInfoMap, mTestWifiClientsMap, true); + verify(mCallback).onClientsDisconnected(eq(mTestSoftApInfoOnSecondInstance), + eq(ImmutableList.of(TEST_DISCONNECTED_CLIENT_2_ON_SECOND_IFACE))); // Verify idle timer in bridged mode is scheduled again verify(mAlarmManager.getAlarmManager(), times(2)).setExact(anyInt(), anyLong(), eq(mSoftApManager.SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG + TEST_SECOND_INSTANCE_NAME), @@ -3284,7 +3372,7 @@ public class SoftApManagerTest extends WifiBaseTest { mLooper.dispatchAll(); // Verify the remove correct iface and instance verify(mWifiNative).removeIfaceInstanceFromBridgedApIface(eq(TEST_INTERFACE_NAME), - eq(TEST_SECOND_INSTANCE_NAME)); + eq(TEST_SECOND_INSTANCE_NAME), eq(false)); mTestSoftApInfoMap.clear(); mTestWifiClientsMap.clear(); @@ -3305,6 +3393,8 @@ public class SoftApManagerTest extends WifiBaseTest { reset(mAlarmManager.getAlarmManager()); mockClientConnectedEvent(TEST_CLIENT_MAC_ADDRESS, false, TEST_FIRST_INSTANCE_NAME, true); mLooper.dispatchAll(); + verify(mCallback).onClientsDisconnected(eq(mTestSoftApInfoOnFirstInstance), + eq(ImmutableList.of(TEST_DISCONNECTED_CLIENT_ON_FIRST_IFACE))); // Verify timer is scheduled verify(mAlarmManager.getAlarmManager()).setExact(anyInt(), anyLong(), eq(mSoftApManager.SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG + TEST_INTERFACE_NAME), @@ -3313,6 +3403,8 @@ public class SoftApManagerTest extends WifiBaseTest { verify(mAlarmManager.getAlarmManager(), never()).setExact(anyInt(), anyLong(), eq(mSoftApManager.SOFT_AP_SEND_MESSAGE_TIMEOUT_TAG + TEST_FIRST_INSTANCE_NAME), any(), any()); + verify(mWifiMetrics, times(3)).reportOnClientsDisconnected( + eq(TEST_DISCONNECT_REASON), eq(TEST_WORKSOURCE)); } @Test @@ -3581,7 +3673,7 @@ public class SoftApManagerTest extends WifiBaseTest { mLooper.dispatchAll(); // Verify the remove correct iface and instance verify(mWifiNative, never()).removeIfaceInstanceFromBridgedApIface(any(), - any()); + any(), anyBoolean()); } @@ -3615,7 +3707,7 @@ public class SoftApManagerTest extends WifiBaseTest { mLooper.dispatchAll(); // Verify the remove correct iface and instance verify(mWifiNative).removeIfaceInstanceFromBridgedApIface(eq(TEST_INTERFACE_NAME), - eq(TEST_SECOND_INSTANCE_NAME)); + eq(TEST_SECOND_INSTANCE_NAME), eq(false)); mLooper.dispatchAll(); mTestSoftApInfoMap.clear(); mTestWifiClientsMap.clear(); @@ -3654,7 +3746,8 @@ public class SoftApManagerTest extends WifiBaseTest { mCoexListenerCaptor.getValue().onCoexUnsafeChannelsChanged(); mLooper.dispatchAll(); // Verify the remove correct iface and instance - verify(mWifiNative, never()).removeIfaceInstanceFromBridgedApIface(any(), any()); + verify(mWifiNative, never()).removeIfaceInstanceFromBridgedApIface(any(), any(), + anyBoolean()); } @Test @@ -3678,7 +3771,8 @@ public class SoftApManagerTest extends WifiBaseTest { mCmiListenerCaptor.getValue().onL2Connected(mConcreteClientModeManager); mLooper.dispatchAll(); // Verify the remove correct iface and instance - verify(mWifiNative, never()).removeIfaceInstanceFromBridgedApIface(any(), any()); + verify(mWifiNative, never()).removeIfaceInstanceFromBridgedApIface(any(), any(), + anyBoolean()); } @Test @@ -3707,7 +3801,7 @@ public class SoftApManagerTest extends WifiBaseTest { mLooper.dispatchAll(); // Verify the remove correct iface and instance verify(mWifiNative).removeIfaceInstanceFromBridgedApIface(eq(TEST_INTERFACE_NAME), - eq(TEST_SECOND_INSTANCE_NAME)); + eq(TEST_SECOND_INSTANCE_NAME), eq(false)); mLooper.dispatchAll(); mTestSoftApInfoMap.clear(); mTestWifiClientsMap.clear(); @@ -3747,7 +3841,7 @@ public class SoftApManagerTest extends WifiBaseTest { mLooper.dispatchAll(); // Verify instance not removed verify(mWifiNative, never()).removeIfaceInstanceFromBridgedApIface(eq(TEST_INTERFACE_NAME), - eq(TEST_SECOND_INSTANCE_NAME)); + eq(TEST_SECOND_INSTANCE_NAME), eq(false)); } @Test @@ -3823,13 +3917,14 @@ public class SoftApManagerTest extends WifiBaseTest { ArgumentCaptor<WifiCountryCode.ChangeListener> changeListenerCaptor = ArgumentCaptor.forClass(WifiCountryCode.ChangeListener.class); verify(mWifiCountryCode).registerListener(changeListenerCaptor.capture()); - verify(mWifiNative, never()).startSoftAp(any(), any(), anyBoolean(), any()); + verify(mWifiNative, never()).startSoftAp(any(), any(), anyBoolean(), any(), + anyBoolean()); // Trigger the timeout mLooper.moveTimeForward(10_000); mLooper.dispatchAll(); - verify(mWifiNative).startSoftAp(any(), any(), anyBoolean(), any()); + verify(mWifiNative).startSoftAp(any(), any(), anyBoolean(), any(), anyBoolean()); verify(mWifiCountryCode).unregisterListener(changeListenerCaptor.getValue()); } @@ -4210,6 +4305,95 @@ public class SoftApManagerTest extends WifiBaseTest { startSoftApAndVerifyEnabled(apConfig, configBuilder.build(), false); } + /** + * Tests that 11BE configuration is disabled if there is existing 11Be SoftApManager. + */ + @Test + public void testStartSoftApWith11BEConfigurationWhenExistingOther11BeSoftApManager() + throws Exception { + assumeTrue(SdkLevel.isAtLeastT()); + when(Flags.mloSap()).thenReturn(true); + when(mResourceCache.getBoolean(R.bool.config_wifiSoftapIeee80211beSupported)) + .thenReturn(true); + when(mResourceCache.getBoolean(R.bool.config_wifiSoftApSingleLinkMloInBridgedModeSupported)) + .thenReturn(false); + when(mDeviceWiphyCapabilities.isWifiStandardSupported(ScanResult.WIFI_STANDARD_11BE)) + .thenReturn(true); + when(mActiveModeWarden.getCurrentMLDAp()).thenReturn(1); + mDeviceWiphyCapabilitiesSupports11Be = true; + Builder configBuilder = new SoftApConfiguration.Builder(); + configBuilder.setBand(SoftApConfiguration.BAND_5GHZ); + configBuilder.setSsid(TEST_SSID); + configBuilder.setIeee80211beEnabled(true); + configBuilder.setPassphrase("somepassword", + SoftApConfiguration.SECURITY_TYPE_WPA3_SAE); + SoftApModeConfiguration apConfig = new SoftApModeConfiguration( + WifiManager.IFACE_IP_MODE_TETHERED, configBuilder.build(), + mTestSoftApCapability, TEST_COUNTRY_CODE, TEST_TETHERING_REQUEST); + SoftApConfiguration expectedConfig = configBuilder.setIeee80211beEnabled(false).build(); + startSoftApAndVerifyEnabled(apConfig, expectedConfig, false); + } + + /** + * Tests that 11BE configuration is NOT disabled even if there is existing 11Be SoftApManager + * when device support single link MLO in bridged mode. (2 MLDs are allowed case) + */ + @Test + public void testStartSoftApWith11BEWhenExistingOther11BeSoftApButDualSingleLinkMLoSupported() + throws Exception { + assumeTrue(SdkLevel.isAtLeastT()); + when(Flags.mloSap()).thenReturn(true); + when(mResourceCache.getBoolean(R.bool.config_wifiSoftapIeee80211beSupported)) + .thenReturn(true); + when(mResourceCache.getBoolean(R.bool.config_wifiSoftApSingleLinkMloInBridgedModeSupported)) + .thenReturn(true); + when(mDeviceWiphyCapabilities.isWifiStandardSupported(ScanResult.WIFI_STANDARD_11BE)) + .thenReturn(true); + when(mActiveModeWarden.getCurrentMLDAp()).thenReturn(1); + mDeviceWiphyCapabilitiesSupports11Be = true; + Builder configBuilder = new SoftApConfiguration.Builder(); + configBuilder.setBand(SoftApConfiguration.BAND_5GHZ); + configBuilder.setSsid(TEST_SSID); + configBuilder.setIeee80211beEnabled(true); + configBuilder.setPassphrase("somepassword", + SoftApConfiguration.SECURITY_TYPE_WPA3_SAE); + SoftApModeConfiguration apConfig = new SoftApModeConfiguration( + WifiManager.IFACE_IP_MODE_TETHERED, configBuilder.build(), + mTestSoftApCapability, TEST_COUNTRY_CODE, TEST_TETHERING_REQUEST); + startSoftApAndVerifyEnabled(apConfig, configBuilder.build(), false); + } + + /** + * Tests that 11BE configuration is NOT disabled when only 1 MLD supported. (MLO case) + */ + @Test + public void testStartSoftApWith11BEForMLOSupportedCase() + throws Exception { + assumeTrue(SdkLevel.isAtLeastT()); + when(Flags.mloSap()).thenReturn(true); + when(mResourceCache.getBoolean(R.bool.config_wifiSoftapIeee80211beSupported)) + .thenReturn(true); + when(mResourceCache.getInteger(R.integer.config_wifiSoftApMaxNumberMLDSupported)) + .thenReturn(1); + when(mWifiNative.isMLDApSupportMLO()).thenReturn(true); + when(mActiveModeWarden.getCurrentMLDAp()).thenReturn(0); + mDeviceWiphyCapabilitiesSupports11Be = true; + Builder configBuilder = new SoftApConfiguration.Builder(); + configBuilder.setBands(new int[] {SoftApConfiguration.BAND_2GHZ, + SoftApConfiguration.BAND_5GHZ}); + configBuilder.setSsid(TEST_SSID); + configBuilder.setIeee80211beEnabled(true); + configBuilder.setPassphrase("somepassword", + SoftApConfiguration.SECURITY_TYPE_WPA3_SAE); + SoftApModeConfiguration apConfig = new SoftApModeConfiguration( + WifiManager.IFACE_IP_MODE_TETHERED, configBuilder.build(), + mTestSoftApCapability, TEST_COUNTRY_CODE, TEST_TETHERING_REQUEST); + when(Flags.mloSap()).thenReturn(true); + startSoftApAndVerifyEnabled(apConfig, configBuilder.build(), false, true); + + assertTrue(mSoftApManager.isUsingMlo()); + } + @Test public void testStartSoftApAutoUpgradeTo2g5gDbs() throws Exception { assumeTrue(SdkLevel.isAtLeastS()); @@ -4219,24 +4403,46 @@ public class SoftApManagerTest extends WifiBaseTest { int[] dual_bands = {SoftApConfiguration.BAND_2GHZ, SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ}; - SoftApConfiguration config = new SoftApConfiguration.Builder().setSsid(TEST_SSID) - .setSsid(TEST_SSID) + mPersistentApConfig = new SoftApConfiguration.Builder(mPersistentApConfig) .setBand(SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ | SoftApConfiguration.BAND_6GHZ) .build(); - SoftApConfiguration dualBandConfig = new SoftApConfiguration.Builder(config) + when(mWifiApConfigStore.getApConfiguration()).thenReturn(mPersistentApConfig); + SoftApConfiguration dualBandConfig = new SoftApConfiguration.Builder(mPersistentApConfig) .setBands(dual_bands) .build(); SoftApCapability no6GhzCapability = new SoftApCapability(mTestSoftApCapability); no6GhzCapability.setSupportedChannelList(WifiScanner.WIFI_BAND_6_GHZ, new int[0]); SoftApModeConfiguration apConfig = new SoftApModeConfiguration( - WifiManager.IFACE_IP_MODE_TETHERED, config, + WifiManager.IFACE_IP_MODE_TETHERED, null, no6GhzCapability, TEST_COUNTRY_CODE, TEST_TETHERING_REQUEST); startSoftApAndVerifyEnabled(apConfig, dualBandConfig, false); } @Test + public void testStartSoftApDoesNotAutoUpgradeTo2g5gDbsWhenConfigIsSpecified() throws Exception { + assumeTrue(SdkLevel.isAtLeastS()); + when(mResourceCache.getBoolean( + R.bool.config_wifiSoftapUpgradeTetheredTo2g5gBridgedIfBandsAreSubset)) + .thenReturn(true); + SoftApConfiguration config = new SoftApConfiguration.Builder(mPersistentApConfig) + .setBand(SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ + | SoftApConfiguration.BAND_6GHZ) + .build(); + SoftApConfiguration expected = new SoftApConfiguration.Builder(config) + .setBand(SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ) + .build(); + + SoftApCapability no6GhzCapability = new SoftApCapability(mTestSoftApCapability); + no6GhzCapability.setSupportedChannelList(WifiScanner.WIFI_BAND_6_GHZ, new int[0]); + SoftApModeConfiguration apConfig = new SoftApModeConfiguration( + WifiManager.IFACE_IP_MODE_TETHERED, config, + no6GhzCapability, TEST_COUNTRY_CODE, TEST_TETHERING_REQUEST); + startSoftApAndVerifyEnabled(apConfig, expected, false); + } + + @Test public void testStartSoftApDoesNotAutoUpgradeTo2g5gDbsWhen6GhzAvailable() throws Exception { assumeTrue(SdkLevel.isAtLeastS()); when(mResourceCache.getBoolean(R.bool.config_wifi6ghzSupport)).thenReturn(true); @@ -4244,21 +4450,20 @@ public class SoftApManagerTest extends WifiBaseTest { when(mResourceCache.getBoolean( R.bool.config_wifiSoftapUpgradeTetheredTo2g5gBridgedIfBandsAreSubset)) .thenReturn(true); - - SoftApConfiguration config = new SoftApConfiguration.Builder().setSsid(TEST_SSID) - .setSsid(TEST_SSID) - .setPassphrase("somepassword", SoftApConfiguration.SECURITY_TYPE_WPA3_SAE) + mPersistentApConfig = new SoftApConfiguration.Builder(mPersistentApConfig) .setBand(SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ | SoftApConfiguration.BAND_6GHZ) + .setPassphrase("somepassword", SoftApConfiguration.SECURITY_TYPE_WPA3_SAE) .build(); + when(mWifiApConfigStore.getApConfiguration()).thenReturn(mPersistentApConfig); SoftApCapability with6GhzCapability = new SoftApCapability(mTestSoftApCapability); with6GhzCapability.setSupportedChannelList( SoftApConfiguration.BAND_6GHZ, new int[]{5, 21}); SoftApModeConfiguration apConfig = new SoftApModeConfiguration( - WifiManager.IFACE_IP_MODE_TETHERED, config, + WifiManager.IFACE_IP_MODE_TETHERED, null, with6GhzCapability, TEST_COUNTRY_CODE, TEST_TETHERING_REQUEST); - startSoftApAndVerifyEnabled(apConfig, config, false); + startSoftApAndVerifyEnabled(apConfig, mPersistentApConfig, false); } @Test @@ -4273,19 +4478,19 @@ public class SoftApManagerTest extends WifiBaseTest { int[] dual_bands = {SoftApConfiguration.BAND_2GHZ, SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ}; - SoftApConfiguration config = new SoftApConfiguration.Builder().setSsid(TEST_SSID) - .setSsid(TEST_SSID) + mPersistentApConfig = new SoftApConfiguration.Builder(mPersistentApConfig) .setBand(SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ | SoftApConfiguration.BAND_6GHZ) .build(); - SoftApConfiguration dualBandConfig = new SoftApConfiguration.Builder(config) + when(mWifiApConfigStore.getApConfiguration()).thenReturn(mPersistentApConfig); + SoftApConfiguration dualBandConfig = new SoftApConfiguration.Builder(mPersistentApConfig) .setBands(dual_bands) .build(); when(mWifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_6_GHZ)) .thenReturn(new int[0]); SoftApModeConfiguration apConfig = new SoftApModeConfiguration( - WifiManager.IFACE_IP_MODE_TETHERED, config, + WifiManager.IFACE_IP_MODE_TETHERED, null, mTestSoftApCapability, "Not " + TEST_COUNTRY_CODE, TEST_TETHERING_REQUEST); startSoftApAndVerifyEnabled(apConfig, dualBandConfig, false); } @@ -4303,18 +4508,18 @@ public class SoftApManagerTest extends WifiBaseTest { R.bool.config_wifiSoftapUpgradeTetheredTo2g5gBridgedIfBandsAreSubset)) .thenReturn(true); - SoftApConfiguration config = new SoftApConfiguration.Builder().setSsid(TEST_SSID) - .setSsid(TEST_SSID) - .setPassphrase("somepassword", SoftApConfiguration.SECURITY_TYPE_WPA3_SAE) + mPersistentApConfig = new SoftApConfiguration.Builder(mPersistentApConfig) .setBand(SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ | SoftApConfiguration.BAND_6GHZ) + .setPassphrase("somepassword", SoftApConfiguration.SECURITY_TYPE_WPA3_SAE) .build(); + when(mWifiApConfigStore.getApConfiguration()).thenReturn(mPersistentApConfig); when(mWifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_6_GHZ)) .thenReturn(ALLOWED_6G_FREQS); SoftApModeConfiguration apConfig = new SoftApModeConfiguration( - WifiManager.IFACE_IP_MODE_TETHERED, config, + WifiManager.IFACE_IP_MODE_TETHERED, null, mTestSoftApCapability, "Not " + TEST_COUNTRY_CODE, TEST_TETHERING_REQUEST); - startSoftApAndVerifyEnabled(apConfig, config, false); + startSoftApAndVerifyEnabled(apConfig); } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/SoftApStoreDataTest.java b/service/tests/wifitests/src/com/android/server/wifi/SoftApStoreDataTest.java index b87832c669..ebb0c67ecb 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/SoftApStoreDataTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/SoftApStoreDataTest.java @@ -18,6 +18,7 @@ package com.android.server.wifi; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; @@ -38,6 +39,7 @@ import android.net.wifi.SoftApInfo; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiMigration; import android.net.wifi.WifiSsid; +import android.net.wifi.util.Environment; import android.util.SparseIntArray; import android.util.Xml; @@ -50,6 +52,7 @@ import com.android.server.wifi.util.EncryptedData; import com.android.server.wifi.util.InformationElementUtil; import com.android.server.wifi.util.SettingsMigrationDataHolder; import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil; +import com.android.server.wifi.util.XmlUtil; import com.android.wifi.flags.Flags; import org.junit.After; @@ -113,6 +116,8 @@ public class SoftApStoreDataTest extends WifiBaseTest { private static final boolean TEST_80211AX_ENABLED = false; private static final boolean TEST_80211BE_ENABLED = false; private static final boolean TEST_USER_CONFIGURATION = false; + // Use non default value true as test data + private static final boolean TEST_CLIENT_ISOLATION = true; private static final String TEST_TWO_VENDOR_ELEMENTS_HEX = "DD04AABBCCDDDD0401020304"; private static final int TEST_MAX_CHANNEL_WIDTH = SoftApInfo.CHANNEL_WIDTH_40MHZ; private MockitoSession mSession; @@ -279,7 +284,7 @@ public class SoftApStoreDataTest extends WifiBaseTest { + "<boolean name=\"UserConfiguration\" value=\"" + TEST_USER_CONFIGURATION + "\" />\n"; - private static final String TEST_CONFIG_STRING_WITH_ALL_CONFIG_LAST_VERSION = + private static final String TEST_CONFIG_STRING_WITH_ALL_CONFIG_IN_T = TEST_CONFIG_STRING_WITH_ALL_CONFIG_IN_S + "<long name=\"BridgedModeOpportunisticShutdownTimeoutMillis\" value=\"" + TEST_BRIDTED_MODE_SHUTDOWN_TIMEOUT_MILLIS + "\" />\n" @@ -290,6 +295,11 @@ public class SoftApStoreDataTest extends WifiBaseTest { + "<boolean name=\"80211beEnabled\" value=\"" + TEST_80211BE_ENABLED + "\" />\n"; + private static final String TEST_CONFIG_STRING_WITH_ALL_CONFIG_IN_B = + TEST_CONFIG_STRING_WITH_ALL_CONFIG_IN_T + + "<boolean name=\"ClientIsolation\" value=\"" + + TEST_CLIENT_ISOLATION + "\" />\n"; + @Mock private Context mContext; @Mock SoftApStoreData.DataSource mDataSource; @Mock private WifiMigration.SettingsMigrationData mOemMigrationData; @@ -327,6 +337,8 @@ public class SoftApStoreDataTest extends WifiBaseTest { } }).when(mWifiConfigStoreEncryptionUtil).decrypt(any()); when(Flags.softapConfigStoreMaxChannelWidth()).thenReturn(false); + // Default flag is enabled. + when(Flags.apIsolate()).thenReturn(true); } /** @@ -434,10 +446,15 @@ public class SoftApStoreDataTest extends WifiBaseTest { TEST_TWO_VENDOR_ELEMENTS_HEX))); softApConfigBuilder.setIeee80211beEnabled(TEST_80211BE_ENABLED); } + if (Environment.isSdkAtLeastB()) { + softApConfigBuilder.setClientIsolationEnabled(TEST_CLIENT_ISOLATION); + } when(mDataSource.toSerialize()).thenReturn(softApConfigBuilder.build()); byte[] actualData = serializeData(); - if (SdkLevel.isAtLeastT()) { - assertEquals(TEST_CONFIG_STRING_WITH_ALL_CONFIG_LAST_VERSION, new String(actualData)); + if (Environment.isSdkAtLeastB()) { + assertEquals(TEST_CONFIG_STRING_WITH_ALL_CONFIG_IN_B, new String(actualData)); + } else if (SdkLevel.isAtLeastT()) { + assertEquals(TEST_CONFIG_STRING_WITH_ALL_CONFIG_IN_T, new String(actualData)); } else if (SdkLevel.isAtLeastS()) { assertEquals(TEST_CONFIG_STRING_WITH_ALL_CONFIG_IN_S, new String(actualData)); } else { @@ -453,7 +470,6 @@ public class SoftApStoreDataTest extends WifiBaseTest { */ @Test public void serializeSoftApNonUtf8() throws Exception { - SoftApConfiguration.Builder softApConfigBuilder = new SoftApConfiguration.Builder(); softApConfigBuilder.setSsid(TEST_SSID); softApConfigBuilder.setBssid(MacAddress.fromString(TEST_BSSID)); @@ -485,10 +501,15 @@ public class SoftApStoreDataTest extends WifiBaseTest { TEST_TWO_VENDOR_ELEMENTS_HEX))); softApConfigBuilder.setIeee80211beEnabled(TEST_80211BE_ENABLED); } + if (Environment.isSdkAtLeastB()) { + softApConfigBuilder.setClientIsolationEnabled(TEST_CLIENT_ISOLATION); + } when(mDataSource.toSerialize()).thenReturn(softApConfigBuilder.build()); byte[] actualData = serializeData(); - if (SdkLevel.isAtLeastT()) { - assertEquals(TEST_CONFIG_STRING_WITH_ALL_CONFIG_LAST_VERSION, new String(actualData)); + if (Environment.isSdkAtLeastB()) { + assertEquals(TEST_CONFIG_STRING_WITH_ALL_CONFIG_IN_B, new String(actualData)); + } else if (SdkLevel.isAtLeastT()) { + assertEquals(TEST_CONFIG_STRING_WITH_ALL_CONFIG_IN_T, new String(actualData)); } else if (SdkLevel.isAtLeastS()) { assertEquals(TEST_CONFIG_STRING_WITH_ALL_CONFIG_IN_S, new String(actualData)); } else { @@ -504,8 +525,10 @@ public class SoftApStoreDataTest extends WifiBaseTest { */ @Test public void deserializeSoftAp() throws Exception { - if (SdkLevel.isAtLeastT()) { - deserializeData(TEST_CONFIG_STRING_WITH_ALL_CONFIG_LAST_VERSION.getBytes()); + if (Environment.isSdkAtLeastB()) { + deserializeData(TEST_CONFIG_STRING_WITH_ALL_CONFIG_IN_B.getBytes()); + } else if (SdkLevel.isAtLeastT()) { + deserializeData(TEST_CONFIG_STRING_WITH_ALL_CONFIG_IN_T.getBytes()); } else if (SdkLevel.isAtLeastS()) { deserializeData(TEST_CONFIG_STRING_WITH_ALL_CONFIG_IN_S.getBytes()); } else { @@ -546,6 +569,9 @@ public class SoftApStoreDataTest extends WifiBaseTest { if (SdkLevel.isAtLeastT()) { assertEquals(softApConfig.isIeee80211beEnabled(), TEST_80211BE_ENABLED); } + if (Environment.isSdkAtLeastB()) { + assertEquals(TEST_CLIENT_ISOLATION, softApConfig.isClientIsolationEnabled()); + } } /** @@ -929,4 +955,31 @@ public class SoftApStoreDataTest extends WifiBaseTest { assertEquals(softApConfig.getSecurityType(), softApConfigDeserialized.getSecurityType()); } + + /** + * Verify that the store data is serialized/deserialized when clientIsolation is disabled. + * + * @throws Exception when test fails it throws exception + */ + @Test + public void testSerializeDeserializeSoftApWhenClientIsolationFalgDisabled() throws Exception { + assumeTrue(Environment.isSdkAtLeastB()); + when(Flags.apIsolate()).thenReturn(false); + SoftApConfiguration testSoftApConfig = + new SoftApConfiguration.Builder().setSsid(TEST_SSID) + .setClientIsolationEnabled(TEST_CLIENT_ISOLATION).build(); + when(mDataSource.toSerialize()).thenReturn(testSoftApConfig); + byte[] actualData = serializeData(); + String serializeDataString = new String(actualData); + assertFalse(serializeDataString.contains( + XmlUtil.SoftApConfigurationXmlUtil.XML_TAG_CLIENT_ISOLATION)); + deserializeData(TEST_CONFIG_STRING_WITH_ALL_CONFIG_IN_B.getBytes()); + ArgumentCaptor<SoftApConfiguration> softapConfigCaptor = + ArgumentCaptor.forClass(SoftApConfiguration.class); + verify(mDataSource).fromDeserialized(softapConfigCaptor.capture()); + SoftApConfiguration softApConfig = softapConfigCaptor.getValue(); + assertNotNull(softApConfig); + // Keep default value false which does NOT equals test data (true) + assertNotEquals(TEST_CLIENT_ISOLATION, softApConfig.isClientIsolationEnabled()); + } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalAidlImplTest.java b/service/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalAidlImplTest.java index 57e0c128f5..9df178a37e 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalAidlImplTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalAidlImplTest.java @@ -28,10 +28,8 @@ import static android.net.wifi.WifiManager.WIFI_FEATURE_TRUST_ON_FIRST_USE; import static android.net.wifi.WifiManager.WIFI_FEATURE_WAPI; import static android.net.wifi.WifiManager.WIFI_FEATURE_WPA3_SAE; import static android.net.wifi.WifiManager.WIFI_FEATURE_WPA3_SUITE_B; -import static android.os.Build.VERSION.SDK_INT; -import static com.android.server.wifi.util.GeneralUtil.getCapabilityIndex; -import static com.android.server.wifi.util.GeneralUtil.longToBitset; +import static com.android.server.wifi.TestUtil.createCapabilityBitset; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -67,7 +65,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.test.MockAnswerUtil; import android.content.Context; -import android.hardware.wifi.V1_0.WifiChannelWidthInMhz; import android.hardware.wifi.supplicant.AnqpData; import android.hardware.wifi.supplicant.AssociationRejectionData; import android.hardware.wifi.supplicant.BssTmData; @@ -104,6 +101,7 @@ import android.hardware.wifi.supplicant.StaIfaceReasonCode; import android.hardware.wifi.supplicant.StaIfaceStatusCode; import android.hardware.wifi.supplicant.SupplicantStateChangeData; import android.hardware.wifi.supplicant.SupplicantStatusCode; +import android.hardware.wifi.supplicant.WifiChannelWidthInMhz; import android.hardware.wifi.supplicant.WifiTechnology; import android.hardware.wifi.supplicant.WpaDriverCapabilitiesMask; import android.hardware.wifi.supplicant.WpsConfigError; @@ -124,6 +122,7 @@ import android.net.wifi.WifiManager; import android.net.wifi.WifiMigration; import android.net.wifi.WifiSsid; import android.net.wifi.flags.Flags; +import android.net.wifi.util.Environment; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; @@ -1260,43 +1259,6 @@ public class SupplicantStaIfaceHalAidlImplTest extends WifiBaseTest { } /** - * Tests the handling of association rejection for WPA3-Personal networks - */ - @Test - public void testWpa3AuthRejectionEverConnected() throws Exception { - executeAndValidateInitializationSequence(); - assertNotNull(mISupplicantStaIfaceCallback); - - WifiConfiguration config = executeAndValidateConnectSequenceWithKeyMgmt( - SUPPLICANT_NETWORK_ID, false, TRANSLATED_SUPPLICANT_SSID.toString(), - WifiConfiguration.SECURITY_TYPE_SAE, null, true); - mISupplicantStaIfaceCallback.onStateChanged( - StaIfaceCallbackState.ASSOCIATING, - NativeUtil.macAddressToByteArray(BSSID), - SUPPLICANT_NETWORK_ID, - NativeUtil.byteArrayFromArrayList(NativeUtil.decodeSsid(SUPPLICANT_SSID)), false); - int statusCode = StaIfaceStatusCode.UNSPECIFIED_FAILURE; - AssociationRejectionData rejectionData = createAssocRejectData(SUPPLICANT_SSID, BSSID, - statusCode, false); - mISupplicantStaIfaceCallback.onAssociationRejected(rejectionData); - verify(mWifiMonitor, never()).broadcastAuthenticationFailureEvent(eq(WLAN0_IFACE_NAME), - anyInt(), anyInt(), any(), any()); - ArgumentCaptor<AssocRejectEventInfo> assocRejectEventInfoCaptor = - ArgumentCaptor.forClass(AssocRejectEventInfo.class); - verify(mWifiMonitor).broadcastAssociationRejectionEvent( - eq(WLAN0_IFACE_NAME), assocRejectEventInfoCaptor.capture()); - AssocRejectEventInfo assocRejectEventInfo = assocRejectEventInfoCaptor.getValue(); - assertNotNull(assocRejectEventInfo); - assertEquals(TRANSLATED_SUPPLICANT_SSID.toString(), assocRejectEventInfo.ssid); - assertEquals(BSSID, assocRejectEventInfo.bssid); - assertEquals(SupplicantStaIfaceCallbackAidlImpl.halToFrameworkStatusCode( - statusCode), assocRejectEventInfo.statusCode); - assertFalse(assocRejectEventInfo.timedOut); - assertNull(assocRejectEventInfo.oceRssiBasedAssocRejectInfo); - assertNull(assocRejectEventInfo.mboAssocDisallowedInfo); - } - - /** * Tests that association rejection due to timeout doesn't broadcast authentication failure * with reason code ERROR_AUTH_FAILURE_WRONG_PSWD. * Driver/Supplicant sets the timedOut field when there is no ACK or response frame for @@ -1845,7 +1807,7 @@ public class SupplicantStaIfaceHalAidlImplTest extends WifiBaseTest { */ @Test public void testGetKeyMgmtCapabilitiesWpa3Sae() throws Exception { - checkKeyMgmtCapabilities(KeyMgmtMask.SAE, longToBitset(WIFI_FEATURE_WPA3_SAE)); + checkKeyMgmtCapabilities(KeyMgmtMask.SAE, createCapabilityBitset(WIFI_FEATURE_WPA3_SAE)); } /** @@ -1853,7 +1815,8 @@ public class SupplicantStaIfaceHalAidlImplTest extends WifiBaseTest { */ @Test public void testGetKeyMgmtCapabilitiesWpa3SuiteB() throws Exception { - checkKeyMgmtCapabilities(KeyMgmtMask.SUITE_B_192, longToBitset(WIFI_FEATURE_WPA3_SUITE_B)); + checkKeyMgmtCapabilities(KeyMgmtMask.SUITE_B_192, + createCapabilityBitset(WIFI_FEATURE_WPA3_SUITE_B)); } /** @@ -1861,7 +1824,7 @@ public class SupplicantStaIfaceHalAidlImplTest extends WifiBaseTest { */ @Test public void testGetKeyMgmtCapabilitiesOwe() throws Exception { - checkKeyMgmtCapabilities(KeyMgmtMask.OWE, longToBitset(WIFI_FEATURE_OWE)); + checkKeyMgmtCapabilities(KeyMgmtMask.OWE, createCapabilityBitset(WIFI_FEATURE_OWE)); } /** @@ -1870,7 +1833,7 @@ public class SupplicantStaIfaceHalAidlImplTest extends WifiBaseTest { @Test public void testGetKeyMgmtCapabilitiesOweAndSae() throws Exception { checkKeyMgmtCapabilities(KeyMgmtMask.OWE | KeyMgmtMask.SAE, - longToBitset(WIFI_FEATURE_OWE | WIFI_FEATURE_WPA3_SAE)); + createCapabilityBitset(WIFI_FEATURE_OWE, WIFI_FEATURE_WPA3_SAE)); } /** @@ -1879,7 +1842,7 @@ public class SupplicantStaIfaceHalAidlImplTest extends WifiBaseTest { @Test public void testGetKeyMgmtCapabilitiesDpp() throws Exception { checkKeyMgmtCapabilities(KeyMgmtMask.DPP, - longToBitset(WIFI_FEATURE_DPP | WIFI_FEATURE_DPP_ENROLLEE_RESPONDER)); + createCapabilityBitset(WIFI_FEATURE_DPP, WIFI_FEATURE_DPP_ENROLLEE_RESPONDER)); } /** @@ -1887,7 +1850,7 @@ public class SupplicantStaIfaceHalAidlImplTest extends WifiBaseTest { */ @Test public void testGetKeyMgmtCapabilitiesWapi() throws Exception { - checkKeyMgmtCapabilities(KeyMgmtMask.WAPI_PSK, longToBitset(WIFI_FEATURE_WAPI)); + checkKeyMgmtCapabilities(KeyMgmtMask.WAPI_PSK, createCapabilityBitset(WIFI_FEATURE_WAPI)); } /** @@ -1895,7 +1858,8 @@ public class SupplicantStaIfaceHalAidlImplTest extends WifiBaseTest { */ @Test public void testGetKeyMgmtCapabilitiesFilsSha256() throws Exception { - checkKeyMgmtCapabilities(KeyMgmtMask.FILS_SHA256, longToBitset(WIFI_FEATURE_FILS_SHA256)); + checkKeyMgmtCapabilities(KeyMgmtMask.FILS_SHA256, + createCapabilityBitset(WIFI_FEATURE_FILS_SHA256)); } /** @@ -1903,7 +1867,8 @@ public class SupplicantStaIfaceHalAidlImplTest extends WifiBaseTest { */ @Test public void testGetKeyMgmtCapabilitiesFilsSha384() throws Exception { - checkKeyMgmtCapabilities(KeyMgmtMask.FILS_SHA384, longToBitset(WIFI_FEATURE_FILS_SHA384)); + checkKeyMgmtCapabilities(KeyMgmtMask.FILS_SHA384, + createCapabilityBitset(WIFI_FEATURE_FILS_SHA384)); } /** @@ -2167,7 +2132,7 @@ public class SupplicantStaIfaceHalAidlImplTest extends WifiBaseTest { executeAndValidateInitializationSequence(); doReturn(WpaDriverCapabilitiesMask.MBO).when(mISupplicantStaIfaceMock) .getWpaDriverCapabilities(); - assertTrue(longToBitset(WIFI_FEATURE_MBO) + assertTrue(createCapabilityBitset(WIFI_FEATURE_MBO) .equals(mDut.getWpaDriverFeatureSet(WLAN0_IFACE_NAME))); } @@ -2179,7 +2144,7 @@ public class SupplicantStaIfaceHalAidlImplTest extends WifiBaseTest { executeAndValidateInitializationSequence(); doReturn(WpaDriverCapabilitiesMask.MBO | WpaDriverCapabilitiesMask.OCE) .when(mISupplicantStaIfaceMock).getWpaDriverCapabilities(); - assertTrue(longToBitset(WIFI_FEATURE_MBO | WIFI_FEATURE_OCE) + assertTrue(createCapabilityBitset(WIFI_FEATURE_MBO, WIFI_FEATURE_OCE) .equals(mDut.getWpaDriverFeatureSet(WLAN0_IFACE_NAME))); } @@ -2191,11 +2156,26 @@ public class SupplicantStaIfaceHalAidlImplTest extends WifiBaseTest { executeAndValidateInitializationSequence(); doReturn(WpaDriverCapabilitiesMask.TRUST_ON_FIRST_USE) .when(mISupplicantStaIfaceMock).getWpaDriverCapabilities(); - assertTrue(longToBitset(WIFI_FEATURE_TRUST_ON_FIRST_USE) + assertTrue(createCapabilityBitset(WIFI_FEATURE_TRUST_ON_FIRST_USE) .equals(mDut.getWpaDriverFeatureSet(WLAN0_IFACE_NAME))); } /** + * Test RSN Overriding feature capability. + */ + @Test + public void testIsRsnOverridingSupported() throws Exception { + executeAndValidateInitializationSequence(); + doReturn(WpaDriverCapabilitiesMask.RSN_OVERRIDING) + .when(mISupplicantStaIfaceMock).getWpaDriverCapabilities(); + if (mDut.isServiceVersionAtLeast(4)) { + assertTrue(mDut.isRsnOverridingSupported(WLAN0_IFACE_NAME)); + } else { + assertFalse(mDut.isRsnOverridingSupported(WLAN0_IFACE_NAME)); + } + } + + /** * Test the handling of BSS transition request callback. */ @Test @@ -2855,8 +2835,8 @@ public class SupplicantStaIfaceHalAidlImplTest extends WifiBaseTest { * i.e. the latest HIDL version before the conversion to AIDL. */ private BitSet addDefaultKeyMgmtCap(BitSet capabilities) { - capabilities.set(getCapabilityIndex(WIFI_FEATURE_PASSPOINT_TERMS_AND_CONDITIONS)); - capabilities.set(getCapabilityIndex(WIFI_FEATURE_DECORATED_IDENTITY)); + capabilities.set(WIFI_FEATURE_PASSPOINT_TERMS_AND_CONDITIONS); + capabilities.set(WIFI_FEATURE_DECORATED_IDENTITY); return capabilities; } @@ -3471,9 +3451,13 @@ public class SupplicantStaIfaceHalAidlImplTest extends WifiBaseTest { */ @Test public void testLegacyKeystoreMigration() throws Exception { - assumeTrue(SDK_INT >= 36); - assertFalse(mDut.mHasMigratedLegacyKeystoreAliases); + assumeTrue(Environment.isSdkAtLeastB()); executeAndValidateInitializationSequence(); + assertFalse(mDut.mHasMigratedLegacyKeystoreAliases); + + // Migration is complete when the consumer receives a success code + mDut.mKeystoreMigrationStatusConsumer.accept( + WifiMigration.KEYSTORE_MIGRATION_SUCCESS_MIGRATION_COMPLETE); assertTrue(mDut.mHasMigratedLegacyKeystoreAliases); } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalHidlImplTest.java b/service/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalHidlImplTest.java index 4ba62fcd06..0bde06bf5e 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalHidlImplTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalHidlImplTest.java @@ -26,8 +26,7 @@ import static android.net.wifi.WifiManager.WIFI_FEATURE_WAPI; import static android.net.wifi.WifiManager.WIFI_FEATURE_WPA3_SAE; import static android.net.wifi.WifiManager.WIFI_FEATURE_WPA3_SUITE_B; -import static com.android.server.wifi.util.GeneralUtil.getCapabilityIndex; -import static com.android.server.wifi.util.GeneralUtil.longToBitset; +import static com.android.server.wifi.TestUtil.createCapabilityBitset; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -1471,42 +1470,6 @@ public class SupplicantStaIfaceHalHidlImplTest extends WifiBaseTest { } /** - * Tests the handling of association rejection for WPA3-Personal networks - */ - @Test - public void testWpa3AuthRejectionEverConnected() throws Exception { - executeAndValidateInitializationSequence(); - assertNotNull(mISupplicantStaIfaceCallback); - - WifiConfiguration config = executeAndValidateConnectSequenceWithKeyMgmt( - SUPPLICANT_NETWORK_ID, false, TRANSLATED_SUPPLICANT_SSID.toString(), - WifiConfiguration.SECURITY_TYPE_SAE, null, true); - mISupplicantStaIfaceCallback.onStateChanged( - ISupplicantStaIfaceCallback.State.ASSOCIATING, - NativeUtil.macAddressToByteArray(BSSID), - SUPPLICANT_NETWORK_ID, - NativeUtil.decodeSsid(SUPPLICANT_SSID)); - int statusCode = ISupplicantStaIfaceCallback.StatusCode.UNSPECIFIED_FAILURE; - mISupplicantStaIfaceCallback.onAssociationRejected( - NativeUtil.macAddressToByteArray(BSSID), statusCode, false); - verify(mWifiMonitor, never()).broadcastAuthenticationFailureEvent(eq(WLAN0_IFACE_NAME), - anyInt(), anyInt(), any(), any()); - ArgumentCaptor<AssocRejectEventInfo> assocRejectEventInfoCaptor = - ArgumentCaptor.forClass(AssocRejectEventInfo.class); - verify(mWifiMonitor).broadcastAssociationRejectionEvent( - eq(WLAN0_IFACE_NAME), assocRejectEventInfoCaptor.capture()); - AssocRejectEventInfo assocRejectEventInfo = - (AssocRejectEventInfo) assocRejectEventInfoCaptor.getValue(); - assertNotNull(assocRejectEventInfo); - assertEquals(TRANSLATED_SUPPLICANT_SSID.toString(), assocRejectEventInfo.ssid); - assertEquals(BSSID, assocRejectEventInfo.bssid); - assertEquals(statusCode, assocRejectEventInfo.statusCode); - assertFalse(assocRejectEventInfo.timedOut); - assertNull(assocRejectEventInfo.oceRssiBasedAssocRejectInfo); - assertNull(assocRejectEventInfo.mboAssocDisallowedInfo); - } - - /** * Tests that association rejection due to timeout doesn't broadcast authentication failure * with reason code ERROR_AUTH_FAILURE_WRONG_PSWD. * Driver/Supplicant sets the timedOut field when there is no ACK or response frame for @@ -2218,7 +2181,7 @@ public class SupplicantStaIfaceHalHidlImplTest extends WifiBaseTest { android.hardware.wifi.supplicant.V1_2.ISupplicantStaIface .getKeyMgmtCapabilitiesCallback.class)); - assertTrue(longToBitset(WIFI_FEATURE_WPA3_SAE) + assertTrue(createCapabilityBitset(WIFI_FEATURE_WPA3_SAE) .equals(mDut.getAdvancedCapabilities(WLAN0_IFACE_NAME))); } @@ -2237,7 +2200,7 @@ public class SupplicantStaIfaceHalHidlImplTest extends WifiBaseTest { android.hardware.wifi.supplicant.V1_2.ISupplicantStaIface .getKeyMgmtCapabilitiesCallback.class)); - assertTrue(longToBitset(WIFI_FEATURE_WPA3_SUITE_B) + assertTrue(createCapabilityBitset(WIFI_FEATURE_WPA3_SUITE_B) .equals(mDut.getAdvancedCapabilities(WLAN0_IFACE_NAME))); } @@ -2256,7 +2219,7 @@ public class SupplicantStaIfaceHalHidlImplTest extends WifiBaseTest { android.hardware.wifi.supplicant.V1_2.ISupplicantStaIface .getKeyMgmtCapabilitiesCallback.class)); - assertTrue(longToBitset(WIFI_FEATURE_OWE) + assertTrue(createCapabilityBitset(WIFI_FEATURE_OWE) .equals(mDut.getAdvancedCapabilities(WLAN0_IFACE_NAME))); } @@ -2276,7 +2239,7 @@ public class SupplicantStaIfaceHalHidlImplTest extends WifiBaseTest { android.hardware.wifi.supplicant.V1_2.ISupplicantStaIface .getKeyMgmtCapabilitiesCallback.class)); - assertTrue(longToBitset(WIFI_FEATURE_OWE | WIFI_FEATURE_WPA3_SAE) + assertTrue(createCapabilityBitset(WIFI_FEATURE_OWE, WIFI_FEATURE_WPA3_SAE) .equals(mDut.getAdvancedCapabilities(WLAN0_IFACE_NAME))); } @@ -2295,7 +2258,7 @@ public class SupplicantStaIfaceHalHidlImplTest extends WifiBaseTest { android.hardware.wifi.supplicant.V1_2.ISupplicantStaIface .getKeyMgmtCapabilitiesCallback.class)); - assertTrue(longToBitset(WIFI_FEATURE_DPP) + assertTrue(createCapabilityBitset(WIFI_FEATURE_DPP) .equals(mDut.getAdvancedCapabilities(WLAN0_IFACE_NAME))); } @@ -2314,7 +2277,7 @@ public class SupplicantStaIfaceHalHidlImplTest extends WifiBaseTest { .getKeyMgmtCapabilities_1_3Callback.class)); assertTrue(mDut.getAdvancedCapabilities(WLAN0_IFACE_NAME) - .get(getCapabilityIndex(WIFI_FEATURE_DPP_ENROLLEE_RESPONDER))); + .get(WIFI_FEATURE_DPP_ENROLLEE_RESPONDER)); } /** @@ -2333,7 +2296,7 @@ public class SupplicantStaIfaceHalHidlImplTest extends WifiBaseTest { .getKeyMgmtCapabilities_1_3Callback.class)); assertFalse(mDut.getAdvancedCapabilities(WLAN0_IFACE_NAME) - .get(getCapabilityIndex(WIFI_FEATURE_DPP_ENROLLEE_RESPONDER))); + .get(WIFI_FEATURE_DPP_ENROLLEE_RESPONDER)); } /** @@ -2351,7 +2314,7 @@ public class SupplicantStaIfaceHalHidlImplTest extends WifiBaseTest { android.hardware.wifi.supplicant.V1_3.ISupplicantStaIface .getKeyMgmtCapabilities_1_3Callback.class)); - assertTrue(longToBitset(WIFI_FEATURE_WAPI) + assertTrue(createCapabilityBitset(WIFI_FEATURE_WAPI) .equals(mDut.getAdvancedCapabilities(WLAN0_IFACE_NAME))); } @@ -2370,7 +2333,7 @@ public class SupplicantStaIfaceHalHidlImplTest extends WifiBaseTest { android.hardware.wifi.supplicant.V1_3.ISupplicantStaIface .getKeyMgmtCapabilities_1_3Callback.class)); - assertTrue(longToBitset(WIFI_FEATURE_FILS_SHA256) + assertTrue(createCapabilityBitset(WIFI_FEATURE_FILS_SHA256) .equals(mDut.getAdvancedCapabilities(WLAN0_IFACE_NAME))); } @@ -2389,7 +2352,7 @@ public class SupplicantStaIfaceHalHidlImplTest extends WifiBaseTest { android.hardware.wifi.supplicant.V1_3.ISupplicantStaIface .getKeyMgmtCapabilities_1_3Callback.class)); - assertTrue(longToBitset(WIFI_FEATURE_FILS_SHA384) + assertTrue(createCapabilityBitset(WIFI_FEATURE_FILS_SHA384) .equals(mDut.getAdvancedCapabilities(WLAN0_IFACE_NAME))); } @@ -3547,7 +3510,7 @@ public class SupplicantStaIfaceHalHidlImplTest extends WifiBaseTest { android.hardware.wifi.supplicant.V1_3.ISupplicantStaIface .getWpaDriverCapabilitiesCallback.class)); - assertTrue(longToBitset(WIFI_FEATURE_MBO) + assertTrue(createCapabilityBitset(WIFI_FEATURE_MBO) .equals(mDut.getWpaDriverFeatureSet(WLAN0_IFACE_NAME))); } @@ -3568,7 +3531,7 @@ public class SupplicantStaIfaceHalHidlImplTest extends WifiBaseTest { android.hardware.wifi.supplicant.V1_3.ISupplicantStaIface .getWpaDriverCapabilitiesCallback.class)); - assertTrue(longToBitset(WIFI_FEATURE_MBO | WIFI_FEATURE_OCE) + assertTrue(createCapabilityBitset(WIFI_FEATURE_MBO, WIFI_FEATURE_OCE) .equals(mDut.getWpaDriverFeatureSet(WLAN0_IFACE_NAME))); } @@ -3589,7 +3552,7 @@ public class SupplicantStaIfaceHalHidlImplTest extends WifiBaseTest { android.hardware.wifi.supplicant.V1_4.ISupplicantStaIface .getWpaDriverCapabilities_1_4Callback.class)); - assertTrue(longToBitset(WIFI_FEATURE_MBO | WIFI_FEATURE_OCE) + assertTrue(createCapabilityBitset(WIFI_FEATURE_MBO, WIFI_FEATURE_OCE) .equals(mDut.getWpaDriverFeatureSet(WLAN0_IFACE_NAME))); } diff --git a/service/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java b/service/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java index 84de1f8d64..e93932b6f8 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/SupplicantStaIfaceHalTest.java @@ -16,7 +16,9 @@ package com.android.server.wifi; -import static com.android.server.wifi.util.GeneralUtil.longToBitset; +import static android.net.wifi.WifiManager.WIFI_FEATURE_OWE; + +import static com.android.server.wifi.TestUtil.createCapabilityBitset; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -928,7 +930,7 @@ public class SupplicantStaIfaceHalTest extends WifiBaseTest { @Test public void testGetAdvancedCapabilities() { initializeWithAidlImpl(true); - BitSet capabilities = longToBitset(0X1234); + BitSet capabilities = createCapabilityBitset(WIFI_FEATURE_OWE); // arbitrary feature when(mStaIfaceHalAidlMock.getAdvancedCapabilities(anyString())).thenReturn(capabilities); assertTrue(capabilities.equals(mDut.getAdvancedCapabilities(IFACE_NAME))); verify(mStaIfaceHalAidlMock).getAdvancedCapabilities(eq(IFACE_NAME)); @@ -940,7 +942,7 @@ public class SupplicantStaIfaceHalTest extends WifiBaseTest { @Test public void testGetWpaDriverFeatureSet() { initializeWithAidlImpl(true); - BitSet capabilities = longToBitset(0X1234); + BitSet capabilities = createCapabilityBitset(WIFI_FEATURE_OWE); // arbitrary feature when(mStaIfaceHalAidlMock.getWpaDriverFeatureSet(anyString())).thenReturn(capabilities); assertTrue(capabilities.equals(mDut.getWpaDriverFeatureSet(IFACE_NAME))); verify(mStaIfaceHalAidlMock).getWpaDriverFeatureSet(eq(IFACE_NAME)); diff --git a/service/tests/wifitests/src/com/android/server/wifi/SupplicantStaNetworkHalAidlImplTest.java b/service/tests/wifitests/src/com/android/server/wifi/SupplicantStaNetworkHalAidlImplTest.java index 9b08be96cf..4d399f1142 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/SupplicantStaNetworkHalAidlImplTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/SupplicantStaNetworkHalAidlImplTest.java @@ -15,8 +15,7 @@ */ package com.android.server.wifi; -import static com.android.server.wifi.util.GeneralUtil.getCapabilityIndex; -import static com.android.server.wifi.util.GeneralUtil.longToBitset; +import static com.android.server.wifi.TestUtil.createCapabilityBitset; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -125,7 +124,7 @@ public class SupplicantStaNetworkHalAidlImplTest extends WifiBaseTest { when(mWifiGlobals.isWpa3SaeUpgradeOffloadEnabled()).thenReturn(true); when(mWifiGlobals.isWpaPersonalDeprecated()).thenReturn(false); - mAdvanceKeyMgmtFeatures.set(getCapabilityIndex(WifiManager.WIFI_FEATURE_WPA3_SUITE_B)); + mAdvanceKeyMgmtFeatures.set(WifiManager.WIFI_FEATURE_WPA3_SUITE_B); mSupplicantNetwork = new SupplicantStaNetworkHalAidlImpl(1, mISupplicantStaNetworkMock, IFACE_NAME, mContext, mWifiMonitor, mWifiGlobals, mAdvanceKeyMgmtFeatures, mWpaDriverFeatures); @@ -1350,7 +1349,7 @@ public class SupplicantStaNetworkHalAidlImplTest extends WifiBaseTest { public void testEapMinimumTlsVersionWifiConfigurationSaveLoadWithAidlV2TlsV13Supported() throws Exception { // Re-init mock to AIDL v2 with TLS v1.3 support. - mWpaDriverFeatures = longToBitset(WifiManager.WIFI_FEATURE_TLS_V1_3); + mWpaDriverFeatures = createCapabilityBitset(WifiManager.WIFI_FEATURE_TLS_V1_3); mSupplicantNetwork = new SupplicantStaNetworkHalAidlImpl(2, mISupplicantStaNetworkMock, IFACE_NAME, mContext, mWifiMonitor, mWifiGlobals, mAdvanceKeyMgmtFeatures, mWpaDriverFeatures); diff --git a/service/tests/wifitests/src/com/android/server/wifi/SupplicantStaNetworkHalHidlImplTest.java b/service/tests/wifitests/src/com/android/server/wifi/SupplicantStaNetworkHalHidlImplTest.java index b34402e793..d1e32ebcd7 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/SupplicantStaNetworkHalHidlImplTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/SupplicantStaNetworkHalHidlImplTest.java @@ -15,7 +15,7 @@ */ package com.android.server.wifi; -import static com.android.server.wifi.util.GeneralUtil.longToBitset; +import static com.android.server.wifi.TestUtil.createCapabilityBitset; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -192,7 +192,7 @@ public class SupplicantStaNetworkHalHidlImplTest extends WifiBaseTest { when(mWifiGlobals.isWpa3SaeUpgradeOffloadEnabled()).thenReturn(true); when(mWifiGlobals.isWpaPersonalDeprecated()).thenReturn(false); - mAdvanceKeyMgmtFeatures = longToBitset(WifiManager.WIFI_FEATURE_WPA3_SUITE_B); + mAdvanceKeyMgmtFeatures = createCapabilityBitset(WifiManager.WIFI_FEATURE_WPA3_SUITE_B); createSupplicantStaNetwork(SupplicantStaNetworkVersion.V1_0); } diff --git a/service/tests/wifitests/src/com/android/server/wifi/TestUtil.java b/service/tests/wifitests/src/com/android/server/wifi/TestUtil.java index 2640209a1c..c12e064ab1 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/TestUtil.java +++ b/service/tests/wifitests/src/com/android/server/wifi/TestUtil.java @@ -26,6 +26,7 @@ import android.net.wifi.WifiManager; import android.os.PowerManager; import java.util.ArrayList; +import java.util.BitSet; /** * Utils for wifi tests. @@ -106,4 +107,45 @@ public class TestUtil { Intent intent = new Intent(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED); broadcastReceiver.onReceive(context, intent); } + + /** + * Create a new capability BitSet containing the provided capabilities. + * + * @param capabilities One or more WifiManager.WIFI_FEATURE_* capabilities + */ + public static BitSet createCapabilityBitset(int... capabilities) { + BitSet capabilityBitset = new BitSet(); + for (int capability : capabilities) { + capabilityBitset.set(capability); + } + return capabilityBitset; + } + + /** + * Add additional capabilities to the provided BitSet. + * + * @param bitset BitSet that the capabilities should be added to + * @param capabilities One or more WifiManager.WIFI_FEATURE_* capabilities + */ + public static BitSet addCapabilitiesToBitset(BitSet bitset, int... capabilities) { + // Clone to avoid modifying the input BitSet + BitSet clonedBitset = (BitSet) bitset.clone(); + for (int capability : capabilities) { + clonedBitset.set(capability); + } + return clonedBitset; + } + + /** + * Combine several BitSets using an OR operation. + * + * @param bitsets BitSets that should be combined + */ + public static BitSet combineBitsets(BitSet... bitsets) { + BitSet combinedBitset = new BitSet(); + for (BitSet bitset : bitsets) { + combinedBitset.or(bitset); + } + return combinedBitset; + } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/ThroughputPredictorTest.java b/service/tests/wifitests/src/com/android/server/wifi/ThroughputPredictorTest.java index 556f7d5073..04ba2a541b 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/ThroughputPredictorTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/ThroughputPredictorTest.java @@ -144,10 +144,10 @@ public class ThroughputPredictorTest extends WifiBaseTest { @Test public void verifyMaxChannelUtilizationBssLoad() { int predictedThroughputMbps = mThroughputPredictor.predictThroughput(mDeviceCapabilities, - ScanResult.WIFI_STANDARD_11AC, ScanResult.CHANNEL_WIDTH_20MHZ, 0, 2412, 1, + ScanResult.WIFI_STANDARD_11AC, ScanResult.CHANNEL_WIDTH_80MHZ, 0, 5210, 1, MAX_CHANNEL_UTILIZATION, 0, false, null); - assertEquals(0, predictedThroughputMbps); + assertEquals(433, predictedThroughputMbps); } @Test @@ -163,7 +163,7 @@ public class ThroughputPredictorTest extends WifiBaseTest { public void verifyHighRssiMinChannelUtilizationAc5g80Mhz2ss() { int predictedThroughputMbps = mThroughputPredictor.predictThroughput(mDeviceCapabilities, ScanResult.WIFI_STANDARD_11AC, ScanResult.CHANNEL_WIDTH_80MHZ, 0, 5180, 2, - MIN_CHANNEL_UTILIZATION, 50, false, null); + 0, MIN_CHANNEL_UTILIZATION, false, null); assertEquals(866, predictedThroughputMbps); } @@ -178,7 +178,7 @@ public class ThroughputPredictorTest extends WifiBaseTest { .thenReturn(1); int predictedThroughputMbps = mThroughputPredictor.predictThroughput(mDeviceCapabilities, ScanResult.WIFI_STANDARD_11AC, ScanResult.CHANNEL_WIDTH_80MHZ, 0, 5180, 2, - MIN_CHANNEL_UTILIZATION, 50, false, null); + 0, MIN_CHANNEL_UTILIZATION, false, null); assertEquals(433, predictedThroughputMbps); } @@ -194,7 +194,7 @@ public class ThroughputPredictorTest extends WifiBaseTest { int predictedThroughputMbps = mThroughputPredictor.predictThroughput(mDeviceCapabilities, ScanResult.WIFI_STANDARD_11BE, ScanResult.CHANNEL_WIDTH_320MHZ, 0, 6180, 4, - MIN_CHANNEL_UTILIZATION, INVALID, false, null); + 0, MIN_CHANNEL_UTILIZATION, false, null); assertEquals(11529, predictedThroughputMbps); } @@ -213,7 +213,7 @@ public class ThroughputPredictorTest extends WifiBaseTest { int predictedThroughputMbps = mThroughputPredictor.predictThroughput(mDeviceCapabilities, ScanResult.WIFI_STANDARD_11AX, ScanResult.CHANNEL_WIDTH_160MHZ, 0, 5180, 4, - MIN_CHANNEL_UTILIZATION, INVALID, false, null); + 0, MIN_CHANNEL_UTILIZATION, false, null); assertEquals(2401, predictedThroughputMbps); } @@ -223,7 +223,7 @@ public class ThroughputPredictorTest extends WifiBaseTest { public void verifyMidRssiMinChannelUtilizationAc5g80Mhz2ss() { int predictedThroughputMbps = mThroughputPredictor.predictThroughput(mDeviceCapabilities, ScanResult.WIFI_STANDARD_11AC, ScanResult.CHANNEL_WIDTH_80MHZ, -50, 5180, 2, - MIN_CHANNEL_UTILIZATION, INVALID, false, null); + 0, MIN_CHANNEL_UTILIZATION, false, null); assertEquals(866, predictedThroughputMbps); } @@ -232,7 +232,7 @@ public class ThroughputPredictorTest extends WifiBaseTest { public void verifyLowRssiMinChannelUtilizationAc5g80Mhz2ss() { int predictedThroughputMbps = mThroughputPredictor.predictThroughput(mDeviceCapabilities, ScanResult.WIFI_STANDARD_11AC, ScanResult.CHANNEL_WIDTH_80MHZ, -80, 5180, 2, - MIN_CHANNEL_UTILIZATION, INVALID, false, null); + 0, MIN_CHANNEL_UTILIZATION, false, null); assertEquals(41, predictedThroughputMbps); } @@ -243,14 +243,14 @@ public class ThroughputPredictorTest extends WifiBaseTest { ScanResult.WIFI_STANDARD_11AC, ScanResult.CHANNEL_WIDTH_80MHZ, -80, 5180, 2, INVALID, INVALID, false, null); - assertEquals(31, predictedThroughputMbps); + assertEquals(38, predictedThroughputMbps); } @Test public void verifyHighRssiMinChannelUtilizationAc2g20Mhz2ss() { int predictedThroughputMbps = mThroughputPredictor.predictThroughput(mDeviceCapabilities, ScanResult.WIFI_STANDARD_11AC, ScanResult.CHANNEL_WIDTH_20MHZ, -20, 2437, 2, - MIN_CHANNEL_UTILIZATION, INVALID, false, null); + 0, MIN_CHANNEL_UTILIZATION, false, null); assertEquals(192, predictedThroughputMbps); } @@ -259,7 +259,7 @@ public class ThroughputPredictorTest extends WifiBaseTest { public void verifyHighRssiMinChannelUtilizationAc2g20Mhz2ssBluetoothConnected() { int predictedThroughputMbps = mThroughputPredictor.predictThroughput(mDeviceCapabilities, ScanResult.WIFI_STANDARD_11AC, ScanResult.CHANNEL_WIDTH_20MHZ, -20, 2437, 2, - MIN_CHANNEL_UTILIZATION, INVALID, true, null); + 0, MIN_CHANNEL_UTILIZATION, true, null); assertEquals(144, predictedThroughputMbps); } @@ -268,7 +268,7 @@ public class ThroughputPredictorTest extends WifiBaseTest { public void verifyHighRssiMinChannelUtilizationLegacy5g20Mhz() { int predictedThroughputMbps = mThroughputPredictor.predictThroughput(mDeviceCapabilities, ScanResult.WIFI_STANDARD_LEGACY, ScanResult.CHANNEL_WIDTH_20MHZ, -50, 5180, - 1, MIN_CHANNEL_UTILIZATION, INVALID, false, null); + 1, 0, MIN_CHANNEL_UTILIZATION, false, null); assertEquals(54, predictedThroughputMbps); } @@ -286,7 +286,7 @@ public class ThroughputPredictorTest extends WifiBaseTest { public void verifyHighRssiMinChannelUtilizationHt2g20Mhz2ss() { int predictedThroughputMbps = mThroughputPredictor.predictThroughput(mDeviceCapabilities, ScanResult.WIFI_STANDARD_11N, ScanResult.CHANNEL_WIDTH_20MHZ, -50, 2437, 2, - MIN_CHANNEL_UTILIZATION, INVALID, false, null); + 0, MIN_CHANNEL_UTILIZATION, false, null); assertEquals(144, predictedThroughputMbps); } @@ -297,7 +297,7 @@ public class ThroughputPredictorTest extends WifiBaseTest { when(mDeviceCapabilities.getMaxNumberRxSpatialStreams()).thenReturn(0); int predictedThroughputMbps = mThroughputPredictor.predictThroughput(mDeviceCapabilities, ScanResult.WIFI_STANDARD_11N, ScanResult.CHANNEL_WIDTH_20MHZ, -50, 2437, 2, - MIN_CHANNEL_UTILIZATION, INVALID, false, null); + 0, MIN_CHANNEL_UTILIZATION, false, null); // Expect to 1SS peak rate because maxNumberSpatialStream is overridden to 1. assertEquals(72, predictedThroughputMbps); } @@ -335,7 +335,7 @@ public class ThroughputPredictorTest extends WifiBaseTest { ScanResult.WIFI_STANDARD_11AC, ScanResult.CHANNEL_WIDTH_40MHZ, -66, 5180, 2, INVALID, 80, false, null); - assertEquals(103, predictedThroughputMbps); + assertEquals(150, predictedThroughputMbps); } @Test @@ -437,7 +437,7 @@ public class ThroughputPredictorTest extends WifiBaseTest { mConnectionCap.channelBandwidth = ScanResult.CHANNEL_WIDTH_160MHZ; mConnectionCap.maxNumberRxSpatialStreams = 4; //mConnectionCap.maxNumberTxSpatialStreams = 4; - assertEquals(2881, mThroughputPredictor.predictRxThroughput(mConnectionCap, + assertEquals(4520, mThroughputPredictor.predictRxThroughput(mConnectionCap, -10, 5180, INVALID)); } @@ -451,12 +451,12 @@ public class ThroughputPredictorTest extends WifiBaseTest { when(mDeviceCapabilities.getMaxNumberRxSpatialStreams()).thenReturn(4); int predictedThroughputMbps = mThroughputPredictor.predictThroughput(mDeviceCapabilities, ScanResult.WIFI_STANDARD_11BE, ScanResult.CHANNEL_WIDTH_160MHZ, 0, 5240, 4, - MIN_CHANNEL_UTILIZATION, 50, false, null); + 0, MIN_CHANNEL_UTILIZATION, false, null); assertEquals(5764, predictedThroughputMbps); predictedThroughputMbps = mThroughputPredictor.predictThroughput(mDeviceCapabilities, ScanResult.WIFI_STANDARD_11BE, ScanResult.CHANNEL_WIDTH_160MHZ, 0, 5240, 4, - MIN_CHANNEL_UTILIZATION, 50, false, new byte[]{(byte) 0x3, (byte) 0x0}); + 0, MIN_CHANNEL_UTILIZATION, false, new byte[]{(byte) 0x3, (byte) 0x0}); assertEquals(4388, predictedThroughputMbps); } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/WakeupControllerTest.java b/service/tests/wifitests/src/com/android/server/wifi/WakeupControllerTest.java index 10ae4d9fbb..a0cd6c653f 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WakeupControllerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WakeupControllerTest.java @@ -17,6 +17,7 @@ package com.android.server.wifi; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; +import static com.android.server.wifi.TestUtil.createCapabilityBitset; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -127,8 +128,9 @@ public class WakeupControllerTest extends WifiBaseTest { when(mWifiInjector.getWifiSettingsStore()).thenReturn(mWifiSettingsStore); when(mWifiInjector.getActiveModeWarden()).thenReturn(mActiveModeWarden); when(mActiveModeWarden.getPrimaryClientModeManager()).thenReturn(mPrimaryClientModeManager); - when(mPrimaryClientModeManager.getSupportedFeatures()).thenReturn( - WifiManager.WIFI_FEATURE_WPA3_SAE | WifiManager.WIFI_FEATURE_OWE); + when(mPrimaryClientModeManager.getSupportedFeaturesBitSet()).thenReturn( + createCapabilityBitset( + WifiManager.WIFI_FEATURE_WPA3_SAE, WifiManager.WIFI_FEATURE_OWE)); when(mWifiInjector.getWifiNative()).thenReturn(mWifiNative); when(mWifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY)) .thenReturn(new int[]{DFS_CHANNEL_FREQ}); diff --git a/service/tests/wifitests/src/com/android/server/wifi/WakeupEvaluatorTest.java b/service/tests/wifitests/src/com/android/server/wifi/WakeupEvaluatorTest.java index d256106967..3952f3cda3 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WakeupEvaluatorTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WakeupEvaluatorTest.java @@ -17,6 +17,7 @@ package com.android.server.wifi; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; +import static com.android.server.wifi.TestUtil.createCapabilityBitset; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -105,8 +106,9 @@ public class WakeupEvaluatorTest extends WifiBaseTest { when(WifiInjector.getInstance()).thenReturn(mWifiInjector); when(mWifiInjector.getActiveModeWarden()).thenReturn(mActiveModeWarden); when(mActiveModeWarden.getPrimaryClientModeManager()).thenReturn(mPrimaryClientModeManager); - when(mPrimaryClientModeManager.getSupportedFeatures()).thenReturn( - WifiManager.WIFI_FEATURE_WPA3_SAE | WifiManager.WIFI_FEATURE_OWE); + when(mPrimaryClientModeManager.getSupportedFeaturesBitSet()).thenReturn( + createCapabilityBitset( + WifiManager.WIFI_FEATURE_WPA3_SAE, WifiManager.WIFI_FEATURE_OWE)); when(mWifiInjector.getWifiGlobals()).thenReturn(mWifiGlobals); when(mWifiGlobals.isWpa3SaeUpgradeEnabled()).thenReturn(true); when(mWifiGlobals.isOweUpgradeEnabled()).thenReturn(true); diff --git a/service/tests/wifitests/src/com/android/server/wifi/WepNetworkUsageControllerTest.java b/service/tests/wifitests/src/com/android/server/wifi/WepNetworkUsageControllerTest.java new file mode 100644 index 0000000000..f56db3a4d2 --- /dev/null +++ b/service/tests/wifitests/src/com/android/server/wifi/WepNetworkUsageControllerTest.java @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2024 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 com.android.server.wifi.WifiSettingsConfigStore.WIFI_WEP_ALLOWED; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.anyBoolean; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.test.MockAnswerUtil.AnswerWithArguments; +import android.net.wifi.WifiInfo; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.test.TestLooper; + +import androidx.test.filters.SmallTest; + +import com.android.wifi.flags.FeatureFlags; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.Arrays; +import java.util.List; + +/** + * Unit tests for {@link com.android.server.wifi.WifiVoipDetectorTest}. + */ +@SmallTest +public class WepNetworkUsageControllerTest extends WifiBaseTest { + + @Mock HandlerThread mHandlerThread; + @Mock WifiDeviceStateChangeManager mWifiDeviceStateChangeManager; + @Mock WifiSettingsConfigStore mWifiSettingsConfigStore; + @Mock WifiGlobals mWifiGlobals; + @Mock ActiveModeWarden mActiveModeWarden; + @Mock FeatureFlags mFeatureFlags; + + private WepNetworkUsageController mWepNetworkUsageController; + private TestLooper mLooper; + final ArgumentCaptor<WifiSettingsConfigStore.OnSettingsChangedListener> + mWepAllowedSettingChangedListenerCaptor = + ArgumentCaptor.forClass(WifiSettingsConfigStore.OnSettingsChangedListener.class); + + final ArgumentCaptor<WifiDeviceStateChangeManager.StateChangeCallback> + mWifiDeviceStateChangeCallbackCaptor = + ArgumentCaptor.forClass(WifiDeviceStateChangeManager.StateChangeCallback.class); + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mLooper = new TestLooper(); + when(mHandlerThread.getLooper()).thenReturn(mLooper.getLooper()); + when(mFeatureFlags.wepDisabledInApm()).thenReturn(true); + when(mWifiSettingsConfigStore.get(eq(WIFI_WEP_ALLOWED))).thenReturn(true); + doAnswer(new AnswerWithArguments() { + public void answer(WifiDeviceStateChangeManager.StateChangeCallback callback) { + callback.onAdvancedProtectionModeStateChanged(false); + } + }).when(mWifiDeviceStateChangeManager).registerStateChangeCallback(any()); + + doAnswer(new AnswerWithArguments() { + public void answer(boolean isWepAllowed) { + when(mWifiGlobals.isWepAllowed()).thenReturn(isWepAllowed); + } + }).when(mWifiGlobals).setWepAllowed(anyBoolean()); + mWepNetworkUsageController = new WepNetworkUsageController( + mHandlerThread, mWifiDeviceStateChangeManager, + mWifiSettingsConfigStore, mWifiGlobals, mActiveModeWarden, mFeatureFlags); + } + + @Test + public void testHandleBootCompleted() { + mWepNetworkUsageController.handleBootCompleted(); + mLooper.dispatchAll(); + verify(mWifiSettingsConfigStore).get(eq(WIFI_WEP_ALLOWED)); + verify(mWifiSettingsConfigStore).registerChangeListener( + eq(WIFI_WEP_ALLOWED), + mWepAllowedSettingChangedListenerCaptor.capture(), + any(Handler.class)); + verify(mWifiDeviceStateChangeManager).registerStateChangeCallback( + mWifiDeviceStateChangeCallbackCaptor.capture()); + mLooper.dispatchAll(); + // WEP should be allowed since WIFI_WEP_ALLOWED is true + // and isAdvancedProtectionEnabled is false. (no mock); + verify(mWifiGlobals).setWepAllowed(true); + } + + @Test + public void testHandleWepAllowedSettingChange() { + mWepNetworkUsageController.handleBootCompleted(); + mLooper.dispatchAll(); + verify(mWifiSettingsConfigStore).registerChangeListener( + eq(WIFI_WEP_ALLOWED), + mWepAllowedSettingChangedListenerCaptor.capture(), + any(Handler.class)); + // WIFI_WEP_ALLOWED is true in setUp + verify(mWifiGlobals).setWepAllowed(true); + + // WIFI_WEP_ALLOWED Settings changed, B&R use case + mWepAllowedSettingChangedListenerCaptor.getValue() + .onSettingsChanged(WIFI_WEP_ALLOWED, false); + verify(mWifiGlobals).setWepAllowed(false); + + mWepAllowedSettingChangedListenerCaptor.getValue() + .onSettingsChanged(WIFI_WEP_ALLOWED, true); + verify(mWifiGlobals, times(2)).setWepAllowed(true); + } + + @Test + public void testAdvancedProtectionModeChanged() { + mWepNetworkUsageController.handleBootCompleted(); + mLooper.dispatchAll(); + verify(mWifiDeviceStateChangeManager).registerStateChangeCallback( + mWifiDeviceStateChangeCallbackCaptor.capture()); + // WIFI_WEP_ALLOWED is true in setUp + verify(mWifiGlobals).setWepAllowed(true); + mWifiDeviceStateChangeCallbackCaptor.getValue() + .onAdvancedProtectionModeStateChanged(true); + verify(mWifiGlobals).setWepAllowed(false); + + mWifiDeviceStateChangeCallbackCaptor.getValue() + .onAdvancedProtectionModeStateChanged(false); + verify(mWifiGlobals, times(2)).setWepAllowed(true); + } + + @Test + public void testHandleWepAllowedChangedWhenWepIsConnected() { + mWepNetworkUsageController.handleBootCompleted(); + mLooper.dispatchAll(); + // WIFI_WEP_ALLOWED is true in setUp + verify(mWifiDeviceStateChangeManager).registerStateChangeCallback( + mWifiDeviceStateChangeCallbackCaptor.capture()); + verify(mWifiGlobals).setWepAllowed(true); + + // Mock wep connection to make sure it will disconnect + ConcreteClientModeManager cmmWep = mock(ConcreteClientModeManager.class); + ConcreteClientModeManager cmmWpa = mock(ConcreteClientModeManager.class); + WifiInfo mockWifiInfoWep = mock(WifiInfo.class); + WifiInfo mockWifiInfoWpa = mock(WifiInfo.class); + List<ClientModeManager> cmms = Arrays.asList(cmmWep, cmmWpa); + when(mActiveModeWarden.getClientModeManagers()).thenReturn(cmms); + when(mockWifiInfoWep.getCurrentSecurityType()).thenReturn(WifiInfo.SECURITY_TYPE_WEP); + when(mockWifiInfoWpa.getCurrentSecurityType()).thenReturn(WifiInfo.SECURITY_TYPE_PSK); + when(cmmWep.getConnectionInfo()).thenReturn(mockWifiInfoWep); + when(cmmWpa.getConnectionInfo()).thenReturn(mockWifiInfoWpa); + // Force setWepAllowed to false by enable APM mode. + mWifiDeviceStateChangeCallbackCaptor.getValue() + .onAdvancedProtectionModeStateChanged(true); + mLooper.dispatchAll(); + verify(mWifiGlobals).setWepAllowed(false); + // Only WEP disconnect + verify(cmmWep).disconnect(); + verify(cmmWpa, never()).disconnect(); + } +} diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java index 77274c17ec..ce826c8f59 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java @@ -568,7 +568,7 @@ public class WifiApConfigStoreTest extends WifiBaseTest { public void generateLocalOnlyHotspotConfigIsValid() throws Exception { WifiApConfigStore store = createWifiApConfigStore(); SoftApConfiguration config = store - .generateLocalOnlyHotspotConfig(mContext, null, mSoftApCapability); + .generateLocalOnlyHotspotConfig(mContext, null, mSoftApCapability, false); verifyDefaultLocalOnlyApConfig(config, TEST_DEFAULT_HOTSPOT_SSID, SoftApConfiguration.BAND_2GHZ); @@ -589,7 +589,7 @@ public class WifiApConfigStoreTest extends WifiBaseTest { mResources.setBoolean(R.bool.config_wifi_local_only_hotspot_5ghz, true); WifiApConfigStore store = createWifiApConfigStore(); SoftApConfiguration config = store - .generateLocalOnlyHotspotConfig(mContext, null, mSoftApCapability); + .generateLocalOnlyHotspotConfig(mContext, null, mSoftApCapability, false); verifyDefaultLocalOnlyApConfig(config, TEST_DEFAULT_HOTSPOT_SSID, SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ); @@ -609,7 +609,7 @@ public class WifiApConfigStoreTest extends WifiBaseTest { mResources.setBoolean(R.bool.config_wifi_local_only_hotspot_5ghz, true); WifiApConfigStore store = createWifiApConfigStore(); SoftApConfiguration config = store - .generateLocalOnlyHotspotConfig(mContext, null, mSoftApCapability); + .generateLocalOnlyHotspotConfig(mContext, null, mSoftApCapability, false); verifyDefaultLocalOnlyApConfig(config, TEST_DEFAULT_HOTSPOT_SSID, SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ); @@ -627,7 +627,7 @@ public class WifiApConfigStoreTest extends WifiBaseTest { mResources.setBoolean(R.bool.config_wifiLocalOnlyHotspot6ghz, true); WifiApConfigStore store = createWifiApConfigStore(); SoftApConfiguration config = store - .generateLocalOnlyHotspotConfig(mContext, null, mSoftApCapability); + .generateLocalOnlyHotspotConfig(mContext, null, mSoftApCapability, false); verifyDefaultLocalOnlyApConfig(config, TEST_DEFAULT_HOTSPOT_SSID, SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_6GHZ); @@ -651,7 +651,7 @@ public class WifiApConfigStoreTest extends WifiBaseTest { mResources.setBoolean(R.bool.config_wifi_local_only_hotspot_5ghz, true); WifiApConfigStore store = createWifiApConfigStore(); SoftApConfiguration config = store - .generateLocalOnlyHotspotConfig(mContext, null, mSoftApCapability); + .generateLocalOnlyHotspotConfig(mContext, null, mSoftApCapability, false); verifyDefaultLocalOnlyApConfig(config, TEST_DEFAULT_HOTSPOT_SSID, SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ | SoftApConfiguration.BAND_6GHZ); @@ -671,13 +671,13 @@ public class WifiApConfigStoreTest extends WifiBaseTest { SoftApConfiguration.Builder customConfigBuilder = new SoftApConfiguration.Builder() .setBand(SoftApConfiguration.BAND_5GHZ); SoftApConfiguration softApConfig = store.generateLocalOnlyHotspotConfig( - mContext, customConfigBuilder.build(), mSoftApCapability); + mContext, customConfigBuilder.build(), mSoftApCapability, true); assertThat(softApConfig.getBand()).isEqualTo(SoftApConfiguration.BAND_5GHZ); // Test 6G band customConfigBuilder.setBand(SoftApConfiguration.BAND_6GHZ); softApConfig = store.generateLocalOnlyHotspotConfig( - mContext, customConfigBuilder.build(), mSoftApCapability); + mContext, customConfigBuilder.build(), mSoftApCapability, true); assertThat(softApConfig.getBand()).isEqualTo(SoftApConfiguration.BAND_6GHZ); } @@ -692,7 +692,7 @@ public class WifiApConfigStoreTest extends WifiBaseTest { } SoftApConfiguration customConfig = customConfigBuilder.build(); SoftApConfiguration softApConfig = store.generateLocalOnlyHotspotConfig( - mContext, customConfig, mSoftApCapability); + mContext, customConfig, mSoftApCapability, true); assertThat(softApConfig.getBssid().toString()).isNotEmpty(); assertThat(softApConfig.getBssid()).isEqualTo(TEST_SAP_BSSID_MAC); } @@ -946,6 +946,16 @@ public class WifiApConfigStoreTest extends WifiBaseTest { configBuilder.build(), true, mContext, mWifiNative)); } + @Test + public void test11BERequires11AXConfigInValidateApWifiConfigurationCheck() { + assumeTrue(SdkLevel.isAtLeastT()); + assertFalse(WifiApConfigStore.validateApWifiConfiguration( + new SoftApConfiguration.Builder() + .setSsid(TEST_DEFAULT_HOTSPOT_SSID) + .setIeee80211axEnabled(false) + .setIeee80211beEnabled(true) + .build(), true, mContext, mWifiNative)); + } /** * Verify the default configuration security when SAE support. @@ -967,7 +977,7 @@ public class WifiApConfigStoreTest extends WifiBaseTest { mResources.setBoolean(R.bool.config_wifi_softap_sae_supported, true); WifiApConfigStore store = createWifiApConfigStore(); SoftApConfiguration config = store - .generateLocalOnlyHotspotConfig(mContext, null, mSoftApCapability); + .generateLocalOnlyHotspotConfig(mContext, null, mSoftApCapability, false); verifyDefaultLocalOnlyApConfig(config, TEST_DEFAULT_HOTSPOT_SSID, SoftApConfiguration.BAND_2GHZ, true); @@ -986,7 +996,7 @@ public class WifiApConfigStoreTest extends WifiBaseTest { mResources.setBoolean(R.bool.config_wifi_ap_mac_randomization_supported, true); WifiApConfigStore store = createWifiApConfigStore(); SoftApConfiguration config = store - .generateLocalOnlyHotspotConfig(mContext, null, mSoftApCapability); + .generateLocalOnlyHotspotConfig(mContext, null, mSoftApCapability, false); verifyDefaultLocalOnlyApConfig(config, TEST_DEFAULT_HOTSPOT_SSID, SoftApConfiguration.BAND_2GHZ, true, true); @@ -1006,7 +1016,7 @@ public class WifiApConfigStoreTest extends WifiBaseTest { mResources.setBoolean(R.bool.config_wifi_ap_mac_randomization_supported, false); WifiApConfigStore store = createWifiApConfigStore(); SoftApConfiguration config = store - .generateLocalOnlyHotspotConfig(mContext, null, mSoftApCapability); + .generateLocalOnlyHotspotConfig(mContext, null, mSoftApCapability, false); verifyDefaultLocalOnlyApConfig(config, TEST_DEFAULT_HOTSPOT_SSID, SoftApConfiguration.BAND_2GHZ, true, false); @@ -1031,7 +1041,7 @@ public class WifiApConfigStoreTest extends WifiBaseTest { .setMacRandomizationSetting(SoftApConfiguration.RANDOMIZATION_NONE).build(); storeMacRandomizationSupported.setApConfiguration(disableMacRandomizationConfig); SoftApConfiguration config = storeMacRandomizationSupported - .generateLocalOnlyHotspotConfig(mContext, null, mSoftApCapability); + .generateLocalOnlyHotspotConfig(mContext, null, mSoftApCapability, false); verifyDefaultLocalOnlyApConfig(config, TEST_DEFAULT_HOTSPOT_SSID, SoftApConfiguration.BAND_2GHZ, true, false); @@ -1452,11 +1462,11 @@ public class WifiApConfigStoreTest extends WifiBaseTest { assertEquals(TEST_RANDOMIZED_MAC, store.getApConfiguration() .getPersistentRandomizedMacAddress()); assertEquals(TEST_RANDOMIZED_MAC, - store.generateLocalOnlyHotspotConfig(mContext, null, mSoftApCapability) + store.generateLocalOnlyHotspotConfig(mContext, null, mSoftApCapability, false) .getPersistentRandomizedMacAddress()); assertEquals(TEST_RANDOMIZED_MAC, store.generateLocalOnlyHotspotConfig(mContext, store.getApConfiguration(), - mSoftApCapability).getPersistentRandomizedMacAddress()); + mSoftApCapability, true).getPersistentRandomizedMacAddress()); } @Test @@ -1465,11 +1475,11 @@ public class WifiApConfigStoreTest extends WifiBaseTest { WifiApConfigStore store = createWifiApConfigStore(); assertNotNull(store.getApConfiguration().getPersistentRandomizedMacAddress()); assertNotNull( - store.generateLocalOnlyHotspotConfig(mContext, null, mSoftApCapability) + store.generateLocalOnlyHotspotConfig(mContext, null, mSoftApCapability, false) .getPersistentRandomizedMacAddress()); assertNotNull( store.generateLocalOnlyHotspotConfig(mContext, store.getApConfiguration(), - mSoftApCapability).getPersistentRandomizedMacAddress()); + mSoftApCapability, true).getPersistentRandomizedMacAddress()); } @Test @@ -1531,4 +1541,24 @@ public class WifiApConfigStoreTest extends WifiBaseTest { // Verify it changes to default band. verifyDefaultApConfig(store.getApConfiguration(), TEST_DEFAULT_AP_SSID, false, true, false); } + + @Test + public void testGenerateLocalOnlyHotspotFromNonExclusiveConfig() throws Exception { + assumeTrue(SdkLevel.isAtLeastS()); + WifiApConfigStore store = createWifiApConfigStore(); + String testSsid = "ShouldNotApplied"; + SoftApConfiguration.Builder customConfigBuilder = new SoftApConfiguration.Builder() + .setSsid(testSsid) + .setBand(SoftApConfiguration.BAND_5GHZ); + SoftApConfiguration softApConfig = store.generateLocalOnlyHotspotConfig( + mContext, customConfigBuilder.build(), mSoftApCapability, false); + assertThat(softApConfig.getBand()).isEqualTo(SoftApConfiguration.BAND_5GHZ); + // Make sure SSID was not applied. + verifyDefaultLocalOnlyApConfig(softApConfig, TEST_DEFAULT_HOTSPOT_SSID, + SoftApConfiguration.BAND_5GHZ); + + // verify that the config passes the validateApWifiConfiguration check + assertTrue(WifiApConfigStore.validateApWifiConfiguration(softApConfig, true, mContext, + mWifiNative)); + } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiBlocklistMonitorTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiBlocklistMonitorTest.java index ce5a929101..621c744214 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiBlocklistMonitorTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiBlocklistMonitorTest.java @@ -1788,4 +1788,34 @@ public class WifiBlocklistMonitorTest extends WifiBaseTest { assertEquals(maxAllowlistSize, ssidAllowlistCaptor.getValue().size()); } + + /** + * Verifies the bssid block list can be cleared for a specific reason code. + */ + @Test + public void testClearBlocklistBssidForReason() { + WifiConfiguration config = WifiConfigurationTestUtil.createPskNetwork(TEST_SSID_1); + long testDuration = 5500L; + // Add bssid's in the block list with different reason codes and validate. + // - TEST_BSSID_1 and TEST_BSSID_2 with REASON_FRAMEWORK_DISCONNECT_FAST_RECONNECT + // - TEST_BSSID_3 with REASON_ASSOCIATION_REJECTION + mWifiBlocklistMonitor.blockBssidForDurationMs(TEST_BSSID_1, config, testDuration, + WifiBlocklistMonitor.REASON_FRAMEWORK_DISCONNECT_FAST_RECONNECT, -TEST_GOOD_RSSI); + mWifiBlocklistMonitor.blockBssidForDurationMs(TEST_BSSID_2, config, testDuration, + WifiBlocklistMonitor.REASON_FRAMEWORK_DISCONNECT_FAST_RECONNECT, -TEST_GOOD_RSSI); + mWifiBlocklistMonitor.blockBssidForDurationMs(TEST_BSSID_3, config, testDuration, + WifiBlocklistMonitor.REASON_ASSOCIATION_REJECTION, -TEST_GOOD_RSSI); + assertEquals(Set.of(TEST_BSSID_1, TEST_BSSID_2, TEST_BSSID_3), + mWifiBlocklistMonitor.updateAndGetBssidBlocklist()); + assertEquals(Set.of(WifiBlocklistMonitor.REASON_ASSOCIATION_REJECTION, + WifiBlocklistMonitor.REASON_FRAMEWORK_DISCONNECT_FAST_RECONNECT), + mWifiBlocklistMonitor.getFailureReasonsForSsid(TEST_SSID_1)); + // Remove entries with reason REASON_FRAMEWORK_DISCONNECT_FAST_RECONNECT, and + // block list should have only TEST_BSSID_3 with reason REASON_ASSOCIATION_REJECTION + mWifiBlocklistMonitor.clearBssidBlocklistForReason( + WifiBlocklistMonitor.REASON_FRAMEWORK_DISCONNECT_FAST_RECONNECT); + assertEquals(Set.of(TEST_BSSID_3), mWifiBlocklistMonitor.updateAndGetBssidBlocklist()); + assertEquals(Set.of(WifiBlocklistMonitor.REASON_ASSOCIATION_REJECTION), + mWifiBlocklistMonitor.getFailureReasonsForSsid(TEST_SSID_1)); + } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiCandidatesTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiCandidatesTest.java index 1f5345c459..9d095242b1 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiCandidatesTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiCandidatesTest.java @@ -19,6 +19,8 @@ package com.android.server.wifi; import static android.net.wifi.WifiManager.WIFI_FEATURE_OWE; import static android.net.wifi.WifiManager.WIFI_FEATURE_WPA3_SAE; +import static com.android.server.wifi.TestUtil.createCapabilityBitset; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; @@ -96,8 +98,8 @@ public class WifiCandidatesTest extends WifiBaseTest { when(mWifiInjector.getWifiGlobals()).thenReturn(mWifiGlobals); when(mWifiInjector.getActiveModeWarden()).thenReturn(mActiveModeWarden); when(mActiveModeWarden.getPrimaryClientModeManager()).thenReturn(mClientModeManager); - when(mClientModeManager.getSupportedFeatures()).thenReturn( - WIFI_FEATURE_OWE | WIFI_FEATURE_WPA3_SAE); + when(mClientModeManager.getSupportedFeaturesBitSet()).thenReturn( + createCapabilityBitset(WIFI_FEATURE_OWE, WIFI_FEATURE_WPA3_SAE)); when(mWifiGlobals.isWpa3SaeUpgradeEnabled()).thenReturn(true); when(mWifiGlobals.isOweUpgradeEnabled()).thenReturn(true); diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java index 6e88ddb744..bd31686a2c 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiConfigManagerTest.java @@ -16,9 +16,12 @@ package com.android.server.wifi; +import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_PSK; import static android.net.wifi.WifiManager.AddNetworkResult.STATUS_INVALID_CONFIGURATION_ENTERPRISE; +import static com.android.server.wifi.TestUtil.createCapabilityBitset; import static com.android.server.wifi.WifiConfigManager.BUFFERED_WRITE_ALARM_TAG; +import static com.android.server.wifi.WifiConfigurationUtil.validatePassword; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -115,6 +118,7 @@ import java.io.StringWriter; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; +import java.util.BitSet; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -363,8 +367,9 @@ public class WifiConfigManagerTest extends WifiBaseTest { when(mWifiInjector.getWifiGlobals()).thenReturn(mWifiGlobals); when(mWifiInjector.getSsidTranslator()).thenReturn(mSsidTranslator); when(mActiveModeWarden.getPrimaryClientModeManager()).thenReturn(mPrimaryClientModeManager); - when(mPrimaryClientModeManager.getSupportedFeatures()).thenReturn( - WifiManager.WIFI_FEATURE_WPA3_SAE | WifiManager.WIFI_FEATURE_OWE); + when(mPrimaryClientModeManager.getSupportedFeaturesBitSet()).thenReturn( + createCapabilityBitset( + WifiManager.WIFI_FEATURE_WPA3_SAE, WifiManager.WIFI_FEATURE_OWE)); when(mWifiGlobals.isWpa3SaeUpgradeEnabled()).thenReturn(true); when(mWifiGlobals.isOweUpgradeEnabled()).thenReturn(true); when(mWifiGlobals.isWpa3SaeUpgradeOffloadEnabled()).thenReturn(true); @@ -2030,8 +2035,8 @@ public class WifiConfigManagerTest extends WifiBaseTest { */ @Test public void testEnterpriseConfigTofuStateMerge() { - long featureSet = WifiManager.WIFI_FEATURE_TRUST_ON_FIRST_USE; - when(mPrimaryClientModeManager.getSupportedFeatures()).thenReturn(featureSet); + when(mPrimaryClientModeManager.getSupportedFeaturesBitSet()).thenReturn( + createCapabilityBitset(WifiManager.WIFI_FEATURE_TRUST_ON_FIRST_USE)); // If the configuration has never connected, the merged TOFU connection state // should be set based on the latest external configuration. @@ -3335,7 +3340,7 @@ public class WifiConfigManagerTest extends WifiBaseTest { for (WifiConfiguration network : mWifiConfigManager.getLinkedNetworksWithoutMasking( network1.networkId).values()) { assertTrue(network.getNetworkSelectionStatus().getCandidateSecurityParams() - .isSecurityType(WifiConfiguration.SECURITY_TYPE_PSK)); + .isSecurityType(SECURITY_TYPE_PSK)); } } @@ -4608,7 +4613,7 @@ public class WifiConfigManagerTest extends WifiBaseTest { assertTrue(mWifiConfigManager.setNetworkCandidateScanResult(network2.networkId, createScanDetailForNetwork(network2).getScanResult(), 54, SecurityParams.createSecurityParamsBySecurityType( - WifiConfiguration.SECURITY_TYPE_PSK))); + SECURITY_TYPE_PSK))); // Retrieve the hidden network list & verify the order of the networks returned. List<WifiScanner.ScanSettings.HiddenNetwork> hiddenNetworks = @@ -4676,7 +4681,7 @@ public class WifiConfigManagerTest extends WifiBaseTest { // successfully. WifiConfiguration network1 = new WifiConfiguration(); network1.SSID = ssid; - network1.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK); + network1.setSecurityParams(SECURITY_TYPE_PSK); network1.preSharedKey = "\"test_blah\""; NetworkUpdateResult result = addNetworkToWifiConfigManager(network1); assertTrue(result.getNetworkId() != WifiConfiguration.INVALID_NETWORK_ID); @@ -6836,7 +6841,13 @@ public class WifiConfigManagerTest extends WifiBaseTest { // Two networks should be merged into one. assertEquals(1, retrievedNetworks.size()); WifiConfiguration mergedNetwork = retrievedNetworks.get(0); - assertTrue(mergedNetwork.isSecurityType(baseSecurityType)); + if (baseConfig.isSecurityType(SECURITY_TYPE_PSK) + && !validatePassword(upgradableConfig.preSharedKey, false, false, false)) { + // PSK should be removed if we saved an SAE-only passphrase over it. + assertFalse(mergedNetwork.isSecurityType(SECURITY_TYPE_PSK)); + } else { + assertTrue(mergedNetwork.isSecurityType(baseSecurityType)); + } assertTrue(mergedNetwork.isSecurityType(upgradableSecurityType)); assertEquals(upgradableConfig.getDefaultSecurityParams().isAddedByAutoUpgrade(), mergedNetwork.getSecurityParams(upgradableSecurityType).isAddedByAutoUpgrade()); @@ -6851,7 +6862,7 @@ public class WifiConfigManagerTest extends WifiBaseTest { public void testAddUpgradableNetworkForPskSae() { WifiConfiguration baseConfig = new WifiConfiguration(); baseConfig.SSID = "\"upgradableNetwork\""; - baseConfig.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK); + baseConfig.setSecurityParams(SECURITY_TYPE_PSK); baseConfig.preSharedKey = "\"Passw0rd\""; WifiConfiguration upgradableConfig = new WifiConfiguration(); upgradableConfig.SSID = "\"upgradableNetwork\""; @@ -6862,6 +6873,24 @@ public class WifiConfigManagerTest extends WifiBaseTest { } /** + * Verifies the addition of an SAE network with an SAE-only passphrase over a PSK/SAE network. + * {@link WifiConfigManager#addOrUpdateNetwork(WifiConfiguration, int)} + */ + @Test + public void testAddUpgradableNetworkForPskSaeIncompatiblePassphrase() { + WifiConfiguration baseConfig = new WifiConfiguration(); + baseConfig.SSID = "\"upgradableNetwork\""; + baseConfig.setSecurityParams(SECURITY_TYPE_PSK); + baseConfig.preSharedKey = "\"Passw0rd\""; + WifiConfiguration upgradableConfig = new WifiConfiguration(); + upgradableConfig.SSID = "\"upgradableNetwork\""; + upgradableConfig.setSecurityParams(WifiConfiguration.SECURITY_TYPE_SAE); + upgradableConfig.preSharedKey = "\"P\""; + + verifyAddUpgradableNetwork(baseConfig, upgradableConfig); + } + + /** * Verifies that updating an existing upgradable network that was added by auto upgrade will * retain the isAddedByAutoUpgrade() value. * {@link WifiConfigManager#addOrUpdateNetwork(WifiConfiguration, int)} @@ -6870,7 +6899,7 @@ public class WifiConfigManagerTest extends WifiBaseTest { public void testUpdateUpgradedNetworkKeepsIsAddedByAutoUpgradeValue() { WifiConfiguration baseConfig = new WifiConfiguration(); baseConfig.SSID = "\"upgradableNetwork\""; - baseConfig.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK); + baseConfig.setSecurityParams(SECURITY_TYPE_PSK); baseConfig.preSharedKey = "\"Passw0rd\""; WifiConfiguration upgradedConfig = new WifiConfiguration(); upgradedConfig.SSID = "\"upgradableNetwork\""; @@ -6890,7 +6919,7 @@ public class WifiConfigManagerTest extends WifiBaseTest { public void testAddUnhiddenUpgradedNetworkOverwritesHiddenSsidValue() { WifiConfiguration baseConfig = new WifiConfiguration(); baseConfig.SSID = "\"upgradableNetwork\""; - baseConfig.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK); + baseConfig.setSecurityParams(SECURITY_TYPE_PSK); baseConfig.preSharedKey = "\"Passw0rd\""; baseConfig.hiddenSSID = true; WifiConfiguration upgradedConfig = new WifiConfiguration(); @@ -6910,7 +6939,7 @@ public class WifiConfigManagerTest extends WifiBaseTest { public void testAddHiddenUpgradedNetworkOverwritesHiddenSsidValue() { WifiConfiguration baseConfig = new WifiConfiguration(); baseConfig.SSID = "\"upgradableNetwork\""; - baseConfig.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK); + baseConfig.setSecurityParams(SECURITY_TYPE_PSK); baseConfig.preSharedKey = "\"Passw0rd\""; baseConfig.hiddenSSID = false; WifiConfiguration upgradedConfig = new WifiConfiguration(); @@ -7011,7 +7040,7 @@ public class WifiConfigManagerTest extends WifiBaseTest { baseConfig.preSharedKey = "\"Passw0rd\""; WifiConfiguration downgradableConfig = new WifiConfiguration(); downgradableConfig.SSID = "\"downgradableNetwork\""; - downgradableConfig.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK); + downgradableConfig.setSecurityParams(SECURITY_TYPE_PSK); downgradableConfig.preSharedKey = "\"Passw0rd\""; verifyAddDowngradableNetwork( @@ -7072,7 +7101,7 @@ public class WifiConfigManagerTest extends WifiBaseTest { baseConfig.hiddenSSID = true; WifiConfiguration downgradableConfig = new WifiConfiguration(); downgradableConfig.SSID = "\"downgradableConfig\""; - downgradableConfig.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK); + downgradableConfig.setSecurityParams(SECURITY_TYPE_PSK); downgradableConfig.preSharedKey = "\"Passw0rd\""; downgradableConfig.hiddenSSID = false; @@ -7092,7 +7121,7 @@ public class WifiConfigManagerTest extends WifiBaseTest { baseConfig.hiddenSSID = false; WifiConfiguration downgradableConfig = new WifiConfiguration(); downgradableConfig.SSID = "\"downgradableConfig\""; - downgradableConfig.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK); + downgradableConfig.setSecurityParams(SECURITY_TYPE_PSK); downgradableConfig.preSharedKey = "\"Passw0rd\""; downgradableConfig.hiddenSSID = true; @@ -7135,7 +7164,7 @@ public class WifiConfigManagerTest extends WifiBaseTest { public void testLoadFromStoreMergeUpgradableConfigurationsPskSae() { WifiConfiguration baseConfig = new WifiConfiguration(); baseConfig.SSID = "\"upgradableNetwork\""; - baseConfig.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK); + baseConfig.setSecurityParams(SECURITY_TYPE_PSK); baseConfig.preSharedKey = "\"Passw0rd\""; WifiConfiguration upgradableConfig = new WifiConfiguration(); upgradableConfig.SSID = "\"upgradableNetwork\""; @@ -7436,7 +7465,7 @@ public class WifiConfigManagerTest extends WifiBaseTest { int networkIdBefore = verifyAddNetworkToWifiConfigManager(testNetwork).getNetworkId(); WifiConfiguration configBefore = mWifiConfigManager.getConfiguredNetwork(networkIdBefore); assertFalse(configBefore.isSecurityType(WifiConfiguration.SECURITY_TYPE_OPEN)); - assertTrue(configBefore.isSecurityType(WifiConfiguration.SECURITY_TYPE_PSK)); + assertTrue(configBefore.isSecurityType(SECURITY_TYPE_PSK)); // Change the type from PSK to Open. testNetwork.networkId = networkIdBefore; @@ -7446,7 +7475,7 @@ public class WifiConfigManagerTest extends WifiBaseTest { assertEquals(networkIdBefore, networkIdAfter); WifiConfiguration configAfter = mWifiConfigManager.getConfiguredNetwork(networkIdAfter); - assertFalse(configAfter.isSecurityType(WifiConfiguration.SECURITY_TYPE_PSK)); + assertFalse(configAfter.isSecurityType(SECURITY_TYPE_PSK)); assertTrue(configAfter.isSecurityType(WifiConfiguration.SECURITY_TYPE_OPEN)); } @@ -7976,8 +8005,11 @@ public class WifiConfigManagerTest extends WifiBaseTest { } private void verifyAddTofuEnterpriseConfig(boolean isTofuSupported) { - long featureSet = isTofuSupported ? WifiManager.WIFI_FEATURE_TRUST_ON_FIRST_USE : 0L; - when(mPrimaryClientModeManager.getSupportedFeatures()).thenReturn(featureSet); + BitSet featureSet = new BitSet(); + if (isTofuSupported) { + featureSet.set(WifiManager.WIFI_FEATURE_TRUST_ON_FIRST_USE); + } + when(mPrimaryClientModeManager.getSupportedFeaturesBitSet()).thenReturn(featureSet); WifiConfiguration config = prepareTofuEapConfig( WifiEnterpriseConfig.Eap.PEAP, WifiEnterpriseConfig.Phase2.NONE); @@ -8097,8 +8129,8 @@ public class WifiConfigManagerTest extends WifiBaseTest { @Test public void testUpdateCaCertificateSuccess() throws Exception { - when(mPrimaryClientModeManager.getSupportedFeatures()).thenReturn( - WifiManager.WIFI_FEATURE_TRUST_ON_FIRST_USE); + when(mPrimaryClientModeManager.getSupportedFeaturesBitSet()).thenReturn( + createCapabilityBitset(WifiManager.WIFI_FEATURE_TRUST_ON_FIRST_USE)); int eapPeapNetId = verifyAddNetwork(prepareTofuEapConfig( WifiEnterpriseConfig.Eap.PEAP, WifiEnterpriseConfig.Phase2.NONE), true); @@ -8112,8 +8144,8 @@ public class WifiConfigManagerTest extends WifiBaseTest { @Test public void testUpdateCaCertificatePathSuccess() throws Exception { - when(mPrimaryClientModeManager.getSupportedFeatures()).thenReturn( - WifiManager.WIFI_FEATURE_TRUST_ON_FIRST_USE); + when(mPrimaryClientModeManager.getSupportedFeaturesBitSet()).thenReturn( + createCapabilityBitset(WifiManager.WIFI_FEATURE_TRUST_ON_FIRST_USE)); int eapPeapNetId = verifyAddNetwork(prepareTofuEapConfig( WifiEnterpriseConfig.Eap.PEAP, WifiEnterpriseConfig.Phase2.NONE), true); @@ -8130,8 +8162,8 @@ public class WifiConfigManagerTest extends WifiBaseTest { @Test public void testUpdateCaCertificateWithoutAltSubjectNames() throws Exception { - when(mPrimaryClientModeManager.getSupportedFeatures()).thenReturn( - WifiManager.WIFI_FEATURE_TRUST_ON_FIRST_USE); + when(mPrimaryClientModeManager.getSupportedFeaturesBitSet()).thenReturn( + createCapabilityBitset(WifiManager.WIFI_FEATURE_TRUST_ON_FIRST_USE)); verifyAddNetwork(WifiConfigurationTestUtil.createOpenNetwork(), true); int eapPeapNetId = verifyAddNetwork(prepareTofuEapConfig( @@ -8156,8 +8188,8 @@ public class WifiConfigManagerTest extends WifiBaseTest { @Test public void testUpdateCaCertificateWithAltSubjectNames() throws Exception { - when(mPrimaryClientModeManager.getSupportedFeatures()).thenReturn( - WifiManager.WIFI_FEATURE_TRUST_ON_FIRST_USE); + when(mPrimaryClientModeManager.getSupportedFeaturesBitSet()).thenReturn( + createCapabilityBitset(WifiManager.WIFI_FEATURE_TRUST_ON_FIRST_USE)); verifyAddNetwork(WifiConfigurationTestUtil.createOpenNetwork(), true); int eapPeapNetId = verifyAddNetwork(prepareTofuEapConfig( @@ -8196,8 +8228,8 @@ public class WifiConfigManagerTest extends WifiBaseTest { @Test public void testUpdateCaCertificateFaiulreInvalidArgument() throws Exception { - when(mPrimaryClientModeManager.getSupportedFeatures()).thenReturn( - WifiManager.WIFI_FEATURE_TRUST_ON_FIRST_USE); + when(mPrimaryClientModeManager.getSupportedFeaturesBitSet()).thenReturn( + createCapabilityBitset(WifiManager.WIFI_FEATURE_TRUST_ON_FIRST_USE)); int openNetId = verifyAddNetwork(WifiConfigurationTestUtil.createOpenNetwork(), true); int eapPeapNetId = verifyAddNetwork(prepareTofuEapConfig( @@ -8232,8 +8264,8 @@ public class WifiConfigManagerTest extends WifiBaseTest { @Test public void testUpdateCaCertificateSuccessWithSelfSignedCertificate() throws Exception { - when(mPrimaryClientModeManager.getSupportedFeatures()).thenReturn( - WifiManager.WIFI_FEATURE_TRUST_ON_FIRST_USE); + when(mPrimaryClientModeManager.getSupportedFeaturesBitSet()).thenReturn( + createCapabilityBitset(WifiManager.WIFI_FEATURE_TRUST_ON_FIRST_USE)); int eapPeapNetId = verifyAddNetwork(prepareTofuEapConfig( WifiEnterpriseConfig.Eap.PEAP, WifiEnterpriseConfig.Phase2.NONE), true); X509Certificate mockCaCert = mock(X509Certificate.class); @@ -8248,8 +8280,8 @@ public class WifiConfigManagerTest extends WifiBaseTest { @Test public void testUpdateServerCertificateHashSuccess() throws Exception { - when(mPrimaryClientModeManager.getSupportedFeatures()).thenReturn( - WifiManager.WIFI_FEATURE_TRUST_ON_FIRST_USE); + when(mPrimaryClientModeManager.getSupportedFeaturesBitSet()).thenReturn( + createCapabilityBitset(WifiManager.WIFI_FEATURE_TRUST_ON_FIRST_USE)); int eapPeapNetId = verifyAddNetwork(prepareTofuEapConfig( WifiEnterpriseConfig.Eap.PEAP, WifiEnterpriseConfig.Phase2.NONE), true); assertTrue(mWifiConfigManager.updateCaCertificate(eapPeapNetId, FakeKeys.CA_CERT1, @@ -8263,8 +8295,8 @@ public class WifiConfigManagerTest extends WifiBaseTest { @Test public void testUpdateCaCertificateFailureWithSelfSignedCertificateAndTofuNotEnabled() throws Exception { - when(mPrimaryClientModeManager.getSupportedFeatures()).thenReturn( - WifiManager.WIFI_FEATURE_TRUST_ON_FIRST_USE); + when(mPrimaryClientModeManager.getSupportedFeaturesBitSet()).thenReturn( + createCapabilityBitset(WifiManager.WIFI_FEATURE_TRUST_ON_FIRST_USE)); int eapPeapNetId = verifyAddNetwork(WifiConfigurationTestUtil.createEapNetwork( WifiEnterpriseConfig.Eap.PEAP, WifiEnterpriseConfig.Phase2.NONE), true); X509Certificate mockCaCert = mock(X509Certificate.class); diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiConfigurationUtilTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiConfigurationUtilTest.java index 81b182c9df..0ff24270f1 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiConfigurationUtilTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiConfigurationUtilTest.java @@ -30,6 +30,7 @@ import static org.mockito.Mockito.withSettings; import android.content.pm.UserInfo; import android.net.IpConfiguration; import android.net.MacAddress; +import android.net.ProxyInfo; import android.net.wifi.ScanResult; import android.net.wifi.SecurityParams; import android.net.wifi.WifiConfiguration; @@ -58,6 +59,7 @@ import java.nio.charset.StandardCharsets; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; +import java.util.BitSet; import java.util.Collections; import java.util.List; @@ -78,7 +80,7 @@ public class WifiConfigurationUtilTest extends WifiBaseTest { static final List<UserInfo> PROFILES = Arrays.asList( new UserInfo(CURRENT_USER_ID, "owner", 0), new UserInfo(CURRENT_USER_MANAGED_PROFILE_USER_ID, "managed profile", 0)); - private static final long SUPPORTED_FEATURES_ALL = Long.MAX_VALUE; + private static final BitSet SUPPORTED_FEATURES_ALL = new BitSet(); private final String mGeneratedString256 = "a".repeat(256); private MockitoSession mSession; @@ -98,6 +100,7 @@ public class WifiConfigurationUtilTest extends WifiBaseTest { .startMocking(); when(WifiInjector.getInstance()).thenReturn(mWifiInjector); when(mWifiInjector.getWifiGlobals()).thenReturn(mWifiGlobals); + SUPPORTED_FEATURES_ALL.set(0, 63); // mark all features as supported } @After @@ -770,7 +773,7 @@ public class WifiConfigurationUtilTest extends WifiBaseTest { new PatternMatcher(TEST_SSID, PatternMatcher.PATTERN_LITERAL), Pair.create(WifiManager.ALL_ZEROS_MAC_ADDRESS, WifiManager.ALL_ZEROS_MAC_ADDRESS), ScanResult.UNSPECIFIED, - WifiConfigurationTestUtil.createOpenNetwork(), new int[0]); + WifiConfigurationTestUtil.createOpenNetwork(), new int[0], false); assertTrue(WifiConfigurationUtil.validateNetworkSpecifier(specifier, 5)); } @@ -784,7 +787,7 @@ public class WifiConfigurationUtilTest extends WifiBaseTest { new PatternMatcher(".*", PatternMatcher.PATTERN_SIMPLE_GLOB), Pair.create(MacAddress.fromString(TEST_BSSID), MacAddress.BROADCAST_ADDRESS), ScanResult.UNSPECIFIED, - WifiConfigurationTestUtil.createOpenNetwork(), new int[0]); + WifiConfigurationTestUtil.createOpenNetwork(), new int[0], false); assertTrue(WifiConfigurationUtil.validateNetworkSpecifier(specifier, 5)); } @@ -798,7 +801,7 @@ public class WifiConfigurationUtilTest extends WifiBaseTest { new PatternMatcher(TEST_SSID, PatternMatcher.PATTERN_LITERAL), Pair.create(MacAddress.fromString(TEST_BSSID), MacAddress.BROADCAST_ADDRESS), ScanResult.UNSPECIFIED, - WifiConfigurationTestUtil.createOpenNetwork(), new int[0]); + WifiConfigurationTestUtil.createOpenNetwork(), new int[0], false); assertTrue(WifiConfigurationUtil.validateNetworkSpecifier(specifier, 5)); } @@ -813,7 +816,7 @@ public class WifiConfigurationUtilTest extends WifiBaseTest { new PatternMatcher(TEST_SSID, PatternMatcher.PATTERN_LITERAL), Pair.create(MacAddress.fromString(TEST_BSSID), MacAddress.BROADCAST_ADDRESS), ScanResult.WIFI_BAND_5_GHZ, - WifiConfigurationTestUtil.createOpenNetwork(), new int[0]); + WifiConfigurationTestUtil.createOpenNetwork(), new int[0], false); assertTrue(WifiConfigurationUtil.validateNetworkSpecifier(specifier, 5)); } @@ -828,7 +831,7 @@ public class WifiConfigurationUtilTest extends WifiBaseTest { new PatternMatcher(".*", PatternMatcher.PATTERN_SIMPLE_GLOB), Pair.create(WifiManager.ALL_ZEROS_MAC_ADDRESS, WifiManager.ALL_ZEROS_MAC_ADDRESS), ScanResult.UNSPECIFIED, - WifiConfigurationTestUtil.createOpenNetwork(), new int[0]); + WifiConfigurationTestUtil.createOpenNetwork(), new int[0], false); assertFalse(WifiConfigurationUtil.validateNetworkSpecifier(specifier, 5)); } @@ -842,7 +845,7 @@ public class WifiConfigurationUtilTest extends WifiBaseTest { new PatternMatcher("", PatternMatcher.PATTERN_LITERAL), Pair.create(WifiManager.ALL_ZEROS_MAC_ADDRESS, WifiManager.ALL_ZEROS_MAC_ADDRESS), ScanResult.UNSPECIFIED, - WifiConfigurationTestUtil.createOpenNetwork(), new int[0]); + WifiConfigurationTestUtil.createOpenNetwork(), new int[0], false); assertFalse(WifiConfigurationUtil.validateNetworkSpecifier(specifier, 5)); } @@ -856,7 +859,7 @@ public class WifiConfigurationUtilTest extends WifiBaseTest { new PatternMatcher(TEST_SSID, PatternMatcher.PATTERN_LITERAL), Pair.create(MacAddress.BROADCAST_ADDRESS, MacAddress.BROADCAST_ADDRESS), ScanResult.UNSPECIFIED, - WifiConfigurationTestUtil.createOpenNetwork(), new int[0]); + WifiConfigurationTestUtil.createOpenNetwork(), new int[0], false); assertFalse(WifiConfigurationUtil.validateNetworkSpecifier(specifier, 5)); } @@ -870,7 +873,7 @@ public class WifiConfigurationUtilTest extends WifiBaseTest { new PatternMatcher(TEST_SSID, PatternMatcher.PATTERN_LITERAL), Pair.create(MacAddress.fromString(TEST_BSSID), WifiManager.ALL_ZEROS_MAC_ADDRESS), ScanResult.UNSPECIFIED, - WifiConfigurationTestUtil.createOpenNetwork(), new int[0]); + WifiConfigurationTestUtil.createOpenNetwork(), new int[0], false); assertFalse(WifiConfigurationUtil.validateNetworkSpecifier(specifier, 5)); } @@ -884,7 +887,7 @@ public class WifiConfigurationUtilTest extends WifiBaseTest { new PatternMatcher(TEST_SSID, PatternMatcher.PATTERN_PREFIX), Pair.create(WifiManager.ALL_ZEROS_MAC_ADDRESS, WifiManager.ALL_ZEROS_MAC_ADDRESS), ScanResult.UNSPECIFIED, - WifiConfigurationTestUtil.createOpenHiddenNetwork(), new int[0]); + WifiConfigurationTestUtil.createOpenHiddenNetwork(), new int[0], false); assertFalse(WifiConfigurationUtil.validateNetworkSpecifier(specifier, 5)); } @@ -897,7 +900,7 @@ public class WifiConfigurationUtilTest extends WifiBaseTest { new PatternMatcher(TEST_SSID, PatternMatcher.PATTERN_LITERAL), Pair.create(MacAddress.fromString(TEST_BSSID), MacAddress.BROADCAST_ADDRESS), 42, // invalid - WifiConfigurationTestUtil.createOpenNetwork(), new int[0]); + WifiConfigurationTestUtil.createOpenNetwork(), new int[0], false); assertFalse(WifiConfigurationUtil.validateNetworkSpecifier(specifier, 5)); } @@ -1501,7 +1504,8 @@ public class WifiConfigurationUtilTest extends WifiBaseTest { */ @Test public void testValidateSecurityTypeDppAkm() { - long supportedFeatures = SUPPORTED_FEATURES_ALL & ~WifiManager.WIFI_FEATURE_DPP_AKM; + BitSet supportedFeatures = SUPPORTED_FEATURES_ALL; + supportedFeatures.set(WifiManager.WIFI_FEATURE_DPP_AKM, false); WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork(); config.setSecurityParams(WifiInfo.SECURITY_TYPE_DPP); @@ -1510,7 +1514,7 @@ public class WifiConfigurationUtilTest extends WifiBaseTest { assertFalse(WifiConfigurationUtil.validate(config, supportedFeatures, WifiConfigurationUtil.VALIDATE_FOR_UPDATE)); - supportedFeatures = WifiManager.WIFI_FEATURE_DPP_AKM; + supportedFeatures.set(WifiManager.WIFI_FEATURE_DPP_AKM, true); assertTrue(WifiConfigurationUtil.validate(config, supportedFeatures, WifiConfigurationUtil.VALIDATE_FOR_ADD)); assertTrue(WifiConfigurationUtil.validate(config, supportedFeatures, @@ -1681,4 +1685,27 @@ public class WifiConfigurationUtilTest extends WifiBaseTest { assertFalse(WifiConfigurationUtil.validate(pskConfig, SUPPORTED_FEATURES_ALL, WifiConfigurationUtil.VALIDATE_FOR_ADD)); } + + @Test + public void testInvalidStaticIpConfig() { + WifiConfiguration pskConfig = WifiConfigurationTestUtil.createPskNetwork(); + IpConfiguration ipConfig = + WifiConfigurationTestUtil.createStaticIpConfigurationWithPacProxy(); + ipConfig.getStaticIpConfiguration().domains = "a".repeat(513); + pskConfig.setIpConfiguration(ipConfig); + assertFalse(WifiConfigurationUtil.validate(pskConfig, SUPPORTED_FEATURES_ALL, + WifiConfigurationUtil.VALIDATE_FOR_ADD)); + } + @Test + public void testInvalidProxyInfo() { + WifiConfiguration pskConfig = WifiConfigurationTestUtil.createPskNetwork(); + IpConfiguration ipConfig = + WifiConfigurationTestUtil.createStaticIpConfigurationWithStaticProxy(); + ProxyInfo proxyInfo = ProxyInfo.buildDirectProxy(ipConfig.getHttpProxy().getHost(), + ipConfig.getHttpProxy().getPort(), List.of("a".repeat(513))); + ipConfig.setHttpProxy(proxyInfo); + pskConfig.setIpConfiguration(ipConfig); + assertFalse(WifiConfigurationUtil.validate(pskConfig, SUPPORTED_FEATURES_ALL, + WifiConfigurationUtil.VALIDATE_FOR_ADD)); + } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiConnectivityHelperTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiConnectivityHelperTest.java index bbe9cf7ea8..24ab6baff0 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiConnectivityHelperTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiConnectivityHelperTest.java @@ -18,6 +18,8 @@ package com.android.server.wifi; import static android.net.wifi.WifiManager.WIFI_FEATURE_CONTROL_ROAMING; +import static com.android.server.wifi.TestUtil.createCapabilityBitset; + import static org.junit.Assert.*; import static org.mockito.Mockito.*; @@ -32,6 +34,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.ArrayList; +import java.util.BitSet; /** * Unit tests for {@link com.android.server.wifi.WifiConnectivityHelper}. @@ -68,7 +71,8 @@ public class WifiConnectivityHelperTest extends WifiBaseTest { when(mActiveModeWarden.getPrimaryClientModeManager()).thenReturn(mClientModeManager); // Return firmware roaming feature as supported by default. - when(mClientModeManager.getSupportedFeatures()).thenReturn(WIFI_FEATURE_CONTROL_ROAMING); + when(mClientModeManager.getSupportedFeaturesBitSet()) + .thenReturn(createCapabilityBitset(WIFI_FEATURE_CONTROL_ROAMING)); WifiNative.RoamingCapabilities roamCap = new WifiNative.RoamingCapabilities(); roamCap.maxBlocklistSize = MAX_BSSID_BLOCKLIST_SIZE; @@ -119,7 +123,9 @@ public class WifiConnectivityHelperTest extends WifiBaseTest { */ @Test public void returnFirmwareRoamingNotSupported() { - when(mClientModeManager.getSupportedFeatures()).thenReturn(~WIFI_FEATURE_CONTROL_ROAMING); + BitSet supportedFeatures = new BitSet(); + supportedFeatures.set(WIFI_FEATURE_CONTROL_ROAMING, false); + when(mClientModeManager.getSupportedFeaturesBitSet()).thenReturn(supportedFeatures); assertTrue(mWifiConnectivityHelper.getFirmwareRoamingInfo()); assertFalse(mWifiConnectivityHelper.isFirmwareRoamingSupported()); } diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java index d0f3ad65f6..252a948210 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java @@ -107,7 +107,6 @@ import com.android.server.wifi.proto.nano.WifiMetricsProto; import com.android.server.wifi.scanner.WifiScannerInternal; import com.android.server.wifi.util.LruConnectionTracker; import com.android.server.wifi.util.WifiPermissionsUtil; -import com.android.wifi.flags.FeatureFlags; import com.android.wifi.resources.R; import org.junit.After; @@ -180,8 +179,6 @@ public class WifiConnectivityManagerTest extends WifiBaseTest { when(mPrimaryClientModeManager.getRole()).thenReturn(ActiveModeManager.ROLE_CLIENT_PRIMARY); when(mPrimaryClientModeManager.getConnectionInfo()).thenReturn(mWifiInfo); when(mActiveModeWarden.getPrimaryClientModeManager()).thenReturn(mPrimaryClientModeManager); - when(mDeviceConfigFacade.getFeatureFlags()).thenReturn(mFeatureFlags); - when(mFeatureFlags.delayedCarrierNetworkSelection()).thenReturn(true); when(mWifiCarrierInfoManager.isCarrierNetworkOffloadEnabled(anyInt(), anyBoolean())) .thenReturn(true); doAnswer(new AnswerWithArguments() { @@ -322,7 +319,6 @@ public class WifiConnectivityManagerTest extends WifiBaseTest { @Mock private WifiNetworkSuggestion mWifiNetworkSuggestion; @Mock private IPowerManager mPowerManagerService; @Mock private DeviceConfigFacade mDeviceConfigFacade; - @Mock private FeatureFlags mFeatureFlags; @Mock private ActiveModeWarden mActiveModeWarden; @Mock private ConcreteClientModeManager mPrimaryClientModeManager; @Mock private ConcreteClientModeManager mSecondaryClientModeManager; @@ -589,7 +585,7 @@ public class WifiConnectivityManagerTest extends WifiBaseTest { mCandidateList = new ArrayList<WifiCandidates.Candidate>(); mCandidateList.add(mCandidate1); when(ns.getCandidatesFromScan(any(), any(), any(), anyBoolean(), anyBoolean(), - anyBoolean(), any(), anyBoolean())).thenReturn(mCandidateList); + anyBoolean(), any(), anyBoolean(), anyInt())).thenReturn(mCandidateList); when(ns.selectNetwork(any())) .then(new AnswerWithArguments() { public WifiConfiguration answer(List<WifiCandidates.Candidate> candidateList) { @@ -1417,7 +1413,7 @@ public class WifiConnectivityManagerTest extends WifiBaseTest { mCandidateList.add(mCandidate3); } when(mWifiNS.getCandidatesFromScan(any(), any(), any(), anyBoolean(), anyBoolean(), - anyBoolean(), any(), anyBoolean())).thenReturn(mCandidateList); + anyBoolean(), any(), anyBoolean(), anyInt())).thenReturn(mCandidateList); doAnswer(new AnswerWithArguments() { public void answer(ExternalClientModeManagerRequestListener listener, @@ -2342,7 +2338,7 @@ public class WifiConnectivityManagerTest extends WifiBaseTest { // Issue a partial scan that does not locate any candidates. This should not affect // the cache populated by the full scan. when(mWifiNS.getCandidatesFromScan(any(), any(), any(), anyBoolean(), anyBoolean(), - anyBoolean(), any(), anyBoolean())).thenReturn(null); + anyBoolean(), any(), anyBoolean(), anyInt())).thenReturn(null); when(mScanData.getScannedBandsInternal()).thenReturn(WifiScanner.WIFI_BAND_6_GHZ); when(mClock.getElapsedSinceBootMillis()) .thenReturn(DELAYED_CARRIER_SELECTION_TIME_MS - 1000L); @@ -2353,7 +2349,8 @@ public class WifiConnectivityManagerTest extends WifiBaseTest { // Issue a full scan after the delay period has passed. Since the cache was not modified by // the partial scan, the delayed carrier candidate should still be in the timestamp cache. when(mWifiNS.getCandidatesFromScan(any(), any(), any(), anyBoolean(), anyBoolean(), - anyBoolean(), any(), anyBoolean())).thenReturn(Arrays.asList(mCandidate1)); + anyBoolean(), any(), anyBoolean(), anyInt())).thenReturn(Arrays.asList( + mCandidate1)); when(mScanData.getScannedBandsInternal()).thenReturn(WifiScanner.WIFI_BAND_ALL); when(mClock.getElapsedSinceBootMillis()) .thenReturn(DELAYED_CARRIER_SELECTION_TIME_MS + 1000L); @@ -2423,7 +2420,7 @@ public class WifiConnectivityManagerTest extends WifiBaseTest { candidateList.add(mCandidate1); candidateList.add(otherCandidate); when(mWifiNS.getCandidatesFromScan(any(), any(), any(), anyBoolean(), anyBoolean(), - anyBoolean(), any(), anyBoolean())).thenReturn(candidateList); + anyBoolean(), any(), anyBoolean(), anyInt())).thenReturn(candidateList); // Set WiFi to disconnected state to trigger scan mWifiConnectivityManager.handleConnectionStateChanged( @@ -2476,7 +2473,7 @@ public class WifiConnectivityManagerTest extends WifiBaseTest { candidateList.add(mCandidate1); candidateList.add(otherCandidate); when(mWifiNS.getCandidatesFromScan(any(), any(), any(), anyBoolean(), anyBoolean(), - anyBoolean(), any(), anyBoolean())).thenReturn(candidateList); + anyBoolean(), any(), anyBoolean(), anyInt())).thenReturn(candidateList); // Set WiFi to disconnected state to trigger scan mWifiConnectivityManager.handleConnectionStateChanged( @@ -2517,7 +2514,7 @@ public class WifiConnectivityManagerTest extends WifiBaseTest { candidateList.add(candidate1); candidateList.add(candidate2); when(mWifiNS.getCandidatesFromScan(any(), any(), any(), anyBoolean(), anyBoolean(), - anyBoolean(), any(), anyBoolean())).thenReturn(candidateList); + anyBoolean(), any(), anyBoolean(), anyInt())).thenReturn(candidateList); // Set WiFi to disconnected state to trigger scan mWifiConnectivityManager.handleConnectionStateChanged( @@ -2552,7 +2549,7 @@ public class WifiConnectivityManagerTest extends WifiBaseTest { candidateList.add(mCandidate1); candidateList.add(otherCandidate); when(mWifiNS.getCandidatesFromScan(any(), any(), any(), anyBoolean(), anyBoolean(), - anyBoolean(), any(), anyBoolean())).thenReturn(candidateList); + anyBoolean(), any(), anyBoolean(), anyInt())).thenReturn(candidateList); // Set WiFi to disconnected state to trigger scan mWifiConnectivityManager.handleConnectionStateChanged( @@ -2594,7 +2591,7 @@ public class WifiConnectivityManagerTest extends WifiBaseTest { candidateList.add(mCandidate1); candidateList.add(otherCandidate); when(mWifiNS.getCandidatesFromScan(any(), any(), any(), anyBoolean(), anyBoolean(), - anyBoolean(), any(), anyBoolean())).thenReturn(candidateList); + anyBoolean(), any(), anyBoolean(), anyInt())).thenReturn(candidateList); // Set WiFi to disconnected state to trigger scan mWifiConnectivityManager.handleConnectionStateChanged( @@ -2649,7 +2646,7 @@ public class WifiConnectivityManagerTest extends WifiBaseTest { candidateList.add(mCandidate1); candidateList.add(otherCandidate); when(mWifiNS.getCandidatesFromScan(any(), any(), any(), anyBoolean(), anyBoolean(), - anyBoolean(), any(), anyBoolean())).thenReturn(candidateList); + anyBoolean(), any(), anyBoolean(), anyInt())).thenReturn(candidateList); // Set WiFi to disconnected state to trigger scan mWifiConnectivityManager.handleConnectionStateChanged( @@ -2689,7 +2686,7 @@ public class WifiConnectivityManagerTest extends WifiBaseTest { candidateList.add(mCandidate1); candidateList.add(otherCandidate); when(mWifiNS.getCandidatesFromScan(any(), any(), any(), anyBoolean(), anyBoolean(), - anyBoolean(), any(), anyBoolean())).thenReturn(candidateList); + anyBoolean(), any(), anyBoolean(), anyInt())).thenReturn(candidateList); // Set WiFi to disconnected state to trigger scan mWifiConnectivityManager.handleConnectionStateChanged( @@ -2733,7 +2730,7 @@ public class WifiConnectivityManagerTest extends WifiBaseTest { candidateList.add(mCandidate1); candidateList.add(otherCandidate); when(mWifiNS.getCandidatesFromScan(any(), any(), any(), anyBoolean(), anyBoolean(), - anyBoolean(), any(), anyBoolean())).thenReturn(candidateList); + anyBoolean(), any(), anyBoolean(), anyInt())).thenReturn(candidateList); // Set WiFi to disconnected state to trigger scan mWifiConnectivityManager.handleConnectionStateChanged( @@ -2776,7 +2773,7 @@ public class WifiConnectivityManagerTest extends WifiBaseTest { candidateList.add(mCandidate1); candidateList.add(otherCandidate); when(mWifiNS.getCandidatesFromScan(any(), any(), any(), anyBoolean(), anyBoolean(), - anyBoolean(), any(), anyBoolean())).thenReturn(candidateList); + anyBoolean(), any(), anyBoolean(), anyInt())).thenReturn(candidateList); // Set WiFi to disconnected state to trigger scan mWifiConnectivityManager.handleConnectionStateChanged( @@ -2899,7 +2896,7 @@ public class WifiConnectivityManagerTest extends WifiBaseTest { public void wifiDisconnected_noCandidatesInScan_openNetworkNotifierScanResultsHandled() { // no connection candidates from scan. when(mWifiNS.getCandidatesFromScan(any(), any(), any(), anyBoolean(), anyBoolean(), - anyBoolean(), any(), anyBoolean())).thenReturn(null); + anyBoolean(), any(), anyBoolean(), anyInt())).thenReturn(null); List<ScanDetail> expectedOpenNetworks = new ArrayList<>(); expectedOpenNetworks.add( @@ -4772,6 +4769,8 @@ public class WifiConnectivityManagerTest extends WifiBaseTest { .updateAndGetBssidBlocklistForSsids(anySet()); mWifiConnectivityManager.forceConnectivityScan(WIFI_WORK_SOURCE); mLooper.dispatchAll(); + inOrder.verify(mWifiBlocklistMonitor).clearBssidBlocklistForReason( + eq(WifiBlocklistMonitor.REASON_FRAMEWORK_DISCONNECT_FAST_RECONNECT)); inOrder.verify(mWifiBlocklistMonitor).tryEnablingBlockedBssids(any()); inOrder.verify(mWifiConfigManager).updateNetworkSelectionStatus(disabledConfig.networkId, WifiConfiguration.NetworkSelectionStatus.DISABLED_NONE); @@ -4922,6 +4921,30 @@ public class WifiConnectivityManagerTest extends WifiBaseTest { assertFalse(mWifiConnectivityManager.getAutoJoinEnabledExternal()); } + /** + * Verify if setAutojoinDisallowedSecurityTypes method is working correctly. + * Also verify getAutojoinDisallowedSecurityTypes method is working correctly. + */ + @Test + public void testSetAndGetAutojoinDisallowedSecurityTypes() { + // test default value of auto-join restriction secirity types (NONE) + assertEquals(0/*restrict none by default*/, + mWifiConnectivityManager.getAutojoinDisallowedSecurityTypes()); + + // test setting auto-join restriction on secirity types OPEN, WEP, and OWE + int restrictOpenWepOwe = (0x1 << WifiInfo.SECURITY_TYPE_OPEN) + | (0x1 << WifiInfo.SECURITY_TYPE_WEP) + | (0x1 << WifiInfo.SECURITY_TYPE_OWE); + mWifiConnectivityManager.setAutojoinDisallowedSecurityTypes(restrictOpenWepOwe); + assertEquals(restrictOpenWepOwe, mWifiConnectivityManager + .getAutojoinDisallowedSecurityTypes()); + + // test resetting auto-join restriction on all secirity types + mWifiConnectivityManager.setAutojoinDisallowedSecurityTypes(0/*restrict none*/); + assertEquals(0/*restrict none*/, mWifiConnectivityManager + .getAutojoinDisallowedSecurityTypes()); + } + /* * Firmware supports controlled roaming. * Connect to a network which doesn't have a config specified BSSID. @@ -5261,7 +5284,7 @@ public class WifiConnectivityManagerTest extends WifiBaseTest { * on a DBS supported device. * * Expected behavior: WifiConnectivityManager invokes - * {@link WifiNetworkSelector#getCandidatesFromScan(List, Set, List, boolean, boolean, Set, boolean)} + * {@link WifiNetworkSelector#getCandidatesFromScan(List, Set, List, boolean, boolean, Set, boolean, int)} * boolean, boolean, boolean)} after filtering out the scan results obtained via DBS scan. */ @Test @@ -5282,12 +5305,14 @@ public class WifiConnectivityManagerTest extends WifiBaseTest { List<WifiNetworkSelector.ClientModeManagerState> cmmStates, boolean untrustedNetworkAllowed, boolean oemPaidNetworkAllowed, boolean oemPrivateNetworkAllowed, - Set<Integer> restrictedNetworkAllowedUids, boolean multiInternetNetworkAllowed) + Set<Integer> restrictedNetworkAllowedUids, boolean skipSufficiencyCheck, + int autojoinRestrictionSecurityTypes) throws Exception { capturedScanDetails.addAll(scanDetails); return null; }}).when(mWifiNS).getCandidatesFromScan( - any(), any(), any(), anyBoolean(), eq(true), eq(false), any(), eq(false)); + any(), any(), any(), anyBoolean(), eq(true), eq(false), any(), eq(false), + anyInt()); mWifiConnectivityManager.setTrustedConnectionAllowed(true); mWifiConnectivityManager.setOemPaidConnectionAllowed(true, new WorkSource()); @@ -5341,12 +5366,13 @@ public class WifiConnectivityManagerTest extends WifiBaseTest { List<WifiNetworkSelector.ClientModeManagerState> cmmStates, boolean untrustedNetworkAllowed, boolean oemPaidNetworkAllowed, boolean oemPrivateNetworkAllowed, - Set<Integer> restrictedNetworkAllowedUids, boolean multiInternetNetworkAllowed) + Set<Integer> restrictedNetworkAllowedUids, boolean skipSufficiencyCheck, + int autojoinRestrictionSecurityTypes) throws Exception { capturedScanDetails.addAll(scanDetails); return null; }}).when(mWifiNS).getCandidatesFromScan( - any(), any(), any(), anyBoolean(), eq(false), eq(true), any(), eq(false)); + any(), any(), any(), anyBoolean(), eq(false), eq(true), any(), eq(false), anyInt()); mWifiConnectivityManager.setTrustedConnectionAllowed(true); mWifiConnectivityManager.setOemPrivateConnectionAllowed(true, new WorkSource()); @@ -6265,8 +6291,8 @@ public class WifiConnectivityManagerTest extends WifiBaseTest { assertEquals(1, mTestHandler.getIntervals().size()); final long delta = Math.abs(NETWORK_CHANGE_TRIGGER_PNO_THROTTLE_MS - mTestHandler.getIntervals().get(0)); - assertTrue("Interval " + " (" + delta + ") not in 1ms error margin", - delta < 2); + assertTrue("Interval " + " (" + delta + ") not in 5ms error margin", + delta < 6); when(mClock.getElapsedSinceBootMillis()).thenReturn(mTestHandler.getIntervals().get(0)); // Now advance the test handler and fire the periodic scan timer mTestHandler.timeAdvance(); @@ -6313,7 +6339,7 @@ public class WifiConnectivityManagerTest extends WifiBaseTest { ROLE_CLIENT_SECONDARY_LONG_LIVED)); verify(mWifiNS).getCandidatesFromScan(any(), any(), eq(expectedCmmStates), anyBoolean(), anyBoolean(), anyBoolean(), any(), - eq(false)); + eq(false), anyInt()); } @Test @@ -6345,7 +6371,7 @@ public class WifiConnectivityManagerTest extends WifiBaseTest { mLooper.dispatchAll(); verify(mWifiNS).getCandidatesFromScan(any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any(), - eq(true)); + eq(true), anyInt()); } @Test @@ -6378,7 +6404,7 @@ public class WifiConnectivityManagerTest extends WifiBaseTest { mLooper.dispatchAll(); verify(mWifiNS).getCandidatesFromScan(any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean(), any(), - eq(true)); + eq(true), anyInt()); } /** diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiCountryCodeTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiCountryCodeTest.java index 5941feeafd..6aa996e7c5 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiCountryCodeTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiCountryCodeTest.java @@ -338,19 +338,20 @@ public class WifiCountryCodeTest extends WifiBaseTest { // Wifi get L2 connected. mClientModeImplListenerCaptor.getValue().onConnectionStart(mClientModeManager); - // Wifi Calling is available - when(mWifiCarrierInfoManager.isWifiCallingAvailable()).thenReturn(true); - // Telephony country code arrives. - mWifiCountryCode.setTelephonyCountryCodeAndUpdate(mTelephonyCountryCode); - // Telephony country code won't be applied at this time. - assertEquals("00", mWifiCountryCode.getCurrentDriverCountryCode()); - verify(mWifiP2pMetrics).setIsCountryCodeWorldMode(true); - // Wifi is not forced to disconnect - verify(mClientModeManager, times(0)).disconnect(); + if (mCallingSupported) { + // Wifi Calling is available + when(mWifiCarrierInfoManager.isWifiCallingAvailable()).thenReturn(true); + // Telephony country code arrives. + mWifiCountryCode.setTelephonyCountryCodeAndUpdate(mTelephonyCountryCode); + // Telephony country code won't be applied at this time. + assertEquals("00", mWifiCountryCode.getCurrentDriverCountryCode()); + verify(mWifiP2pMetrics).setIsCountryCodeWorldMode(true); + verify(mClientModeManager, never()).disconnect(); + } + // Test wifi traffic is high. // Wifi Calling is not available when(mWifiCarrierInfoManager.isWifiCallingAvailable()).thenReturn(false); - // Wifi traffic is high when(mWifiInfo.getSuccessfulTxPacketsPerSecond()).thenReturn(20.0); // Telephony country code arrives. mWifiCountryCode.setTelephonyCountryCodeAndUpdate(mTelephonyCountryCode); @@ -358,9 +359,9 @@ public class WifiCountryCodeTest extends WifiBaseTest { assertEquals("00", mWifiCountryCode.getCurrentDriverCountryCode()); verify(mWifiP2pMetrics).setIsCountryCodeWorldMode(true); // Wifi is not forced to disconnect - verify(mClientModeManager, times(0)).disconnect(); + verify(mClientModeManager, never()).disconnect(); - // Wifi traffic is low + // Wifi traffic is low (And no wifi calling) when(mWifiInfo.getSuccessfulTxPacketsPerSecond()).thenReturn(10.0); // Telephony country code arrives for multiple times for (int i = 0; i < 3; i++) { @@ -368,10 +369,8 @@ public class WifiCountryCodeTest extends WifiBaseTest { } // Telephony country code still won't be applied. assertEquals("00", mWifiCountryCode.getCurrentDriverCountryCode()); - if (mCallingSupported) { - // Wifi is forced to disconnect - verify(mClientModeManager, times(1)).disconnect(); - } + // Wifi is forced to disconnect + verify(mClientModeManager, times(1)).disconnect(); mClientModeImplListenerCaptor.getValue().onConnectionEnd(mClientModeManager); // Telephony country is applied after supplicant is ready. diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiDataStallTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiDataStallTest.java index 592b16c420..cdce00ed6c 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiDataStallTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiDataStallTest.java @@ -33,6 +33,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; +import android.net.wifi.ScanResult; import android.net.wifi.WifiInfo; import android.os.Handler; import android.os.test.TestLooper; @@ -655,16 +656,17 @@ public class WifiDataStallTest extends WifiBaseTest { mNewLlStats.timeStampInMs = mOldLlStats.timeStampInMs + 1000; // Expect 1st throughput sufficiency check to return true // because it hits mLastTxBytes == 0 || mLastRxBytes == 0 + mCapabilities.channelBandwidth = ScanResult.CHANNEL_WIDTH_80MHZ; mWifiDataStall.checkDataStallAndThroughputSufficiency(TEST_IFACE_NAME, mCapabilities, mOldLlStats, mNewLlStats, mWifiInfo, mTxBytes, mRxBytes); verify(mWifiMetrics, times(1)).incrementConnectionDuration(TEST_IFACE_NAME, - 1000, true, true, TEST_RSSI, 960, 9609); + 1000, true, true, TEST_RSSI, 960, 9609, 10, 10, ScanResult.CHANNEL_WIDTH_80MHZ); // Expect 2nd throughput sufficiency check to return false mWifiDataStall.checkDataStallAndThroughputSufficiency(TEST_IFACE_NAME, mCapabilities, mOldLlStats, mNewLlStats, mWifiInfo, mTxBytes, mRxBytes); verify(mWifiMetrics, times(1)).incrementConnectionDuration(TEST_IFACE_NAME, - 1000, false, true, TEST_RSSI, 960, 9609); + 1000, false, true, TEST_RSSI, 960, 9609, 10, 10, ScanResult.CHANNEL_WIDTH_80MHZ); mNewLlStats.timeStampInMs = mOldLlStats.timeStampInMs + 2000; phoneStateListener.onDataConnectionStateChanged( @@ -673,7 +675,7 @@ public class WifiDataStallTest extends WifiBaseTest { mWifiDataStall.checkDataStallAndThroughputSufficiency(TEST_IFACE_NAME, mCapabilities, mOldLlStats, mNewLlStats, mWifiInfo, mTxBytes, mRxBytes); verify(mWifiMetrics, times(1)).incrementConnectionDuration(TEST_IFACE_NAME, - 2000, false, false, TEST_RSSI, 960, 9609); + 2000, false, false, TEST_RSSI, 960, 9609, 10, 10, ScanResult.CHANNEL_WIDTH_80MHZ); // Expect this update to be ignored by connection duration counters due to its // too large poll interval @@ -681,7 +683,7 @@ public class WifiDataStallTest extends WifiBaseTest { mWifiDataStall.checkDataStallAndThroughputSufficiency(TEST_IFACE_NAME, mCapabilities, mOldLlStats, mNewLlStats, mWifiInfo, mTxBytes, mRxBytes); verify(mWifiMetrics, never()).incrementConnectionDuration(TEST_IFACE_NAME, - 10000, false, false, TEST_RSSI, 960, 9609); + 10000, false, false, TEST_RSSI, 960, 9609, 10, 10, ScanResult.CHANNEL_WIDTH_80MHZ); setWifiEnabled(false); } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiDeviceStateChangeManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiDeviceStateChangeManagerTest.java index 95eac5fa53..394e4ff334 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiDeviceStateChangeManagerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiDeviceStateChangeManagerTest.java @@ -20,9 +20,11 @@ import static android.content.Intent.ACTION_SCREEN_OFF; import static android.content.Intent.ACTION_SCREEN_ON; import static org.junit.Assert.assertNotNull; +import static org.junit.Assume.assumeTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; @@ -31,12 +33,16 @@ import static org.mockito.Mockito.when; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.net.wifi.util.Environment; import android.os.Handler; import android.os.PowerManager; import android.os.test.TestLooper; +import android.security.advancedprotection.AdvancedProtectionManager; import androidx.test.filters.SmallTest; +import com.android.wifi.flags.FeatureFlags; + import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; @@ -46,25 +52,40 @@ import org.mockito.MockitoAnnotations; @SmallTest public class WifiDeviceStateChangeManagerTest extends WifiBaseTest { - @Mock Context mContext; @Mock WifiDeviceStateChangeManager.StateChangeCallback mStateChangeCallback; @Mock PowerManager mPowerManager; + @Mock WifiInjector mWifiInjector; + @Mock DeviceConfigFacade mDeviceConfigFacade; + @Mock FeatureFlags mFeatureFlags; @Captor ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor; private TestLooper mLooper; private Handler mHandler; - private WifiDeviceStateChangeManager mWifiDeviceStateChangeManager; + private WifiDeviceStateChangeManagerSpy mWifiDeviceStateChangeManager; + private boolean mIsAapmApiFlagEnabled = false; + + class WifiDeviceStateChangeManagerSpy extends WifiDeviceStateChangeManager { + WifiDeviceStateChangeManagerSpy() { + super(mContext, mHandler, mWifiInjector); + } + + @Override + public boolean isAapmApiFlagEnabled() { + return mIsAapmApiFlagEnabled; + } + } @Before public void setUp() { MockitoAnnotations.initMocks(this); when(mContext.getSystemService(PowerManager.class)).thenReturn(mPowerManager); + when(mWifiInjector.getDeviceConfigFacade()).thenReturn(mDeviceConfigFacade); + when(mDeviceConfigFacade.getFeatureFlags()).thenReturn(mFeatureFlags); when(mPowerManager.isInteractive()).thenReturn(true); mLooper = new TestLooper(); mHandler = new Handler(mLooper.getLooper()); - mWifiDeviceStateChangeManager = new WifiDeviceStateChangeManager(mContext, mHandler); - + mWifiDeviceStateChangeManager = new WifiDeviceStateChangeManagerSpy(); } @Test @@ -72,6 +93,7 @@ public class WifiDeviceStateChangeManagerTest extends WifiBaseTest { mWifiDeviceStateChangeManager.registerStateChangeCallback(mStateChangeCallback); // Should be no callback event before the boot completed verify(mStateChangeCallback, never()).onScreenStateChanged(anyBoolean()); + verify(mStateChangeCallback, never()).onAdvancedProtectionModeStateChanged(anyBoolean()); mWifiDeviceStateChangeManager.handleBootCompleted(); verify(mContext, atLeastOnce()) .registerReceiver(mBroadcastReceiverCaptor.capture(), any()); @@ -95,6 +117,8 @@ public class WifiDeviceStateChangeManagerTest extends WifiBaseTest { mWifiDeviceStateChangeManager.registerStateChangeCallback(mStateChangeCallback); // Register after boot completed should immediately get a callback verify(mStateChangeCallback).onScreenStateChanged(true); + // No Advance protection manager, should false. + verify(mStateChangeCallback).onAdvancedProtectionModeStateChanged(false); } private void setScreenState(boolean screenOn) { @@ -104,4 +128,38 @@ public class WifiDeviceStateChangeManagerTest extends WifiBaseTest { broadcastReceiver.onReceive(mContext, intent); mLooper.dispatchAll(); } + + @Test + public void testCallbackWhenAdvancedProtectionModeSupported() { + assumeTrue(Environment.isSdkAtLeastB()); + mIsAapmApiFlagEnabled = true; + ArgumentCaptor<AdvancedProtectionManager.Callback> apmCallbackCaptor = + ArgumentCaptor.forClass(AdvancedProtectionManager.Callback.class); + when(mFeatureFlags.wepDisabledInApm()).thenReturn(true); + AdvancedProtectionManager mockAdvancedProtectionManager = + mock(AdvancedProtectionManager.class); + when(mContext.getSystemService(AdvancedProtectionManager.class)) + .thenReturn(mockAdvancedProtectionManager); + when(mockAdvancedProtectionManager.isAdvancedProtectionEnabled()).thenReturn(false); + mWifiDeviceStateChangeManager.registerStateChangeCallback(mStateChangeCallback); + // Should be no callback event before the boot completed + verify(mStateChangeCallback, never()).onAdvancedProtectionModeStateChanged(anyBoolean()); + + mWifiDeviceStateChangeManager.handleBootCompleted(); + verify(mockAdvancedProtectionManager).registerAdvancedProtectionCallback(any(), + apmCallbackCaptor.capture()); + verify(mStateChangeCallback).onAdvancedProtectionModeStateChanged(false); + + reset(mStateChangeCallback); + apmCallbackCaptor.getValue().onAdvancedProtectionChanged(true); + verify(mStateChangeCallback).onAdvancedProtectionModeStateChanged(true); + + apmCallbackCaptor.getValue().onAdvancedProtectionChanged(false); + verify(mStateChangeCallback).onAdvancedProtectionModeStateChanged(false); + + reset(mStateChangeCallback); + mWifiDeviceStateChangeManager.unregisterStateChangeCallback(mStateChangeCallback); + apmCallbackCaptor.getValue().onAdvancedProtectionChanged(true); + verify(mStateChangeCallback, never()).onAdvancedProtectionModeStateChanged(anyBoolean()); + } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiDiagnosticsTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiDiagnosticsTest.java index 4457977b37..1501289646 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiDiagnosticsTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiDiagnosticsTest.java @@ -979,6 +979,58 @@ public class WifiDiagnosticsTest extends WifiBaseTest { verify(mBugreportManager, never()).requestBugreport(any(), any(), any()); } + @Test + public void takeBugReportDoesNothingWhenSSRConfigOverlayDisabled() { + when(mBuildProperties.isUserBuild()).thenReturn(false); + mResources.setBoolean(R.bool.config_wifi_diagnostics_bugreport_enabled, true); + mResources.setBoolean(R.bool.config_wifi_subsystem_restart_bugreport_enabled, false); + mWifiDiagnostics = new WifiDiagnostics( + mContext, mWifiInjector, mWifiNative, mBuildProperties, mLastMileLogger, mClock, + mTestLooper.getLooper()); + + mWifiDiagnostics.takeBugReport("", "Subsystem Restart"); + verify(mPackageManager, never()).queryIntentActivities(any(), anyInt()); + } + + @Test + public void takeBugReportWhenSSRConfigOverlayEnabled() { + when(mBuildProperties.isUserBuild()).thenReturn(false); + mResources.setBoolean(R.bool.config_wifi_diagnostics_bugreport_enabled, true); + mResources.setBoolean(R.bool.config_wifi_subsystem_restart_bugreport_enabled, true); + mWifiDiagnostics = new WifiDiagnostics( + mContext, mWifiInjector, mWifiNative, mBuildProperties, mLastMileLogger, mClock, + mTestLooper.getLooper()); + + mWifiDiagnostics.takeBugReport("", "Subsystem Restart"); + verify(mPackageManager, times(1)).queryIntentActivities(any(), anyInt()); + } + + @Test + public void takeBugReportWhenSSRConfigOverlayDisabledWhenNonSRREvent() { + when(mBuildProperties.isUserBuild()).thenReturn(false); + mResources.setBoolean(R.bool.config_wifi_diagnostics_bugreport_enabled, true); + mResources.setBoolean(R.bool.config_wifi_subsystem_restart_bugreport_enabled, false); + mWifiDiagnostics = new WifiDiagnostics( + mContext, mWifiInjector, mWifiNative, mBuildProperties, mLastMileLogger, mClock, + mTestLooper.getLooper()); + + mWifiDiagnostics.takeBugReport("", "Last Resort Watchdog"); + verify(mPackageManager, times(1)).queryIntentActivities(any(), anyInt()); + } + + @Test + public void takeBugReportWhenSSRConfigOverlayEnabledWhenNonSRREvent() { + when(mBuildProperties.isUserBuild()).thenReturn(false); + mResources.setBoolean(R.bool.config_wifi_diagnostics_bugreport_enabled, true); + mResources.setBoolean(R.bool.config_wifi_subsystem_restart_bugreport_enabled, true); + mWifiDiagnostics = new WifiDiagnostics( + mContext, mWifiInjector, mWifiNative, mBuildProperties, mLastMileLogger, mClock, + mTestLooper.getLooper()); + + mWifiDiagnostics.takeBugReport("", "Last Resort Watchdog"); + verify(mPackageManager, times(1)).queryIntentActivities(any(), anyInt()); + } + /** Verifies that we flush HAL ringbuffer when capture bugreport. */ @Test public void triggerBugReportFlushRingBufferDataCapture() { diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiDialogManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiDialogManagerTest.java index 58ff485f74..362fc6aab8 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiDialogManagerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiDialogManagerTest.java @@ -70,13 +70,13 @@ import org.mockito.MockitoAnnotations; */ @SmallTest public class WifiDialogManagerTest extends WifiBaseTest { - private static final int TIMEOUT_MILLIS = 30_000; private static final String TEST_TITLE = "Title"; private static final String TEST_MESSAGE = "Message"; private static final String TEST_POSITIVE_BUTTON_TEXT = "Yes"; private static final String TEST_NEGATIVE_BUTTON_TEXT = "No"; private static final String TEST_NEUTRAL_BUTTON_TEXT = "Maybe"; private static final String TEST_DEVICE_NAME = "TEST_DEVICE_NAME"; + private static final int TEST_P2P_TIMEOUT_MS = 15 * 60 * 1000; private static final String WIFI_DIALOG_APK_PKG_NAME = "WifiDialogApkPkgName"; @Mock WifiContext mWifiContext; @@ -115,14 +115,12 @@ public class WifiDialogManagerTest extends WifiBaseTest { /** * Helper method to synchronously call {@link DialogHandle#launchDialog(long)}. * @param dialogHandle Dialog handle to call on. - * @param timeoutMs Timeout for {@link DialogHandle#launchDialog(long)}. * @param wifiThreadRunner Main Wi-Fi thread runner of the WifiDialogManager. */ private void launchDialogSynchronous( @NonNull DialogHandle dialogHandle, - long timeoutMs, @NonNull WifiThreadRunner wifiThreadRunner) { - dialogHandle.launchDialog(timeoutMs); + dialogHandle.launchDialog(); ArgumentCaptor<Runnable> launchRunnableArgumentCaptor = ArgumentCaptor.forClass(Runnable.class); verify(wifiThreadRunner, atLeastOnce()).post(launchRunnableArgumentCaptor.capture(), @@ -250,7 +248,7 @@ public class WifiDialogManagerTest extends WifiBaseTest { DialogHandle dialogHandle = mDialogManager.createSimpleDialog(TEST_TITLE, TEST_MESSAGE, TEST_POSITIVE_BUTTON_TEXT, TEST_NEGATIVE_BUTTON_TEXT, TEST_NEUTRAL_BUTTON_TEXT, callback, callbackThreadRunner); - launchDialogSynchronous(dialogHandle, 0, mWifiThreadRunner); + launchDialogSynchronous(dialogHandle, mWifiThreadRunner); Intent intent = verifyStartActivityAsUser(1, mWifiContext); int dialogId = verifySimpleDialogLaunchIntent(intent, TEST_TITLE, TEST_MESSAGE, TEST_POSITIVE_BUTTON_TEXT, TEST_NEGATIVE_BUTTON_TEXT, TEST_NEUTRAL_BUTTON_TEXT); @@ -273,7 +271,7 @@ public class WifiDialogManagerTest extends WifiBaseTest { dialogHandle = mDialogManager.createSimpleDialog(TEST_TITLE, TEST_MESSAGE, TEST_POSITIVE_BUTTON_TEXT, TEST_NEGATIVE_BUTTON_TEXT, TEST_NEUTRAL_BUTTON_TEXT, callback, callbackThreadRunner); - launchDialogSynchronous(dialogHandle, 0, mWifiThreadRunner); + launchDialogSynchronous(dialogHandle, mWifiThreadRunner); intent = verifyStartActivityAsUser(2, mWifiContext); dialogId = verifySimpleDialogLaunchIntent(intent, TEST_TITLE, TEST_MESSAGE, TEST_POSITIVE_BUTTON_TEXT, TEST_NEGATIVE_BUTTON_TEXT, TEST_NEUTRAL_BUTTON_TEXT); @@ -288,7 +286,7 @@ public class WifiDialogManagerTest extends WifiBaseTest { dialogHandle = mDialogManager.createSimpleDialog( TEST_TITLE, TEST_MESSAGE, TEST_POSITIVE_BUTTON_TEXT, TEST_NEGATIVE_BUTTON_TEXT, TEST_NEUTRAL_BUTTON_TEXT, callback, callbackThreadRunner); - launchDialogSynchronous(dialogHandle, 0, mWifiThreadRunner); + launchDialogSynchronous(dialogHandle, mWifiThreadRunner); intent = verifyStartActivityAsUser(3, mWifiContext); dialogId = verifySimpleDialogLaunchIntent(intent, TEST_TITLE, TEST_MESSAGE, TEST_POSITIVE_BUTTON_TEXT, TEST_NEGATIVE_BUTTON_TEXT, TEST_NEUTRAL_BUTTON_TEXT); @@ -303,7 +301,7 @@ public class WifiDialogManagerTest extends WifiBaseTest { dialogHandle = mDialogManager.createSimpleDialog(TEST_TITLE, TEST_MESSAGE, TEST_POSITIVE_BUTTON_TEXT, TEST_NEGATIVE_BUTTON_TEXT, TEST_NEUTRAL_BUTTON_TEXT, callback, callbackThreadRunner); - launchDialogSynchronous(dialogHandle, 0, mWifiThreadRunner); + launchDialogSynchronous(dialogHandle, mWifiThreadRunner); intent = verifyStartActivityAsUser(4, mWifiContext); dialogId = verifySimpleDialogLaunchIntent(intent, TEST_TITLE, TEST_MESSAGE, TEST_POSITIVE_BUTTON_TEXT, TEST_NEGATIVE_BUTTON_TEXT, TEST_NEUTRAL_BUTTON_TEXT); @@ -330,7 +328,7 @@ public class WifiDialogManagerTest extends WifiBaseTest { DialogHandle dialogHandle = mDialogManager.createSimpleDialog(TEST_TITLE, TEST_MESSAGE, TEST_POSITIVE_BUTTON_TEXT, TEST_NEGATIVE_BUTTON_TEXT, TEST_NEUTRAL_BUTTON_TEXT, callback, callbackThreadRunner); - launchDialogSynchronous(dialogHandle, 0, mWifiThreadRunner); + launchDialogSynchronous(dialogHandle, mWifiThreadRunner); Intent intent = verifyStartActivityAsUser(1, mWifiContext); int dialogId = verifySimpleDialogLaunchIntent(intent, TEST_TITLE, TEST_MESSAGE, TEST_POSITIVE_BUTTON_TEXT, TEST_NEGATIVE_BUTTON_TEXT, TEST_NEUTRAL_BUTTON_TEXT); @@ -352,7 +350,7 @@ public class WifiDialogManagerTest extends WifiBaseTest { dialogHandle = mDialogManager.createSimpleDialog(TEST_TITLE, TEST_MESSAGE, TEST_POSITIVE_BUTTON_TEXT, TEST_NEGATIVE_BUTTON_TEXT, TEST_NEUTRAL_BUTTON_TEXT, callback, callbackThreadRunner); - launchDialogSynchronous(dialogHandle, 0, mWifiThreadRunner); + launchDialogSynchronous(dialogHandle, mWifiThreadRunner); intent = verifyStartActivityAsUser(3, mWifiContext); dialogId = verifySimpleDialogLaunchIntent(intent, TEST_TITLE, TEST_MESSAGE, TEST_POSITIVE_BUTTON_TEXT, TEST_NEGATIVE_BUTTON_TEXT, TEST_NEUTRAL_BUTTON_TEXT); @@ -377,7 +375,7 @@ public class WifiDialogManagerTest extends WifiBaseTest { DialogHandle dialogHandle1 = mDialogManager.createSimpleDialog(TEST_TITLE, TEST_MESSAGE, TEST_POSITIVE_BUTTON_TEXT, TEST_NEGATIVE_BUTTON_TEXT, TEST_NEUTRAL_BUTTON_TEXT, callback1, callbackThreadRunner); - launchDialogSynchronous(dialogHandle1, 0, mWifiThreadRunner); + launchDialogSynchronous(dialogHandle1, mWifiThreadRunner); Intent intent = verifyStartActivityAsUser(1, mWifiContext); int dialogId1 = verifySimpleDialogLaunchIntent(intent, TEST_TITLE, TEST_MESSAGE, TEST_POSITIVE_BUTTON_TEXT, TEST_NEGATIVE_BUTTON_TEXT, TEST_NEUTRAL_BUTTON_TEXT); @@ -387,7 +385,7 @@ public class WifiDialogManagerTest extends WifiBaseTest { DialogHandle dialogHandle2 = mDialogManager.createSimpleDialog(TEST_TITLE, TEST_MESSAGE, TEST_POSITIVE_BUTTON_TEXT, TEST_NEGATIVE_BUTTON_TEXT, TEST_NEUTRAL_BUTTON_TEXT, callback2, callbackThreadRunner); - launchDialogSynchronous(dialogHandle2, 0, mWifiThreadRunner); + launchDialogSynchronous(dialogHandle2, mWifiThreadRunner); intent = verifyStartActivityAsUser(2, mWifiContext); int dialogId2 = verifySimpleDialogLaunchIntent(intent, TEST_TITLE, TEST_MESSAGE, TEST_POSITIVE_BUTTON_TEXT, TEST_NEGATIVE_BUTTON_TEXT, TEST_NEUTRAL_BUTTON_TEXT); @@ -434,7 +432,7 @@ public class WifiDialogManagerTest extends WifiBaseTest { DialogHandle dialogHandle = mDialogManager.createSimpleDialog(TEST_TITLE, TEST_MESSAGE, TEST_POSITIVE_BUTTON_TEXT, TEST_NEGATIVE_BUTTON_TEXT, TEST_NEUTRAL_BUTTON_TEXT, callback, callbackThreadRunner); - launchDialogSynchronous(dialogHandle, 0, mWifiThreadRunner); + launchDialogSynchronous(dialogHandle, mWifiThreadRunner); ArgumentCaptor<DialogInterface.OnClickListener> positiveButtonListenerCaptor = ArgumentCaptor.forClass(DialogInterface.OnClickListener.class); @@ -479,90 +477,6 @@ public class WifiDialogManagerTest extends WifiBaseTest { } @Test - public void testSimpleDialog_timeoutCancelsDialog_preT() { - Assume.assumeTrue(!SdkLevel.isAtLeastT()); - SimpleDialogCallback callback = mock(SimpleDialogCallback.class); - WifiThreadRunner callbackThreadRunner = mock(WifiThreadRunner.class); - - - AlertDialog.Builder builder = mock(AlertDialog.Builder.class); - AlertDialog dialog = mock(AlertDialog.class); - when(builder.setTitle(any())).thenReturn(builder); - when(builder.setMessage(any())).thenReturn(builder); - when(builder.setPositiveButton(any(), any())).thenReturn(builder); - when(builder.setNegativeButton(any(), any())).thenReturn(builder); - when(builder.setNeutralButton(any(), any())).thenReturn(builder); - when(builder.setOnCancelListener(any())).thenReturn(builder); - when(builder.setOnDismissListener(any())).thenReturn(builder); - when(builder.create()).thenReturn(dialog); - Window window = mock(Window.class); - WindowManager.LayoutParams layoutParams = mock(WindowManager.LayoutParams.class); - when(window.getAttributes()).thenReturn(layoutParams); - when(dialog.getWindow()).thenReturn(window); - when(mFrameworkFacade.makeAlertDialogBuilder(any())).thenReturn(builder); - DialogHandle dialogHandle = mDialogManager.createSimpleDialog(TEST_TITLE, TEST_MESSAGE, - TEST_POSITIVE_BUTTON_TEXT, TEST_NEGATIVE_BUTTON_TEXT, TEST_NEUTRAL_BUTTON_TEXT, - callback, callbackThreadRunner); - launchDialogSynchronous(dialogHandle, TIMEOUT_MILLIS, mWifiThreadRunner); - - // Verify the timeout runnable was posted and run it. - ArgumentCaptor<Runnable> runnableArgumentCaptor = ArgumentCaptor.forClass(Runnable.class); - verify(mWifiThreadRunner, times(1)) - .postDelayed(runnableArgumentCaptor.capture(), eq((long) TIMEOUT_MILLIS), - anyString()); - runnableArgumentCaptor.getValue().run(); - - // Verify that the dialog was cancelled. - verify(dialog).cancel(); - } - - @Test - public void testSimpleDialog_dismissedBeforeTimeout_preT() { - Assume.assumeTrue(!SdkLevel.isAtLeastT()); - SimpleDialogCallback callback = mock(SimpleDialogCallback.class); - WifiThreadRunner callbackThreadRunner = mock(WifiThreadRunner.class); - - - AlertDialog.Builder builder = mock(AlertDialog.Builder.class); - AlertDialog dialog = mock(AlertDialog.class); - when(builder.setTitle(any())).thenReturn(builder); - when(builder.setMessage(any())).thenReturn(builder); - when(builder.setPositiveButton(any(), any())).thenReturn(builder); - when(builder.setNegativeButton(any(), any())).thenReturn(builder); - when(builder.setNeutralButton(any(), any())).thenReturn(builder); - when(builder.setOnCancelListener(any())).thenReturn(builder); - when(builder.setOnDismissListener(any())).thenReturn(builder); - when(builder.create()).thenReturn(dialog); - Window window = mock(Window.class); - WindowManager.LayoutParams layoutParams = mock(WindowManager.LayoutParams.class); - when(window.getAttributes()).thenReturn(layoutParams); - when(dialog.getWindow()).thenReturn(window); - when(mFrameworkFacade.makeAlertDialogBuilder(any())).thenReturn(builder); - - DialogHandle dialogHandle = mDialogManager.createSimpleDialog(TEST_TITLE, TEST_MESSAGE, - TEST_POSITIVE_BUTTON_TEXT, TEST_NEGATIVE_BUTTON_TEXT, TEST_NEUTRAL_BUTTON_TEXT, - callback, callbackThreadRunner); - launchDialogSynchronous(dialogHandle, TIMEOUT_MILLIS, mWifiThreadRunner); - - // Verify the timeout runnable was posted. - ArgumentCaptor<Runnable> runnableArgumentCaptor = ArgumentCaptor.forClass(Runnable.class); - verify(mWifiThreadRunner, times(1)) - .postDelayed(runnableArgumentCaptor.capture(), eq((long) TIMEOUT_MILLIS), - anyString()); - runnableArgumentCaptor.getValue().run(); - - // Dismiss the dialog before the timeout runnable executes. - ArgumentCaptor<DialogInterface.OnDismissListener> dismissListenerCaptor = - ArgumentCaptor.forClass(DialogInterface.OnDismissListener.class); - verify(builder).setOnDismissListener(dismissListenerCaptor.capture()); - dismissListenerCaptor.getValue().onDismiss(dialog); - dispatchMockWifiThreadRunner(mWifiThreadRunner); - - // Verify that the timeout runnable was removed. - verify(mWifiThreadRunner).removeCallbacks(runnableArgumentCaptor.getValue()); - } - - @Test public void testSimpleDialog_nullWifiResourceApkName_doesNotLaunchDialog() { Assume.assumeTrue(SdkLevel.isAtLeastT()); when(mWifiContext.getWifiDialogApkPkgName()).thenReturn(null); @@ -573,7 +487,7 @@ public class WifiDialogManagerTest extends WifiBaseTest { DialogHandle dialogHandle = mDialogManager.createSimpleDialog(TEST_TITLE, TEST_MESSAGE, TEST_POSITIVE_BUTTON_TEXT, TEST_NEGATIVE_BUTTON_TEXT, TEST_NEUTRAL_BUTTON_TEXT, callback, callbackThreadRunner); - launchDialogSynchronous(dialogHandle, 0, mWifiThreadRunner); + launchDialogSynchronous(dialogHandle, mWifiThreadRunner); verify(mWifiContext, never()).startActivityAsUser(any(), eq(UserHandle.CURRENT)); } @@ -607,7 +521,7 @@ public class WifiDialogManagerTest extends WifiBaseTest { TEST_MESSAGE, TEST_POSITIVE_BUTTON_TEXT, TEST_NEGATIVE_BUTTON_TEXT, TEST_NEUTRAL_BUTTON_TEXT, callback, callbackThreadRunner); - launchDialogSynchronous(dialogHandle, 0, mWifiThreadRunner); + launchDialogSynchronous(dialogHandle, mWifiThreadRunner); ArgumentCaptor<DialogInterface.OnClickListener> positiveButtonListenerCaptor = ArgumentCaptor.forClass(DialogInterface.OnClickListener.class); @@ -652,90 +566,6 @@ public class WifiDialogManagerTest extends WifiBaseTest { } @Test - public void testLegacySimpleDialog_timeoutCancelsDialog() { - SimpleDialogCallback callback = mock(SimpleDialogCallback.class); - WifiThreadRunner callbackThreadRunner = mock(WifiThreadRunner.class); - - - AlertDialog.Builder builder = mock(AlertDialog.Builder.class); - AlertDialog dialog = mock(AlertDialog.class); - when(builder.setTitle(any())).thenReturn(builder); - when(builder.setMessage(any())).thenReturn(builder); - when(builder.setPositiveButton(any(), any())).thenReturn(builder); - when(builder.setNegativeButton(any(), any())).thenReturn(builder); - when(builder.setNeutralButton(any(), any())).thenReturn(builder); - when(builder.setOnCancelListener(any())).thenReturn(builder); - when(builder.setOnDismissListener(any())).thenReturn(builder); - when(builder.create()).thenReturn(dialog); - Window window = mock(Window.class); - WindowManager.LayoutParams layoutParams = mock(WindowManager.LayoutParams.class); - when(window.getAttributes()).thenReturn(layoutParams); - when(dialog.getWindow()).thenReturn(window); - when(mFrameworkFacade.makeAlertDialogBuilder(any())).thenReturn(builder); - DialogHandle dialogHandle = mDialogManager.createLegacySimpleDialog(TEST_TITLE, - TEST_MESSAGE, TEST_POSITIVE_BUTTON_TEXT, TEST_NEGATIVE_BUTTON_TEXT, - TEST_NEUTRAL_BUTTON_TEXT, - callback, callbackThreadRunner); - launchDialogSynchronous(dialogHandle, TIMEOUT_MILLIS, mWifiThreadRunner); - - // Verify the timeout runnable was posted and run it. - ArgumentCaptor<Runnable> runnableArgumentCaptor = ArgumentCaptor.forClass(Runnable.class); - verify(mWifiThreadRunner, times(1)) - .postDelayed(runnableArgumentCaptor.capture(), eq((long) TIMEOUT_MILLIS), - anyString()); - runnableArgumentCaptor.getValue().run(); - - // Verify that the dialog was cancelled. - verify(dialog).cancel(); - } - - @Test - public void testLegacySimpleDialog_dismissedBeforeTimeout() { - SimpleDialogCallback callback = mock(SimpleDialogCallback.class); - WifiThreadRunner callbackThreadRunner = mock(WifiThreadRunner.class); - - - AlertDialog.Builder builder = mock(AlertDialog.Builder.class); - AlertDialog dialog = mock(AlertDialog.class); - when(builder.setTitle(any())).thenReturn(builder); - when(builder.setMessage(any())).thenReturn(builder); - when(builder.setPositiveButton(any(), any())).thenReturn(builder); - when(builder.setNegativeButton(any(), any())).thenReturn(builder); - when(builder.setNeutralButton(any(), any())).thenReturn(builder); - when(builder.setOnCancelListener(any())).thenReturn(builder); - when(builder.setOnDismissListener(any())).thenReturn(builder); - when(builder.create()).thenReturn(dialog); - Window window = mock(Window.class); - WindowManager.LayoutParams layoutParams = mock(WindowManager.LayoutParams.class); - when(window.getAttributes()).thenReturn(layoutParams); - when(dialog.getWindow()).thenReturn(window); - when(mFrameworkFacade.makeAlertDialogBuilder(any())).thenReturn(builder); - - DialogHandle dialogHandle = mDialogManager.createLegacySimpleDialog(TEST_TITLE, - TEST_MESSAGE, TEST_POSITIVE_BUTTON_TEXT, TEST_NEGATIVE_BUTTON_TEXT, - TEST_NEUTRAL_BUTTON_TEXT, - callback, callbackThreadRunner); - launchDialogSynchronous(dialogHandle, TIMEOUT_MILLIS, mWifiThreadRunner); - - // Verify the timeout runnable was posted. - ArgumentCaptor<Runnable> runnableArgumentCaptor = ArgumentCaptor.forClass(Runnable.class); - verify(mWifiThreadRunner, times(1)) - .postDelayed(runnableArgumentCaptor.capture(), eq((long) TIMEOUT_MILLIS), - anyString()); - runnableArgumentCaptor.getValue().run(); - - // Dismiss the dialog before the timeout runnable executes. - ArgumentCaptor<DialogInterface.OnDismissListener> dismissListenerCaptor = - ArgumentCaptor.forClass(DialogInterface.OnDismissListener.class); - verify(builder).setOnDismissListener(dismissListenerCaptor.capture()); - dismissListenerCaptor.getValue().onDismiss(dialog); - dispatchMockWifiThreadRunner(mWifiThreadRunner); - - // Verify that the timeout runnable was removed. - verify(mWifiThreadRunner).removeCallbacks(runnableArgumentCaptor.getValue()); - } - - @Test public void testLegacySimpleDialog_cancelledDueToActionCloseSystemDialogs() { SimpleDialogCallback callback = mock(SimpleDialogCallback.class); WifiThreadRunner callbackThreadRunner = mock(WifiThreadRunner.class); @@ -761,7 +591,7 @@ public class WifiDialogManagerTest extends WifiBaseTest { TEST_MESSAGE, TEST_POSITIVE_BUTTON_TEXT, TEST_NEGATIVE_BUTTON_TEXT, TEST_NEUTRAL_BUTTON_TEXT, callback, callbackThreadRunner); - launchDialogSynchronous(dialogHandle, TIMEOUT_MILLIS, mWifiThreadRunner); + launchDialogSynchronous(dialogHandle, mWifiThreadRunner); // ACTION_CLOSE_SYSTEM_DIALOGS with EXTRA_CLOSE_SYSTEM_DIALOGS_EXCEPT_WIFI should be // ignored. @@ -807,7 +637,7 @@ public class WifiDialogManagerTest extends WifiBaseTest { TEST_MESSAGE, TEST_POSITIVE_BUTTON_TEXT, TEST_NEGATIVE_BUTTON_TEXT, TEST_NEUTRAL_BUTTON_TEXT, callback, callbackThreadRunner); - launchDialogSynchronous(dialogHandle, TIMEOUT_MILLIS, mWifiThreadRunner); + launchDialogSynchronous(dialogHandle, mWifiThreadRunner); verify(window).setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); // Receive screen off event. @@ -833,7 +663,8 @@ public class WifiDialogManagerTest extends WifiBaseTest { @NonNull Intent launchIntent, String expectedDeviceName, boolean expectedIsPinRequested, - @Nullable String expectedDisplayPin) { + @Nullable String expectedDisplayPin, + long expectedTimeoutMs) { assertThat(launchIntent.getAction()).isEqualTo(WifiManager.ACTION_LAUNCH_DIALOG); ComponentName component = launchIntent.getComponent(); assertThat(component.getPackageName()).isEqualTo(WIFI_DIALOG_APK_PKG_NAME); @@ -855,6 +686,9 @@ public class WifiDialogManagerTest extends WifiBaseTest { assertThat(launchIntent.hasExtra(WifiManager.EXTRA_P2P_DISPLAY_PIN)).isTrue(); assertThat(launchIntent.getStringExtra(WifiManager.EXTRA_P2P_DISPLAY_PIN)) .isEqualTo(expectedDisplayPin); + assertThat(launchIntent.hasExtra(WifiManager.EXTRA_DIALOG_TIMEOUT_MS)).isTrue(); + assertThat(launchIntent.getIntExtra(WifiManager.EXTRA_DIALOG_TIMEOUT_MS, -1)) + .isEqualTo(expectedTimeoutMs); return dialogId; } @@ -871,12 +705,12 @@ public class WifiDialogManagerTest extends WifiBaseTest { // Accept without PIN DialogHandle dialogHandle = mDialogManager.createP2pInvitationReceivedDialog( - TEST_DEVICE_NAME, false, null, Display.DEFAULT_DISPLAY, + TEST_DEVICE_NAME, false, null, TEST_P2P_TIMEOUT_MS, Display.DEFAULT_DISPLAY, callback, callbackThreadRunner); - launchDialogSynchronous(dialogHandle, 0, mWifiThreadRunner); + launchDialogSynchronous(dialogHandle, mWifiThreadRunner); Intent intent = verifyStartActivityAsUser(1, mWifiContext); int dialogId = verifyP2pInvitationReceivedDialogLaunchIntent(intent, - TEST_DEVICE_NAME, false, null); + TEST_DEVICE_NAME, false, null, TEST_P2P_TIMEOUT_MS); mDialogManager.replyToP2pInvitationReceivedDialog(dialogId, true, null); dispatchMockWifiThreadRunner(callbackThreadRunner); verify(callback, times(1)).onAccepted(null); @@ -887,86 +721,87 @@ public class WifiDialogManagerTest extends WifiBaseTest { // Accept with PIN dialogHandle = mDialogManager.createP2pInvitationReceivedDialog( - TEST_DEVICE_NAME, true, null, Display.DEFAULT_DISPLAY, + TEST_DEVICE_NAME, true, null, TEST_P2P_TIMEOUT_MS, Display.DEFAULT_DISPLAY, callback, callbackThreadRunner); - launchDialogSynchronous(dialogHandle, 0, mWifiThreadRunner); + launchDialogSynchronous(dialogHandle, mWifiThreadRunner); intent = verifyStartActivityAsUser(2, mWifiContext); dialogId = verifyP2pInvitationReceivedDialogLaunchIntent(intent, - TEST_DEVICE_NAME, true, null); + TEST_DEVICE_NAME, true, null, TEST_P2P_TIMEOUT_MS); mDialogManager.replyToP2pInvitationReceivedDialog(dialogId, true, "012345"); dispatchMockWifiThreadRunner(callbackThreadRunner); verify(callback, times(1)).onAccepted("012345"); // Accept with PIN but PIN was not requested dialogHandle = mDialogManager.createP2pInvitationReceivedDialog( - TEST_DEVICE_NAME, false, null, 123, callback, callbackThreadRunner); - launchDialogSynchronous(dialogHandle, 0, mWifiThreadRunner); + TEST_DEVICE_NAME, false, null, TEST_P2P_TIMEOUT_MS, 123, callback, + callbackThreadRunner); + launchDialogSynchronous(dialogHandle, mWifiThreadRunner); if (SdkLevel.isAtLeastT()) { verifyStartActivityAsUser(1, 123, mWifiContext); } intent = verifyStartActivityAsUser(3, mWifiContext); dialogId = verifyP2pInvitationReceivedDialogLaunchIntent(intent, - TEST_DEVICE_NAME, false, null); + TEST_DEVICE_NAME, false, null, TEST_P2P_TIMEOUT_MS); mDialogManager.replyToP2pInvitationReceivedDialog(dialogId, true, "012345"); dispatchMockWifiThreadRunner(callbackThreadRunner); verify(callback, times(2)).onAccepted("012345"); // Accept without PIN but PIN was requested dialogHandle = mDialogManager.createP2pInvitationReceivedDialog( - TEST_DEVICE_NAME, true, null, Display.DEFAULT_DISPLAY, + TEST_DEVICE_NAME, true, null, TEST_P2P_TIMEOUT_MS, Display.DEFAULT_DISPLAY, callback, callbackThreadRunner); - launchDialogSynchronous(dialogHandle, 0, mWifiThreadRunner); + launchDialogSynchronous(dialogHandle, mWifiThreadRunner); intent = verifyStartActivityAsUser(4, mWifiContext); dialogId = verifyP2pInvitationReceivedDialogLaunchIntent(intent, - TEST_DEVICE_NAME, true, null); + TEST_DEVICE_NAME, true, null, TEST_P2P_TIMEOUT_MS); mDialogManager.replyToP2pInvitationReceivedDialog(dialogId, true, null); dispatchMockWifiThreadRunner(callbackThreadRunner); verify(callback, times(2)).onAccepted(null); // Decline without PIN dialogHandle = mDialogManager.createP2pInvitationReceivedDialog( - TEST_DEVICE_NAME, false, null, Display.DEFAULT_DISPLAY, + TEST_DEVICE_NAME, false, null, TEST_P2P_TIMEOUT_MS, Display.DEFAULT_DISPLAY, callback, callbackThreadRunner); - launchDialogSynchronous(dialogHandle, 0, mWifiThreadRunner); + launchDialogSynchronous(dialogHandle, mWifiThreadRunner); intent = verifyStartActivityAsUser(5, mWifiContext); dialogId = verifyP2pInvitationReceivedDialogLaunchIntent(intent, - TEST_DEVICE_NAME, false, null); + TEST_DEVICE_NAME, false, null, TEST_P2P_TIMEOUT_MS); mDialogManager.replyToP2pInvitationReceivedDialog(dialogId, false, null); dispatchMockWifiThreadRunner(callbackThreadRunner); verify(callback, times(1)).onDeclined(); // Decline with PIN dialogHandle = mDialogManager.createP2pInvitationReceivedDialog( - TEST_DEVICE_NAME, true, null, Display.DEFAULT_DISPLAY, + TEST_DEVICE_NAME, true, null, TEST_P2P_TIMEOUT_MS, Display.DEFAULT_DISPLAY, callback, callbackThreadRunner); - launchDialogSynchronous(dialogHandle, 0, mWifiThreadRunner); + launchDialogSynchronous(dialogHandle, mWifiThreadRunner); intent = verifyStartActivityAsUser(6, mWifiContext); dialogId = verifyP2pInvitationReceivedDialogLaunchIntent(intent, - TEST_DEVICE_NAME, true, null); + TEST_DEVICE_NAME, true, null, TEST_P2P_TIMEOUT_MS); mDialogManager.replyToP2pInvitationReceivedDialog(dialogId, false, "012345"); dispatchMockWifiThreadRunner(callbackThreadRunner); verify(callback, times(2)).onDeclined(); // Decline with PIN but PIN was not requested dialogHandle = mDialogManager.createP2pInvitationReceivedDialog( - TEST_DEVICE_NAME, false, null, Display.DEFAULT_DISPLAY, + TEST_DEVICE_NAME, false, null, TEST_P2P_TIMEOUT_MS, Display.DEFAULT_DISPLAY, callback, callbackThreadRunner); - launchDialogSynchronous(dialogHandle, 0, mWifiThreadRunner); + launchDialogSynchronous(dialogHandle, mWifiThreadRunner); intent = verifyStartActivityAsUser(7, mWifiContext); dialogId = verifyP2pInvitationReceivedDialogLaunchIntent(intent, - TEST_DEVICE_NAME, false, null); + TEST_DEVICE_NAME, false, null, TEST_P2P_TIMEOUT_MS); mDialogManager.replyToP2pInvitationReceivedDialog(dialogId, false, "012345"); dispatchMockWifiThreadRunner(callbackThreadRunner); verify(callback, times(3)).onDeclined(); // Decline without PIN but PIN was requested dialogHandle = mDialogManager.createP2pInvitationReceivedDialog( - TEST_DEVICE_NAME, true, null, Display.DEFAULT_DISPLAY, + TEST_DEVICE_NAME, true, null, TEST_P2P_TIMEOUT_MS, Display.DEFAULT_DISPLAY, callback, callbackThreadRunner); - launchDialogSynchronous(dialogHandle, 0, mWifiThreadRunner); + launchDialogSynchronous(dialogHandle, mWifiThreadRunner); intent = verifyStartActivityAsUser(8, mWifiContext); dialogId = verifyP2pInvitationReceivedDialogLaunchIntent(intent, - TEST_DEVICE_NAME, true, null); + TEST_DEVICE_NAME, true, null, TEST_P2P_TIMEOUT_MS); mDialogManager.replyToP2pInvitationReceivedDialog(dialogId, false, null); dispatchMockWifiThreadRunner(callbackThreadRunner); verify(callback, times(4)).onDeclined(); @@ -985,12 +820,12 @@ public class WifiDialogManagerTest extends WifiBaseTest { // Launch and dismiss dialog. DialogHandle dialogHandle = mDialogManager.createP2pInvitationReceivedDialog( - TEST_DEVICE_NAME, false, null, Display.DEFAULT_DISPLAY, + TEST_DEVICE_NAME, false, null, TEST_P2P_TIMEOUT_MS, Display.DEFAULT_DISPLAY, callback, callbackThreadRunner); - launchDialogSynchronous(dialogHandle, 0, mWifiThreadRunner); + launchDialogSynchronous(dialogHandle, mWifiThreadRunner); Intent intent = verifyStartActivityAsUser(1, mWifiContext); int dialogId = verifyP2pInvitationReceivedDialogLaunchIntent(intent, - TEST_DEVICE_NAME, false, null); + TEST_DEVICE_NAME, false, null, TEST_P2P_TIMEOUT_MS); dismissDialogSynchronous(dialogHandle, mWifiThreadRunner); intent = verifyStartActivityAsUser(2, mWifiContext); verifyDismissIntent(intent); @@ -1007,12 +842,12 @@ public class WifiDialogManagerTest extends WifiBaseTest { // Launch dialog again dialogHandle = mDialogManager.createP2pInvitationReceivedDialog( - TEST_DEVICE_NAME, false, null, Display.DEFAULT_DISPLAY, + TEST_DEVICE_NAME, false, null, TEST_P2P_TIMEOUT_MS, Display.DEFAULT_DISPLAY, callback, callbackThreadRunner); - launchDialogSynchronous(dialogHandle, 0, mWifiThreadRunner); + launchDialogSynchronous(dialogHandle, mWifiThreadRunner); intent = verifyStartActivityAsUser(3, mWifiContext); dialogId = verifyP2pInvitationReceivedDialogLaunchIntent(intent, - TEST_DEVICE_NAME, false, null); + TEST_DEVICE_NAME, false, null, TEST_P2P_TIMEOUT_MS); // Callback should receive replies to the corresponding dialogId now. mDialogManager.replyToP2pInvitationReceivedDialog(dialogId, true, null); @@ -1032,23 +867,23 @@ public class WifiDialogManagerTest extends WifiBaseTest { mock(P2pInvitationReceivedDialogCallback.class); WifiThreadRunner callbackThreadRunner = mock(WifiThreadRunner.class); DialogHandle dialogHandle1 = mDialogManager.createP2pInvitationReceivedDialog( - TEST_DEVICE_NAME, false, null, Display.DEFAULT_DISPLAY, + TEST_DEVICE_NAME, false, null, TEST_P2P_TIMEOUT_MS, Display.DEFAULT_DISPLAY, callback1, callbackThreadRunner); - launchDialogSynchronous(dialogHandle1, 0, mWifiThreadRunner); + launchDialogSynchronous(dialogHandle1, mWifiThreadRunner); Intent intent1 = verifyStartActivityAsUser(1, mWifiContext); int dialogId1 = verifyP2pInvitationReceivedDialogLaunchIntent(intent1, - TEST_DEVICE_NAME, false, null); + TEST_DEVICE_NAME, false, null, TEST_P2P_TIMEOUT_MS); // Launch Dialog2 P2pInvitationReceivedDialogCallback callback2 = mock(P2pInvitationReceivedDialogCallback.class); DialogHandle dialogHandle2 = mDialogManager.createP2pInvitationReceivedDialog( - TEST_DEVICE_NAME, false, null, Display.DEFAULT_DISPLAY, + TEST_DEVICE_NAME, false, null, TEST_P2P_TIMEOUT_MS, Display.DEFAULT_DISPLAY, callback2, callbackThreadRunner); - launchDialogSynchronous(dialogHandle2, 0, mWifiThreadRunner); + launchDialogSynchronous(dialogHandle2, mWifiThreadRunner); Intent intent2 = verifyStartActivityAsUser(2, mWifiContext); int dialogId2 = verifyP2pInvitationReceivedDialogLaunchIntent(intent2, - TEST_DEVICE_NAME, false, null); + TEST_DEVICE_NAME, false, null, TEST_P2P_TIMEOUT_MS); // callback1 notified mDialogManager.replyToP2pInvitationReceivedDialog(dialogId1, true, null); @@ -1103,7 +938,7 @@ public class WifiDialogManagerTest extends WifiBaseTest { // Launch and dismiss dialog. DialogHandle dialogHandle = mDialogManager.createP2pInvitationSentDialog( TEST_DEVICE_NAME, null, Display.DEFAULT_DISPLAY); - launchDialogSynchronous(dialogHandle, 0, mWifiThreadRunner); + launchDialogSynchronous(dialogHandle, mWifiThreadRunner); verifyP2pInvitationSentDialogLaunchIntent(verifyStartActivityAsUser(1, mWifiContext), TEST_DEVICE_NAME, null); dismissDialogSynchronous(dialogHandle, mWifiThreadRunner); diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiGlobalsTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiGlobalsTest.java index 0fefd7ab9e..41ff5ce232 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiGlobalsTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiGlobalsTest.java @@ -337,4 +337,12 @@ public class WifiGlobalsTest extends WifiBaseTest { mWifiGlobals.setD2dStaConcurrencySupported(false); assertTrue(mWifiGlobals.isD2dSupportedWhenInfraStaDisabled()); } + + @Test + public void testIsMLDApSupported() { + assertFalse(mWifiGlobals.isMLDApSupported()); + mWifiResourceCache.reset(); + mResources.setInteger(R.integer.config_wifiSoftApMaxNumberMLDSupported, 1); + assertTrue(mWifiGlobals.isMLDApSupported()); + } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiLockManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiLockManagerTest.java index 6af6d0c7bb..4e5250d387 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiLockManagerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiLockManagerTest.java @@ -19,6 +19,8 @@ package com.android.server.wifi; import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_LOCAL_ONLY; import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_PRIMARY; import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_SECONDARY_TRANSIENT; +import static com.android.server.wifi.TestUtil.createCapabilityBitset; +import static com.android.server.wifi.WifiLockManager.DELAY_LOCK_RELEASE_MS; import static org.hamcrest.CoreMatchers.not; import static org.junit.Assert.assertEquals; @@ -69,6 +71,7 @@ import org.mockito.MockitoAnnotations; import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; +import java.util.BitSet; import java.util.List; import java.util.NoSuchElementException; import java.util.Random; @@ -133,6 +136,7 @@ public class WifiLockManagerTest extends WifiBaseTest { when(mContext.getSystemService(PowerManager.class)).thenReturn(mPowerManager); when(mClientModeManager.getRole()).thenReturn(ROLE_CLIENT_PRIMARY); + when(mClientModeManager.getSupportedFeaturesBitSet()).thenReturn(new BitSet()); when(mActiveModeWarden.getPrimaryClientModeManager()).thenReturn(mClientModeManager); when(mClientModeManager2.getRole()).thenReturn(ROLE_CLIENT_SECONDARY_TRANSIENT); @@ -528,8 +532,8 @@ public class WifiLockManagerTest extends WifiBaseTest { setScreenState(true); when(mActivityManager.getUidImportance(anyInt())).thenReturn( ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND); - when(mClientModeManager.getSupportedFeatures()) - .thenReturn((long) WifiManager.WIFI_FEATURE_LOW_LATENCY); + when(mClientModeManager.getSupportedFeaturesBitSet()) + .thenReturn(createCapabilityBitset(WifiManager.WIFI_FEATURE_LOW_LATENCY)); when(mClientModeManager.setLowLatencyMode(anyBoolean())).thenReturn(true); } @@ -559,8 +563,8 @@ public class WifiLockManagerTest extends WifiBaseTest { setScreenState(true); when(mActivityManager.getUidImportance(anyInt())).thenReturn( ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND); - when(mClientModeManager.getSupportedFeatures()) - .thenReturn((long) WifiManager.WIFI_FEATURE_LOW_LATENCY); + when(mClientModeManager.getSupportedFeaturesBitSet()) + .thenReturn(createCapabilityBitset(WifiManager.WIFI_FEATURE_LOW_LATENCY)); when(mClientModeManager.setLowLatencyMode(anyBoolean())).thenReturn(true); expectedMode = WifiManager.WIFI_MODE_FULL_LOW_LATENCY; } @@ -572,8 +576,25 @@ public class WifiLockManagerTest extends WifiBaseTest { false); releaseWifiLockSuccessful(mBinder); - assertEquals(WifiManager.WIFI_MODE_NO_LOCKS_HELD, - mWifiLockManager.getStrongestLockMode()); + assertEquals(WifiManager.WIFI_MODE_NO_LOCKS_HELD, mWifiLockManager.getStrongestLockMode()); + + mLooper.moveTimeForward(DELAY_LOCK_RELEASE_MS / 2 + 1); + + acquireWifiLockSuccessful(WifiManager.WIFI_MODE_FULL_HIGH_PERF, "", + mBinder, mWorkSource); + assertEquals(expectedMode, mWifiLockManager.getStrongestLockMode()); + releaseWifiLockSuccessful(mBinder); + + mLooper.moveTimeForward(DELAY_LOCK_RELEASE_MS / 2 + 1); + assertEquals(WifiManager.WIFI_MODE_NO_LOCKS_HELD, mWifiLockManager.getStrongestLockMode()); + mLooper.dispatchAll(); + // Verify the first release is not triggered + inOrder.verify(mClientModeManager, never()).setPowerSave( + eq(ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK), anyBoolean()); + + // Verify the last release is triggered + mLooper.moveTimeForward(DELAY_LOCK_RELEASE_MS + 1); + mLooper.dispatchAll(); inOrder.verify(mClientModeManager).setPowerSave(ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK, true); verify(mWifiMetrics).addWifiLockActiveSession(eq(expectedMode), @@ -623,6 +644,8 @@ public class WifiLockManagerTest extends WifiBaseTest { // Release the second lock releaseWifiLockSuccessful(mBinder2); + mLooper.moveTimeForward(DELAY_LOCK_RELEASE_MS + 1); + mLooper.dispatchAll(); verify(mWifiMetrics).addWifiLockActiveSession(eq(WifiManager.WIFI_MODE_FULL_HIGH_PERF), eq(new int[]{DEFAULT_TEST_UID_1}), eq(new String[]{null}), anyLong(), anyBoolean(), anyBoolean(), anyBoolean()); @@ -701,8 +724,8 @@ public class WifiLockManagerTest extends WifiBaseTest { setScreenState(true); when(mActivityManager.getUidImportance(anyInt())).thenReturn( ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND); - when(mClientModeManager.getSupportedFeatures()) - .thenReturn((long) WifiManager.WIFI_FEATURE_LOW_LATENCY); + when(mClientModeManager.getSupportedFeaturesBitSet()) + .thenReturn(createCapabilityBitset(WifiManager.WIFI_FEATURE_LOW_LATENCY)); when(mClientModeManager.setLowLatencyMode(anyBoolean())).thenReturn(true); expectedMode = WifiManager.WIFI_MODE_FULL_LOW_LATENCY; } @@ -746,8 +769,8 @@ public class WifiLockManagerTest extends WifiBaseTest { setScreenState(true); when(mActivityManager.getUidImportance(anyInt())).thenReturn( ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND); - when(mClientModeManager.getSupportedFeatures()) - .thenReturn((long) WifiManager.WIFI_MODE_FULL_LOW_LATENCY); + when(mClientModeManager.getSupportedFeaturesBitSet()) + .thenReturn(createCapabilityBitset(WifiManager.WIFI_MODE_FULL_LOW_LATENCY)); when(mClientModeManager.setLowLatencyMode(anyBoolean())).thenReturn(true); expectedMode = WifiManager.WIFI_MODE_FULL_LOW_LATENCY; } @@ -764,6 +787,8 @@ public class WifiLockManagerTest extends WifiBaseTest { anyBoolean(), anyBoolean(), anyBoolean()); assertEquals(WifiManager.WIFI_MODE_NO_LOCKS_HELD, mWifiLockManager.getStrongestLockMode()); + mLooper.moveTimeForward(DELAY_LOCK_RELEASE_MS + 1); + mLooper.dispatchAll(); inOrder.verify(mClientModeManager).setPowerSave(ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK, true); verify(mWifiMetrics, never()).addWifiLockActiveSession( @@ -1008,8 +1033,8 @@ public class WifiLockManagerTest extends WifiBaseTest { when(mActivityManager.getUidImportance(anyInt())).thenReturn( ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND); when(mClientModeManager.setLowLatencyMode(anyBoolean())).thenReturn(true); - when(mClientModeManager.getSupportedFeatures()) - .thenReturn(WifiManager.WIFI_FEATURE_LOW_LATENCY); + when(mClientModeManager.getSupportedFeaturesBitSet()) + .thenReturn(createCapabilityBitset(WifiManager.WIFI_FEATURE_LOW_LATENCY)); acquireWifiLockSuccessful(WifiManager.WIFI_MODE_FULL_LOW_LATENCY, "", mBinder, mWorkSource); @@ -1028,8 +1053,8 @@ public class WifiLockManagerTest extends WifiBaseTest { setScreenState(true); when(mActivityManager.getUidImportance(anyInt())).thenReturn( ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND); - when(mClientModeManager.getSupportedFeatures()) - .thenReturn((long) WifiManager.WIFI_FEATURE_TX_POWER_LIMIT); + when(mClientModeManager.getSupportedFeaturesBitSet()) + .thenReturn(createCapabilityBitset(WifiManager.WIFI_FEATURE_TX_POWER_LIMIT)); acquireWifiLockSuccessful(WifiManager.WIFI_MODE_FULL_LOW_LATENCY, "", mBinder, mWorkSource); @@ -1048,8 +1073,8 @@ public class WifiLockManagerTest extends WifiBaseTest { setScreenState(true); when(mActivityManager.getUidImportance(anyInt())).thenReturn( ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND); - when(mClientModeManager.getSupportedFeatures()) - .thenReturn((long) WifiManager.WIFI_FEATURE_LOW_LATENCY); + when(mClientModeManager.getSupportedFeaturesBitSet()) + .thenReturn(createCapabilityBitset(WifiManager.WIFI_FEATURE_LOW_LATENCY)); // Make sure setLowLatencyMode() is successful when(mClientModeManager.setLowLatencyMode(anyBoolean())).thenReturn(true); @@ -1067,6 +1092,12 @@ public class WifiLockManagerTest extends WifiBaseTest { releaseLowLatencyWifiLockSuccessful(mBinder); assertEquals(WifiManager.WIFI_MODE_NO_LOCKS_HELD, mWifiLockManager.getStrongestLockMode()); + mLooper.dispatchAll(); + verify(mClientModeManager, never()).setLowLatencyMode(false); + verify(mClientModeManager, never()).setPowerSave(ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK, + true); + mLooper.moveTimeForward(DELAY_LOCK_RELEASE_MS + 1); + mLooper.dispatchAll(); inOrder.verify(mClientModeManager).setLowLatencyMode(false); inOrder.verify(mClientModeManager).setPowerSave(ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK, true); @@ -1087,8 +1118,8 @@ public class WifiLockManagerTest extends WifiBaseTest { setScreenState(true); when(mActivityManager.getUidImportance(anyInt())).thenReturn( ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND); - when(mClientModeManager.getSupportedFeatures()) - .thenReturn((long) WifiManager.WIFI_FEATURE_LOW_LATENCY); + when(mClientModeManager.getSupportedFeaturesBitSet()) + .thenReturn(createCapabilityBitset(WifiManager.WIFI_FEATURE_LOW_LATENCY)); // Fail the call to ClientModeManager when(mClientModeManager.setLowLatencyMode(true)).thenReturn(false); @@ -1117,8 +1148,8 @@ public class WifiLockManagerTest extends WifiBaseTest { setScreenState(true); when(mActivityManager.getUidImportance(anyInt())).thenReturn( ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND); - when(mClientModeManager.getSupportedFeatures()) - .thenReturn((long) WifiManager.WIFI_FEATURE_LOW_LATENCY); + when(mClientModeManager.getSupportedFeaturesBitSet()) + .thenReturn(createCapabilityBitset(WifiManager.WIFI_FEATURE_LOW_LATENCY)); // Succeed to setLowLatencyMode() when(mClientModeManager.setLowLatencyMode(anyBoolean())).thenReturn(true); @@ -1146,8 +1177,8 @@ public class WifiLockManagerTest extends WifiBaseTest { setScreenState(true); when(mActivityManager.getUidImportance(anyInt())).thenReturn( ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND); - when(mClientModeManager.getSupportedFeatures()) - .thenReturn((long) WifiManager.WIFI_FEATURE_LOW_LATENCY); + when(mClientModeManager.getSupportedFeaturesBitSet()) + .thenReturn(createCapabilityBitset(WifiManager.WIFI_FEATURE_LOW_LATENCY)); // Make sure setLowLatencyMode() is successful when(mClientModeManager.setLowLatencyMode(anyBoolean())).thenReturn(true); @@ -1185,8 +1216,8 @@ public class WifiLockManagerTest extends WifiBaseTest { setScreenState(true); when(mActivityManager.getUidImportance(anyInt())).thenReturn( ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND); - when(mClientModeManager.getSupportedFeatures()) - .thenReturn((long) WifiManager.WIFI_FEATURE_LOW_LATENCY); + when(mClientModeManager.getSupportedFeaturesBitSet()) + .thenReturn(createCapabilityBitset(WifiManager.WIFI_FEATURE_LOW_LATENCY)); // Make sure setLowLatencyMode() is successful when(mClientModeManager.setLowLatencyMode(anyBoolean())).thenReturn(true); @@ -1228,8 +1259,8 @@ public class WifiLockManagerTest extends WifiBaseTest { // Initially, set screen on, and app background setScreenState(true); when(mFrameworkFacade.isAppForeground(any(), anyInt())).thenReturn(false); - when(mClientModeManager.getSupportedFeatures()) - .thenReturn((long) WifiManager.WIFI_FEATURE_LOW_LATENCY); + when(mClientModeManager.getSupportedFeaturesBitSet()) + .thenReturn(createCapabilityBitset(WifiManager.WIFI_FEATURE_LOW_LATENCY)); // Make sure setLowLatencyMode() is successful when(mClientModeManager.setLowLatencyMode(anyBoolean())).thenReturn(true); @@ -1271,8 +1302,8 @@ public class WifiLockManagerTest extends WifiBaseTest { // Initially, set screen on, and app background setScreenState(true); when(mFrameworkFacade.isAppForeground(any(), anyInt())).thenReturn(false); - when(mClientModeManager.getSupportedFeatures()) - .thenReturn((long) WifiManager.WIFI_FEATURE_LOW_LATENCY); + when(mClientModeManager.getSupportedFeaturesBitSet()) + .thenReturn(createCapabilityBitset(WifiManager.WIFI_FEATURE_LOW_LATENCY)); mWifiLockManager.updateWifiClientConnected(mClientModeManager, true); // Make sure setLowLatencyMode()/setPowerSave() is successful @@ -1323,8 +1354,8 @@ public class WifiLockManagerTest extends WifiBaseTest { when(mClientModeManager.setLowLatencyMode(anyBoolean())).thenReturn(true); when(mClientModeManager.setPowerSave(eq(ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK), anyBoolean())).thenReturn(true); - when(mClientModeManager.getSupportedFeatures()) - .thenReturn((long) WifiManager.WIFI_FEATURE_LOW_LATENCY); + when(mClientModeManager.getSupportedFeaturesBitSet()) + .thenReturn(createCapabilityBitset(WifiManager.WIFI_FEATURE_LOW_LATENCY)); InOrder inOrder = inOrder(mClientModeManager); @@ -1358,8 +1389,8 @@ public class WifiLockManagerTest extends WifiBaseTest { when(mClientModeManager.setPowerSave(eq(ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK), anyBoolean())).thenReturn(true); setScreenState(false); - when(mClientModeManager.getSupportedFeatures()) - .thenReturn((long) WifiManager.WIFI_FEATURE_LOW_LATENCY); + when(mClientModeManager.getSupportedFeaturesBitSet()) + .thenReturn(createCapabilityBitset(WifiManager.WIFI_FEATURE_LOW_LATENCY)); mWifiLockManager.updateWifiClientConnected(mClientModeManager, true); InOrder inOrder = inOrder(mClientModeManager); @@ -1399,8 +1430,8 @@ public class WifiLockManagerTest extends WifiBaseTest { setScreenState(true); when(mActivityManager.getUidImportance(anyInt())).thenReturn( ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND); - when(mClientModeManager.getSupportedFeatures()) - .thenReturn((long) WifiManager.WIFI_FEATURE_LOW_LATENCY); + when(mClientModeManager.getSupportedFeaturesBitSet()) + .thenReturn(createCapabilityBitset(WifiManager.WIFI_FEATURE_LOW_LATENCY)); InOrder inOrder = inOrder(mClientModeManager); @@ -1445,8 +1476,8 @@ public class WifiLockManagerTest extends WifiBaseTest { when(mClientModeManager.setLowLatencyMode(anyBoolean())).thenReturn(true); when(mClientModeManager.setPowerSave(eq(ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK), anyBoolean())).thenReturn(true); - when(mClientModeManager.getSupportedFeatures()) - .thenReturn((long) WifiManager.WIFI_FEATURE_LOW_LATENCY); + when(mClientModeManager.getSupportedFeaturesBitSet()) + .thenReturn(createCapabilityBitset(WifiManager.WIFI_FEATURE_LOW_LATENCY)); mWifiLockManager.updateWifiClientConnected(mClientModeManager, true); InOrder inOrder = inOrder(mClientModeManager); @@ -1474,8 +1505,8 @@ public class WifiLockManagerTest extends WifiBaseTest { when(mClientModeManager.setLowLatencyMode(anyBoolean())).thenReturn(true); when(mClientModeManager.setPowerSave(eq(ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK), anyBoolean())).thenReturn(true); - when(mClientModeManager.getSupportedFeatures()) - .thenReturn((long) WifiManager.WIFI_FEATURE_LOW_LATENCY); + when(mClientModeManager.getSupportedFeaturesBitSet()) + .thenReturn(createCapabilityBitset(WifiManager.WIFI_FEATURE_LOW_LATENCY)); mWifiLockManager.updateWifiClientConnected(mClientModeManager, true); InOrder inOrder = inOrder(mClientModeManager); @@ -1507,8 +1538,8 @@ public class WifiLockManagerTest extends WifiBaseTest { when(mClientModeManager.setLowLatencyMode(anyBoolean())).thenReturn(true); when(mClientModeManager.setPowerSave(eq(ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK), anyBoolean())).thenReturn(true); - when(mClientModeManager.getSupportedFeatures()) - .thenReturn((long) WifiManager.WIFI_FEATURE_LOW_LATENCY); + when(mClientModeManager.getSupportedFeaturesBitSet()) + .thenReturn(createCapabilityBitset(WifiManager.WIFI_FEATURE_LOW_LATENCY)); mWifiLockManager.updateWifiClientConnected(mClientModeManager, true); InOrder inOrder = inOrder(mClientModeManager); @@ -1540,8 +1571,8 @@ public class WifiLockManagerTest extends WifiBaseTest { public void testForceLowLatencyFailure() throws Exception { int expectedMode = WifiManager.WIFI_MODE_FULL_HIGH_PERF; when(mClientModeManager.setLowLatencyMode(anyBoolean())).thenReturn(false); - when(mClientModeManager.getSupportedFeatures()) - .thenReturn((long) WifiManager.WIFI_FEATURE_LOW_LATENCY); + when(mClientModeManager.getSupportedFeaturesBitSet()) + .thenReturn(createCapabilityBitset(WifiManager.WIFI_FEATURE_LOW_LATENCY)); InOrder inOrder = inOrder(mClientModeManager); @@ -1698,8 +1729,8 @@ public class WifiLockManagerTest extends WifiBaseTest { setScreenState(true); when(mActivityManager.getUidImportance(anyInt())).thenReturn( ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND); - when(mClientModeManager.getSupportedFeatures()) - .thenReturn((long) WifiManager.WIFI_FEATURE_LOW_LATENCY); + when(mClientModeManager.getSupportedFeaturesBitSet()) + .thenReturn(createCapabilityBitset(WifiManager.WIFI_FEATURE_LOW_LATENCY)); when(mClientModeManager.setLowLatencyMode(anyBoolean())).thenReturn(true); expectedMode = WifiManager.WIFI_MODE_FULL_LOW_LATENCY; } @@ -1751,8 +1782,8 @@ public class WifiLockManagerTest extends WifiBaseTest { setScreenState(true); when(mActivityManager.getUidImportance(anyInt())).thenReturn( ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND); - when(mClientModeManager.getSupportedFeatures()) - .thenReturn((long) WifiManager.WIFI_FEATURE_LOW_LATENCY); + when(mClientModeManager.getSupportedFeaturesBitSet()) + .thenReturn(createCapabilityBitset(WifiManager.WIFI_FEATURE_LOW_LATENCY)); InOrder inOrder = inOrder(mWifiMetrics); @@ -1898,8 +1929,8 @@ public class WifiLockManagerTest extends WifiBaseTest { when(mClientModeManager.setLowLatencyMode(anyBoolean())).thenReturn(true); when(mClientModeManager.setPowerSave(eq(ClientMode.POWER_SAVE_CLIENT_WIFI_LOCK), anyBoolean())).thenReturn(false); - when(mClientModeManager.getSupportedFeatures()) - .thenReturn((long) WifiManager.WIFI_FEATURE_LOW_LATENCY); + when(mClientModeManager.getSupportedFeaturesBitSet()) + .thenReturn(createCapabilityBitset(WifiManager.WIFI_FEATURE_LOW_LATENCY)); InOrder inOrder = inOrder(mClientModeManager); @@ -1945,12 +1976,13 @@ public class WifiLockManagerTest extends WifiBaseTest { when(mActivityManager.getUidImportance(DEFAULT_TEST_UID_1)).thenReturn( ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND); when(mClientModeManager.setLowLatencyMode(anyBoolean())).thenReturn(true); - when(mClientModeManager.getSupportedFeatures()).thenReturn( - WifiManager.WIFI_FEATURE_LOW_LATENCY); + when(mClientModeManager.getSupportedFeaturesBitSet()) + .thenReturn(createCapabilityBitset(WifiManager.WIFI_FEATURE_LOW_LATENCY)); acquireWifiLockSuccessful(WifiManager.WIFI_MODE_FULL_LOW_LATENCY, "", mBinder, mWorkSource); mWifiLockManager.updateWifiClientConnected(mClientModeManager, true); inOrder.verify(testListener).onOwnershipChanged(eq(new int[]{DEFAULT_TEST_UID_1})); inOrder.verify(testListener).onActivatedStateChanged(true); + verify(mWifiMetrics).setLowLatencyState(eq(true)); inOrder.verify(testListener).onActiveUsersChanged(eq(new int[]{DEFAULT_TEST_UID_1})); // Acquire a second lock and check the owners & active users changed. @@ -2032,8 +2064,13 @@ public class WifiLockManagerTest extends WifiBaseTest { when(mActivityManager.getUidImportance(DEFAULT_TEST_UID_1)).thenReturn( ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND); when(mClientModeManager.setLowLatencyMode(anyBoolean())).thenReturn(true); - when(mClientModeManager.getSupportedFeatures()).thenReturn( - ~WifiManager.WIFI_FEATURE_LOW_LATENCY); + + // Disable low latency, but support other arbitrary features + BitSet supportedFeatures = new BitSet(); + supportedFeatures.set(WifiManager.WIFI_FEATURE_LOW_LATENCY, false); + supportedFeatures.set(WifiManager.WIFI_FEATURE_DPP, true); + when(mClientModeManager.getSupportedFeaturesBitSet()).thenReturn(supportedFeatures); + acquireWifiLockSuccessful(WifiManager.WIFI_MODE_FULL_LOW_LATENCY, "", mBinder, mWorkSource); mWifiLockManager.updateWifiClientConnected(mClientModeManager, true); inOrder.verify(testListener).onOwnershipChanged(eq(new int[]{DEFAULT_TEST_UID_1})); @@ -2057,8 +2094,8 @@ public class WifiLockManagerTest extends WifiBaseTest { when(mActivityManager.getUidImportance(DEFAULT_TEST_UID_1)).thenReturn( ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND); when(mClientModeManager.setLowLatencyMode(anyBoolean())).thenReturn(true); - when(mClientModeManager.getSupportedFeatures()).thenReturn( - WifiManager.WIFI_FEATURE_LOW_LATENCY); + when(mClientModeManager.getSupportedFeaturesBitSet()) + .thenReturn(createCapabilityBitset(WifiManager.WIFI_FEATURE_LOW_LATENCY)); mWifiLockManager.updateWifiClientConnected(mClientModeManager, true); // Acquire the lock should report @@ -2103,8 +2140,8 @@ public class WifiLockManagerTest extends WifiBaseTest { when(mActivityManager.getUidImportance(DEFAULT_TEST_UID_1)).thenReturn( ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND); when(mClientModeManager.setLowLatencyMode(anyBoolean())).thenReturn(true); - when(mClientModeManager.getSupportedFeatures()).thenReturn( - WifiManager.WIFI_FEATURE_LOW_LATENCY); + when(mClientModeManager.getSupportedFeaturesBitSet()) + .thenReturn(createCapabilityBitset(WifiManager.WIFI_FEATURE_LOW_LATENCY)); mWifiLockManager.updateWifiClientConnected(mClientModeManager, true); // Acquire the lock should report @@ -2151,8 +2188,8 @@ public class WifiLockManagerTest extends WifiBaseTest { when(mActivityManager.getUidImportance(DEFAULT_TEST_UID_1)).thenReturn( ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND); when(mClientModeManager.setLowLatencyMode(anyBoolean())).thenReturn(true); - when(mClientModeManager.getSupportedFeatures()).thenReturn( - WifiManager.WIFI_FEATURE_LOW_LATENCY); + when(mClientModeManager.getSupportedFeaturesBitSet()) + .thenReturn(createCapabilityBitset(WifiManager.WIFI_FEATURE_LOW_LATENCY)); mWifiLockManager.updateWifiClientConnected(mClientModeManager, true); // Acquire --> reportFullWifiLockAcquiredFromSource diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java index 8035444dbc..8cd6ef014e 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java @@ -86,7 +86,6 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import static org.mockito.Mockito.withSettings; import static java.lang.StrictMath.toIntExact; @@ -164,11 +163,10 @@ import com.android.server.wifi.proto.nano.WifiMetricsProto.SoftApConnectedClient import com.android.server.wifi.proto.nano.WifiMetricsProto.StaEvent; import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiIsUnusableEvent; import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiRadioUsage; -import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiUsabilityStats; import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiUsabilityStatsEntry; +import com.android.server.wifi.proto.nano.WifiMetricsProto.WifiUsabilityStatsTraining; import com.android.server.wifi.rtt.RttMetrics; import com.android.server.wifi.util.InformationElementUtil; -import com.android.wifi.flags.Flags; import com.android.wifi.resources.R; import org.junit.After; @@ -186,6 +184,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.io.StringWriter; import java.time.Duration; +import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; @@ -219,7 +218,6 @@ public class WifiMetricsTest extends WifiBaseTest { private static final int TEST_CHANNEL = 36; private static final int POLLING_INTERVAL_DEFAULT = 3000; private static final int POLLING_INTERVAL_NOT_DEFAULT = 6000; - private MockitoSession mSession; @Mock Context mContext; MockResources mResources; @@ -251,6 +249,7 @@ public class WifiMetricsTest extends WifiBaseTest { @Mock Network mNetwork; @Mock WifiInfo mWifiInfo; @Mock WifiNative.ConnectionCapabilities mCapabilities; + @Mock WifiGlobals mWifiGlobals; @Captor ArgumentCaptor<ActiveModeWarden.ModeChangeCallback> mModeChangeCallbackArgumentCaptor; @Captor ArgumentCaptor<Handler> mHandlerCaptor; @Captor @@ -284,7 +283,8 @@ public class WifiMetricsTest extends WifiBaseTest { mWifiP2pMetrics, mDppMetrics, mWifiMonitor, - mWifiDeviceStateChangeManager); + mWifiDeviceStateChangeManager, + mWifiGlobals); mWifiMetrics.setWifiConfigManager(mWcm); mWifiMetrics.setWifiBlocklistMonitor(mWifiBlocklistMonitor); mWifiMetrics.setPasspointManager(mPpm); @@ -319,7 +319,6 @@ public class WifiMetricsTest extends WifiBaseTest { mSession = ExtendedMockito.mockitoSession() .strictness(Strictness.LENIENT) .mockStatic(WifiStatsLog.class) - .mockStatic(Flags.class, withSettings().lenient()) .startMocking(); when(mWifiInfo.getLinkSpeed()).thenReturn(10); @@ -327,7 +326,6 @@ public class WifiMetricsTest extends WifiBaseTest { when(mWifiInfo.getFrequency()).thenReturn(5850); when(mWifiInfo.getBSSID()).thenReturn("5G_WiFi"); when(mWifiInfo.getRssi()).thenReturn(-55); - } @After @@ -2553,7 +2551,7 @@ public class WifiMetricsTest extends WifiBaseTest { eq(true), eq(false), eq(1), eq(TEST_CONNECTION_FAILURE_STATUS_CODE), anyInt(), anyInt(), anyInt(), - anyInt(), anyInt(), eq(TEST_UID))); + anyInt(), anyInt(), eq(TEST_UID), eq(TEST_CANDIDATE_FREQ))); } /** @@ -4234,11 +4232,11 @@ public class WifiMetricsTest extends WifiBaseTest { } private void assertUsabilityStatsAssignment(WifiInfo info, WifiLinkLayerStats stats, - WifiUsabilityStatsEntry usabilityStats) { + WifiUsabilityStatsEntry usabilityStats, int expectedTimestampMs) { assertEquals(info.getRssi(), usabilityStats.rssi); assertEquals(info.getLinkSpeed(), usabilityStats.linkSpeedMbps); assertEquals(info.getRxLinkSpeedMbps(), usabilityStats.rxLinkSpeedMbps); - assertEquals(stats.timeStampInMs, usabilityStats.timeStampMs); + assertEquals(expectedTimestampMs, usabilityStats.timeStampMs); assertEquals(stats.txmpdu_be + stats.txmpdu_bk + stats.txmpdu_vi + stats.txmpdu_vo, usabilityStats.totalTxSuccess); assertEquals(stats.retries_be + stats.retries_bk + stats.retries_vi + stats.retries_vo, @@ -4325,39 +4323,204 @@ public class WifiMetricsTest extends WifiBaseTest { } } - // Simulate adding a LABEL_GOOD WifiUsabilityStats - private WifiLinkLayerStats addGoodWifiUsabilityStats(WifiLinkLayerStats start) { - WifiInfo info = mock(WifiInfo.class); - when(info.getRssi()).thenReturn(nextRandInt()); - when(info.getLinkSpeed()).thenReturn(nextRandInt()); - WifiLinkLayerStats stats = start; - for (int i = 0; i < WifiMetrics.NUM_WIFI_USABILITY_STATS_ENTRIES_PER_WIFI_GOOD; i++) { - mWifiMetrics.updateWifiUsabilityStatsEntries(TEST_IFACE_NAME, info, stats); - stats = nextRandomStats(stats); + /** + * When ring buffer is empty, verify that full-capture will capture empty results + */ + @Test + public void testStoreCapturedDataEmptyRingbufferFullCapture() throws Exception { + Instant testCurrentInstant = + Instant.parse("2024-01-01T00:00:00Z").plus(Duration.ofSeconds(258)); + when(mClock.getCurrentInstant()).thenReturn(testCurrentInstant); + when(mClock.getElapsedSinceBootMillis()).thenReturn((long) 10 + * mWifiMetrics.MILLIS_IN_A_SECOND); + assertEquals(0, mWifiMetrics.mWifiUsabilityStatsEntriesRingBuffer.size()); + mWifiMetrics.storeCapturedData(123, true, 2 * mWifiMetrics.MILLIS_IN_A_SECOND, + 8 * mWifiMetrics.MILLIS_IN_A_SECOND); + dumpProtoAndDeserialize(); + assertEquals(1, mDecodedProto.wifiUsabilityStatsTraining.length); + assertEquals(123, mDecodedProto.wifiUsabilityStatsTraining[0].dataCaptureType); + assertEquals(1704067200, + mDecodedProto.wifiUsabilityStatsTraining[0].captureStartTimestampSecs); + assertEquals(0, mDecodedProto.wifiUsabilityStatsTraining[0].trainingData.stats.length); + assertEquals(0, mDecodedProto.wifiUsabilityStatsTraining[0].storeTimeOffsetMs); + } + + /** + * When ring buffer is empty, verify that non full-capture will capture empty results + */ + @Test + public void testStoreCapturedDataEmptyRingbufferNonFullCapture() throws Exception { + Instant testCurrentInstant = + Instant.parse("2024-01-01T00:00:00Z").plus(Duration.ofSeconds(258)); + when(mClock.getCurrentInstant()).thenReturn(testCurrentInstant); + when(mClock.getElapsedSinceBootMillis()).thenReturn((long) 258 + * mWifiMetrics.MILLIS_IN_A_SECOND); + assertEquals(0, mWifiMetrics.mWifiUsabilityStatsEntriesRingBuffer.size()); + mWifiMetrics.storeCapturedData(123, false, 2 * mWifiMetrics.MILLIS_IN_A_SECOND, + 8 * mWifiMetrics.MILLIS_IN_A_SECOND); + dumpProtoAndDeserialize(); + assertEquals(1, mDecodedProto.wifiUsabilityStatsTraining.length); + assertEquals(123, mDecodedProto.wifiUsabilityStatsTraining[0].dataCaptureType); + assertEquals(1704067200, + mDecodedProto.wifiUsabilityStatsTraining[0].captureStartTimestampSecs); + assertEquals(0, mDecodedProto.wifiUsabilityStatsTraining[0].trainingData.stats.length); + // 258 (current time) - 8 (triggerStopTimeMillis) = 250 + assertEquals(250 * mWifiMetrics.MILLIS_IN_A_SECOND, + mDecodedProto.wifiUsabilityStatsTraining[0].storeTimeOffsetMs); + } + + private void ringBufferSetupForTestStoreCapturedData() { + // Starting from 20s, add a WifiUsabilityStatsEntry into ring buffer every 3s, + // the last timestamp is 20 + 3 * (80-1) = 257s + for (int i = 0; i < mWifiMetrics.MAX_WIFI_USABILITY_STATS_ENTRIES_RING_BUFFER_SIZE; ++i) { + WifiUsabilityStatsEntry entry = new WifiUsabilityStatsEntry(); + entry.timeStampMs = (20 + i * 3) * mWifiMetrics.MILLIS_IN_A_SECOND; + mWifiMetrics.mWifiUsabilityStatsEntriesRingBuffer.add(entry); + } + assertEquals(80, mWifiMetrics.mWifiUsabilityStatsEntriesRingBuffer.size()); + assertEquals(0, mWifiMetrics.mWifiUsabilityStatsTrainingExamples.size()); + // Set current time since boot to 258s + when(mClock.getElapsedSinceBootMillis()).thenReturn((long) 258 + * mWifiMetrics.MILLIS_IN_A_SECOND); + // Assume device boot up time is 2024-01-01, 00:00:00 UTC, unix timestamp in seconds + // is 1704067200 + Instant testCurrentInstant = + Instant.parse("2024-01-01T00:00:00Z").plus(Duration.ofSeconds(258)); + when(mClock.getCurrentInstant()).thenReturn(testCurrentInstant); + } + + /** + * In non full-capture, verify: + * triggerStartTimeMillis has to be positive + */ + @Test + public void testStoreCapturedDataNonFullCaptureStartTimePositive() throws Exception { + ringBufferSetupForTestStoreCapturedData(); + mWifiMetrics.storeCapturedData(1, false, -1 * mWifiMetrics.MILLIS_IN_A_SECOND, + 20 * mWifiMetrics.MILLIS_IN_A_SECOND); + dumpProtoAndDeserialize(); + assertEquals(0, mDecodedProto.wifiUsabilityStatsTraining.length); + } + + /** + * In non full-capture, verify: + * triggerStopTimeMillis has to be positive + */ + @Test + public void testStoreCapturedDataNonFullCaptureStopTimePositive() throws Exception { + ringBufferSetupForTestStoreCapturedData(); + mWifiMetrics.storeCapturedData(1, false, 30 * mWifiMetrics.MILLIS_IN_A_SECOND, + -1 * mWifiMetrics.MILLIS_IN_A_SECOND); + dumpProtoAndDeserialize(); + assertEquals(0, mDecodedProto.wifiUsabilityStatsTraining.length); + } + + /** + * In non full-capture, verify: + * triggerStartTimeMillis must be smaller than triggerStopTimeMillis + */ + @Test + public void testStoreCapturedDataNonFullCaptureStartTimeEalierThanStopTime() throws Exception { + ringBufferSetupForTestStoreCapturedData(); + mWifiMetrics.storeCapturedData(1, false, 30 * mWifiMetrics.MILLIS_IN_A_SECOND, + 20 * mWifiMetrics.MILLIS_IN_A_SECOND); + dumpProtoAndDeserialize(); + assertEquals(0, mDecodedProto.wifiUsabilityStatsTraining.length); + } + + /** + * In non full-capture, verify results + */ + @Test + public void testStoreCapturedDataNonFullCapture() throws Exception { + ringBufferSetupForTestStoreCapturedData(); + // Do a successful capture in [30s, 150s], and verify each field + mWifiMetrics.storeCapturedData(1, false, 30 * mWifiMetrics.MILLIS_IN_A_SECOND, + 150 * mWifiMetrics.MILLIS_IN_A_SECOND); + dumpProtoAndDeserialize(); + assertEquals(1, mDecodedProto.wifiUsabilityStatsTraining.length); + WifiUsabilityStatsTraining result = mDecodedProto.wifiUsabilityStatsTraining[0]; + assertEquals(1, result.dataCaptureType); + assertEquals(1704067200, result.captureStartTimestampSecs); + // 258 (current time) - 150 (triggerStopTimeMillis) = 108 + assertEquals(108 * mWifiMetrics.MILLIS_IN_A_SECOND, result.storeTimeOffsetMs); + // Capture period is 150 - 30 = 120s, 120 / 3 = 40 WifiUsabilityStatsEntries + assertEquals(40, result.trainingData.stats.length); + for (int i = 0; i < 40; ++i) { + WifiUsabilityStatsEntry resultEntry = result.trainingData.stats[i]; + assertEquals(0, resultEntry.timeStampMs); + // The timestamp of WifiUsabilityStatsEntries who are in captured result are: + // 32, 35, ... 149 + assertEquals((2 + 3 * i) * mWifiMetrics.MILLIS_IN_A_SECOND, + resultEntry.timestampOffsetMs); } - return stats; } - // Simulate adding a LABEL_BAD WifiUsabilityStats - private WifiLinkLayerStats addBadWifiUsabilityStats(WifiLinkLayerStats start) { - WifiInfo info = mock(WifiInfo.class); - when(info.getRssi()).thenReturn(nextRandInt()); - when(info.getLinkSpeed()).thenReturn(nextRandInt()); - WifiLinkLayerStats stats1 = start; - WifiLinkLayerStats stats2 = nextRandomStats(stats1); - mWifiMetrics.updateWifiUsabilityStatsEntries(TEST_IFACE_NAME, info, stats1); - mWifiMetrics.updateWifiUsabilityStatsEntries(TEST_IFACE_NAME, info, stats2); - mWifiMetrics.addToWifiUsabilityStatsList(TEST_IFACE_NAME, WifiUsabilityStats.LABEL_BAD, - WifiUsabilityStats.TYPE_DATA_STALL_BAD_TX, -1); - return nextRandomStats(stats2); + /** + * In full-capture, verify results + */ + @Test + public void testStoreCapturedDataFullCapture() throws Exception { + ringBufferSetupForTestStoreCapturedData(); + // Do a successful full-capture, and verify each field + mWifiMetrics.storeCapturedData(2, true, 30 * mWifiMetrics.MILLIS_IN_A_SECOND, + 150 * mWifiMetrics.MILLIS_IN_A_SECOND); + dumpProtoAndDeserialize(); + assertEquals(1, mDecodedProto.wifiUsabilityStatsTraining.length); + WifiUsabilityStatsTraining result = mDecodedProto.wifiUsabilityStatsTraining[0]; + assertEquals(2, result.dataCaptureType); + assertEquals(1704067200, result.captureStartTimestampSecs); + // 258 (current time) - 257 (triggerStopTimeMillis) = 1 + assertEquals(1 * mWifiMetrics.MILLIS_IN_A_SECOND, result.storeTimeOffsetMs); + // Capture period is 257 - 20 = 237s, (237 / 3) + 1 = 80 WifiUsabilityStatsEntries + assertEquals(mWifiMetrics.MAX_WIFI_USABILITY_STATS_ENTRIES_RING_BUFFER_SIZE, + result.trainingData.stats.length); + for (int i = 0; i < mWifiMetrics.MAX_WIFI_USABILITY_STATS_ENTRIES_RING_BUFFER_SIZE; ++i) { + WifiUsabilityStatsEntry resultEntry = result.trainingData.stats[i]; + assertEquals(0, resultEntry.timeStampMs); + // The timestamps of WifiUsabilityStatsEntries who are in captured result are: + // 20, 23, ... 257, offsets are 0, 3, ... 237 + assertEquals((3 * i) * mWifiMetrics.MILLIS_IN_A_SECOND, + resultEntry.timestampOffsetMs); + } + } + + /** + * Verify wifiUsabilityStatsTraining size limit + */ + @Test + public void testwifiUsabilityStatsTrainingSize() throws Exception { + ringBufferSetupForTestStoreCapturedData(); + // Do MAX_WIFI_USABILITY_STATS_TRAINING_SIZE times successful data capture + for (int i = 0; i < WifiMetrics.MAX_WIFI_USABILITY_STATS_TRAINING_SIZE; ++i) { + mWifiMetrics.storeCapturedData(2, false, (30 + i * 3) * mWifiMetrics.MILLIS_IN_A_SECOND, + (150 + i * 3) * mWifiMetrics.MILLIS_IN_A_SECOND); + } + assertEquals(WifiMetrics.MAX_WIFI_USABILITY_STATS_TRAINING_SIZE, + mWifiMetrics.mWifiUsabilityStatsTrainingExamples.size()); + // 1st capture period is [30s, 150s), current time is 258s, storeTimeOffsetMs is 108s + assertEquals(108 * mWifiMetrics.MILLIS_IN_A_SECOND, + mWifiMetrics.mWifiUsabilityStatsTrainingExamples.get(0).storeTimeOffsetMs); + + // Do another successful data capture, the size should not grow + mWifiMetrics.storeCapturedData(2, false, + (30 + WifiMetrics.MAX_WIFI_USABILITY_STATS_TRAINING_SIZE) + * mWifiMetrics.MILLIS_IN_A_SECOND, + (150 + WifiMetrics.MAX_WIFI_USABILITY_STATS_TRAINING_SIZE) + * mWifiMetrics.MILLIS_IN_A_SECOND); + assertEquals(WifiMetrics.MAX_WIFI_USABILITY_STATS_TRAINING_SIZE, + mWifiMetrics.mWifiUsabilityStatsTrainingExamples.size()); + // 1st capture period is [33s, 153s), current time is 258s, storeTimeOffsetMs is 105s + assertEquals(105 * mWifiMetrics.MILLIS_IN_A_SECOND, + mWifiMetrics.mWifiUsabilityStatsTrainingExamples.get(0).storeTimeOffsetMs); + dumpProtoAndDeserialize(); + assertEquals(10, mDecodedProto.wifiUsabilityStatsTraining.length); } /** * Verify that updateWifiUsabilityStatsEntries correctly converts the inputs into * a WifiUsabilityStatsEntry Object and then stores it. * - * Verify that the converted metrics proto contains pairs of WifiUsabilityStats with - * LABEL_GOOD and LABEL_BAD * @throws Exception */ @Test @@ -4379,64 +4542,43 @@ public class WifiMetricsTest extends WifiBaseTest { mWifiMetrics.incrementWifiUsabilityScoreCount(TEST_IFACE_NAME, 2, 55, 15); mWifiMetrics.logLinkProbeSuccess( TEST_IFACE_NAME, nextRandInt(), nextRandInt(), nextRandInt(), 12); - mWifiMetrics.updateWifiUsabilityStatsEntries(TEST_IFACE_NAME, info, stats1); + // This is used as the timestamp when the record lands in the ring buffer. + when(mClock.getElapsedSinceBootMillis()).thenReturn((long) 618); + mWifiMetrics.updateWifiUsabilityStatsEntries(TEST_IFACE_NAME, info, stats1, false, 0); mWifiMetrics.incrementWifiScoreCount(TEST_IFACE_NAME, 58); mWifiMetrics.incrementWifiUsabilityScoreCount(TEST_IFACE_NAME, 3, 56, 15); mWifiMetrics.logLinkProbeFailure(TEST_IFACE_NAME, nextRandInt(), nextRandInt(), nextRandInt(), nextRandInt()); mWifiMetrics.enterDeviceMobilityState(DEVICE_MOBILITY_STATE_HIGH_MVMT); - mWifiMetrics.updateWifiUsabilityStatsEntries(TEST_IFACE_NAME, info, stats2); + // This is used as the timestamp when the record lands in the ring buffer. + when(mClock.getElapsedSinceBootMillis()).thenReturn((long) 1791); + mWifiMetrics.updateWifiUsabilityStatsEntries(TEST_IFACE_NAME, info, stats2, false, 0); assertEquals(stats2.beacon_rx, mWifiMetrics.getTotalBeaconRxCount()); - mWifiMetrics.addToWifiUsabilityStatsList(TEST_IFACE_NAME, WifiUsabilityStats.LABEL_BAD, - WifiUsabilityStats.TYPE_DATA_STALL_BAD_TX, -1); - - // Add 2 LABEL_GOOD but only 1 should remain in the converted proto - WifiLinkLayerStats statsGood = addGoodWifiUsabilityStats(nextRandomStats(stats2)); - statsGood.timeStampInMs += WifiMetrics.MIN_WIFI_GOOD_USABILITY_STATS_PERIOD_MS; - addGoodWifiUsabilityStats(statsGood); - dumpProtoAndDeserialize(); - assertEquals(2, mDecodedProto.wifiUsabilityStatsList.length); - assertEquals(WifiUsabilityStats.LABEL_GOOD, mDecodedProto.wifiUsabilityStatsList[0].label); - assertEquals(WifiUsabilityStats.LABEL_BAD, mDecodedProto.wifiUsabilityStatsList[1].label); - assertUsabilityStatsAssignment(info, stats1, - mDecodedProto.wifiUsabilityStatsList[1].stats[0]); - assertUsabilityStatsAssignment(info, stats2, - mDecodedProto.wifiUsabilityStatsList[1].stats[1]); - - assertEquals(2, mDecodedProto.wifiUsabilityStatsList[1].stats[0].seqNumToFramework); - assertEquals(3, mDecodedProto.wifiUsabilityStatsList[1].stats[1].seqNumToFramework); - assertEquals(0, mDecodedProto.wifiUsabilityStatsList[1].stats[0].seqNumInsideFramework); - assertEquals(1, mDecodedProto.wifiUsabilityStatsList[1].stats[1].seqNumInsideFramework); - assertEquals(60, mDecodedProto.wifiUsabilityStatsList[1].stats[0].wifiScore); - assertEquals(58, mDecodedProto.wifiUsabilityStatsList[1].stats[1].wifiScore); - assertEquals(55, mDecodedProto.wifiUsabilityStatsList[1].stats[0].wifiUsabilityScore); - assertEquals(56, mDecodedProto.wifiUsabilityStatsList[1].stats[1].wifiUsabilityScore); - assertEquals(15, mDecodedProto.wifiUsabilityStatsList[1].stats[0].predictionHorizonSec); - assertEquals(true, mDecodedProto.wifiUsabilityStatsList[1].stats[0].isSameBssidAndFreq); + assertEquals(2, mWifiMetrics.mWifiUsabilityStatsEntriesRingBuffer.size()); + WifiUsabilityStatsEntry result1 = mWifiMetrics.mWifiUsabilityStatsEntriesRingBuffer.get(0); + WifiUsabilityStatsEntry result2 = mWifiMetrics.mWifiUsabilityStatsEntriesRingBuffer.get(1); + + assertUsabilityStatsAssignment(info, stats1, result1, 618); + assertUsabilityStatsAssignment(info, stats2, result2, 1791); + assertEquals(2, result1.seqNumToFramework); + assertEquals(3, result2.seqNumToFramework); + assertEquals(0, result1.seqNumInsideFramework); + assertEquals(1, result2.seqNumInsideFramework); + assertEquals(60, result1.wifiScore); + assertEquals(58, result2.wifiScore); + assertEquals(55, result1.wifiUsabilityScore); + assertEquals(56, result2.wifiUsabilityScore); + assertEquals(15, result1.predictionHorizonSec); + assertEquals(true, result1.isSameBssidAndFreq); assertEquals(android.net.wifi.WifiUsabilityStatsEntry.PROBE_STATUS_SUCCESS, - mDecodedProto.wifiUsabilityStatsList[1].stats[0].probeStatusSinceLastUpdate); + result1.probeStatusSinceLastUpdate); assertEquals(android.net.wifi.WifiUsabilityStatsEntry.PROBE_STATUS_FAILURE, - mDecodedProto.wifiUsabilityStatsList[1].stats[1].probeStatusSinceLastUpdate); - assertEquals(android.net.wifi.WifiUsabilityStatsEntry.PROBE_STATUS_NO_PROBE, - mDecodedProto.wifiUsabilityStatsList[0].stats[0].probeStatusSinceLastUpdate); - assertEquals(12, - mDecodedProto.wifiUsabilityStatsList[1].stats[0].probeElapsedTimeSinceLastUpdateMs); - assertEquals(Integer.MAX_VALUE, mDecodedProto.wifiUsabilityStatsList[1] - .stats[1].probeElapsedTimeSinceLastUpdateMs); - assertEquals(-1, mDecodedProto.wifiUsabilityStatsList[0] - .stats[0].probeElapsedTimeSinceLastUpdateMs); - assertEquals(DEVICE_MOBILITY_STATE_HIGH_MVMT, mDecodedProto.wifiUsabilityStatsList[1] - .stats[mDecodedProto.wifiUsabilityStatsList[1].stats.length - 1] - .deviceMobilityState); - assertEquals(true, mDecodedProto.wifiUsabilityStatsList[0].stats[0].isWifiScoringEnabled); - assertEquals(true, - mDecodedProto.wifiUsabilityStatsList[1].stats[0].isCellularDataAvailable); - assertEquals(false, - mDecodedProto.wifiUsabilityStatsList[1].stats[1].isThroughputSufficient); - assertEquals(150, - mDecodedProto.wifiUsabilityStatsList[0].stats[0].channelUtilizationRatio); + result2.probeStatusSinceLastUpdate); + assertEquals(12, result1.probeElapsedTimeSinceLastUpdateMs); + assertEquals(true, result1.isCellularDataAvailable); + assertEquals(false, result2.isThroughputSufficient); } private WifiLinkLayerStats createNewWifiLinkLayerStats() { @@ -4474,148 +4616,74 @@ public class WifiMetricsTest extends WifiBaseTest { } /** - * Verify that we discard a WifiUsabilityStats with LABEL_GOOD if there is no corresponding - * LABEL_BAD - * @throws Exception - */ - @Test - public void testWifiUsabilityStatsIgnoreSingleLabelGood() throws Exception { - addGoodWifiUsabilityStats(new WifiLinkLayerStats()); - dumpProtoAndDeserialize(); - assertEquals(0, mDecodedProto.wifiUsabilityStatsList.length); - } - - /** - * Verify that we discard a WifiUsabilityStats with LABEL_BAD if there is no corresponding - * LABEL_GOOD - * @throws Exception - */ - @Test - public void testWifiUsabilityStatsIgnoreSingleLabelBad() throws Exception { - addBadWifiUsabilityStats(new WifiLinkLayerStats()); - dumpProtoAndDeserialize(); - assertEquals(0, mDecodedProto.wifiUsabilityStatsList.length); - } - - /** - * Verify that the buffer for WifiUsabilityStats does not exceed the max length. - * Do this by trying to add more WifiUsabilityStats than the max length and then - * verifying that the decoded proto's length does not exceed the max length. + * Verify that records are properly added to mWifiUsabilityStatsEntriesRingBuffer and that the + * size does not grow indefinitely. * - * Also verify that the length for the list of WifiUsabilityStatsEntry is capped. * @throws Exception */ @Test - public void testWifiUsabilityStatsBufferSizeIsCapped() throws Exception { - // simulate adding LABEL_GOOD WifiUsabilityStats 1 time over the max limit - WifiLinkLayerStats stats = new WifiLinkLayerStats(); - for (int j = 0; j < WifiMetrics.MAX_WIFI_USABILITY_STATS_LIST_SIZE_PER_TYPE + 1; j++) { - stats = addGoodWifiUsabilityStats(stats); - stats = addBadWifiUsabilityStats(stats); - stats.timeStampInMs += WifiMetrics.MIN_WIFI_GOOD_USABILITY_STATS_PERIOD_MS; - } - dumpProtoAndDeserialize(); - assertEquals(2 * WifiMetrics.MAX_WIFI_USABILITY_STATS_PER_TYPE_TO_UPLOAD, - mDecodedProto.wifiUsabilityStatsList.length); - for (int i = 0; i < WifiMetrics.MAX_WIFI_USABILITY_STATS_PER_TYPE_TO_UPLOAD; i++) { - assertEquals(WifiMetrics.MAX_WIFI_USABILITY_STATS_ENTRIES_LIST_SIZE, - mDecodedProto.wifiUsabilityStatsList[2 * i].stats.length); - assertEquals(2, mDecodedProto.wifiUsabilityStatsList[2 * i + 1].stats.length); - } + public void testLogAsynchronousEvent() throws Exception { + when(mClock.getElapsedSinceBootMillis()).thenReturn((long) 123); + + // Buffer starts out empty. + assertEquals(0, mWifiMetrics.mWifiUsabilityStatsEntriesRingBuffer.size()); + + // Check that exactly one record is added and with default subcode. + mWifiMetrics.logAsynchronousEvent(TEST_IFACE_NAME, + WifiUsabilityStatsEntry.CAPTURE_EVENT_TYPE_RSSI_POLLING_ENABLED); + assertEquals(1, mWifiMetrics.mWifiUsabilityStatsEntriesRingBuffer.size()); + WifiUsabilityStatsEntry actual = mWifiMetrics.mWifiUsabilityStatsEntriesRingBuffer.get(0); + assertEquals(123, actual.timeStampMs); + assertEquals(WifiUsabilityStatsEntry.CAPTURE_EVENT_TYPE_RSSI_POLLING_ENABLED, + actual.captureEventType); + assertEquals(-1, actual.captureEventTypeSubcode); + + // Check that exactly one record is added with given subcode. + mWifiMetrics.logAsynchronousEvent(TEST_IFACE_NAME, + WifiUsabilityStatsEntry.CAPTURE_EVENT_TYPE_RSSI_POLLING_DISABLED, -9876); + assertEquals(2, mWifiMetrics.mWifiUsabilityStatsEntriesRingBuffer.size()); + actual = mWifiMetrics.mWifiUsabilityStatsEntriesRingBuffer.get(1); + assertEquals(123, actual.timeStampMs); + assertEquals(WifiUsabilityStatsEntry.CAPTURE_EVENT_TYPE_RSSI_POLLING_DISABLED, + actual.captureEventType); + assertEquals(-9876, actual.captureEventTypeSubcode); + + // Fill the ring buffer + for (int i = 0; i < WifiMetrics.MAX_WIFI_USABILITY_STATS_ENTRIES_RING_BUFFER_SIZE - 2; + i++) { + mWifiMetrics.logAsynchronousEvent(TEST_IFACE_NAME, + WifiUsabilityStatsEntry.CAPTURE_EVENT_TYPE_RSSI_POLLING_ENABLED); + } + assertEquals(WifiMetrics.MAX_WIFI_USABILITY_STATS_ENTRIES_RING_BUFFER_SIZE, + mWifiMetrics.mWifiUsabilityStatsEntriesRingBuffer.size()); + + // Should not grow further. + mWifiMetrics.logAsynchronousEvent(TEST_IFACE_NAME, + WifiUsabilityStatsEntry.CAPTURE_EVENT_TYPE_RSSI_POLLING_ENABLED); + assertEquals(WifiMetrics.MAX_WIFI_USABILITY_STATS_ENTRIES_RING_BUFFER_SIZE, + mWifiMetrics.mWifiUsabilityStatsEntriesRingBuffer.size()); } /** - * Verify that LABEL_GOOD stats are not generated more frequently than - * |MIN_WIFI_GOOD_USABILITY_STATS_PERIOD_MS| - * @throws Exception + * Verify that firmware alerts appear in the ring buffer. */ @Test - public void testWifiUsabilityStatsLabelGoodHasMinimumPeriod() throws Exception { - // simulate adding LABEL_GOOD WifiUsabilityStats 1 time over the max limit - WifiLinkLayerStats stats = new WifiLinkLayerStats(); - for (int j = 0; j < 2; j++) { - stats = addGoodWifiUsabilityStats(stats); - stats = addBadWifiUsabilityStats(stats); - } - dumpProtoAndDeserialize(); - assertEquals(2, mDecodedProto.wifiUsabilityStatsList.length); - } + public void testLogFirmwareAlert() throws Exception { + when(mClock.getElapsedSinceBootMillis()).thenReturn((long) 123); - /** - * Verify that LABEL_BAD stats are not generated more frequently than |MIN_DATA_STALL_WAIT_MS| - * @throws Exception - */ - @Test - public void testWifiUsabilityStatsLabelBadNotGeneratedGapLessThanMinimum() throws Exception { - // simulate adding two LABEL_GOOD WifiUsabilityStats - WifiInfo info = mock(WifiInfo.class); - when(info.getRssi()).thenReturn(nextRandInt()); - when(info.getLinkSpeed()).thenReturn(nextRandInt()); - WifiLinkLayerStats stats1 = new WifiLinkLayerStats(); - WifiLinkLayerStats stats2 = new WifiLinkLayerStats(); - stats1 = addGoodWifiUsabilityStats(stats1); - stats2.timeStampInMs = stats1.timeStampInMs - + WifiMetrics.MIN_WIFI_GOOD_USABILITY_STATS_PERIOD_MS; - addGoodWifiUsabilityStats(stats2); - - WifiLinkLayerStats stats3 = new WifiLinkLayerStats(); - WifiLinkLayerStats stats4 = new WifiLinkLayerStats(); - for (int i = 0; i < WifiMetrics.MAX_WIFI_USABILITY_STATS_ENTRIES_LIST_SIZE - 1; i++) { - mWifiMetrics.updateWifiUsabilityStatsEntries(TEST_IFACE_NAME, info, stats3); - stats3 = nextRandomStats(stats3); - } - mWifiMetrics.updateWifiUsabilityStatsEntries(TEST_IFACE_NAME, info, stats3); - mWifiMetrics.addToWifiUsabilityStatsList(TEST_IFACE_NAME, WifiUsabilityStats.LABEL_BAD, - WifiUsabilityStats.TYPE_DATA_STALL_BAD_TX, -1); - for (int i = 0; i < WifiMetrics.MAX_WIFI_USABILITY_STATS_ENTRIES_LIST_SIZE - 1; i++) { - mWifiMetrics.updateWifiUsabilityStatsEntries(TEST_IFACE_NAME, info, stats4); - stats4 = nextRandomStats(stats4); - } - stats4.timeStampInMs = stats3.timeStampInMs - 1 + WifiMetrics.MIN_DATA_STALL_WAIT_MS; - mWifiMetrics.updateWifiUsabilityStatsEntries(TEST_IFACE_NAME, info, stats4); - mWifiMetrics.addToWifiUsabilityStatsList(TEST_IFACE_NAME, WifiUsabilityStats.LABEL_BAD, - WifiUsabilityStats.TYPE_DATA_STALL_BAD_TX, -1); - dumpProtoAndDeserialize(); - assertEquals(2, mDecodedProto.wifiUsabilityStatsList.length); - } + // Buffer starts out empty. + assertEquals(0, mWifiMetrics.mWifiUsabilityStatsEntriesRingBuffer.size()); - /** - * Verify that LABEL_BAD stats are generated if timestamp gap is larger than - * |MIN_DATA_STALL_WAIT_MS| - * @throws Exception - */ - @Test - public void testWifiUsabilityStatsLabelBadGeneratedGapLargerThanMinimum() throws Exception { - // simulate adding two LABEL_GOOD WifiUsabilityStats - WifiInfo info = mock(WifiInfo.class); - when(info.getRssi()).thenReturn(nextRandInt()); - when(info.getLinkSpeed()).thenReturn(nextRandInt()); - WifiLinkLayerStats stats1 = new WifiLinkLayerStats(); - WifiLinkLayerStats stats2 = new WifiLinkLayerStats(); - stats1 = addGoodWifiUsabilityStats(stats1); - stats2.timeStampInMs = stats1.timeStampInMs - + WifiMetrics.MIN_WIFI_GOOD_USABILITY_STATS_PERIOD_MS; - addGoodWifiUsabilityStats(stats2); - - WifiLinkLayerStats stats3 = new WifiLinkLayerStats(); - WifiLinkLayerStats stats4 = new WifiLinkLayerStats(); - for (int i = 0; i < WifiMetrics.MAX_WIFI_USABILITY_STATS_ENTRIES_LIST_SIZE - 1; i++) { - mWifiMetrics.updateWifiUsabilityStatsEntries(TEST_IFACE_NAME, info, stats3); - stats3 = nextRandomStats(stats3); - } - mWifiMetrics.updateWifiUsabilityStatsEntries(TEST_IFACE_NAME, info, stats3); - mWifiMetrics.addToWifiUsabilityStatsList(TEST_IFACE_NAME, WifiUsabilityStats.LABEL_BAD, - WifiUsabilityStats.TYPE_DATA_STALL_BAD_TX, -1); - for (int i = 0; i < WifiMetrics.MAX_WIFI_USABILITY_STATS_ENTRIES_LIST_SIZE - 1; i++) { - mWifiMetrics.updateWifiUsabilityStatsEntries(TEST_IFACE_NAME, info, stats4); - stats4 = nextRandomStats(stats4); - } - stats4.timeStampInMs = stats3.timeStampInMs + 1 + WifiMetrics.MIN_DATA_STALL_WAIT_MS; - mWifiMetrics.updateWifiUsabilityStatsEntries(TEST_IFACE_NAME, info, stats4); - mWifiMetrics.addToWifiUsabilityStatsList(TEST_IFACE_NAME, WifiUsabilityStats.LABEL_BAD, - WifiUsabilityStats.TYPE_DATA_STALL_BAD_TX, -1); - dumpProtoAndDeserialize(); - assertEquals(4, mDecodedProto.wifiUsabilityStatsList.length); + // Add record + mWifiMetrics.logFirmwareAlert(TEST_IFACE_NAME, 789); + + // Confirm that exactly one record is added and with default subcode. + assertEquals(1, mWifiMetrics.mWifiUsabilityStatsEntriesRingBuffer.size()); + WifiUsabilityStatsEntry actual = mWifiMetrics.mWifiUsabilityStatsEntriesRingBuffer.get(0); + assertEquals(123, actual.timeStampMs); + assertEquals(WifiUsabilityStatsEntry.CAPTURE_EVENT_TYPE_FIRMWARE_ALERT, + actual.captureEventType); + assertEquals(789, actual.captureEventTypeSubcode); } /** @@ -4782,7 +4850,8 @@ public class WifiMetricsTest extends WifiBaseTest { ActiveModeManager.ROLE_CLIENT_SECONDARY_LONG_LIVED); mModeChangeCallbackArgumentCaptor.getValue() .onActiveModeManagerRoleChanged(concreteClientModeManager); - mWifiMetrics.updateWifiUsabilityStatsEntries(TEST_IFACE_NAME, info, linkLayerStats); + mWifiMetrics.updateWifiUsabilityStatsEntries(TEST_IFACE_NAME, info, linkLayerStats, false, + 0); verify(mOnWifiUsabilityStatsListener, never()).onWifiUsabilityStats(anyInt(), anyBoolean(), any()); @@ -4792,7 +4861,8 @@ public class WifiMetricsTest extends WifiBaseTest { when(concreteClientModeManager.getRole()).thenReturn(ActiveModeManager.ROLE_CLIENT_PRIMARY); mModeChangeCallbackArgumentCaptor.getValue() .onActiveModeManagerRoleChanged(concreteClientModeManager); - mWifiMetrics.updateWifiUsabilityStatsEntries(TEST_IFACE_NAME, info, linkLayerStats); + mWifiMetrics.updateWifiUsabilityStatsEntries(TEST_IFACE_NAME, info, linkLayerStats, false, + 0); // Client should get the stats. verify(mOnWifiUsabilityStatsListener).onWifiUsabilityStats(anyInt(), anyBoolean(), @@ -4928,7 +4998,8 @@ public class WifiMetricsTest extends WifiBaseTest { when(info.getRssi()).thenReturn(nextRandInt()); when(info.getLinkSpeed()).thenReturn(nextRandInt()); WifiLinkLayerStats linkLayerStats = nextRandomStats(new WifiLinkLayerStats()); - mWifiMetrics.updateWifiUsabilityStatsEntries(TEST_IFACE_NAME, info, linkLayerStats); + mWifiMetrics.updateWifiUsabilityStatsEntries(TEST_IFACE_NAME, info, linkLayerStats, false, + 0); verify(mOnWifiUsabilityStatsListener, never()).onWifiUsabilityStats(anyInt(), anyBoolean(), any()); @@ -4957,7 +5028,8 @@ public class WifiMetricsTest extends WifiBaseTest { when(info.getRssi()).thenReturn(nextRandInt()); when(info.getLinkSpeed()).thenReturn(nextRandInt()); WifiLinkLayerStats linkLayerStats = nextRandomStats(new WifiLinkLayerStats()); - mWifiMetrics.updateWifiUsabilityStatsEntries(TEST_IFACE_NAME, info, linkLayerStats); + mWifiMetrics.updateWifiUsabilityStatsEntries(TEST_IFACE_NAME, info, linkLayerStats, false, + 0); // Client should not get any message listener add failed. verify(mOnWifiUsabilityStatsListener, never()).onWifiUsabilityStats(anyInt(), @@ -4965,67 +5037,6 @@ public class WifiMetricsTest extends WifiBaseTest { } /** - * Verify that the label and the triggerType of Wifi usability stats are saved correctly - * during firmware alert is triggered. - * @throws Exception - */ - @Test - public void verifyFirmwareAlertUpdatesWifiUsabilityMetrics() throws Exception { - WifiInfo info = mock(WifiInfo.class); - when(info.getRssi()).thenReturn(nextRandInt()); - when(info.getLinkSpeed()).thenReturn(nextRandInt()); - long eventTimeMs = nextRandInt(); - when(mClock.getElapsedSinceBootMillis()).thenReturn(eventTimeMs); - WifiLinkLayerStats stats1 = nextRandomStats(new WifiLinkLayerStats()); - mWifiMetrics.updateWifiUsabilityStatsEntries(TEST_IFACE_NAME, info, stats1); - - // Add 1 LABEL_GOOD - WifiLinkLayerStats statsGood = addGoodWifiUsabilityStats(nextRandomStats(stats1)); - // Firmware alert occurs - mWifiMetrics.logFirmwareAlert(TEST_IFACE_NAME, 2); - - dumpProtoAndDeserialize(); - assertEquals(2, mDecodedProto.wifiUsabilityStatsList.length); - - WifiUsabilityStats[] statsList = mDecodedProto.wifiUsabilityStatsList; - assertEquals(WifiUsabilityStats.LABEL_GOOD, statsList[0].label); - assertEquals(WifiUsabilityStats.LABEL_BAD, statsList[1].label); - assertEquals(WifiIsUnusableEvent.TYPE_FIRMWARE_ALERT, statsList[1].triggerType); - assertEquals(eventTimeMs, statsList[1].timeStampMs); - assertEquals(2, statsList[1].firmwareAlertCode); - } - - /** - * Verify that the label and the triggerType of Wifi usability stats are saved correctly - * during Wifi data stall is triggered. - * @throws Exception - */ - @Test - public void verifyWifiDataStallUpdatesWifiUsabilityMetrics() throws Exception { - WifiInfo info = mock(WifiInfo.class); - when(info.getRssi()).thenReturn(nextRandInt()); - when(info.getLinkSpeed()).thenReturn(nextRandInt()); - long eventTimeMs = nextRandInt(); - when(mClock.getElapsedSinceBootMillis()).thenReturn(eventTimeMs); - WifiLinkLayerStats stats1 = nextRandomStats(new WifiLinkLayerStats()); - mWifiMetrics.updateWifiUsabilityStatsEntries(TEST_IFACE_NAME, info, stats1); - - // Add 1 LABEL_GOOD - WifiLinkLayerStats statsGood = addGoodWifiUsabilityStats(nextRandomStats(stats1)); - // Wifi data stall occurs - mWifiMetrics.addToWifiUsabilityStatsList(TEST_IFACE_NAME, WifiUsabilityStats.LABEL_BAD, - WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX, -1); - - dumpProtoAndDeserialize(); - assertEquals(2, mDecodedProto.wifiUsabilityStatsList.length); - WifiUsabilityStats[] statsList = mDecodedProto.wifiUsabilityStatsList; - assertEquals(WifiUsabilityStats.LABEL_BAD, statsList[1].label); - assertEquals(WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX, statsList[1].triggerType); - assertEquals(-1, statsList[1].firmwareAlertCode); - assertEquals(eventTimeMs, statsList[1].timeStampMs); - } - - /** * Test the generation of 'WifiConfigStoreIODuration' read histograms. */ @Test @@ -5510,35 +5521,6 @@ public class WifiMetricsTest extends WifiBaseTest { } /** - * Verify that the label and the triggerType of Wifi usability stats are saved correctly - * during IP reachability lost message is received. - * @throws Exception - */ - @Test - public void verifyIpReachabilityLostUpdatesWifiUsabilityMetrics() throws Exception { - WifiInfo info = mock(WifiInfo.class); - when(info.getRssi()).thenReturn(nextRandInt()); - when(info.getLinkSpeed()).thenReturn(nextRandInt()); - long eventTimeMs = nextRandInt(); - when(mClock.getElapsedSinceBootMillis()).thenReturn(eventTimeMs); - WifiLinkLayerStats stats1 = nextRandomStats(new WifiLinkLayerStats()); - mWifiMetrics.updateWifiUsabilityStatsEntries(TEST_IFACE_NAME, info, stats1); - - // Add 1 LABEL_GOOD - WifiLinkLayerStats statsGood = addGoodWifiUsabilityStats(nextRandomStats(stats1)); - // IP reachability lost occurs - mWifiMetrics.addToWifiUsabilityStatsList(TEST_IFACE_NAME, WifiUsabilityStats.LABEL_BAD, - WifiUsabilityStats.TYPE_IP_REACHABILITY_LOST, -1); - - dumpProtoAndDeserialize(); - assertEquals(2, mDecodedProto.wifiUsabilityStatsList.length); - WifiUsabilityStats[] statsList = mDecodedProto.wifiUsabilityStatsList; - assertEquals(WifiUsabilityStats.LABEL_BAD, statsList[1].label); - assertEquals(WifiUsabilityStats.TYPE_IP_REACHABILITY_LOST, statsList[1].triggerType); - assertEquals(eventTimeMs, statsList[1].timeStampMs); - } - - /** * Test the WifiLock active session statistics */ @Test @@ -5727,126 +5709,6 @@ public class WifiMetricsTest extends WifiBaseTest { } /** - * Verify that LABEL_GOOD stats are generated if Wifi score breaches low and there - * is no WifiIsUnusableEvent in MIN_SCORE_BREACH_TO_GOOD_STATS_WAIT_TIME_MS millis - * @throws Exception - */ - @Test - public void testGoodStatsAreGeneratedByWifiScoreBreachLow() throws Exception { - // The elapsed time falls into the interval for adding good stats - createTestForDataCollectionByScoreBreach( - WifiMetrics.MIN_SCORE_BREACH_TO_GOOD_STATS_WAIT_TIME_MS + 1, - false, true); - dumpProtoAndDeserialize(); - assertEquals(2, mDecodedProto.wifiUsabilityStatsList.length); - } - - /** - * Verify that LABEL_GOOD stats are not generated if Wifi score breaches low and the checking - * time is less than MIN_SCORE_BREACH_TO_GOOD_STATS_WAIT_TIME_MS millis - * @throws Exception - */ - @Test - public void testGoodStatsAreNotGeneratedByWifiScoreBreachLow() throws Exception { - // The elapsed time is shorter than necessary to add good stats - createTestForDataCollectionByScoreBreach( - WifiMetrics.MIN_SCORE_BREACH_TO_GOOD_STATS_WAIT_TIME_MS - 1, - false, true); - dumpProtoAndDeserialize(); - assertEquals(0, mDecodedProto.wifiUsabilityStatsList.length); - } - - /** - * Verify that LABEL_GOOD stats are not generated if Wifi score breaches low and the checking - * time is greater than VALIDITY_PERIOD_OF_SCORE_BREACH_LOW_MS - * @throws Exception - */ - @Test - public void testGoodStatsAreNotGeneratedIfWifiScoreBreachExpires() throws Exception { - // The Wifi score breaching expires for adding good stats - createTestForDataCollectionByScoreBreach( - WifiMetrics.VALIDITY_PERIOD_OF_SCORE_BREACH_LOW_MS + 1, - false, true); - dumpProtoAndDeserialize(); - assertEquals(0, mDecodedProto.wifiUsabilityStatsList.length); - } - - /** - * Verify that LABEL_GOOD stats are not generated if Wifi score breaches low and there is - * WifiIsUnusableEvent occured within MIN_SCORE_BREACH_TO_GOOD_STATS_WAIT_TIME_MS millis - * @throws Exception - */ - @Test - public void testGoodStatsAreNotGeneratedIfBadEventOccured() throws Exception { - // The elapsed time falls into the interval for adding good stats and bad event occurs - createTestForDataCollectionByScoreBreach( - WifiMetrics.MIN_SCORE_BREACH_TO_GOOD_STATS_WAIT_TIME_MS + 1, - true, true); - dumpProtoAndDeserialize(); - assertEquals(0, mDecodedProto.wifiUsabilityStatsList.length); - } - - /** - * Verify that LABEL_GOOD stats are generated if Wifi usability score breaches low and there - * is no WifiIsUnusableEvent in MIN_SCORE_BREACH_TO_GOOD_STATS_WAIT_TIME_MS millis - * @throws Exception - */ - @Test - public void testGoodStatsAreGeneratedByWifiUsabilityScoreBreachLow() throws Exception { - // The elapsed time falls into the interval for adding good stats - createTestForDataCollectionByScoreBreach( - WifiMetrics.MIN_SCORE_BREACH_TO_GOOD_STATS_WAIT_TIME_MS + 1, - false, false); - dumpProtoAndDeserialize(); - assertEquals(2, mDecodedProto.wifiUsabilityStatsList.length); - } - - /** - * Verify that LABEL_GOOD stats are not generated if Wifi usability score breaches low and - * the checking time is less than MIN_SCORE_BREACH_TO_GOOD_STATS_WAIT_TIME_MS millis - * @throws Exception - */ - @Test - public void testGoodStatsAreNotGeneratedByWifiUsabilityScoreBreachLow() throws Exception { - // The elapsed time is shorter than necessary to add good stats - createTestForDataCollectionByScoreBreach( - WifiMetrics.MIN_SCORE_BREACH_TO_GOOD_STATS_WAIT_TIME_MS - 1, - false, false); - dumpProtoAndDeserialize(); - assertEquals(0, mDecodedProto.wifiUsabilityStatsList.length); - } - - /** - * Verify that LABEL_GOOD stats are not generated if Wifi usability score breaches low and - * the checking time is greater than VALIDITY_PERIOD_OF_SCORE_BREACH_LOW_MS - * @throws Exception - */ - @Test - public void testGoodStatsAreNotGeneratedIfWifiUsabilityScoreBreachExpires() throws Exception { - // The Wifi usability score breaching expires for adding good stats - createTestForDataCollectionByScoreBreach( - WifiMetrics.VALIDITY_PERIOD_OF_SCORE_BREACH_LOW_MS + 1, - false, false); - dumpProtoAndDeserialize(); - assertEquals(0, mDecodedProto.wifiUsabilityStatsList.length); - } - - /** - * Verify that LABEL_GOOD stats are not generated if Wifi usability score breaches low and there - * is WifiIsUnusableEvent occured within MIN_SCORE_BREACH_TO_GOOD_STATS_WAIT_TIME_MS millis - * @throws Exception - */ - @Test - public void testGoodStatsAreNotGeneratedIfBadEventOccuredForUsabilityScore() throws Exception { - // The elapsed time falls into the interval for adding good stats and bad event occurs - createTestForDataCollectionByScoreBreach( - WifiMetrics.MIN_SCORE_BREACH_TO_GOOD_STATS_WAIT_TIME_MS + 1, - true, false); - dumpProtoAndDeserialize(); - assertEquals(0, mDecodedProto.wifiUsabilityStatsList.length); - } - - /** * Verify that incrementNumWifiToggles increments the corrects fields based on input. */ @Test @@ -5929,54 +5791,18 @@ public class WifiMetricsTest extends WifiBaseTest { assertEquals(0, mDecodedProto.meteredNetworkStatsSuggestion.numOverrideUnmetered); } - /** - * Create a test to verify data collection logic triggered by score breaching low - * @param elapsedTimeAfterBreach The elapsed time after score breaches low - * @param isThereBadEvent Whether there is a bad event happened after score breaches low - * @param isWifiScore Whether it is Wifi score or not that breaches the threshold - */ - private void createTestForDataCollectionByScoreBreach( - long elapsedTimeAfterBreach, boolean isThereBadEvent, boolean isWifiScore) { - WifiInfo info = mock(WifiInfo.class); - when(info.getRssi()).thenReturn(nextRandInt()); - when(info.getLinkSpeed()).thenReturn(nextRandInt()); - WifiLinkLayerStats stats2 = new WifiLinkLayerStats(); - mWifiMetrics.setWifiState(TEST_IFACE_NAME, WifiMetricsProto.WifiLog.WIFI_ASSOCIATED); - - addOneBadWifiUsabilityStats(info); - if (isWifiScore) { - stats2 = wifiScoreBreachesLow(info, stats2); - } else { - stats2 = wifiUsabilityScoreBreachesLow(info, stats2); - } - if (isThereBadEvent) { - mWifiMetrics.logWifiIsUnusableEvent(TEST_IFACE_NAME, - WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX, -1); - } - when(mClock.getElapsedSinceBootMillis()).thenReturn(elapsedTimeAfterBreach); - mWifiMetrics.updateWifiUsabilityStatsEntries(TEST_IFACE_NAME, info, stats2); - } - - // Simulate adding one LABEL_BAD WifiUsabilityStats - private void addOneBadWifiUsabilityStats(WifiInfo info) { - WifiLinkLayerStats stats1 = new WifiLinkLayerStats(); - mWifiMetrics.updateWifiUsabilityStatsEntries(TEST_IFACE_NAME, info, stats1); - mWifiMetrics.addToWifiUsabilityStatsList(TEST_IFACE_NAME, WifiUsabilityStats.LABEL_BAD, - WifiUsabilityStats.TYPE_DATA_STALL_BAD_TX, -1); - } - // Simulate that Wifi score breaches low private WifiLinkLayerStats wifiScoreBreachesLow(WifiInfo info, WifiLinkLayerStats stats2) { int upper = WifiMetrics.LOW_WIFI_SCORE + 7; int lower = WifiMetrics.LOW_WIFI_SCORE - 8; mWifiMetrics.incrementWifiScoreCount(TEST_IFACE_NAME, upper); - mWifiMetrics.updateWifiUsabilityStatsEntries(TEST_IFACE_NAME, info, stats2); + mWifiMetrics.updateWifiUsabilityStatsEntries(TEST_IFACE_NAME, info, stats2, false, 0); stats2 = nextRandomStats(stats2); long timeMs = 0; when(mClock.getElapsedSinceBootMillis()).thenReturn(timeMs); // Wifi score breaches low mWifiMetrics.incrementWifiScoreCount(TEST_IFACE_NAME, lower); - mWifiMetrics.updateWifiUsabilityStatsEntries(TEST_IFACE_NAME, info, stats2); + mWifiMetrics.updateWifiUsabilityStatsEntries(TEST_IFACE_NAME, info, stats2, false, 0); stats2 = nextRandomStats(stats2); return stats2; } @@ -5987,13 +5813,13 @@ public class WifiMetricsTest extends WifiBaseTest { int upper = WifiMetrics.LOW_WIFI_USABILITY_SCORE + 7; int lower = WifiMetrics.LOW_WIFI_USABILITY_SCORE - 8; mWifiMetrics.incrementWifiUsabilityScoreCount(TEST_IFACE_NAME, 1, upper, 30); - mWifiMetrics.updateWifiUsabilityStatsEntries(TEST_IFACE_NAME, info, stats2); + mWifiMetrics.updateWifiUsabilityStatsEntries(TEST_IFACE_NAME, info, stats2, false, 0); stats2 = nextRandomStats(stats2); long timeMs = 0; when(mClock.getElapsedSinceBootMillis()).thenReturn(timeMs); // Wifi usability score breaches low mWifiMetrics.incrementWifiUsabilityScoreCount(TEST_IFACE_NAME, 2, lower, 30); - mWifiMetrics.updateWifiUsabilityStatsEntries(TEST_IFACE_NAME, info, stats2); + mWifiMetrics.updateWifiUsabilityStatsEntries(TEST_IFACE_NAME, info, stats2, false, 0); stats2 = nextRandomStats(stats2); return stats2; } @@ -6027,34 +5853,6 @@ public class WifiMetricsTest extends WifiBaseTest { } /** - * Verify that the LABEL_BAD Wifi usability stats are not saved if screen state is off. - * @throws Exception - */ - @Test - public void verifyLabelBadStatsAreNotSavedIfScreenIsOff() throws Exception { - setScreenState(false); - WifiInfo info = mock(WifiInfo.class); - when(info.getRssi()).thenReturn(nextRandInt()); - when(info.getLinkSpeed()).thenReturn(nextRandInt()); - WifiLinkLayerStats stats1 = nextRandomStats(new WifiLinkLayerStats()); - mWifiMetrics.updateWifiUsabilityStatsEntries(TEST_IFACE_NAME, info, stats1); - - // Add 1 LABEL_GOOD - WifiLinkLayerStats statsGood = addGoodWifiUsabilityStats(nextRandomStats(stats1)); - // IP reachability lost occurs - mWifiMetrics.addToWifiUsabilityStatsList(TEST_IFACE_NAME, WifiUsabilityStats.LABEL_BAD, - WifiUsabilityStats.TYPE_IP_REACHABILITY_LOST, -1); - // Wifi data stall occurs - mWifiMetrics.addToWifiUsabilityStatsList(TEST_IFACE_NAME, WifiUsabilityStats.LABEL_BAD, - WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX, -1); - // Firmware alert occurs - mWifiMetrics.logFirmwareAlert(TEST_IFACE_NAME, 2); - - dumpProtoAndDeserialize(); - assertEquals(0, mDecodedProto.wifiUsabilityStatsList.length); - } - - /** * Test the logging of connection duration stats */ @Test @@ -6062,16 +5860,16 @@ public class WifiMetricsTest extends WifiBaseTest { for (int i = 0; i < 2; i++) { mWifiMetrics.incrementWifiScoreCount(TEST_IFACE_NAME, 52); mWifiMetrics.incrementConnectionDuration(TEST_IFACE_NAME, 5000, false, true, -50, 10000, - 10000); + 10000, 10, 10, ScanResult.CHANNEL_WIDTH_80MHZ); mWifiMetrics.incrementWifiScoreCount(TEST_IFACE_NAME, 40); mWifiMetrics.incrementConnectionDuration(TEST_IFACE_NAME, 5000, false, true, -50, 10000, - 10000); + 10000, 10, 10, ScanResult.CHANNEL_WIDTH_80MHZ); mWifiMetrics.incrementConnectionDuration(TEST_IFACE_NAME, 3000, true, true, -50, 10000, - 10000); + 10000, 10, 10, ScanResult.CHANNEL_WIDTH_80MHZ); mWifiMetrics.incrementConnectionDuration(TEST_IFACE_NAME, 1000, false, false, -50, - 10000, 10000); + 10000, 10000, 10, 10, ScanResult.CHANNEL_WIDTH_80MHZ); mWifiMetrics.incrementConnectionDuration(TEST_IFACE_NAME, 500, true, false, -50, 10000, - 10000); + 10000, 10, 10, ScanResult.CHANNEL_WIDTH_80MHZ); } dumpProtoAndDeserialize(); @@ -6458,7 +6256,7 @@ public class WifiMetricsTest extends WifiBaseTest { eq(WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED), anyBoolean(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyInt(), anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), - eq(TEST_UID)), + eq(TEST_UID), anyInt()), times(0)); } @@ -6474,7 +6272,7 @@ public class WifiMetricsTest extends WifiBaseTest { eq(WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED), anyBoolean(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyInt(), anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), - eq(TEST_UID)), + eq(TEST_UID), eq(TEST_CANDIDATE_FREQ)), times(0)); } @@ -6501,7 +6299,8 @@ public class WifiMetricsTest extends WifiBaseTest { eq(WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__TRIGGER__AUTOCONNECT_BOOT), eq(true), eq(0), eq(true), eq(false), eq(1), eq(TEST_CONNECTION_FAILURE_STATUS_CODE), - anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), eq(TEST_UID)), + anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), eq(TEST_UID), + eq(TEST_CANDIDATE_FREQ)), times(1)); } @@ -6537,7 +6336,8 @@ public class WifiMetricsTest extends WifiBaseTest { eq(WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__TRIGGER__AUTOCONNECT_BOOT), eq(true), eq(0), eq(true), eq(true), eq(1), eq(TEST_CONNECTION_FAILURE_STATUS_CODE), - anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), eq(TEST_UID)), + anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), eq(TEST_UID), + eq(TEST_CANDIDATE_FREQ)), times(1)); } @@ -6561,7 +6361,7 @@ public class WifiMetricsTest extends WifiBaseTest { eq(WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__TRIGGER__AUTOCONNECT_BOOT), anyBoolean(), anyInt(), anyBoolean(), anyBoolean(), anyInt(), eq(TEST_CONNECTION_FAILURE_STATUS_CODE), anyInt(), anyInt(), anyInt(), anyInt(), - anyInt(), eq(TEST_UID))); + anyInt(), eq(TEST_UID), eq(TEST_CANDIDATE_FREQ))); mWifiMetrics.startConnectionEvent(TEST_IFACE_NAME, createComplexWifiConfig(), "RED", WifiMetricsProto.ConnectionEvent.ROAM_ENTERPRISE, false, @@ -6581,7 +6381,7 @@ public class WifiMetricsTest extends WifiBaseTest { eq(WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__TRIGGER__RECONNECT_SAME_NETWORK), anyBoolean(), anyInt(), anyBoolean(), anyBoolean(), anyInt(), eq(TEST_CONNECTION_FAILURE_STATUS_CODE), anyInt(), anyInt(), anyInt(), anyInt(), - anyInt(), eq(TEST_UID))); + anyInt(), eq(TEST_UID), eq(TEST_CANDIDATE_FREQ))); WifiConfiguration configOtherNetwork = createComplexWifiConfig(); configOtherNetwork.networkId = 21; @@ -6607,7 +6407,7 @@ public class WifiMetricsTest extends WifiBaseTest { eq(WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__TRIGGER__AUTOCONNECT_CONFIGURED_NETWORK), anyBoolean(), anyInt(), anyBoolean(), anyBoolean(), anyInt(), eq(TEST_CONNECTION_FAILURE_STATUS_CODE), anyInt(), anyInt(), anyInt(), anyInt(), - anyInt(), eq(TEST_UID))); + anyInt(), eq(TEST_UID), eq(TEST_CANDIDATE_FREQ))); WifiConfiguration config = createComplexWifiConfig(); config.networkId = 42; @@ -6630,7 +6430,7 @@ public class WifiMetricsTest extends WifiBaseTest { eq(WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__TRIGGER__MANUAL), anyBoolean(), anyInt(), anyBoolean(), anyBoolean(), anyInt(), eq(TEST_CONNECTION_FAILURE_STATUS_CODE), anyInt(), anyInt(), anyInt(), anyInt(), - anyInt(), eq(TEST_UID))); + anyInt(), eq(TEST_UID), eq(TEST_CANDIDATE_FREQ))); } @Test @@ -6791,7 +6591,7 @@ public class WifiMetricsTest extends WifiBaseTest { eq(WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__TRIGGER__AUTOCONNECT_BOOT), anyBoolean(), eq(10), anyBoolean(), anyBoolean(), anyInt(), eq(TEST_CONNECTION_FAILURE_STATUS_CODE), anyInt(), anyInt(), anyInt(), anyInt(), - anyInt(), eq(TEST_UID))); + anyInt(), eq(TEST_UID), eq(TEST_CANDIDATE_FREQ))); mWifiMetrics.reportNetworkDisconnect(TEST_IFACE_NAME, 0, 0, 0, 0); @@ -6813,7 +6613,7 @@ public class WifiMetricsTest extends WifiBaseTest { eq(WifiStatsLog.WIFI_CONNECTION_RESULT_REPORTED__TRIGGER__RECONNECT_SAME_NETWORK), anyBoolean(), eq(20), anyBoolean(), anyBoolean(), anyInt(), eq(TEST_CONNECTION_FAILURE_STATUS_CODE), anyInt(), anyInt(), anyInt(), anyInt(), - anyInt(), eq(TEST_UID))); + anyInt(), eq(TEST_UID), eq(TEST_CANDIDATE_FREQ))); mWifiMetrics.reportNetworkDisconnect(TEST_IFACE_NAME, 0, 0, 0, 0); } @@ -7080,13 +6880,15 @@ public class WifiMetricsTest extends WifiBaseTest { mWifiMetrics.incrementWifiScoreCount("", 60); mWifiMetrics.handlePollResult(TEST_IFACE_NAME, wifiInfo); mWifiMetrics.incrementConnectionDuration(TEST_IFACE_NAME, 3000, true, true, -50, 10002, - 10001); + 10001, 10, 10, ScanResult.CHANNEL_WIDTH_80MHZ); ExtendedMockito.verify(() -> WifiStatsLog.write( WifiStatsLog.WIFI_HEALTH_STAT_REPORTED, 3000, true, true, WifiStatsLog.WIFI_HEALTH_STAT_REPORTED__BAND__BAND_5G_HIGH, -50, 10002, 10001, Process.WIFI_UID, false, - WIFI_IS_UNUSABLE_REPORTED__WIFI_PREDICTED_USABILITY_STATE__WIFI_USABILITY_PREDICTED_UNKNOWN)); + WIFI_IS_UNUSABLE_REPORTED__WIFI_PREDICTED_USABILITY_STATE__WIFI_USABILITY_PREDICTED_UNKNOWN, + 10, 10, + WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__CHANNEL_WIDTH_MHZ__CHANNEL_WIDTH_80MHZ)); when(wifiInfo.getFrequency()).thenReturn(2412); mWifiMetrics.setIsExternalWifiScorerOn(true, TEST_UID); @@ -7095,12 +6897,14 @@ public class WifiMetricsTest extends WifiBaseTest { mWifiMetrics.incrementWifiScoreCount("", 30); mWifiMetrics.handlePollResult(TEST_IFACE_NAME, wifiInfo); mWifiMetrics.incrementConnectionDuration(TEST_IFACE_NAME, 2000, false, true, -55, 20002, - 20001); + 20001, 10, 10, ScanResult.CHANNEL_WIDTH_80MHZ); ExtendedMockito.verify( () -> WifiStatsLog.write(WifiStatsLog.WIFI_HEALTH_STAT_REPORTED, 2000, true, true, WifiStatsLog.WIFI_HEALTH_STAT_REPORTED__BAND__BAND_2G, -55, 20002, 20001, TEST_UID, true, - WIFI_IS_UNUSABLE_REPORTED__WIFI_PREDICTED_USABILITY_STATE__WIFI_USABILITY_PREDICTED_USABLE)); + WIFI_IS_UNUSABLE_REPORTED__WIFI_PREDICTED_USABILITY_STATE__WIFI_USABILITY_PREDICTED_USABLE, + 10, 10, + WifiStatsLog.WIFI_AP_CAPABILITIES_REPORTED__CHANNEL_WIDTH_MHZ__CHANNEL_WIDTH_80MHZ)); } /** @@ -7618,11 +7422,13 @@ public class WifiMetricsTest extends WifiBaseTest { WifiMetrics.SessionData currentSession = new WifiMetrics.SessionData(connectionEvent, "", (long) 1000, 0, 0); mWifiMetrics.mCurrentSession = currentSession; + mWifiMetrics.mLastScreenOffTimeMillis = 1000; + mWifiMetrics.mLastIgnoredPollTimeMillis = 3000; + mWifiMetrics.updateWiFiEvaluationAndScorerStats(true, null, null); mWifiMetrics.logScorerPredictionResult(false, false, false, POLLING_INTERVAL_DEFAULT, WIFI_IS_UNUSABLE_REPORTED__WIFI_PREDICTED_USABILITY_STATE__WIFI_USABILITY_PREDICTED_USABLE, - WIFI_IS_UNUSABLE_REPORTED__WIFI_PREDICTED_USABILITY_STATE__WIFI_USABILITY_PREDICTED_USABLE, - true, mWifiInfo, mCapabilities); + WIFI_IS_UNUSABLE_REPORTED__WIFI_PREDICTED_USABILITY_STATE__WIFI_USABILITY_PREDICTED_USABLE); ExtendedMockito.verify(() -> WifiStatsLog.write_non_chained( SCORER_PREDICTION_RESULT_REPORTED, @@ -7632,7 +7438,7 @@ public class WifiMetricsTest extends WifiBaseTest { false, SCORER_PREDICTION_RESULT_REPORTED__DEVICE_STATE__STATE_NO_CELLULAR_MODEM, POLLING_INTERVAL_DEFAULT, - SCORER_PREDICTION_RESULT_REPORTED__WIFI_FRAMEWORK_STATE__FRAMEWORK_STATE_AWAKENING, + SCORER_PREDICTION_RESULT_REPORTED__WIFI_FRAMEWORK_STATE__FRAMEWORK_STATE_LINGERING, SCORER_PREDICTION_RESULT_REPORTED__SPEED_SUFFICIENT_NETWORK_CAPABILITIES_DS__UNKNOWN, SCORER_PREDICTION_RESULT_REPORTED__SPEED_SUFFICIENT_NETWORK_CAPABILITIES_US__UNKNOWN, SCORER_PREDICTION_RESULT_REPORTED__SPEED_SUFFICIENT_THROUGHPUT_PREDICTOR_DS__UNKNOWN, @@ -7650,11 +7456,13 @@ public class WifiMetricsTest extends WifiBaseTest { WifiMetrics.SessionData currentSession = new WifiMetrics.SessionData(connectionEvent, "", (long) 1000, 0, 0); mWifiMetrics.mCurrentSession = currentSession; + mWifiMetrics.mLastScreenOffTimeMillis = 1000; + mWifiMetrics.mLastIgnoredPollTimeMillis = 3000; + mWifiMetrics.updateWiFiEvaluationAndScorerStats(true, null, null); mWifiMetrics.logScorerPredictionResult(false, false, false, POLLING_INTERVAL_DEFAULT, WIFI_IS_UNUSABLE_REPORTED__WIFI_PREDICTED_USABILITY_STATE__WIFI_USABILITY_PREDICTED_USABLE, - WIFI_IS_UNUSABLE_REPORTED__WIFI_PREDICTED_USABILITY_STATE__WIFI_USABILITY_PREDICTED_USABLE, - true, mWifiInfo, mCapabilities); + WIFI_IS_UNUSABLE_REPORTED__WIFI_PREDICTED_USABILITY_STATE__WIFI_USABILITY_PREDICTED_USABLE); ExtendedMockito.verify(() -> WifiStatsLog.write_non_chained( SCORER_PREDICTION_RESULT_REPORTED, @@ -7664,7 +7472,7 @@ public class WifiMetricsTest extends WifiBaseTest { false, SCORER_PREDICTION_RESULT_REPORTED__DEVICE_STATE__STATE_NO_CELLULAR_MODEM, POLLING_INTERVAL_DEFAULT, - SCORER_PREDICTION_RESULT_REPORTED__WIFI_FRAMEWORK_STATE__FRAMEWORK_STATE_AWAKENING, + SCORER_PREDICTION_RESULT_REPORTED__WIFI_FRAMEWORK_STATE__FRAMEWORK_STATE_LINGERING, SCORER_PREDICTION_RESULT_REPORTED__SPEED_SUFFICIENT_NETWORK_CAPABILITIES_DS__UNKNOWN, SCORER_PREDICTION_RESULT_REPORTED__SPEED_SUFFICIENT_NETWORK_CAPABILITIES_US__UNKNOWN, SCORER_PREDICTION_RESULT_REPORTED__SPEED_SUFFICIENT_THROUGHPUT_PREDICTOR_DS__UNKNOWN, @@ -7678,7 +7486,7 @@ public class WifiMetricsTest extends WifiBaseTest { false, SCORER_PREDICTION_RESULT_REPORTED__DEVICE_STATE__STATE_NO_CELLULAR_MODEM, POLLING_INTERVAL_DEFAULT, - SCORER_PREDICTION_RESULT_REPORTED__WIFI_FRAMEWORK_STATE__FRAMEWORK_STATE_AWAKENING, + SCORER_PREDICTION_RESULT_REPORTED__WIFI_FRAMEWORK_STATE__FRAMEWORK_STATE_LINGERING, SCORER_PREDICTION_RESULT_REPORTED__SPEED_SUFFICIENT_NETWORK_CAPABILITIES_DS__UNKNOWN, SCORER_PREDICTION_RESULT_REPORTED__SPEED_SUFFICIENT_NETWORK_CAPABILITIES_US__UNKNOWN, SCORER_PREDICTION_RESULT_REPORTED__SPEED_SUFFICIENT_THROUGHPUT_PREDICTOR_DS__UNKNOWN, @@ -7695,11 +7503,13 @@ public class WifiMetricsTest extends WifiBaseTest { WifiMetrics.SessionData currentSession = new WifiMetrics.SessionData(connectionEvent, "", (long) 1000, 0, 0); mWifiMetrics.mCurrentSession = currentSession; + mWifiMetrics.mLastScreenOffTimeMillis = 1000; + mWifiMetrics.mLastIgnoredPollTimeMillis = 3000; + mWifiMetrics.updateWiFiEvaluationAndScorerStats(true, null, null); mWifiMetrics.logScorerPredictionResult(false, false, false, POLLING_INTERVAL_NOT_DEFAULT, WIFI_IS_UNUSABLE_REPORTED__WIFI_PREDICTED_USABILITY_STATE__WIFI_USABILITY_PREDICTED_USABLE, - WIFI_IS_UNUSABLE_REPORTED__WIFI_PREDICTED_USABILITY_STATE__WIFI_USABILITY_PREDICTED_USABLE, - true, mWifiInfo, mCapabilities); + WIFI_IS_UNUSABLE_REPORTED__WIFI_PREDICTED_USABILITY_STATE__WIFI_USABILITY_PREDICTED_USABLE); ExtendedMockito.verify(() -> WifiStatsLog.write_non_chained( SCORER_PREDICTION_RESULT_REPORTED, @@ -7709,7 +7519,7 @@ public class WifiMetricsTest extends WifiBaseTest { false, SCORER_PREDICTION_RESULT_REPORTED__DEVICE_STATE__STATE_NO_CELLULAR_MODEM, POLLING_INTERVAL_NOT_DEFAULT, - SCORER_PREDICTION_RESULT_REPORTED__WIFI_FRAMEWORK_STATE__FRAMEWORK_STATE_AWAKENING, + SCORER_PREDICTION_RESULT_REPORTED__WIFI_FRAMEWORK_STATE__FRAMEWORK_STATE_LINGERING, SCORER_PREDICTION_RESULT_REPORTED__SPEED_SUFFICIENT_NETWORK_CAPABILITIES_DS__UNKNOWN, SCORER_PREDICTION_RESULT_REPORTED__SPEED_SUFFICIENT_NETWORK_CAPABILITIES_US__UNKNOWN, SCORER_PREDICTION_RESULT_REPORTED__SPEED_SUFFICIENT_THROUGHPUT_PREDICTOR_DS__UNKNOWN, @@ -7726,14 +7536,15 @@ public class WifiMetricsTest extends WifiBaseTest { WifiMetrics.SessionData currentSession = new WifiMetrics.SessionData(connectionEvent, "", (long) 1000, 0, 0); mWifiMetrics.mCurrentSession = currentSession; + mWifiMetrics.mLastScreenOffTimeMillis = 1000; + mWifiMetrics.mLastIgnoredPollTimeMillis = 3000; mWifiMetrics.logWifiIsUnusableEvent(TEST_IFACE_NAME, WifiIsUnusableEvent.TYPE_DATA_STALL_BAD_TX); - + mWifiMetrics.updateWiFiEvaluationAndScorerStats(true, null, null); mWifiMetrics.logScorerPredictionResult(false, false, false, POLLING_INTERVAL_DEFAULT, WIFI_IS_UNUSABLE_REPORTED__WIFI_PREDICTED_USABILITY_STATE__WIFI_USABILITY_PREDICTED_USABLE, - WIFI_IS_UNUSABLE_REPORTED__WIFI_PREDICTED_USABILITY_STATE__WIFI_USABILITY_PREDICTED_USABLE, - true, mWifiInfo, mCapabilities); + WIFI_IS_UNUSABLE_REPORTED__WIFI_PREDICTED_USABILITY_STATE__WIFI_USABILITY_PREDICTED_USABLE); ExtendedMockito.verify(() -> WifiStatsLog.write_non_chained( SCORER_PREDICTION_RESULT_REPORTED, @@ -7743,7 +7554,7 @@ public class WifiMetricsTest extends WifiBaseTest { false, SCORER_PREDICTION_RESULT_REPORTED__DEVICE_STATE__STATE_NO_CELLULAR_MODEM, POLLING_INTERVAL_DEFAULT, - SCORER_PREDICTION_RESULT_REPORTED__WIFI_FRAMEWORK_STATE__FRAMEWORK_STATE_AWAKENING, + SCORER_PREDICTION_RESULT_REPORTED__WIFI_FRAMEWORK_STATE__FRAMEWORK_STATE_LINGERING, SCORER_PREDICTION_RESULT_REPORTED__SPEED_SUFFICIENT_NETWORK_CAPABILITIES_DS__UNKNOWN, SCORER_PREDICTION_RESULT_REPORTED__SPEED_SUFFICIENT_NETWORK_CAPABILITIES_US__UNKNOWN, SCORER_PREDICTION_RESULT_REPORTED__SPEED_SUFFICIENT_THROUGHPUT_PREDICTOR_DS__UNKNOWN, @@ -7760,11 +7571,13 @@ public class WifiMetricsTest extends WifiBaseTest { WifiMetrics.SessionData currentSession = new WifiMetrics.SessionData(connectionEvent, "", (long) 1000, 0, 0); mWifiMetrics.mCurrentSession = currentSession; + mWifiMetrics.mLastScreenOffTimeMillis = 1000; + mWifiMetrics.mLastIgnoredPollTimeMillis = 3000; + mWifiMetrics.updateWiFiEvaluationAndScorerStats(true, null, null); mWifiMetrics.logScorerPredictionResult(false, false, false, POLLING_INTERVAL_DEFAULT, WIFI_IS_UNUSABLE_REPORTED__WIFI_PREDICTED_USABILITY_STATE__WIFI_USABILITY_PREDICTED_USABLE, - WIFI_IS_UNUSABLE_REPORTED__WIFI_PREDICTED_USABILITY_STATE__WIFI_USABILITY_PREDICTED_USABLE, - true, mWifiInfo, mCapabilities); + WIFI_IS_UNUSABLE_REPORTED__WIFI_PREDICTED_USABILITY_STATE__WIFI_USABILITY_PREDICTED_USABLE); ExtendedMockito.verify(() -> WifiStatsLog.write_non_chained( SCORER_PREDICTION_RESULT_REPORTED, @@ -7774,7 +7587,7 @@ public class WifiMetricsTest extends WifiBaseTest { true, SCORER_PREDICTION_RESULT_REPORTED__DEVICE_STATE__STATE_NO_CELLULAR_MODEM, POLLING_INTERVAL_DEFAULT, - SCORER_PREDICTION_RESULT_REPORTED__WIFI_FRAMEWORK_STATE__FRAMEWORK_STATE_AWAKENING, + SCORER_PREDICTION_RESULT_REPORTED__WIFI_FRAMEWORK_STATE__FRAMEWORK_STATE_LINGERING, SCORER_PREDICTION_RESULT_REPORTED__SPEED_SUFFICIENT_NETWORK_CAPABILITIES_DS__UNKNOWN, SCORER_PREDICTION_RESULT_REPORTED__SPEED_SUFFICIENT_NETWORK_CAPABILITIES_US__UNKNOWN, SCORER_PREDICTION_RESULT_REPORTED__SPEED_SUFFICIENT_THROUGHPUT_PREDICTOR_DS__UNKNOWN, @@ -7826,13 +7639,4 @@ public class WifiMetricsTest extends WifiBaseTest { mWifiMetrics.resetWifiUnusableEvent(); assertEquals(WifiIsUnusableEvent.TYPE_UNKNOWN, mWifiMetrics.mUnusableEventType); } - - /** Verifies WiFi Scorer new stats collection flag could be set properly */ - @Test - public void verifyWifiScorerNewStatsCollectionFlagTrue() { - when(Flags.wifiScorerNewStatsCollection()).thenReturn(true); - assertEquals(mWifiMetrics.isWiFiScorerNewStatsCollected(), true); - when(Flags.wifiScorerNewStatsCollection()).thenReturn(false); - assertEquals(mWifiMetrics.isWiFiScorerNewStatsCollected(), false); - } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiMulticastLockManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiMulticastLockManagerTest.java index 8464293508..f57f8c43b1 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiMulticastLockManagerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiMulticastLockManagerTest.java @@ -22,8 +22,9 @@ import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_SECONDARY_TR import static org.junit.Assert.*; import static org.mockito.Mockito.*; +import android.app.ActivityManager; +import android.content.Context; import android.os.BatteryStatsManager; -import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.os.WorkSource; @@ -50,6 +51,7 @@ import org.mockito.Spy; public class WifiMulticastLockManagerTest extends WifiBaseTest { private static final String WL_1_TAG = "Wakelock-1"; private static final String WL_2_TAG = "Wakelock-2"; + private static final int TEST_UID = 123; private TestLooper mLooper; @Mock ConcreteClientModeManager mClientModeManager; @@ -58,7 +60,11 @@ public class WifiMulticastLockManagerTest extends WifiBaseTest { @Spy FakeFilterController mFilterController2 = new FakeFilterController(); @Mock BatteryStatsManager mBatteryStats; @Mock ActiveModeWarden mActiveModeWarden; + @Mock Context mContext; + @Mock ActivityManager mActivityManager; @Captor ArgumentCaptor<PrimaryClientModeManagerChangedCallback> mPrimaryChangedCallbackCaptor; + @Captor ArgumentCaptor<ActivityManager.OnUidImportanceListener> mUidImportanceListenerCaptor = + ArgumentCaptor.forClass(ActivityManager.OnUidImportanceListener.class); WifiMulticastLockManager mManager; /** @@ -78,11 +84,14 @@ public class WifiMulticastLockManagerTest extends WifiBaseTest { when(mClientModeManager2.getRole()).thenReturn(ROLE_CLIENT_SECONDARY_TRANSIENT); when(mActiveModeWarden.getPrimaryClientModeManager()).thenReturn(mClientModeManager); + when(mContext.getSystemService(ActivityManager.class)).thenReturn(mActivityManager); mManager = new WifiMulticastLockManager(mActiveModeWarden, mBatteryStats, - mLooper.getLooper()); + mLooper.getLooper(), mContext); verify(mActiveModeWarden).registerPrimaryClientModeManagerChangedCallback( mPrimaryChangedCallbackCaptor.capture()); + verify(mActivityManager).addOnUidImportanceListener( + mUidImportanceListenerCaptor.capture(), anyInt()); } /** @@ -91,7 +100,7 @@ public class WifiMulticastLockManagerTest extends WifiBaseTest { @Test public void noLocks() { assertFalse(mManager.isMulticastEnabled()); - mManager.initializeFiltering(); + mManager.startFilteringMulticastPackets(); verify(mFilterController, times(1)).startFilteringMulticastPackets(); } @@ -101,21 +110,21 @@ public class WifiMulticastLockManagerTest extends WifiBaseTest { @Test public void oneLock() throws RemoteException { IBinder binder = mock(IBinder.class); - mManager.acquireLock(binder, WL_1_TAG); + mManager.acquireLock(TEST_UID, binder, WL_1_TAG); assertTrue(mManager.isMulticastEnabled()); verify(mFilterController).stopFilteringMulticastPackets(); - mManager.initializeFiltering(); + mManager.startFilteringMulticastPackets(); verify(mFilterController, times(0)).startFilteringMulticastPackets(); ArgumentCaptor<WorkSource> wsCaptor = ArgumentCaptor.forClass(WorkSource.class); verify(mBatteryStats).reportWifiMulticastEnabled(wsCaptor.capture()); assertNotNull(wsCaptor.getValue()); - assertEquals(Binder.getCallingUid(), wsCaptor.getValue().getAttributionUid()); + assertEquals(TEST_UID, wsCaptor.getValue().getAttributionUid()); verify(mBatteryStats, times(0)).reportWifiMulticastDisabled(any()); - mManager.releaseLock(WL_1_TAG); + mManager.releaseLock(TEST_UID, binder, WL_1_TAG); verify(mBatteryStats).reportWifiMulticastDisabled(wsCaptor.capture()); assertNotNull(wsCaptor.getValue()); - assertEquals(Binder.getCallingUid(), wsCaptor.getValue().getAttributionUid()); + assertEquals(TEST_UID, wsCaptor.getValue().getAttributionUid()); assertFalse(mManager.isMulticastEnabled()); } @@ -151,7 +160,7 @@ public class WifiMulticastLockManagerTest extends WifiBaseTest { assertTrue(mFilterController2.isFilteringStarted()); IBinder binder = mock(IBinder.class); - mManager.acquireLock(binder, WL_1_TAG); + mManager.acquireLock(TEST_UID, binder, WL_1_TAG); assertTrue(mManager.isMulticastEnabled()); // CMM1 filtering stopped assertFalse(mFilterController.isFilteringStarted()); @@ -172,7 +181,7 @@ public class WifiMulticastLockManagerTest extends WifiBaseTest { // CMM2 filter stopped assertFalse(mFilterController2.isFilteringStarted()); - mManager.releaseLock(WL_1_TAG); + mManager.releaseLock(TEST_UID, binder, WL_1_TAG); assertFalse(mManager.isMulticastEnabled()); // CMM1 filter started assertTrue(mFilterController.isFilteringStarted()); @@ -186,15 +195,15 @@ public class WifiMulticastLockManagerTest extends WifiBaseTest { @Test public void oneLock_wrongName() throws RemoteException { IBinder binder = mock(IBinder.class); - mManager.acquireLock(binder, WL_1_TAG); + mManager.acquireLock(TEST_UID, binder, WL_1_TAG); assertTrue(mManager.isMulticastEnabled()); verify(mFilterController).stopFilteringMulticastPackets(); - mManager.initializeFiltering(); + mManager.startFilteringMulticastPackets(); verify(mFilterController, never()).startFilteringMulticastPackets(); verify(mBatteryStats).reportWifiMulticastEnabled(any()); verify(mBatteryStats, never()).reportWifiMulticastDisabled(any()); - mManager.releaseLock(WL_2_TAG); + mManager.releaseLock(TEST_UID, binder, WL_2_TAG); verify(mBatteryStats, never()).reportWifiMulticastDisabled(any()); assertTrue(mManager.isMulticastEnabled()); } @@ -209,25 +218,25 @@ public class WifiMulticastLockManagerTest extends WifiBaseTest { InOrder inOrderHandler = inOrder(mFilterController); InOrder inOrderBatteryStats = inOrder(mBatteryStats); - mManager.acquireLock(binder, WL_1_TAG); + mManager.acquireLock(TEST_UID, binder, WL_1_TAG); inOrderHandler.verify(mFilterController).stopFilteringMulticastPackets(); inOrderBatteryStats.verify(mBatteryStats).reportWifiMulticastEnabled(any()); assertTrue(mManager.isMulticastEnabled()); - mManager.acquireLock(binder, WL_2_TAG); + mManager.acquireLock(TEST_UID, binder, WL_2_TAG); inOrderHandler.verify(mFilterController).stopFilteringMulticastPackets(); inOrderBatteryStats.verify(mBatteryStats).reportWifiMulticastEnabled(any()); assertTrue(mManager.isMulticastEnabled()); - mManager.initializeFiltering(); + mManager.startFilteringMulticastPackets(); inOrderHandler.verify(mFilterController, never()).startFilteringMulticastPackets(); - mManager.releaseLock(WL_2_TAG); + mManager.releaseLock(TEST_UID, binder, WL_2_TAG); inOrderHandler.verify(mFilterController, never()).startFilteringMulticastPackets(); inOrderBatteryStats.verify(mBatteryStats).reportWifiMulticastDisabled(any()); assertTrue(mManager.isMulticastEnabled()); - mManager.releaseLock(WL_1_TAG); + mManager.releaseLock(TEST_UID, binder, WL_1_TAG); inOrderHandler.verify(mFilterController).startFilteringMulticastPackets(); inOrderBatteryStats.verify(mBatteryStats).reportWifiMulticastDisabled(any()); assertFalse(mManager.isMulticastEnabled()); @@ -243,27 +252,131 @@ public class WifiMulticastLockManagerTest extends WifiBaseTest { InOrder inOrderHandler = inOrder(mFilterController); InOrder inOrderBatteryStats = inOrder(mBatteryStats); - mManager.acquireLock(binder, WL_1_TAG); + mManager.acquireLock(TEST_UID, binder, WL_1_TAG); inOrderHandler.verify(mFilterController).stopFilteringMulticastPackets(); inOrderBatteryStats.verify(mBatteryStats).reportWifiMulticastEnabled(any()); assertTrue(mManager.isMulticastEnabled()); - mManager.acquireLock(binder, WL_2_TAG); + mManager.acquireLock(TEST_UID, binder, WL_2_TAG); inOrderHandler.verify(mFilterController).stopFilteringMulticastPackets(); inOrderBatteryStats.verify(mBatteryStats).reportWifiMulticastEnabled(any()); assertTrue(mManager.isMulticastEnabled()); - mManager.initializeFiltering(); + mManager.startFilteringMulticastPackets(); inOrderHandler.verify(mFilterController, never()).startFilteringMulticastPackets(); - mManager.releaseLock(WL_1_TAG); + mManager.releaseLock(TEST_UID, binder, WL_1_TAG); inOrderHandler.verify(mFilterController, never()).startFilteringMulticastPackets(); inOrderBatteryStats.verify(mBatteryStats).reportWifiMulticastDisabled(any()); assertTrue(mManager.isMulticastEnabled()); - mManager.releaseLock(WL_2_TAG); + mManager.releaseLock(TEST_UID, binder, WL_2_TAG); inOrderHandler.verify(mFilterController).startFilteringMulticastPackets(); inOrderBatteryStats.verify(mBatteryStats).reportWifiMulticastDisabled(any()); assertFalse(mManager.isMulticastEnabled()); } + + /** + * Verify the behavior when two separate locks are created using the same tag. + * + * Since locks are uniquely identified by (binder, tag), we expect that multicast is + * enabled until both locks have been released. + */ + @Test + public void testMultipleLocksWithSameTag() throws RemoteException { + IBinder binder1 = mock(IBinder.class); + IBinder binder2 = mock(IBinder.class); + + // Both acquired locks have the same tag + mManager.acquireLock(TEST_UID, binder1, WL_1_TAG); + mManager.acquireLock(TEST_UID, binder2, WL_1_TAG); + assertTrue(mManager.isMulticastEnabled()); + + mManager.releaseLock(TEST_UID, binder1, WL_1_TAG); + verify(mBatteryStats, times(1)).reportWifiMulticastDisabled(any()); + assertTrue(mManager.isMulticastEnabled()); + + mManager.releaseLock(TEST_UID, binder2, WL_1_TAG); + verify(mBatteryStats, times(2)).reportWifiMulticastDisabled(any()); + assertFalse(mManager.isMulticastEnabled()); + } + + /** + * Test that mulicast filtering is toggled correctly when the owner of + * a single lock transitions between importance levels. + */ + @Test + public void testSingleLockActiveStateChange() { + IBinder binder = mock(IBinder.class); + mManager.acquireLock(TEST_UID, binder, WL_1_TAG); + assertTrue(mManager.isMulticastEnabled()); + verify(mFilterController).stopFilteringMulticastPackets(); + + // Transition UID to low importance + mUidImportanceListenerCaptor.getValue().onUidImportance( + TEST_UID, ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED); + mLooper.dispatchAll(); + assertFalse(mManager.isMulticastEnabled()); + verify(mFilterController).startFilteringMulticastPackets(); + + // Transition UID to high importance + mUidImportanceListenerCaptor.getValue().onUidImportance( + TEST_UID, ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND); + mLooper.dispatchAll(); + assertTrue(mManager.isMulticastEnabled()); + verify(mFilterController, times(2)).stopFilteringMulticastPackets(); + + mManager.releaseLock(TEST_UID, binder, WL_1_TAG); + assertFalse(mManager.isMulticastEnabled()); + verify(mFilterController, times(2)).startFilteringMulticastPackets(); + } + + /** + * Test that mulicast filtering is toggled correctly when multiple lock owners + * transition between importance levels. + */ + @Test + public void testMultipleOwnersActiveStateChange() { + int uid1 = TEST_UID; + int uid2 = TEST_UID + 1; + IBinder binder1 = mock(IBinder.class); + IBinder binder2 = mock(IBinder.class); + + mManager.acquireLock(uid1, binder1, WL_1_TAG); + mManager.acquireLock(uid2, binder2, WL_2_TAG); + assertTrue(mManager.isMulticastEnabled()); + verify(mFilterController, times(2)).stopFilteringMulticastPackets(); + + // Transition UID 1 to low importance. Since UID 2 is still active, + // multicast should still be enabled. + mUidImportanceListenerCaptor.getValue().onUidImportance( + uid1, ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED); + mLooper.dispatchAll(); + assertTrue(mManager.isMulticastEnabled()); + verify(mFilterController, never()).startFilteringMulticastPackets(); + + // Transition UID 2 to low importance. Since no lock owners are active, + // multicast should be disabled. + mUidImportanceListenerCaptor.getValue().onUidImportance( + uid2, ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED); + mLooper.dispatchAll(); + assertFalse(mManager.isMulticastEnabled()); + verify(mFilterController).startFilteringMulticastPackets(); + + // Transition UID 2 back to high importance. Multicast should be re-enabled. + mUidImportanceListenerCaptor.getValue().onUidImportance( + uid2, ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND); + mLooper.dispatchAll(); + assertTrue(mManager.isMulticastEnabled()); + verify(mFilterController, times(3)).stopFilteringMulticastPackets(); + + // Release the lock held by UID 1. An active lock is still held by UID 2. + mManager.releaseLock(uid1, binder1, WL_1_TAG); + assertTrue(mManager.isMulticastEnabled()); + + // Release the lock held by UID 2. No locks are active. + mManager.releaseLock(uid2, binder2, WL_2_TAG); + assertFalse(mManager.isMulticastEnabled()); + verify(mFilterController, times(2)).startFilteringMulticastPackets(); + } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiNativeInterfaceManagementTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiNativeInterfaceManagementTest.java index e2806fe6de..b0dec70dfe 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiNativeInterfaceManagementTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiNativeInterfaceManagementTest.java @@ -40,11 +40,13 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import static org.mockito.Mockito.withSettings; import android.app.test.MockAnswerUtil; import android.net.wifi.OuiKeyedData; import android.net.wifi.SoftApConfiguration; import android.net.wifi.WifiContext; +import android.net.wifi.WifiMigration; import android.net.wifi.WifiScanner; import android.net.wifi.nl80211.WifiNl80211Manager; import android.os.Handler; @@ -53,6 +55,7 @@ import android.os.test.TestLooper; import androidx.test.filters.SmallTest; +import com.android.dx.mockito.inline.extended.ExtendedMockito; import com.android.modules.utils.build.SdkLevel; import com.android.server.wifi.HalDeviceManager.InterfaceDestroyedListener; import com.android.server.wifi.WifiNative.SupplicantDeathEventHandler; @@ -61,6 +64,7 @@ import com.android.server.wifi.hal.WifiNanIface; import com.android.server.wifi.p2p.WifiP2pNative; import com.android.server.wifi.util.NetdWrapper; import com.android.server.wifi.util.NetdWrapper.NetdEventObserver; +import com.android.wifi.flags.Flags; import com.android.wifi.resources.R; import org.junit.After; @@ -70,6 +74,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.MockitoSession; import java.util.ArrayList; import java.util.BitSet; @@ -85,6 +90,7 @@ public class WifiNativeInterfaceManagementTest extends WifiBaseTest { private static final String IFACE_NAME_0 = "mockWlan0"; private static final String IFACE_NAME_1 = "mockWlan1"; private static final String SELF_RECOVERY_IFACE_NAME = "mockWlan2"; + private static final String IFACE_NAME_AWARE = "MockAware"; private static final WorkSource TEST_WORKSOURCE = new WorkSource(); private static final long[] TEST_SUPPORTED_FEATURES = new long[]{ 0 }; private static final int STA_FAILURE_CODE_START_DAEMON = 1; @@ -127,6 +133,7 @@ public class WifiNativeInterfaceManagementTest extends WifiBaseTest { @Mock private SoftApManager mSoftApManager; @Mock private WifiNanIface mActiveWifiNanIface; @Mock DeviceConfigFacade mDeviceConfigFacade; + private MockitoSession mSession; private TestLooper mLooper; private WifiNative.Iface mActiveP2pIface; @@ -214,7 +221,8 @@ public class WifiNativeInterfaceManagementTest extends WifiBaseTest { when(mHostapdHal.isInitializationStarted()).thenReturn(false); when(mHostapdHal.isInitializationComplete()).thenReturn(true); when(mHostapdHal.startDaemon()).thenReturn(true); - when(mHostapdHal.addAccessPoint(any(), any(), anyBoolean(), any())).thenReturn(true); + when(mHostapdHal.addAccessPoint(any(), any(), anyBoolean(), + anyBoolean(), any(), any())).thenReturn(true); when(mHostapdHal.removeAccessPoint(any())).thenReturn(true); when(mHostapdHal.registerApCallback(any(), any())).thenReturn(true); @@ -241,6 +249,13 @@ public class WifiNativeInterfaceManagementTest extends WifiBaseTest { eq(WifiSettingsConfigStore.WIFI_NATIVE_SUPPORTED_STA_BANDS))) .thenReturn(TEST_SUPPORTED_BANDS); + mSession = ExtendedMockito.mockitoSession() + .mockStatic(Flags.class, withSettings().lenient()) + .mockStatic(WifiMigration.class, withSettings().lenient()) + .startMocking(); + when(Flags.rsnOverriding()).thenReturn(false); + when(mActiveWifiNanIface.getName()).thenReturn(IFACE_NAME_AWARE); + mInOrder = inOrder(mWifiVendorHal, mWificondControl, mSupplicantStaIfaceHal, mHostapdHal, mWifiMonitor, mNetdWrapper, mIfaceCallback0, mIfaceCallback1, mIfaceEventCallback0, mWifiMetrics, mWifiP2pNative); @@ -263,6 +278,9 @@ public class WifiNativeInterfaceManagementTest extends WifiBaseTest { @After public void tearDown() throws Exception { + if (mSession != null) { + mSession.finishMocking(); + } verifyNoMoreInteractions(mWifiVendorHal, mWificondControl, mSupplicantStaIfaceHal, mHostapdHal, mWifiMonitor, mNetdWrapper, mIfaceCallback0, mIfaceCallback1, mIfaceEventCallback0, mWifiMetrics); @@ -562,7 +580,7 @@ public class WifiNativeInterfaceManagementTest extends WifiBaseTest { }).when(mWifiVendorHal).createApIface(any(), any(), anyInt(), eq(false), any(), anyList()); assertEquals(IFACE_NAME_0, mWifiNative.setupInterfaceForSoftApMode(mIfaceCallback1, TEST_WORKSOURCE, SoftApConfiguration.BAND_2GHZ, false, mSoftApManager, - new ArrayList<>())); + new ArrayList<>(), false)); validateHostApdStart(); // Creation of AP interface should trigger the STA interface destroy @@ -588,6 +606,7 @@ public class WifiNativeInterfaceManagementTest extends WifiBaseTest { mInOrder.verify(mSupplicantStaIfaceHal).getWpaDriverFeatureSet(ifaceName); mInOrder.verify(mWifiVendorHal).getSupportedFeatureSet(ifaceName); mInOrder.verify(mWifiVendorHal).getTwtCapabilities(ifaceName); + mInOrder.verify(mSupplicantStaIfaceHal).getUsdCapabilities(ifaceName); mInOrder.verify(mWifiVendorHal).getUsableChannels(anyInt(), anyInt(), anyInt()); } @@ -616,6 +635,7 @@ public class WifiNativeInterfaceManagementTest extends WifiBaseTest { mInOrder.verify(mSupplicantStaIfaceHal).getWpaDriverFeatureSet(ifaceName); mInOrder.verify(mWifiVendorHal).getSupportedFeatureSet(ifaceName); mInOrder.verify(mWifiVendorHal).getTwtCapabilities(ifaceName); + mInOrder.verify(mSupplicantStaIfaceHal).getUsdCapabilities(ifaceName); mInOrder.verify(mWifiVendorHal).getUsableChannels(anyInt(), anyInt(), anyInt()); mInOrder.verify(mWifiVendorHal).enableStaChannelForPeerNetwork(anyBoolean(), anyBoolean()); } @@ -629,6 +649,7 @@ public class WifiNativeInterfaceManagementTest extends WifiBaseTest { mInOrder.verify(mSupplicantStaIfaceHal).getWpaDriverFeatureSet(ifaceName); mInOrder.verify(mWifiVendorHal).getSupportedFeatureSet(ifaceName); mInOrder.verify(mWifiVendorHal).getTwtCapabilities(ifaceName); + mInOrder.verify(mSupplicantStaIfaceHal).getUsdCapabilities(ifaceName); mInOrder.verify(mWifiVendorHal).getUsableChannels(anyInt(), anyInt(), anyInt()); } @@ -881,7 +902,7 @@ public class WifiNativeInterfaceManagementTest extends WifiBaseTest { assertEquals(IFACE_NAME_0, mWifiNative.setupInterfaceForSoftApMode(mIfaceCallback1, TEST_WORKSOURCE, SoftApConfiguration.BAND_2GHZ, false, mSoftApManager, - new ArrayList<>())); + new ArrayList<>(), false)); validateHostApdStart(); // Creation of AP interface should trigger the STA interface destroy validateOnDestroyedClientInterface( @@ -973,11 +994,14 @@ public class WifiNativeInterfaceManagementTest extends WifiBaseTest { // Start softap assertEquals(SoftApManager.START_RESULT_SUCCESS, mWifiNative.startSoftAp(IFACE_NAME_0, new SoftApConfiguration.Builder().build(), - true, mock(WifiNative.SoftApHalCallback.class))); + true, mock(WifiNative.SoftApHalCallback.class), false)); mInOrder.verify(mHostapdHal).isApInfoCallbackSupported(); mInOrder.verify(mHostapdHal).registerApCallback(any(), any()); - mInOrder.verify(mHostapdHal).addAccessPoint(any(), any(), anyBoolean(), any()); + mInOrder.verify(mWifiVendorHal).isVendorHalSupported(); + mInOrder.verify(mWifiVendorHal).getBridgedApInstances(IFACE_NAME_0); + mInOrder.verify(mHostapdHal).addAccessPoint(any(), any(), anyBoolean(), + anyBoolean(), any(), any()); // Trigger vendor HAL death mHostapdDeathHandlerCaptor.getValue().onDeath(); @@ -1002,12 +1026,15 @@ public class WifiNativeInterfaceManagementTest extends WifiBaseTest { // Start softap assertEquals(SoftApManager.START_RESULT_SUCCESS, mWifiNative.startSoftAp(IFACE_NAME_0, new SoftApConfiguration.Builder().build(), - true, mock(WifiNative.SoftApHalCallback.class))); + true, mock(WifiNative.SoftApHalCallback.class), false)); mInOrder.verify(mHostapdHal).isApInfoCallbackSupported(); mInOrder.verify(mWificondControl).registerApCallback(any(), any(), any()); verify(mHostapdHal, never()).registerApCallback(any(), any()); - mInOrder.verify(mHostapdHal).addAccessPoint(any(), any(), anyBoolean(), any()); + mInOrder.verify(mWifiVendorHal).isVendorHalSupported(); + mInOrder.verify(mWifiVendorHal).getBridgedApInstances(IFACE_NAME_0); + mInOrder.verify(mHostapdHal).addAccessPoint(any(), any(), anyBoolean(), + anyBoolean(), any(), any()); // Trigger vendor HAL death mHostapdDeathHandlerCaptor.getValue().onDeath(); @@ -1130,7 +1157,7 @@ public class WifiNativeInterfaceManagementTest extends WifiBaseTest { when(mWifiVendorHal.startVendorHal()).thenReturn(false); assertNull(mWifiNative.setupInterfaceForSoftApMode(mIfaceCallback0, TEST_WORKSOURCE, SoftApConfiguration.BAND_2GHZ, false, mSoftApManager, - new ArrayList<>())); + new ArrayList<>(), false)); mInOrder.verify(mWifiVendorHal).isVendorHalSupported(); mInOrder.verify(mWifiVendorHal).startVendorHal(); @@ -1384,6 +1411,7 @@ public class WifiNativeInterfaceManagementTest extends WifiBaseTest { mInOrder.verify(mSupplicantStaIfaceHal).getWpaDriverFeatureSet(ifaceName); mInOrder.verify(mWifiVendorHal).getSupportedFeatureSet(ifaceName); mInOrder.verify(mWifiVendorHal).getTwtCapabilities(ifaceName); + mInOrder.verify(mSupplicantStaIfaceHal).getUsdCapabilities(ifaceName); mInOrder.verify(mWifiVendorHal).getUsableChannels(anyInt(), anyInt(), anyInt()); } } @@ -1572,6 +1600,30 @@ public class WifiNativeInterfaceManagementTest extends WifiBaseTest { executeAndValidateTeardownNanInterface(false, false, false, false, mActiveNanIface); } + @Test + public void testCreateNanIfaceFailureWhenFailToCreateNan() throws Exception { + when(mHalDeviceManager.createNanIface(any(), any(), any())) + .thenReturn(null); + mActiveNanIface = mWifiNative.createNanIface(mTestInterfaceDestroyedListener, + mCreateIfaceEventHandler, TEST_WORKSOURCE); + validateStartHal(false, true); + assertNull(mActiveNanIface); + validateOnDestroyedNanInterface(false, false, false, false); + } + + @Test + public void testCreateNanIfaceFailureWhenFailToGetNanIfaceName() throws Exception { + when(mHalDeviceManager.createNanIface(any(), any(), any())) + .thenReturn(mActiveWifiNanIface); + // The empty aware iface will cause failure + when(mActiveWifiNanIface.getName()).thenReturn(null); + mActiveNanIface = mWifiNative.createNanIface(mTestInterfaceDestroyedListener, + mCreateIfaceEventHandler, TEST_WORKSOURCE); + validateStartHal(false, true); + assertNull(mActiveNanIface); + validateOnDestroyedNanInterface(false, false, false, false); + } + private void executeAndValidateSetupClientInterface( boolean hasStaIface, boolean hasApIface, String ifaceName, @Mock WifiNative.InterfaceCallback callback, @@ -1836,6 +1888,7 @@ public class WifiNativeInterfaceManagementTest extends WifiBaseTest { mInOrder.verify(mSupplicantStaIfaceHal).getWpaDriverFeatureSet(ifaceName); mInOrder.verify(mWifiVendorHal).getSupportedFeatureSet(ifaceName); mInOrder.verify(mWifiVendorHal).getTwtCapabilities(ifaceName); + mInOrder.verify(mSupplicantStaIfaceHal).getUsdCapabilities(ifaceName); mInOrder.verify(mWifiVendorHal).getUsableChannels(anyInt(), anyInt(), anyInt()); mInOrder.verify(mWifiVendorHal).enableStaChannelForPeerNetwork(anyBoolean(), anyBoolean()); } @@ -1955,7 +2008,7 @@ public class WifiNativeInterfaceManagementTest extends WifiBaseTest { .thenReturn(ifaceName); assertEquals(failureCode == 0 ? ifaceName : null, mWifiNative.setupInterfaceForSoftApMode( callback, TEST_WORKSOURCE, SoftApConfiguration.BAND_2GHZ, isBridged, - mSoftApManager, new ArrayList<>())); + mSoftApManager, new ArrayList<>(), false)); validateSetupSoftApInterface( hasStaIface, hasApIface, hasP2pIface, hasNanIface, ifaceName, @@ -2052,6 +2105,7 @@ public class WifiNativeInterfaceManagementTest extends WifiBaseTest { mInOrder.verify(mSupplicantStaIfaceHal).getWpaDriverFeatureSet(ifaceName); mInOrder.verify(mWifiVendorHal).getSupportedFeatureSet(ifaceName); mInOrder.verify(mWifiVendorHal).getTwtCapabilities(ifaceName); + mInOrder.verify(mSupplicantStaIfaceHal).getUsdCapabilities(ifaceName); mInOrder.verify(mWifiVendorHal).getUsableChannels(anyInt(), anyInt(), anyInt()); } diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java index a970e187a1..fbe3322961 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiNativeTest.java @@ -21,7 +21,8 @@ import static android.net.wifi.WifiScanner.WIFI_BAND_5_GHZ; import static com.android.server.wifi.WifiSettingsConfigStore.WIFI_NATIVE_SUPPORTED_FEATURES; import static com.android.server.wifi.WifiSettingsConfigStore.WIFI_NATIVE_EXTENDED_SUPPORTED_FEATURES; -import static com.android.server.wifi.util.GeneralUtil.longToBitset; +import static com.android.server.wifi.util.GeneralUtil.bitsetToLong; +import static com.android.server.wifi.TestUtil.createCapabilityBitset; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -74,6 +75,7 @@ import com.android.dx.mockito.inline.extended.ExtendedMockito; import com.android.modules.utils.build.SdkLevel; import com.android.server.wifi.coex.CoexManager; import com.android.server.wifi.hal.WifiChip; +import com.android.server.wifi.p2p.WifiP2pNative; import com.android.server.wifi.proto.WifiStatsLog; import com.android.server.wifi.util.NativeUtil; import com.android.server.wifi.util.NetdWrapper; @@ -256,8 +258,8 @@ public class WifiNativeTest extends WifiBaseTest { return result; } - private static final BitSet WIFI_TEST_FEATURE = longToBitset(0x800000000L); - + private static final BitSet WIFI_TEST_FEATURE = + createCapabilityBitset(WifiManager.WIFI_FEATURE_OWE); private static final RadioChainInfo MOCK_NATIVE_RADIO_CHAIN_INFO_1 = new RadioChainInfo(1, -89); private static final RadioChainInfo MOCK_NATIVE_RADIO_CHAIN_INFO_2 = new RadioChainInfo(0, -78); private static final WorkSource TEST_WORKSOURCE = new WorkSource(); @@ -289,6 +291,7 @@ public class WifiNativeTest extends WifiBaseTest { @Mock private WifiGlobals mWifiGlobals; @Mock DeviceConfigFacade mDeviceConfigFacade; @Mock WifiChip.AfcChannelAllowance mAfcChannelAllowance; + @Mock private WifiP2pNative mWifiP2pNative; private MockitoSession mSession; ArgumentCaptor<WifiNl80211Manager.ScanEventCallback> mScanCallbackCaptor = @@ -309,6 +312,8 @@ public class WifiNativeTest extends WifiBaseTest { when(mWifiVendorHal.createApIface(any(), any(), anyInt(), anyBoolean(), any(), anyList())) .thenReturn(WIFI_IFACE_NAME); when(mWifiVendorHal.getSupportedFeatureSet(anyString())).thenReturn(new BitSet()); + when(mWifiVendorHal.replaceStaIfaceRequestorWs(WIFI_IFACE_NAME, TEST_WORKSOURCE)) + .thenReturn(true); when(mBuildProperties.isEngBuild()).thenReturn(false); when(mBuildProperties.isUserdebugBuild()).thenReturn(false); @@ -338,6 +343,7 @@ public class WifiNativeTest extends WifiBaseTest { when(mWifiInjector.getContext()).thenReturn(mContext); when(mWifiInjector.getSsidTranslator()).thenReturn(mSsidTranslator); when(mWifiInjector.getWifiGlobals()).thenReturn(mWifiGlobals); + when(mWifiInjector.getWifiP2pNative()).thenReturn(mWifiP2pNative); mResources = getMockResources(); mResources.setBoolean(R.bool.config_wifiNetworkCentricQosPolicyFeatureEnabled, false); when(mContext.getResources()).thenReturn(mResources); @@ -357,7 +363,7 @@ public class WifiNativeTest extends WifiBaseTest { .mockStatic(WifiMigration.class, withSettings().lenient()) .startMocking(); - when(Flags.rsnOverriding()).thenReturn(false); + when(Flags.rsnOverriding()).thenReturn(true); mWifiNative = new WifiNative( mWifiVendorHal, mStaIfaceHal, mHostapdHal, mWificondControl, @@ -725,6 +731,51 @@ public class WifiNativeTest extends WifiBaseTest { } /** + * Verifies valules of ScanResultWithSameFreq in getWifiLinkLayerStats() call + * + */ + @Test + public void testGetWifiLinkLayerStatsForScanResultWithSameFreq() { + WifiLinkLayerStats testWifiLinkLayerStats = new WifiLinkLayerStats(); + testWifiLinkLayerStats.links = new WifiLinkLayerStats.LinkSpecificStats[2]; + // Define 2 test WiFi links, whose frequencyMhz are 0, 1, respectively + for (int i = 0; i < 2; ++i) { + testWifiLinkLayerStats.links[i] = new WifiLinkLayerStats.LinkSpecificStats(); + testWifiLinkLayerStats.links[i].frequencyMhz = i; + } + when(mWifiVendorHal.getWifiLinkLayerStats(WIFI_IFACE_NAME)) + .thenReturn(testWifiLinkLayerStats); + + // Define 6 test WiFi scan results with unique BSSID + // Their frequency are 0, 1, 2, 0, 1, 2, respectively + ScanResult[] scanResults = new ScanResult[6]; + for (int i = 0; i < 6; i++) { + ScanResult scanResult = new ScanResult(); + scanResult.BSSID = Integer.toString(i); + scanResult.frequency = i < 3 ? i : (i - 3); + // Make sure the timestamp is valid + scanResult.timestamp = Long.MAX_VALUE; + scanResults[i] = scanResult; + } + ScanData testScanData = new ScanData(0, 0, + 0, WifiScanner.WIFI_BAND_UNSPECIFIED, scanResults); + when(mWifiVendorHal.getCachedScanData(WIFI_IFACE_NAME)).thenReturn(testScanData); + mWifiNative.setLocationModeEnabled(true); + + WifiLinkLayerStats resultWifiLinkLayerStats = + mWifiNative.getWifiLinkLayerStats(WIFI_IFACE_NAME); + assertEquals(2, resultWifiLinkLayerStats.links.length); + // WiFi link 0's frequency is 0, scan results 0 and 3 have the same frequency + assertEquals(2, resultWifiLinkLayerStats.links[0].scan_results_same_freq.size()); + assertEquals("0", resultWifiLinkLayerStats.links[0].scan_results_same_freq.get(0).bssid); + assertEquals("3", resultWifiLinkLayerStats.links[0].scan_results_same_freq.get(1).bssid); + // WiFi link 1's frequency is 1, scan results 1 and 4 have the same frequency + assertEquals(2, resultWifiLinkLayerStats.links[1].scan_results_same_freq.size()); + assertEquals("1", resultWifiLinkLayerStats.links[1].scan_results_same_freq.get(0).bssid); + assertEquals("4", resultWifiLinkLayerStats.links[1].scan_results_same_freq.get(1).bssid); + } + + /** * Verifies client mode + scan success. */ @Test @@ -819,7 +870,7 @@ public class WifiNativeTest extends WifiBaseTest { when(mWifiVendorHal.getBridgedApInstances(WIFI_IFACE_NAME)) .thenReturn(Arrays.asList(instance1, instance2)); mWifiNative.setupInterfaceForSoftApMode(null, TEST_WORKSOURCE, SoftApConfiguration.BAND_2GHZ - | SoftApConfiguration.BAND_5GHZ, true, mSoftApManager, new ArrayList<>()); + | SoftApConfiguration.BAND_5GHZ, true, mSoftApManager, new ArrayList<>(), false); ArgumentCaptor<HalDeviceManager.InterfaceDestroyedListener> ifaceDestroyedListenerCaptor = ArgumentCaptor.forClass(HalDeviceManager.InterfaceDestroyedListener.class); verify(mWifiVendorHal).createApIface(ifaceDestroyedListenerCaptor.capture(), any(), @@ -908,7 +959,7 @@ public class WifiNativeTest extends WifiBaseTest { mWifiNative.teardownAllInterfaces(); mWifiNative.setupInterfaceForSoftApMode(null, TEST_WORKSOURCE, WIFI_BAND_24_GHZ, false, - mSoftApManager, new ArrayList<>()); + mSoftApManager, new ArrayList<>(), false); verify(mWifiVendorHal, times(4)).setCoexUnsafeChannels(unsafeChannels, restrictions); } @@ -1126,9 +1177,30 @@ public class WifiNativeTest extends WifiBaseTest { @Test public void testRemoveIfaceInstanceFromBridgedApIface() throws Exception { mWifiNative.removeIfaceInstanceFromBridgedApIface( - "br_" + WIFI_IFACE_NAME, WIFI_IFACE_NAME); + "br_" + WIFI_IFACE_NAME, WIFI_IFACE_NAME, false); + verify(mHostapdHal, never()).removeLinkFromMultipleLinkBridgedApIface(anyString(), + anyString()); verify(mWifiVendorHal).removeIfaceInstanceFromBridgedApIface( "br_" + WIFI_IFACE_NAME, WIFI_IFACE_NAME); + + // verify removeLinkFromMultipleLinkBridgedApIface never call when flags is not enabled. + when(Flags.mloSap()).thenReturn(false); + mWifiNative.removeIfaceInstanceFromBridgedApIface( + "br_" + WIFI_IFACE_NAME, WIFI_IFACE_NAME, true); + verify(mHostapdHal, never()).removeLinkFromMultipleLinkBridgedApIface(anyString(), + anyString()); + verify(mWifiVendorHal, times(2)).removeIfaceInstanceFromBridgedApIface( + "br_" + WIFI_IFACE_NAME, WIFI_IFACE_NAME); + + // verify removeLinkFromMultipleLinkBridgedApIface will be called when feature flag + // is enabled. + when(Flags.mloSap()).thenReturn(true); + mWifiNative.removeIfaceInstanceFromBridgedApIface( + "br_" + WIFI_IFACE_NAME, WIFI_IFACE_NAME, true); + verify(mHostapdHal).removeLinkFromMultipleLinkBridgedApIface("br_" + WIFI_IFACE_NAME, + WIFI_IFACE_NAME); + verify(mWifiVendorHal, times(3)).removeIfaceInstanceFromBridgedApIface( + "br_" + WIFI_IFACE_NAME, WIFI_IFACE_NAME); } /** @@ -1609,7 +1681,7 @@ public class WifiNativeTest extends WifiBaseTest { .thenReturn(legacyFeatures); when(mSettingsConfigStore.get(eq(WIFI_NATIVE_EXTENDED_SUPPORTED_FEATURES))) .thenReturn(WIFI_TEST_FEATURE.toLongArray()); - BitSet featureSet = longToBitset(mWifiNative.getSupportedFeatureSet(null)); + BitSet featureSet = mWifiNative.getSupportedFeatureSet(null); assertTrue(featureSet.equals(WIFI_TEST_FEATURE)); } @@ -1620,13 +1692,13 @@ public class WifiNativeTest extends WifiBaseTest { */ @Test public void testGetLegacyFeaturesWhenInterfaceDoesntExist() throws Exception { - long legacyFeatures = 0x321; + long legacyFeatures = bitsetToLong(WIFI_TEST_FEATURE); when(mSettingsConfigStore.get(eq(WIFI_NATIVE_SUPPORTED_FEATURES))) .thenReturn(legacyFeatures); when(mSettingsConfigStore.get(eq(WIFI_NATIVE_EXTENDED_SUPPORTED_FEATURES))) .thenReturn(new long[0]); // no extended features - BitSet featureSet = longToBitset(mWifiNative.getSupportedFeatureSet(null)); - assertTrue(featureSet.equals(longToBitset(legacyFeatures))); + BitSet featureSet = mWifiNative.getSupportedFeatureSet(null); + assertTrue(featureSet.equals(WIFI_TEST_FEATURE)); } /** @@ -1667,8 +1739,37 @@ public class WifiNativeTest extends WifiBaseTest { mWifiNative.setupInterfaceForClientInScanMode(null, TEST_WORKSOURCE, mConcreteClientModeManager); mWifiNative.switchClientInterfaceToConnectivityMode(WIFI_IFACE_NAME, TEST_WORKSOURCE); - verify(mWificondControl).getChannelsMhzForBand(WifiScanner.WIFI_BAND_24_GHZ); - verify(mWificondControl).getChannelsMhzForBand(WifiScanner.WIFI_BAND_5_GHZ); + verify(mWificondControl, times(2)).getChannelsMhzForBand(WifiScanner.WIFI_BAND_24_GHZ); + verify(mWificondControl, times(2)).getChannelsMhzForBand(WifiScanner.WIFI_BAND_5_GHZ); + assertEquals(3, mWifiNative.getSupportedBandsForSta(WIFI_IFACE_NAME)); + } + + /** + * Verifies that getSupportedBandsForStaFromWifiCond() calls underlying wificond + * when all 5G available channels are DFS channels. + */ + @Test + public void testGetSupportedBandsWhenOnly5DhsExist() throws Exception { + when(mWificondControl.getChannelsMhzForBand(WifiScanner.WIFI_BAND_24_GHZ)).thenReturn( + new int[]{2412}); + when(mWificondControl.getChannelsMhzForBand(WifiScanner.WIFI_BAND_5_GHZ)).thenReturn( + new int[0]); + when(mWificondControl.getChannelsMhzForBand(WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY)) + .thenReturn(new int[]{5500}); + when(mWificondControl.getChannelsMhzForBand(WifiScanner.WIFI_BAND_6_GHZ)).thenReturn( + new int[0]); + when(mWificondControl.getChannelsMhzForBand(WifiScanner.WIFI_BAND_60_GHZ)).thenReturn( + new int[0]); + when(mWifiVendorHal.getUsableChannels(WifiScanner.WIFI_BAND_24_5_WITH_DFS_6_60_GHZ, + WifiAvailableChannel.OP_MODE_STA, + WifiAvailableChannel.FILTER_REGULATORY)).thenReturn(null); + mWifiNative.setupInterfaceForClientInScanMode(null, TEST_WORKSOURCE, + mConcreteClientModeManager); + mWifiNative.switchClientInterfaceToConnectivityMode(WIFI_IFACE_NAME, TEST_WORKSOURCE); + verify(mWificondControl, times(2)).getChannelsMhzForBand(WifiScanner.WIFI_BAND_24_GHZ); + verify(mWificondControl, times(2)).getChannelsMhzForBand(WifiScanner.WIFI_BAND_5_GHZ); + verify(mWificondControl, times(2)) + .getChannelsMhzForBand(WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY); assertEquals(3, mWifiNative.getSupportedBandsForSta(WIFI_IFACE_NAME)); } @@ -1856,16 +1957,69 @@ public class WifiNativeTest extends WifiBaseTest { } @Test - public void testRsnOverridingFeatureFlag() throws Exception { + public void testRsnOverridingFeatureSupportOnOlderHals() throws Exception { + when(mStaIfaceHal.isAidlServiceVersionAtLeast(4)).thenReturn(false); + + /* RSN Overriding feature is enabled when overlay config item is set to true */ mResources.setBoolean(R.bool.config_wifiRsnOverridingEnabled, true); - when(Flags.rsnOverriding()).thenReturn(false); mWifiNative.setupInterfaceForClientInScanMode(null, TEST_WORKSOURCE, mConcreteClientModeManager); + mWifiNative.switchClientInterfaceToConnectivityMode(WIFI_IFACE_NAME, TEST_WORKSOURCE); + assertTrue(mWifiNative.mIsRsnOverridingSupported); + mWifiNative.teardownAllInterfaces(); + + /* RSN Overriding feature is disabled when overlay config item is set to false */ + mResources.setBoolean(R.bool.config_wifiRsnOverridingEnabled, false); + mWifiNative.setupInterfaceForClientInScanMode(null, TEST_WORKSOURCE, + mConcreteClientModeManager); + mWifiNative.switchClientInterfaceToConnectivityMode(WIFI_IFACE_NAME, TEST_WORKSOURCE); assertFalse(mWifiNative.mIsRsnOverridingSupported); mWifiNative.teardownAllInterfaces(); - when(Flags.rsnOverriding()).thenReturn(true); + } + + @Test + public void testRsnOverridingFeatureSupportOnNewerHals() throws Exception { + when(mStaIfaceHal.isAidlServiceVersionAtLeast(4)).thenReturn(true); + + /* RSN Overriding feature is enabled based on chip capability */ + mResources.setBoolean(R.bool.config_wifiRsnOverridingEnabled, false); + when(mStaIfaceHal.isRsnOverridingSupported(WIFI_IFACE_NAME)).thenReturn(true); mWifiNative.setupInterfaceForClientInScanMode(null, TEST_WORKSOURCE, mConcreteClientModeManager); + mWifiNative.switchClientInterfaceToConnectivityMode(WIFI_IFACE_NAME, TEST_WORKSOURCE); assertTrue(mWifiNative.mIsRsnOverridingSupported); + mWifiNative.teardownAllInterfaces(); + + /* Overlay config has no effect on newer HALs */ + mResources.setBoolean(R.bool.config_wifiRsnOverridingEnabled, true); + + when(mStaIfaceHal.isRsnOverridingSupported(WIFI_IFACE_NAME)).thenReturn(false); + mWifiNative.setupInterfaceForClientInScanMode(null, TEST_WORKSOURCE, + mConcreteClientModeManager); + mWifiNative.switchClientInterfaceToConnectivityMode(WIFI_IFACE_NAME, TEST_WORKSOURCE); + assertFalse(mWifiNative.mIsRsnOverridingSupported); + mWifiNative.teardownAllInterfaces(); + } + + @Test + public void testIsMLDApSupportMLO() throws Exception { + when(Flags.mloSap()).thenReturn(true); + BitSet mloFeature = + createCapabilityBitset(WifiManager.WIFI_FEATURE_SOFTAP_MLO); + when(mWifiGlobals.isMLDApSupported()).thenReturn(true); + assertFalse(mWifiNative.isMLDApSupportMLO()); + + when(mSettingsConfigStore.get(eq(WIFI_NATIVE_EXTENDED_SUPPORTED_FEATURES))) + .thenReturn(mloFeature.toLongArray()); + mWifiNative = new WifiNative( + mWifiVendorHal, mStaIfaceHal, mHostapdHal, mWificondControl, + mWifiMonitor, mPropertyService, mWifiMetrics, + mHandler, mRandom, mBuildProperties, mWifiInjector); + assertTrue(mWifiNative.isMLDApSupportMLO()); + when(Flags.mloSap()).thenReturn(false); + assertFalse(mWifiNative.isMLDApSupportMLO()); + when(Flags.mloSap()).thenReturn(true); + when(mWifiGlobals.isMLDApSupported()).thenReturn(false); + assertFalse(mWifiNative.isMLDApSupportMLO()); } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiNetworkFactoryTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiNetworkFactoryTest.java index 14048100e0..cebb2b6864 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiNetworkFactoryTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiNetworkFactoryTest.java @@ -22,6 +22,7 @@ import static android.net.wifi.WifiManager.STATUS_LOCAL_ONLY_CONNECTION_FAILURE_ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.android.server.wifi.WifiNetworkFactory.PERIODIC_SCAN_INTERVAL_MS; +import static com.android.server.wifi.TestUtil.createCapabilityBitset; import static com.android.server.wifi.util.NativeUtil.addEnclosingQuotes; import static org.junit.Assert.assertArrayEquals; @@ -293,13 +294,14 @@ public class WifiNetworkFactoryTest extends WifiBaseTest { requestListener.onAnswer(mClientModeManager); return null; }).when(mActiveModeWarden).requestLocalOnlyClientModeManager(any(), any(), any(), any(), - anyBoolean()); + anyBoolean(), anyBoolean()); when(mClientModeManager.getRole()).thenReturn(ActiveModeManager.ROLE_CLIENT_PRIMARY); when(mFrameworkFacade.getSettingsWorkSource(any())).thenReturn( new WorkSource(Process.SYSTEM_UID, "system-service")); - when(mPrimaryClientModeManager.getSupportedFeatures()).thenReturn( - WifiManager.WIFI_FEATURE_WPA3_SAE | WifiManager.WIFI_FEATURE_OWE); + when(mPrimaryClientModeManager.getSupportedFeaturesBitSet()).thenReturn( + createCapabilityBitset( + WifiManager.WIFI_FEATURE_WPA3_SAE, WifiManager.WIFI_FEATURE_OWE)); mWifiNetworkFactory = new WifiNetworkFactory(mLooper.getLooper(), mContext, mNetworkCapabilities, mActivityManager, mAlarmManager, mAppOpsManager, @@ -735,6 +737,20 @@ public class WifiNetworkFactoryTest extends WifiBaseTest { } /** + * Verify handling of new network request with network specifier. + */ + @Test + public void testScanScheduleWithPreferredChannel() { + attachDefaultWifiNetworkSpecifierAndAppInfo(TEST_UID_1, false, + new int[]{TEST_PREFERRED_CHANNEL_FREQ}, false); + mWifiNetworkFactory.needNetworkFor(mNetworkRequest); + verifyPeriodicScans(false, 0, 0, + PERIODIC_SCAN_INTERVAL_MS, // 10s + PERIODIC_SCAN_INTERVAL_MS, // 10s + PERIODIC_SCAN_INTERVAL_MS); // 10s + } + + /** * Validates handling of new network request with unsupported network specifier. */ @Test @@ -2428,7 +2444,7 @@ public class WifiNetworkFactoryTest extends WifiBaseTest { @Test public void testHandleNewNetworkRequestWithSpecifierWhenAwaitingCmRetrieval() throws Exception { doNothing().when(mActiveModeWarden).requestLocalOnlyClientModeManager( - any(), any(), any(), any(), anyBoolean()); + any(), any(), any(), any(), anyBoolean(), anyBoolean()); WorkSource ws = new WorkSource(TEST_UID_1, TEST_PACKAGE_NAME_1); when(mClock.getElapsedSinceBootMillis()).thenReturn(0L); @@ -2450,7 +2466,7 @@ public class WifiNetworkFactoryTest extends WifiBaseTest { ActiveModeWarden.ExternalClientModeManagerRequestListener.class); verify(mActiveModeWarden).requestLocalOnlyClientModeManager( cmListenerCaptor.capture(), eq(ws), - eq("\"" + TEST_SSID_1 + "\""), eq(TEST_BSSID_1), eq(true)); + eq("\"" + TEST_SSID_1 + "\""), eq(TEST_BSSID_1), eq(true), eq(false)); assertNotNull(cmListenerCaptor.getValue()); NetworkRequest oldRequest = new NetworkRequest(mNetworkRequest); @@ -2475,7 +2491,7 @@ public class WifiNetworkFactoryTest extends WifiBaseTest { // Ensure we request a new ClientModeManager. verify(mActiveModeWarden, times(2)).requestLocalOnlyClientModeManager( - any(), any(), any(), any(), anyBoolean()); + any(), any(), any(), any(), anyBoolean(), anyBoolean()); // Now return the CM instance for the previous request. cmListenerCaptor.getValue().onAnswer(mClientModeManager); @@ -2543,7 +2559,7 @@ public class WifiNetworkFactoryTest extends WifiBaseTest { // Ensure we don't request a new ClientModeManager. verify(mActiveModeWarden, never()).requestLocalOnlyClientModeManager( - any(), any(), any(), any(), anyBoolean()); + any(), any(), any(), any(), anyBoolean(), anyBoolean()); // Ignore stale callbacks. WifiConfiguration selectedNetwork = WifiConfigurationTestUtil.createOpenNetwork(); @@ -2584,7 +2600,7 @@ public class WifiNetworkFactoryTest extends WifiBaseTest { // Ensure we don't request a new ClientModeManager. verify(mActiveModeWarden, times(1)).requestLocalOnlyClientModeManager( - any(), any(), any(), any(), anyBoolean()); + any(), any(), any(), any(), anyBoolean(), anyBoolean()); verify(mNetworkRequestMatchCallback).onAbort(); verify(mNetworkRequestMatchCallback, atLeastOnce()).asBinder(); @@ -2632,7 +2648,7 @@ public class WifiNetworkFactoryTest extends WifiBaseTest { // Ensure we do request a new ClientModeManager. verify(mActiveModeWarden, times(1)).requestLocalOnlyClientModeManager( - any(), any(), any(), any(), anyBoolean()); + any(), any(), any(), any(), anyBoolean(), anyBoolean()); verify(mWifiConnectivityManager, times(1)).setSpecificNetworkRequestInProgress(true); verify(mWifiScanner, times(2)).getSingleScanResults(); @@ -3718,7 +3734,7 @@ public class WifiNetworkFactoryTest extends WifiBaseTest { mLooper.dispatchAll(); verify(mActiveModeWarden, never()).requestLocalOnlyClientModeManager( - any(), any(), any(), any(), anyBoolean()); + any(), any(), any(), any(), anyBoolean(), anyBoolean()); verify(mActiveModeWarden, atLeastOnce()).getPrimaryClientModeManager(); if (SdkLevel.isAtLeastS()) { verify(mPrimaryClientModeManager, atLeastOnce()).getConnectedWifiConfiguration(); @@ -3781,7 +3797,7 @@ public class WifiNetworkFactoryTest extends WifiBaseTest { mLooper.dispatchAll(); verify(mActiveModeWarden, atLeastOnce()).requestLocalOnlyClientModeManager( - any(), any(), any(), any(), anyBoolean()); + any(), any(), any(), any(), anyBoolean(), anyBoolean()); if (SdkLevel.isAtLeastS()) { verify(mClientModeManager, atLeastOnce()).getConnectedWifiConfiguration(); verify(mClientModeManager, atLeastOnce()).getConnectingWifiConfiguration(); @@ -3915,11 +3931,13 @@ public class WifiNetworkFactoryTest extends WifiBaseTest { if (stopAtLastSchedule && i == scanParams.length - 2) { break; } - mInOrder.verify(mAlarmManager).set(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), - eq(expectedNextIntervalInMs + nowMs), any(), - mPeriodicScanListenerArgumentCaptor.capture(), any()); - alarmListener = mPeriodicScanListenerArgumentCaptor.getValue(); - assertNotNull(alarmListener); + if (expectedNextIntervalInMs != 0) { + mInOrder.verify(mAlarmManager).set(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), + eq(expectedNextIntervalInMs + nowMs), any(), + mPeriodicScanListenerArgumentCaptor.capture(), any()); + alarmListener = mPeriodicScanListenerArgumentCaptor.getValue(); + assertNotNull(alarmListener); + } } mInOrder.verifyNoMoreInteractions(); @@ -3957,7 +3975,7 @@ public class WifiNetworkFactoryTest extends WifiBaseTest { mNetworkCapabilities.setRequestorPackageName(packageName); mNetworkCapabilities.setNetworkSpecifier( new WifiNetworkSpecifier(ssidPatternMatch, bssidPatternMatch, - ScanResult.UNSPECIFIED, wifiConfiguration, channels)); + ScanResult.UNSPECIFIED, wifiConfiguration, channels, false)); mNetworkRequest = new NetworkRequest.Builder() .setCapabilities(mNetworkCapabilities) .build(); diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java index d5e102383b..191d7d007e 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTest.java @@ -28,6 +28,7 @@ import static com.android.server.wifi.WifiConfigurationTestUtil.SECURITY_PSK; import static com.android.server.wifi.WifiConfigurationTestUtil.SECURITY_SAE; import static com.android.server.wifi.WifiConfigurationTestUtil.SECURITY_WEP; import static com.android.server.wifi.WifiNetworkSelector.experimentIdFromIdentifier; +import static com.android.server.wifi.TestUtil.createCapabilityBitset; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; @@ -72,6 +73,7 @@ import org.mockito.Spy; import java.util.ArrayList; import java.util.Arrays; +import java.util.BitSet; import java.util.Collection; import java.util.Collections; import java.util.HashSet; @@ -157,6 +159,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { when(mWifiInjector.getWifiGlobals()).thenReturn(mWifiGlobals); when(mWifiGlobals.getWifiLowConnectedScoreThresholdToTriggerScanForMbb()).thenReturn( ConnectedScore.WIFI_TRANSITION_SCORE); + when(mClientModeManager.getSupportedFeaturesBitSet()).thenReturn(new BitSet()); when(mActiveModeWarden.getPrimaryClientModeManager()).thenReturn(mClientModeManager); if (WifiNetworkSelector.PRESET_CANDIDATE_SCORER_NAME.equals( mThroughputScorer.getIdentifier())) { @@ -555,7 +558,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates); assertEquals("Expect null configuration", null, candidate); assertTrue(mWifiNetworkSelector.getConnectableScanDetails().isEmpty()); @@ -589,7 +592,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates); assertEquals("Expect null configuration", null, candidate); assertTrue(mWifiNetworkSelector.getConnectableScanDetails().isEmpty()); @@ -626,7 +629,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates); assertNotNull(candidate); @@ -638,7 +641,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, true, false, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); candidate = mWifiNetworkSelector.selectNetwork(candidates); assertNull("Expect null configuration", candidate); @@ -680,7 +683,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates); WifiConfigurationTestUtil.assertConfigurationEqual(savedConfigs[0], candidate); @@ -692,7 +695,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); candidate = mWifiNetworkSelector.selectNetwork(candidates); ScanResult chosenScanResult = scanDetails.get(0).getScanResult(); @@ -732,7 +735,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates); when(mWifiInfo.getSupplicantState()).thenReturn(SupplicantState.COMPLETED); when(mWifiInfo.getNetworkId()).thenReturn(0); @@ -750,7 +753,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, true, false, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); candidate = mWifiNetworkSelector.selectNetwork(candidates); ScanResult chosenScanResult = scanDetails.get(0).getScanResult(); @@ -792,7 +795,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates); when(mWifiInfo.getSupplicantState()).thenReturn(SupplicantState.COMPLETED); when(mWifiInfo.getNetworkId()).thenReturn(0); @@ -818,7 +821,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, true, false, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); candidate = mWifiNetworkSelector.selectNetwork(candidates); ScanResult chosenScanResult = scanDetails.get(0).getScanResult(); @@ -854,7 +857,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates); verify(mWifiMetrics).incrementNetworkSelectionFilteredBssidCount(0); @@ -895,7 +898,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates); verify(mWifiMetrics).incrementNetworkSelectionFilteredBssidCount(1); assertEquals("Expect null configuration", null, candidate); @@ -935,7 +938,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates); assertEquals("Expect null configuration", null, candidate); } @@ -973,7 +976,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates); assertEquals("Expect null configuration", null, candidate); } @@ -1013,7 +1016,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates); assertEquals("Expect null configuration", null, candidate); } @@ -1056,7 +1059,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates); assertEquals("Expect null configuration", null, candidate); } @@ -1105,7 +1108,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates); } @@ -1139,7 +1142,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates); assertEquals("Expect null configuration", null, candidate); } @@ -1174,12 +1177,47 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates); assertEquals("Expect null configuration", null, candidate); } /** + * Autojoin restricted security type network is filtered out for network selection. + * + * ClientModeImpl is disconnected. + * scanDetails contains a network which is Autojoin restricted security type. + * + * Expected behavior: no network recommended by Network Selector + */ + @Test + public void filterOutAutojoinRestrictionSecurityTypeBssid() { + String[] ssids = {"\"test1\""}; + String[] bssids = {"6c:f3:7f:ae:8c:f3"}; + int[] freqs = {5180}; + String[] caps = {"[WEP][ESS]"}; + int[] levels = {mThresholdQualifiedRssi5G + 8}; + int[] securities = {SECURITY_WEP}; + when(WifiInfo.convertWifiConfigurationSecurityType( + WifiConfiguration.SECURITY_TYPE_WEP)).thenReturn(WifiInfo.SECURITY_TYPE_WEP); + + ScanDetailsAndWifiConfigs scanDetailsAndConfigs = + WifiNetworkSelectorTestUtil.setupScanDetailsAndConfigStore(ssids, bssids, + freqs, caps, levels, securities, mWifiConfigManager, mClock); + List<ScanDetail> scanDetails = scanDetailsAndConfigs.getScanDetails(); + HashSet<String> blocklist = new HashSet<String>(); + + List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan( + scanDetails, blocklist, + Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, + false, ROLE_CLIENT_PRIMARY)), + false, true, true, Collections.emptySet(), false, + (0x1 << WifiInfo.SECURITY_TYPE_WEP)); + WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates); + assertNull("Expect null configuration", candidate); + } + + /** * Unsupported security type WPA-Personal should not filter WPA/WPA2 networks. * * ClientModeImpl is disconnected. @@ -1209,7 +1247,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates); assertEquals(ssids[0], candidate.SSID); } @@ -1243,7 +1281,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates); when(mWifiInfo.getSupplicantState()).thenReturn(SupplicantState.COMPLETED); @@ -1269,7 +1307,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, true, false, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); candidate = mWifiNetworkSelector.selectNetwork(candidates); // The second network selection is skipped since current connected network is @@ -1308,7 +1346,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates); WifiConfiguration[] configs = scanDetailsAndConfigs.getWifiConfigs(); @@ -1339,7 +1377,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, true, false, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); assertEquals(2, candidates.size()); assertEquals(100, candidates.get(0).getPredictedThroughputMbps()); } @@ -1379,7 +1417,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates); ArgumentCaptor<Integer> nominatorIdCaptor = ArgumentCaptor.forClass(int.class); @@ -1405,7 +1443,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); candidate = mWifiNetworkSelector.selectNetwork(candidates); verify(mWifiMetrics, atLeastOnce()).setNominatorForNetwork(eq(candidate.networkId), @@ -1458,7 +1496,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates); WifiConfigurationTestUtil.assertConfigurationEqual(userChoice, candidate); @@ -1473,7 +1511,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); candidate = mWifiNetworkSelector.selectNetwork(candidates); WifiConfigurationTestUtil.assertConfigurationEqual(networkSelectorChoice, candidate); } @@ -1514,7 +1552,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates); WifiConfigurationTestUtil.assertConfigurationEqual(userChoice, candidate); @@ -1527,7 +1565,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); candidate = mWifiNetworkSelector.selectNetwork(candidates); // Should now select the non user choice network. @@ -1569,7 +1607,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates); WifiConfigurationTestUtil.assertConfigurationEqual(userChoice, candidate); @@ -1580,7 +1618,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); candidate = mWifiNetworkSelector.selectNetwork(candidates); WifiConfigurationTestUtil.assertConfigurationEqual(networkSelectorChoice, candidate); } @@ -1615,7 +1653,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); assertFalse(candidates.isEmpty()); for (WifiCandidates.Candidate candidate: candidates) { @@ -1632,7 +1670,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); assertFalse(candidates.isEmpty()); for (WifiCandidates.Candidate candidate: candidates) { @@ -1682,7 +1720,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates); ArgumentCaptor<Integer> nominatorIdCaptor = ArgumentCaptor.forClass(int.class); @@ -1898,7 +1936,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); //WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates); assertEquals(1, candidates.size()); assertTrue(candidates.get(0).getLastSelectionWeight() > 0); @@ -1910,7 +1948,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); //WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates); assertEquals(1, candidates.size()); assertEquals(0, candidates.get(0).getLastSelectionWeight(), 0); @@ -2026,7 +2064,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - true, true, true, Collections.emptySet(), false); + true, true, true, Collections.emptySet(), false, 0); WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates); assertNotNull("Result should be not null", candidate); WifiNetworkSelectorTestUtil.verifySelectedScanResult(mWifiConfigManager, @@ -2068,7 +2106,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, true, false, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - true, true, true, Collections.emptySet(), false); + true, true, true, Collections.emptySet(), false, 0); candidate = mWifiNetworkSelector.selectNetwork(candidates); // PlaceholderNominator always return the first network in the scan results @@ -2106,7 +2144,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates); List<ScanDetail> expectedOpenUnsavedNetworks = new ArrayList<>(); expectedOpenUnsavedNetworks.add(scanDetails.get(1)); @@ -2141,7 +2179,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { unSavedScanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates); assertEquals("Expect open unsaved networks", unSavedScanDetails, @@ -2156,7 +2194,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { savedScanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); candidate = mWifiNetworkSelector.selectNetwork(candidates); // Saved networks are filtered out. assertTrue(mWifiNetworkSelector.getFilteredScanDetailsForOpenUnsavedNetworks().isEmpty()); @@ -2186,7 +2224,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates); List<ScanDetail> expectedOpenUnsavedNetworks = new ArrayList<>(); expectedOpenUnsavedNetworks.add(scanDetails.get(1)); @@ -2218,7 +2256,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates); assertTrue(mWifiNetworkSelector.getFilteredScanDetailsForOpenUnsavedNetworks().isEmpty()); } @@ -2250,7 +2288,8 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { int[] levels = {mThresholdMinimumRssi2G, mThresholdMinimumRssi5G + RSSI_BUMP, mThresholdMinimumRssi2G + RSSI_BUMP}; mPlaceholderNominator.setNominatorToSelectCandidate(false); - when(mClientModeManager.getSupportedFeatures()).thenReturn(WIFI_FEATURE_OWE); + when(mClientModeManager.getSupportedFeaturesBitSet()).thenReturn( + createCapabilityBitset(WIFI_FEATURE_OWE)); List<ScanDetail> scanDetails = WifiNetworkSelectorTestUtil.buildScanDetails( ssids, bssids, freqs, caps, levels, mClock); @@ -2260,7 +2299,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates); List<ScanDetail> expectedOpenUnsavedNetworks = new ArrayList<>(); expectedOpenUnsavedNetworks.add(scanDetails.get(1)); @@ -2286,7 +2325,10 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { int[] levels = {mThresholdMinimumRssi2G, mThresholdMinimumRssi5G + RSSI_BUMP, mThresholdMinimumRssi2G + RSSI_BUMP}; mPlaceholderNominator.setNominatorToSelectCandidate(false); - when(mClientModeManager.getSupportedFeatures()).thenReturn(~WIFI_FEATURE_OWE); + + BitSet supportedFeatures = new BitSet(); + supportedFeatures.set(WIFI_FEATURE_OWE, false); + when(mClientModeManager.getSupportedFeaturesBitSet()).thenReturn(supportedFeatures); List<ScanDetail> scanDetails = WifiNetworkSelectorTestUtil.buildScanDetails( ssids, bssids, freqs, caps, levels, mClock); @@ -2296,7 +2338,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates); List<ScanDetail> expectedOpenUnsavedNetworks = new ArrayList<>(); expectedOpenUnsavedNetworks.add(scanDetails.get(1)); @@ -2327,7 +2369,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { EMPTY_BLOCKLIST, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - true, true, true, Collections.emptySet(), false); + true, true, true, Collections.emptySet(), false, 0); WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates); verify(mCandidateScorer, atLeastOnce()).scoreCandidates(any()); @@ -2465,7 +2507,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - true, true, true, Collections.emptySet(), false); + true, true, true, Collections.emptySet(), false, 0); WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates); // Check if the wifiConfig is updated with the latest verify(mWifiConfigManager).addOrUpdateNetwork(existingConfig, @@ -2496,7 +2538,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); // Expect one privileged and one regular candidate. assertEquals(2, candidates.size()); boolean foundCarrierOrPrivilegedAppCandidate = false; @@ -2546,7 +2588,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates); verify(mWifiMetrics, times(1)) .incrementNetworkSelectionFilteredBssidCountDueToMboAssocDisallowInd(); @@ -2673,7 +2715,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { new ClientModeManagerState( TEST_IFACE_NAME_SECONDARY, true, false, mSecondaryWifiInfo, false, ROLE_CLIENT_SECONDARY_LONG_LIVED)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); WifiConfiguration candidate = mWifiNetworkSelector.selectNetwork(candidates); ScanResult chosenScanResult = scanDetails.get(0).getScanResult(); @@ -2743,7 +2785,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { // Do network selection. List<WifiCandidates.Candidate> candidates = mWifiNetworkSelector.getCandidatesFromScan( scanDetails, blocklist, cmmStates, false, true, true, Collections.emptySet(), - false); + false, 0); assertNull(candidates); // Mock that the primary connection has a user connect choice pointing something @@ -2757,7 +2799,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { primaryConfig); candidates = mWifiNetworkSelector.getCandidatesFromScan( scanDetails, blocklist, cmmStates, false, true, true, Collections.emptySet(), - false); + false, 0); // Candidate should not be null assertNotNull(candidates); @@ -2766,7 +2808,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { R.bool.config_wifi_framework_enable_associated_network_selection); assertNull(mWifiNetworkSelector.getCandidatesFromScan( scanDetails, blocklist, cmmStates, false, true, true, Collections.emptySet(), - false)); + false, 0)); } private void runNetworkSelectionWith(ScanDetailsAndWifiConfigs scanDetailsAndConfigs) { @@ -2778,7 +2820,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { true, // untrustedNetworkAllowed true, // oemPaid true, // oemPrivate - Collections.emptySet(), false); + Collections.emptySet(), false, 0); WifiConfiguration wifiConfiguration = mWifiNetworkSelector.selectNetwork(candidates); assertNotNull(wifiConfiguration); } @@ -2817,7 +2859,8 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { */ @Test public void testSaeAutoUpgradeWithPskNetworkWhenAutoUpgradeEnabled() { - when(mClientModeManager.getSupportedFeatures()).thenReturn(WIFI_FEATURE_WPA3_SAE); + when(mClientModeManager.getSupportedFeaturesBitSet()) + .thenReturn(createCapabilityBitset(WIFI_FEATURE_WPA3_SAE)); when(mWifiGlobals.isWpa3SaeUpgradeEnabled()).thenReturn(true); when(mWifiGlobals.isWpa3SaeUpgradeOffloadEnabled()).thenReturn(true); @@ -2834,7 +2877,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, new HashSet<>(), Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); assertEquals(2, candidates.size()); // Verify that SAE network is selected if offload is supported. @@ -2872,7 +2915,8 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { */ @Test public void testSaeAutoUpgradeWithPskNetworkWhenPskTypeIsDisabled() { - when(mClientModeManager.getSupportedFeatures()).thenReturn(WIFI_FEATURE_WPA3_SAE); + when(mClientModeManager.getSupportedFeaturesBitSet()) + .thenReturn(createCapabilityBitset(WIFI_FEATURE_WPA3_SAE)); when(mWifiGlobals.isWpa3SaeUpgradeEnabled()).thenReturn(true); when(mWifiGlobals.isWpa3SaeUpgradeOffloadEnabled()).thenReturn(false); @@ -2890,7 +2934,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, new HashSet<>(), Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); // PSK type is disabled, PSK network is not matched. assertEquals(1, candidates.size()); @@ -2907,7 +2951,8 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { */ @Test public void testSaeNoAutoUpgradeWithPskNetworkWhenAutoUpgradeDisabled() { - when(mClientModeManager.getSupportedFeatures()).thenReturn(WIFI_FEATURE_WPA3_SAE); + when(mClientModeManager.getSupportedFeaturesBitSet()) + .thenReturn(createCapabilityBitset(WIFI_FEATURE_WPA3_SAE)); when(mWifiGlobals.isWpa3SaeUpgradeEnabled()).thenReturn(false); when(mWifiGlobals.isWpa3SaeUpgradeOffloadEnabled()).thenReturn(false); @@ -2924,7 +2969,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, new HashSet<>(), Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); assertEquals(2, candidates.size()); // Verify that PSK network is selected. @@ -2940,7 +2985,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { */ @Test public void testSaeNoAutoUpgradeWithPskNetworkWhenSaeNotSupported() { - when(mClientModeManager.getSupportedFeatures()).thenReturn(0L); + when(mClientModeManager.getSupportedFeaturesBitSet()).thenReturn(new BitSet()); when(mWifiGlobals.isWpa3SaeUpgradeEnabled()).thenReturn(true); when(mWifiGlobals.isWpa3SaeUpgradeOffloadEnabled()).thenReturn(true); @@ -2957,7 +3002,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, new HashSet<>(), Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); // The SAE-only network should be filtered. assertEquals(1, candidates.size()); @@ -2976,7 +3021,8 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { */ @Test public void testOweAutoUpgradeWithOpenNetworkWhenAutoUpgradeEnabled() { - when(mClientModeManager.getSupportedFeatures()).thenReturn(WIFI_FEATURE_OWE); + when(mClientModeManager.getSupportedFeaturesBitSet()).thenReturn( + createCapabilityBitset(WIFI_FEATURE_OWE)); when(mWifiGlobals.isOweUpgradeEnabled()).thenReturn(true); when(mScanRequestProxy.isOpenOnlyNetworkInRange(eq(TEST_AUTO_UPGRADE_SSID))) @@ -2992,7 +3038,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, new HashSet<>(), Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); assertEquals(2, candidates.size()); // Verify that OWE network is selected (assume offload is not supported.). @@ -3018,7 +3064,8 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { */ @Test public void testOweAutoUpgradeWithOpenNetworkWhenOpenTypeIsDisabled() { - when(mClientModeManager.getSupportedFeatures()).thenReturn(WIFI_FEATURE_OWE); + when(mClientModeManager.getSupportedFeaturesBitSet()).thenReturn( + createCapabilityBitset(WIFI_FEATURE_OWE)); when(mWifiGlobals.isOweUpgradeEnabled()).thenReturn(true); when(mScanRequestProxy.isOpenOnlyNetworkInRange(eq(TEST_AUTO_UPGRADE_SSID))) @@ -3036,7 +3083,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, new HashSet<>(), Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); // OPEN type is disabled, OPEN network is not matched. assertEquals(1, candidates.size()); @@ -3053,7 +3100,8 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { */ @Test public void testOweNoAutoUpgradeWithOpenNetworkWhenAutoUpgradeDisabled() { - when(mClientModeManager.getSupportedFeatures()).thenReturn(WIFI_FEATURE_OWE); + when(mClientModeManager.getSupportedFeaturesBitSet()).thenReturn( + createCapabilityBitset(WIFI_FEATURE_OWE)); when(mWifiGlobals.isOweUpgradeEnabled()).thenReturn(false); when(mScanRequestProxy.isOpenOnlyNetworkInRange(eq(TEST_AUTO_UPGRADE_SSID))) @@ -3069,7 +3117,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, new HashSet<>(), Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); assertEquals(2, candidates.size()); // Verify that OPEN network is selected. @@ -3085,7 +3133,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { */ @Test public void testOweNoAutoUpgradeWithOweNetworkWhenOweNotSupported() { - when(mClientModeManager.getSupportedFeatures()).thenReturn(0L); + when(mClientModeManager.getSupportedFeaturesBitSet()).thenReturn(new BitSet()); when(mWifiGlobals.isOweUpgradeEnabled()).thenReturn(true); when(mScanRequestProxy.isOpenOnlyNetworkInRange(eq(TEST_AUTO_UPGRADE_SSID))) @@ -3102,7 +3150,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, new HashSet<>(), Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); // The OWE-only network should be filtered. assertEquals(1, candidates.size()); @@ -3135,7 +3183,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, new HashSet<>(), Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); assertEquals(2, candidates.size()); // Verify that WPA2 Enterprise network is selected (assume offload is not supported.). @@ -3179,7 +3227,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, new HashSet<>(), Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); // WPA2 Enterprise type is disabled, WPA2 Enterprise network is not matched. assertEquals(1, candidates.size()); @@ -3194,7 +3242,8 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { @Test public void verifySecurityParamsSelectionForPskSaeConfigAndSaeScan() { - when(mClientModeManager.getSupportedFeatures()).thenReturn(WIFI_FEATURE_WPA3_SAE); + when(mClientModeManager.getSupportedFeaturesBitSet()) + .thenReturn(createCapabilityBitset(WIFI_FEATURE_WPA3_SAE)); when(mWifiGlobals.isWpa3SaeUpgradeEnabled()).thenReturn(true); setupMultiConfigAndSingleScanAndVerify("[RSN-SAE-CCMP][ESS][MFPR]", SECURITY_PSK | SECURITY_SAE, WifiConfiguration.SECURITY_TYPE_SAE); @@ -3202,14 +3251,17 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { @Test public void verifySecurityParamsSelectionForPskSaeConfigAndSaeScanNegative() { - when(mClientModeManager.getSupportedFeatures()).thenReturn(~WIFI_FEATURE_WPA3_SAE); + BitSet supportedFeatures = new BitSet(); + supportedFeatures.set(WIFI_FEATURE_WPA3_SAE, false); + when(mClientModeManager.getSupportedFeaturesBitSet()).thenReturn(supportedFeatures); setupMultiConfigAndSingleScanAndVerify("[RSN-SAE-CCMP][ESS][MFPR]", SECURITY_PSK | SECURITY_SAE, -1); } @Test public void verifySecurityParamsSelectionForOpenOweConfigAndOweScan() { - when(mClientModeManager.getSupportedFeatures()).thenReturn(WIFI_FEATURE_OWE); + when(mClientModeManager.getSupportedFeaturesBitSet()).thenReturn( + createCapabilityBitset(WIFI_FEATURE_OWE)); when(mWifiGlobals.isOweUpgradeEnabled()).thenReturn(true); setupMultiConfigAndSingleScanAndVerify("[OWE-SAE-CCMP][ESS][MFPR]", SECURITY_NONE | SECURITY_OWE, WifiConfiguration.SECURITY_TYPE_OWE); @@ -3217,7 +3269,9 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { @Test public void verifySecurityParamsSelectionForOpenOweConfigAndOweScanNegative() { - when(mClientModeManager.getSupportedFeatures()).thenReturn(~WIFI_FEATURE_OWE); + BitSet supportedFeatures = new BitSet(); + supportedFeatures.set(WIFI_FEATURE_OWE, false); + when(mClientModeManager.getSupportedFeaturesBitSet()).thenReturn(supportedFeatures); setupMultiConfigAndSingleScanAndVerify("[OWE-SAE-CCMP][ESS][MFPR]", SECURITY_NONE | SECURITY_OWE, -1); } @@ -3251,7 +3305,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); assertNotNull(candidates); if (expectedSecurityParamType == -1) { assertEquals(0, candidates.size()); @@ -3271,7 +3325,8 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { */ @Test public void testPskWithPskOnlyForPskSaeTransitionNetworks() { - when(mClientModeManager.getSupportedFeatures()).thenReturn(WIFI_FEATURE_WPA3_SAE); + when(mClientModeManager.getSupportedFeaturesBitSet()) + .thenReturn(createCapabilityBitset(WIFI_FEATURE_WPA3_SAE)); when(mWifiGlobals.isWpa3SaeUpgradeEnabled()).thenReturn(true); when(mWifiGlobals.isWpa3SaeUpgradeOffloadEnabled()).thenReturn(true); @@ -3290,7 +3345,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, new HashSet<>(), Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); assertEquals(2, candidates.size()); // Verify that PSK network is still selected if offload is not supported @@ -3308,7 +3363,8 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { */ @Test public void testNetworkSelectionForUserSelectedNetwork() { - when(mClientModeManager.getSupportedFeatures()).thenReturn(WIFI_FEATURE_WPA3_SAE); + when(mClientModeManager.getSupportedFeaturesBitSet()) + .thenReturn(createCapabilityBitset(WIFI_FEATURE_WPA3_SAE)); when(mWifiGlobals.isWpa3SaeUpgradeEnabled()).thenReturn(true); ScanDetailsAndWifiConfigs scanDetailsAndConfigs = setupAutoUpgradeNetworks( @@ -3398,7 +3454,7 @@ public class WifiNetworkSelectorTest extends WifiBaseTest { scanDetails, blocklist, Arrays.asList(new ClientModeManagerState(TEST_IFACE_NAME, false, true, mWifiInfo, false, ROLE_CLIENT_PRIMARY)), - false, true, true, Collections.emptySet(), false); + false, true, true, Collections.emptySet(), false, 0); return candidates; } diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTestUtil.java b/service/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTestUtil.java index 59e62ca16b..77c98097e9 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTestUtil.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiNetworkSelectorTestUtil.java @@ -46,6 +46,7 @@ import com.android.server.wifi.util.InformationElementUtil; import com.android.server.wifi.util.NativeUtil; import java.util.ArrayList; +import java.util.BitSet; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -57,7 +58,7 @@ import java.util.Set; */ public class WifiNetworkSelectorTestUtil { private static final String TAG = "WifiNetworkSelectorTestUtil"; - private static final long SUPPORTED_FEATURES_ALL = Long.MAX_VALUE; + /** * A class that holds a list of scanDetail and their associated WifiConfiguration. */ @@ -273,6 +274,9 @@ public class WifiNetworkSelectorTestUtil { throw new IllegalArgumentException(); } + BitSet supportedFeaturesAll = new BitSet(); + supportedFeaturesAll.set(0, 63); // mark all features as supported + Map<String, Integer> netIdMap = new HashMap<>(); int netId = 0; @@ -292,7 +296,7 @@ public class WifiNetworkSelectorTestUtil { || (securities[index] & SECURITY_WAPI_PSK) != 0) { configs[index].preSharedKey = "\"PA55W0RD\""; // needed to validate with PSK } - if (!WifiConfigurationUtil.validate(configs[index], SUPPORTED_FEATURES_ALL, true)) { + if (!WifiConfigurationUtil.validate(configs[index], supportedFeaturesAll, true)) { throw new IllegalArgumentException("Invalid generated config: " + configs[index]); } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiNetworkSuggestionsManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiNetworkSuggestionsManagerTest.java index 09f5969952..346cc96d93 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiNetworkSuggestionsManagerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiNetworkSuggestionsManagerTest.java @@ -31,6 +31,7 @@ import static com.android.server.wifi.WifiNetworkSuggestionsManager.DEFAULT_LING import static com.android.server.wifi.WifiNetworkSuggestionsManager.NOTIFICATION_USER_ALLOWED_APP_INTENT_ACTION; import static com.android.server.wifi.WifiNetworkSuggestionsManager.NOTIFICATION_USER_DISALLOWED_APP_INTENT_ACTION; import static com.android.server.wifi.WifiNetworkSuggestionsManager.NOTIFICATION_USER_DISMISSED_INTENT_ACTION; +import static com.android.server.wifi.TestUtil.createCapabilityBitset; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -264,8 +265,9 @@ public class WifiNetworkSuggestionsManagerTest extends WifiBaseTest { when(mWifiPermissionsUtil.doesUidBelongToCurrentUserOrDeviceOwner(anyInt())) .thenReturn(true); when(mActiveModeWarden.getPrimaryClientModeManager()).thenReturn(mPrimaryClientModeManager); - when(mPrimaryClientModeManager.getSupportedFeatures()).thenReturn( - WifiManager.WIFI_FEATURE_WPA3_SAE | WifiManager.WIFI_FEATURE_OWE); + when(mPrimaryClientModeManager.getSupportedFeaturesBitSet()).thenReturn( + createCapabilityBitset( + WifiManager.WIFI_FEATURE_WPA3_SAE, WifiManager.WIFI_FEATURE_OWE)); when(mWifiGlobals.isWpa3SaeUpgradeEnabled()).thenReturn(true); when(mWifiGlobals.isOweUpgradeEnabled()).thenReturn(true); when(mSsidTranslator.getAllPossibleOriginalSsids(any())).thenAnswer( diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiPulledAtomLoggerTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiPulledAtomLoggerTest.java index adaddf5bb3..276bbcaf8b 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiPulledAtomLoggerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiPulledAtomLoggerTest.java @@ -184,12 +184,13 @@ public class WifiPulledAtomLoggerTest extends WifiBaseTest { when(mWifiInjector.getWakeupController()).thenReturn(mock(WakeupController.class)); when(mWifiInjector.getOpenNetworkNotifier()).thenReturn(mock(OpenNetworkNotifier.class)); when(mWifiInjector.getWifiPermissionsUtil()).thenReturn(mock(WifiPermissionsUtil.class)); + when(mWifiInjector.getDeviceConfigFacade()).thenReturn(mock(DeviceConfigFacade.class)); - // Verify that all 8 settings were retrieved. + // Verify that all settings were retrieved. List<StatsEvent> data = new ArrayList<>(); assertEquals(StatsManager.PULL_SUCCESS, mPullAtomCallbackArgumentCaptor.getValue() .onPullAtom(WifiStatsLog.WIFI_SETTING_INFO, data)); - assertEquals(8, data.size()); + assertEquals(9, data.size()); } @Test diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java index 25cbb86de8..436820ed36 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java @@ -66,6 +66,7 @@ import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_SECONDARY_LO import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_SECONDARY_TRANSIENT; import static com.android.server.wifi.LocalOnlyHotspotRequestInfo.HOTSPOT_NO_ERROR; import static com.android.server.wifi.SelfRecovery.REASON_API_CALL; +import static com.android.server.wifi.TestUtil.createCapabilityBitset; import static com.android.server.wifi.WifiConfigurationTestUtil.SECURITY_NONE; import static com.android.server.wifi.WifiSettingsConfigStore.D2D_ALLOWED_WHEN_INFRA_STA_DISABLED; import static com.android.server.wifi.WifiSettingsConfigStore.SHOW_DIALOG_WHEN_THIRD_PARTY_APPS_ENABLE_WIFI; @@ -148,6 +149,7 @@ import android.net.Network; import android.net.NetworkStack; import android.net.TetheringManager; import android.net.Uri; +import android.net.wifi.BlockingOption; import android.net.wifi.CoexUnsafeChannel; import android.net.wifi.IActionListener; import android.net.wifi.IBooleanListener; @@ -182,6 +184,7 @@ import android.net.wifi.IWifiConnectedNetworkScorer; import android.net.wifi.IWifiLowLatencyLockListener; import android.net.wifi.IWifiNetworkSelectionConfigListener; import android.net.wifi.IWifiNetworkStateChangedListener; +import android.net.wifi.IWifiStateChangedListener; import android.net.wifi.IWifiVerboseLoggingStatusChangedListener; import android.net.wifi.MscsParams; import android.net.wifi.QosCharacteristics; @@ -213,6 +216,7 @@ import android.net.wifi.hotspot2.pps.Credential; import android.net.wifi.hotspot2.pps.HomeSp; import android.net.wifi.twt.TwtRequest; import android.net.wifi.twt.TwtSessionCallback; +import android.net.wifi.util.Environment; import android.net.wifi.util.WifiResourceCache; import android.os.Binder; import android.os.Build; @@ -248,6 +252,8 @@ import com.android.modules.utils.StringParceledListSlice; import com.android.modules.utils.build.SdkLevel; import com.android.server.wifi.WifiServiceImpl.LocalOnlyRequestorCallback; import com.android.server.wifi.WifiServiceImpl.SoftApCallbackInternal; +import com.android.server.wifi.WifiServiceImpl.ThreadStateListener; +import com.android.server.wifi.WifiServiceImpl.UwbAdapterStateListener; import com.android.server.wifi.b2b.WifiRoamingModeManager; import com.android.server.wifi.coex.CoexManager; import com.android.server.wifi.entitlement.PseudonymInfo; @@ -261,6 +267,7 @@ import com.android.server.wifi.util.ApConfigUtil; import com.android.server.wifi.util.LastCallerInfoManager; import com.android.server.wifi.util.WifiPermissionsUtil; import com.android.server.wifi.util.WifiPermissionsWrapper; +import com.android.server.wifi.util.WorkSourceHelper; import com.android.wifi.flags.FeatureFlags; import com.android.wifi.resources.R; @@ -284,6 +291,7 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.BitSet; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -369,8 +377,6 @@ public class WifiServiceImplTest extends WifiBaseTest { private WifiThreadRunner mWifiThreadRunner; private PowerManager mPowerManager; private PhoneStateListener mPhoneStateListener; - private int mPid; - private int mPid2 = Process.myPid(); private OsuProvider mOsuProvider; private SoftApCallbackInternal mStateMachineSoftApCallback; private SoftApCallbackInternal mLohsApCallback; @@ -383,7 +389,7 @@ public class WifiServiceImplTest extends WifiBaseTest { private static final String DPP_PRODUCT_INFO = "DPP:some_dpp_uri_info"; private static final WorkSource SETTINGS_WORKSOURCE = new WorkSource(Process.SYSTEM_UID, "system-service"); - + private static final String EXTERNAL_SCORER_PKG_NAME = "com.scorer"; private final ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor = ArgumentCaptor.forClass(BroadcastReceiver.class); @@ -449,6 +455,7 @@ public class WifiServiceImplTest extends WifiBaseTest { @Mock IDppCallback mDppCallback; @Mock ILocalOnlyHotspotCallback mLohsCallback; @Mock ICoexCallback mCoexCallback; + @Mock IWifiStateChangedListener mWifiStateChangedListener; @Mock IScanResultsCallback mScanResultsCallback; @Mock ISuggestionConnectionStatusListener mSuggestionConnectionStatusListener; @Mock ILocalOnlyConnectionStatusListener mLocalOnlyConnectionStatusListener; @@ -507,6 +514,7 @@ public class WifiServiceImplTest extends WifiBaseTest { @Captor ArgumentCaptor<List> mListCaptor; @Mock TwtManager mTwtManager; @Mock WifiResourceCache mResourceCache; + @Mock WorkSourceHelper mWorkSourceHelper; @Rule // For frameworks @@ -532,8 +540,9 @@ public class WifiServiceImplTest extends WifiBaseTest { when(mResourceCache.getInteger(R.integer.config_wifiHardwareSoftapMaxClientCount)) .thenReturn(10); WifiInjector.sWifiInjector = mWifiInjector; - when(mRequestInfo.getPid()).thenReturn(mPid); - when(mRequestInfo2.getPid()).thenReturn(mPid2); + when(mRequestInfo.getPid()).thenReturn(TEST_PID); + when(mRequestInfo2.getPid()).thenReturn(TEST_PID2); + when(mWifiInjector.getContext()).thenReturn(mContext); when(mWifiInjector.getUserManager()).thenReturn(mUserManager); when(mWifiInjector.getWifiCountryCode()).thenReturn(mWifiCountryCode); when(mWifiInjector.getWifiMetrics()).thenReturn(mWifiMetrics); @@ -563,6 +572,7 @@ public class WifiServiceImplTest extends WifiBaseTest { .thenReturn(mWifiDeviceStateChangeManager); when(mWifiInjector.getWifiSettingsBackupRestore()).thenReturn(mWifiSettingsBackupRestore); when(mWifiInjector.getBackupRestoreController()).thenReturn(mBackupRestoreController); + when(mWifiInjector.makeWsHelper(any())).thenReturn(mWorkSourceHelper); when(mHandlerThread.getThreadHandler()).thenReturn(new Handler(mLooper.getLooper())); when(mHandlerThread.getLooper()).thenReturn(mLooper.getLooper()); when(mContext.getResources()).thenReturn(mResources); @@ -667,6 +677,11 @@ public class WifiServiceImplTest extends WifiBaseTest { when(mWifiPermissionsUtil.isTargetSdkLessThan(any(), eq(Build.VERSION_CODES.UPSIDE_DOWN_CAKE), anyInt())).thenReturn(true); + if (SdkLevel.isAtLeastS()) { + // AttributionSource arg is only available from S. + when(mWifiPermissionsUtil.checkNearbyDevicesPermission(any(), anyBoolean(), any())) + .thenReturn(true); + } when(mWifiInjector.getWifiCarrierInfoManager()).thenReturn(mWifiCarrierInfoManager); when(mWifiInjector.getWifiPseudonymManager()).thenReturn(mWifiPseudonymManager); when(mWifiInjector.getOpenNetworkNotifier()).thenReturn(mOpenNetworkNotifier); @@ -676,6 +691,7 @@ public class WifiServiceImplTest extends WifiBaseTest { when(mWifiInjector.getSarManager()).thenReturn(mSarManager); mClientModeManagers = Arrays.asList(mClientModeManager, mock(ClientModeManager.class)); when(mActiveModeWarden.getClientModeManagers()).thenReturn(mClientModeManagers); + when(mActiveModeWarden.getSupportedFeatureSet()).thenReturn(new BitSet()); when(mWifiInjector.getSelfRecovery()).thenReturn(mSelfRecovery); when(mWifiInjector.getLastCallerInfoManager()).thenReturn(mLastCallerInfoManager); when(mUserManager.getUserRestrictions()).thenReturn(mBundle); @@ -701,6 +717,12 @@ public class WifiServiceImplTest extends WifiBaseTest { } }).when(mMakeBeforeBreakManager).stopAllSecondaryTransientClientModeManagers(any()); + doAnswer(new AnswerWithArguments() { + public void answer(boolean isWepAllowed) { + when(mWifiGlobals.isWepAllowed()).thenReturn(isWepAllowed); + } + }).when(mWifiGlobals).setWepAllowed(anyBoolean()); + when(mWifiSettingsConfigStore.get(eq(WIFI_WEP_ALLOWED))).thenReturn(true); mWifiServiceImpl = makeWifiServiceImpl(); mDppCallback = new IDppCallback() { @@ -758,6 +780,9 @@ public class WifiServiceImplTest extends WifiBaseTest { mWifiConfig.networkId = TEST_NETWORK_ID; setup24GhzSupported(); + SoftApConfiguration lohsConfig = createValidSoftApConfiguration(); + when(mWifiApConfigStore.generateLocalOnlyHotspotConfig( + eq(mContext), eq(null), any(), eq(false))).thenReturn(lohsConfig); } /** @@ -2371,6 +2396,7 @@ public class WifiServiceImplTest extends WifiBaseTest { */ @Test public void testStartTetheredHotspotRequestWithPermissions() { + assumeTrue(Build.VERSION.SDK_INT < Build.VERSION_CODES.BAKLAVA); TetheringManager.TetheringRequest request = new TetheringManager.TetheringRequest.Builder( TetheringManager.TETHERING_WIFI).build(); mWifiServiceImpl.startTetheredHotspotRequest(request, @@ -2384,6 +2410,27 @@ public class WifiServiceImplTest extends WifiBaseTest { } /** + * Verify startTetheredHotspot with TetheringRequest use the TetheringRequest's config. + */ + @Test + public void testStartTetheredHotspotRequestWithSoftApConfiguration() { + assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA); + SoftApConfiguration config = createValidSoftApConfiguration(); + TetheringManager.TetheringRequest request = new TetheringManager.TetheringRequest.Builder( + TetheringManager.TETHERING_WIFI) + .setSoftApConfiguration(config) + .build(); + mWifiServiceImpl.startTetheredHotspotRequest(request, + mClientSoftApCallback, TEST_PACKAGE_NAME); + verify(mActiveModeWarden).startSoftAp(mSoftApModeConfigCaptor.capture(), + eq(new WorkSource(Binder.getCallingUid(), TEST_PACKAGE_NAME))); + assertThat(mSoftApModeConfigCaptor.getValue().getSoftApConfiguration()).isEqualTo(config); + assertThat(mSoftApModeConfigCaptor.getValue().getTetheringRequest()).isEqualTo(request); + verify(mLastCallerInfoManager).put(eq(WifiManager.API_TETHERED_HOTSPOT), anyInt(), + anyInt(), anyInt(), anyString(), eq(true)); + } + + /** * Verify caller with proper permissions but an invalid config does not start softap. */ @Test @@ -3555,7 +3602,7 @@ public class WifiServiceImplTest extends WifiBaseTest { mLooper.startAutoDispatch(); setupLohsPermissions(); int result = mWifiServiceImpl.startLocalOnlyHotspot(mLohsCallback, TEST_PACKAGE_NAME, - TEST_FEATURE_ID, null, mExtras); + TEST_FEATURE_ID, null, mExtras, false); mLooper.stopAutoDispatchAndIgnoreExceptions(); assertEquals(LocalOnlyHotspotCallback.REQUEST_REGISTERED, result); verifyCheckChangePermission(TEST_PACKAGE_NAME); @@ -3583,7 +3630,7 @@ public class WifiServiceImplTest extends WifiBaseTest { .enforceCallingOrSelfPermission(eq(android.Manifest.permission.CHANGE_WIFI_STATE), eq("WifiService")); mWifiServiceImpl.startLocalOnlyHotspot( - mLohsCallback, TEST_PACKAGE_NAME, TEST_FEATURE_ID, null, mExtras); + mLohsCallback, TEST_PACKAGE_NAME, TEST_FEATURE_ID, null, mExtras, false); } /** @@ -3597,7 +3644,7 @@ public class WifiServiceImplTest extends WifiBaseTest { eq(TEST_FEATURE_ID), anyInt()); mWifiServiceImpl.startLocalOnlyHotspot( - mLohsCallback, TEST_PACKAGE_NAME, TEST_FEATURE_ID, null, mExtras); + mLohsCallback, TEST_PACKAGE_NAME, TEST_FEATURE_ID, null, mExtras, false); } /** @@ -3613,7 +3660,7 @@ public class WifiServiceImplTest extends WifiBaseTest { .when(mWifiPermissionsUtil).enforceNearbyDevicesPermission( any(), anyBoolean(), any()); mWifiServiceImpl.startLocalOnlyHotspot( - mLohsCallback, TEST_PACKAGE_NAME, TEST_FEATURE_ID, null, mExtras); + mLohsCallback, TEST_PACKAGE_NAME, TEST_FEATURE_ID, null, mExtras, false); } /** @@ -3627,7 +3674,7 @@ public class WifiServiceImplTest extends WifiBaseTest { when(mWifiPermissionsUtil.isTargetSdkLessThan(anyString(), eq(Build.VERSION_CODES.TIRAMISU), anyInt())).thenReturn(true); mWifiServiceImpl.startLocalOnlyHotspot( - mLohsCallback, TEST_PACKAGE_NAME, TEST_FEATURE_ID, null, mExtras); + mLohsCallback, TEST_PACKAGE_NAME, TEST_FEATURE_ID, null, mExtras, false); verify(mWifiPermissionsUtil, never()).enforceNearbyDevicesPermission(any(), anyBoolean(), any()); } @@ -3640,7 +3687,7 @@ public class WifiServiceImplTest extends WifiBaseTest { public void testStartLocalOnlyHotspotThrowsSecurityExceptionWithoutLocationEnabled() { when(mWifiPermissionsUtil.isLocationModeEnabled()).thenReturn(false); mWifiServiceImpl.startLocalOnlyHotspot( - mLohsCallback, TEST_PACKAGE_NAME, TEST_FEATURE_ID, null, mExtras); + mLohsCallback, TEST_PACKAGE_NAME, TEST_FEATURE_ID, null, mExtras, false); } /** @@ -3652,7 +3699,7 @@ public class WifiServiceImplTest extends WifiBaseTest { when(mFrameworkFacade.isAppForeground(any(), anyInt())).thenReturn(false); int result = mWifiServiceImpl.startLocalOnlyHotspot( - mLohsCallback, TEST_PACKAGE_NAME, TEST_FEATURE_ID, null, mExtras); + mLohsCallback, TEST_PACKAGE_NAME, TEST_FEATURE_ID, null, mExtras, false); assertEquals(LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE, result); } @@ -3760,7 +3807,7 @@ public class WifiServiceImplTest extends WifiBaseTest { when(mFrameworkFacade.isAppForeground(any(), anyInt())).thenReturn(true); mLooper.dispatchAll(); int returnCode = mWifiServiceImpl.startLocalOnlyHotspot( - mLohsCallback, TEST_PACKAGE_NAME, TEST_FEATURE_ID, null, mExtras); + mLohsCallback, TEST_PACKAGE_NAME, TEST_FEATURE_ID, null, mExtras, false); assertEquals(ERROR_INCOMPATIBLE_MODE, returnCode); } @@ -3775,7 +3822,7 @@ public class WifiServiceImplTest extends WifiBaseTest { eq(UserManager.DISALLOW_CONFIG_TETHERING), any())) .thenReturn(true); int returnCode = mWifiServiceImpl.startLocalOnlyHotspot( - mLohsCallback, TEST_PACKAGE_NAME, TEST_FEATURE_ID, null, mExtras); + mLohsCallback, TEST_PACKAGE_NAME, TEST_FEATURE_ID, null, mExtras, false); assertEquals(ERROR_TETHERING_DISALLOWED, returnCode); } @@ -3788,7 +3835,7 @@ public class WifiServiceImplTest extends WifiBaseTest { // now do the second request that will fail mWifiServiceImpl.startLocalOnlyHotspot( - mLohsCallback, TEST_PACKAGE_NAME, TEST_FEATURE_ID, null, mExtras); + mLohsCallback, TEST_PACKAGE_NAME, TEST_FEATURE_ID, null, mExtras, false); } /** @@ -3810,9 +3857,8 @@ public class WifiServiceImplTest extends WifiBaseTest { */ @Test public void testStopLocalOnlyHotspotDoesNothingWithRemainingRequest() throws Exception { - // register a request that will remain after the stopLOHS call - mWifiServiceImpl.registerLOHSForTest(mPid, mRequestInfo); + mWifiServiceImpl.registerLOHSForTest(TEST_PID, mRequestInfo); mLooper.dispatchAll(); setupLocalOnlyHotspot(); // Since we are calling with the same pid, the second register call will be removed @@ -3850,10 +3896,10 @@ public class WifiServiceImplTest extends WifiBaseTest { public void testStartLocalOnlyHotspotAt2Ghz() { SoftApConfiguration lohsConfig = createValidSoftApConfiguration(); when(mWifiApConfigStore.generateLocalOnlyHotspotConfig( - eq(mContext), eq(null), any())).thenReturn(lohsConfig); + eq(mContext), eq(null), any(), eq(false))).thenReturn(lohsConfig); registerLOHSRequestFull(); verify(mWifiApConfigStore).generateLocalOnlyHotspotConfig( - eq(mContext), eq(null), any()); + eq(mContext), eq(null), any(), eq(false)); verifyLohsBand(SoftApConfiguration.BAND_2GHZ); } @@ -3905,14 +3951,16 @@ public class WifiServiceImplTest extends WifiBaseTest { @Test(expected = SecurityException.class) public void testCustomLohs_FailsWithoutPermission() { - SoftApConfiguration customConfig = new SoftApConfiguration.Builder() - .setSsid("customConfig") - .build(); + SoftApConfiguration.Builder customConfigBuilder = new SoftApConfiguration.Builder() + .setSsid("customConfig"); + if (Environment.isSdkAtLeastB()) { + customConfigBuilder.setUserConfiguration(false); + } // set up basic permissions, but not NETWORK_SETUP_WIZARD setupLohsPermissions(); setupWardenForCustomLohs(); mWifiServiceImpl.startLocalOnlyHotspot(mLohsCallback, TEST_PACKAGE_NAME, TEST_FEATURE_ID, - customConfig, mExtras); + customConfigBuilder.build(), mExtras, true); } private static void nopDeathCallback(LocalOnlyHotspotRequestInfo requestor) { @@ -3928,11 +3976,11 @@ public class WifiServiceImplTest extends WifiBaseTest { .build(); setupForCustomLohs(); - mWifiServiceImpl.registerLOHSForTest(mPid, + mWifiServiceImpl.registerLOHSForTest(TEST_PID, new LocalOnlyHotspotRequestInfo(mLooper.getLooper(), new WorkSource(), sharedCallback, WifiServiceImplTest::nopDeathCallback, null)); assertThat(mWifiServiceImpl.startLocalOnlyHotspot(exclusiveCallback, TEST_PACKAGE_NAME, - TEST_FEATURE_ID, exclusiveConfig, mExtras)).isEqualTo(ERROR_GENERIC); + TEST_FEATURE_ID, exclusiveConfig, mExtras, true)).isEqualTo(ERROR_GENERIC); stopAutoDispatchWithDispatchAllBeforeStopAndIgnoreExceptions(mLooper); assertThat(sharedCallback.mIsStarted).isTrue(); assertThat(exclusiveCallback.mIsStarted).isFalse(); @@ -3940,20 +3988,23 @@ public class WifiServiceImplTest extends WifiBaseTest { @Test public void testCustomLohs_ExclusiveBeforeShared() { + when(mWorkSourceHelper.getRequestorWsPriority()) + .thenReturn(WorkSourceHelper.PRIORITY_SYSTEM); mLooper.startAutoDispatch(); FakeLohsCallback sharedCallback = new FakeLohsCallback(); FakeLohsCallback exclusiveCallback = new FakeLohsCallback(); SoftApConfiguration exclusiveConfig = new SoftApConfiguration.Builder() .setSsid("customSsid") .build(); - + when(mWifiApConfigStore.generateLocalOnlyHotspotConfig( + eq(mContext), eq(exclusiveConfig), any(), eq(true))).thenReturn(exclusiveConfig); setupForCustomLohs(); - mWifiServiceImpl.registerLOHSForTest(mPid, + mWifiServiceImpl.registerLOHSForTest(TEST_PID, new LocalOnlyHotspotRequestInfo(mLooper.getLooper(), new WorkSource(), exclusiveCallback, WifiServiceImplTest::nopDeathCallback, exclusiveConfig)); stopAutoDispatchWithDispatchAllBeforeStopAndIgnoreExceptions(mLooper); assertThat(mWifiServiceImpl.startLocalOnlyHotspot(sharedCallback, TEST_PACKAGE_NAME, - TEST_FEATURE_ID, null, mExtras)).isEqualTo(ERROR_GENERIC); + TEST_FEATURE_ID, null, mExtras, false)).isEqualTo(ERROR_GENERIC); assertThat(exclusiveCallback.mIsStarted).isTrue(); assertThat(sharedCallback.mIsStarted).isFalse(); } @@ -3965,16 +4016,16 @@ public class WifiServiceImplTest extends WifiBaseTest { .setPassphrase("passphrase", SoftApConfiguration.SECURITY_TYPE_WPA2_PSK) .build(); when(mWifiApConfigStore.generateLocalOnlyHotspotConfig( - eq(mContext), eq(config), any())).thenReturn(config); + eq(mContext), eq(config), any(), eq(true))).thenReturn(config); FakeLohsCallback callback = new FakeLohsCallback(); mLooper.startAutoDispatch(); setupForCustomLohs(); assertThat( mWifiServiceImpl.startLocalOnlyHotspot(callback, TEST_PACKAGE_NAME, TEST_FEATURE_ID, - config, mExtras)).isEqualTo(REQUEST_REGISTERED); + config, mExtras, true)).isEqualTo(REQUEST_REGISTERED); stopAutoDispatchWithDispatchAllBeforeStopAndIgnoreExceptions(mLooper); verify(mWifiApConfigStore).generateLocalOnlyHotspotConfig( - eq(mContext), eq(config), any()); + eq(mContext), eq(config), any(), eq(true)); // Use app's worksouce. verify(mActiveModeWarden).startSoftAp(any(), eq(new WorkSource(Binder.getCallingUid(), TEST_PACKAGE_NAME))); @@ -3991,16 +4042,16 @@ public class WifiServiceImplTest extends WifiBaseTest { .setSsid("customSsid") .build(); when(mWifiApConfigStore.generateLocalOnlyHotspotConfig( - eq(mContext), eq(config), any())).thenReturn(config); + eq(mContext), eq(config), any(), eq(true))).thenReturn(config); FakeLohsCallback callback = new FakeLohsCallback(); mLooper.startAutoDispatch(); setupForCustomLohs(); assertThat( mWifiServiceImpl.startLocalOnlyHotspot(callback, TEST_PACKAGE_NAME, TEST_FEATURE_ID, - config, mExtras)).isEqualTo(REQUEST_REGISTERED); + config, mExtras, true)).isEqualTo(REQUEST_REGISTERED); stopAutoDispatchWithDispatchAllBeforeStopAndIgnoreExceptions(mLooper); verify(mWifiApConfigStore).generateLocalOnlyHotspotConfig( - eq(mContext), eq(config), any()); + eq(mContext), eq(config), any(), eq(true)); // Use app's worksouce. verify(mActiveModeWarden).startSoftAp(any(), eq(new WorkSource(Binder.getCallingUid(), TEST_PACKAGE_NAME))); @@ -4018,7 +4069,7 @@ public class WifiServiceImplTest extends WifiBaseTest { .setPassphrase("passphrase", SoftApConfiguration.SECURITY_TYPE_WPA2_PSK) .build(); when(mWifiApConfigStore.generateLocalOnlyHotspotConfig( - eq(mContext), eq(customizedConfig), any())) + eq(mContext), eq(customizedConfig), any(), eq(true))) .thenReturn(lohsConfig); mLooper.startAutoDispatch(); FakeLohsCallback callback = new FakeLohsCallback(); @@ -4026,10 +4077,10 @@ public class WifiServiceImplTest extends WifiBaseTest { setupForCustomLohs(); assertThat( mWifiServiceImpl.startLocalOnlyHotspot(callback, TEST_PACKAGE_NAME, TEST_FEATURE_ID, - customizedConfig, mExtras)).isEqualTo(REQUEST_REGISTERED); + customizedConfig, mExtras, true)).isEqualTo(REQUEST_REGISTERED); stopAutoDispatchWithDispatchAllBeforeStopAndIgnoreExceptions(mLooper); verify(mWifiApConfigStore).generateLocalOnlyHotspotConfig( - eq(mContext), eq(customizedConfig), any()); + eq(mContext), eq(customizedConfig), any(), eq(true)); // Use app's worksouce. verify(mActiveModeWarden).startSoftAp(any(), eq(new WorkSource(Binder.getCallingUid(), TEST_PACKAGE_NAME))); @@ -4050,19 +4101,19 @@ public class WifiServiceImplTest extends WifiBaseTest { } SoftApConfiguration customizedConfig = customizedConfigBuilder.build(); when(mWifiApConfigStore.generateLocalOnlyHotspotConfig( - eq(mContext), eq(customizedConfig), any())) + eq(mContext), eq(customizedConfig), any(), eq(true))) .thenReturn(customizedConfig); FakeLohsCallback callback = new FakeLohsCallback(); setupForCustomLohs(); assertThat( mWifiServiceImpl.startLocalOnlyHotspot(callback, TEST_PACKAGE_NAME, TEST_FEATURE_ID, - customizedConfig, mExtras)).isEqualTo(REQUEST_REGISTERED); + customizedConfig, mExtras, true)).isEqualTo(REQUEST_REGISTERED); stopAutoDispatchWithDispatchAllBeforeStopAndIgnoreExceptions(mLooper); // Use app's worksouce. verify(mWifiApConfigStore).generateLocalOnlyHotspotConfig( - eq(mContext), eq(customizedConfig), any()); + eq(mContext), eq(customizedConfig), any(), eq(true)); verify(mActiveModeWarden).startSoftAp(any(), eq(new WorkSource(Binder.getCallingUid(), TEST_PACKAGE_NAME))); assertThat(callback.mIsStarted).isTrue(); @@ -4096,7 +4147,7 @@ public class WifiServiceImplTest extends WifiBaseTest { // registering a request directly from the test will not trigger a message to start // softap mode - mWifiServiceImpl.registerLOHSForTest(mPid, mRequestInfo); + mWifiServiceImpl.registerLOHSForTest(TEST_PID, mRequestInfo); mLooper.dispatchAll(); setupLocalOnlyHotspot(); @@ -4330,6 +4381,19 @@ public class WifiServiceImplTest extends WifiBaseTest { } /** + * Verify that soft AP callback is called on ClientsDisconnected event + */ + @Test + public void callsRegisteredCallbacksOnClientsDisconnectedEvent() throws Exception { + List<WifiClient> testClients = new ArrayList<>(); + registerSoftApCallbackAndVerify(mClientSoftApCallback); + + mStateMachineSoftApCallback.onClientsDisconnected(mTestSoftApInfo, testClients); + mLooper.dispatchAll(); + verify(mClientSoftApCallback).onClientsDisconnected(eq(mTestSoftApInfo), eq(testClients)); + } + + /** * Verify that mSoftApState and mSoftApNumClients in WifiServiceImpl are being updated on soft * Ap events, even when no callbacks are registered. */ @@ -4602,10 +4666,10 @@ public class WifiServiceImplTest extends WifiBaseTest { throws Exception { SoftApConfiguration lohsConfig = createValidSoftApConfiguration(); when(mWifiApConfigStore.generateLocalOnlyHotspotConfig( - eq(mContext), eq(null), any())).thenReturn(lohsConfig); + eq(mContext), eq(null), any(), eq(false))).thenReturn(lohsConfig); registerLOHSRequestFull(); verify(mWifiApConfigStore).generateLocalOnlyHotspotConfig( - eq(mContext), eq(null), any()); + eq(mContext), eq(null), any(), eq(false)); mWifiServiceImpl.registerLOHSForTest(TEST_PID, mRequestInfo); mWifiServiceImpl.updateInterfaceIpState(WIFI_IFACE_NAME, IFACE_IP_MODE_LOCAL_ONLY); @@ -4745,7 +4809,7 @@ public class WifiServiceImplTest extends WifiBaseTest { when(callback2.asBinder()).thenReturn(mock(IBinder.class)); int result = mWifiServiceImpl.startLocalOnlyHotspot( - callback2, TEST_PACKAGE_NAME, TEST_FEATURE_ID, null, mExtras); + callback2, TEST_PACKAGE_NAME, TEST_FEATURE_ID, null, mExtras, false); assertEquals(LocalOnlyHotspotCallback.REQUEST_REGISTERED, result); mLooper.dispatchAll(); @@ -6543,6 +6607,7 @@ public class WifiServiceImplTest extends WifiBaseTest { mBroadcastReceiverCaptor.getValue().onReceive(mContext, intent); mLooper.dispatchAll(); + verify(mResourceCache).reset(); verify(mWifiConfigManager).resetSimNetworks(); verify(mWifiConfigManager).stopRestrictingAutoJoinToSubscriptionId(); verify(mSimRequiredNotifier, never()).dismissSimRequiredNotification(); @@ -6571,6 +6636,7 @@ public class WifiServiceImplTest extends WifiBaseTest { mBroadcastReceiverCaptor.getValue().onReceive(mContext, intent); mLooper.dispatchAll(); + verify(mResourceCache).reset(); verify(mWifiConfigManager, never()).resetSimNetworks(); verify(mPasspointManager, never()).resetSimPasspointNetwork(); verify(mWifiNetworkSuggestionsManager, never()).resetSimNetworkSuggestions(); @@ -6598,6 +6664,7 @@ public class WifiServiceImplTest extends WifiBaseTest { mBroadcastReceiverCaptor.getValue().onReceive(mContext, intent); mLooper.dispatchAll(); + verify(mResourceCache).reset(); verify(mWifiConfigManager).resetSimNetworks(); verify(mWifiConfigManager).stopRestrictingAutoJoinToSubscriptionId(); verify(mSimRequiredNotifier, never()).dismissSimRequiredNotification(); @@ -8959,7 +9026,7 @@ public class WifiServiceImplTest extends WifiBaseTest { */ @Test public void getWifiActivityEnergyInfoAsyncFeatureUnsupported() throws Exception { - when(mActiveModeWarden.getSupportedFeatureSet()).thenReturn(0L); + when(mActiveModeWarden.getSupportedFeatureSet()).thenReturn(new BitSet()); mWifiServiceImpl.getWifiActivityEnergyInfoAsync(mOnWifiActivityEnergyInfoListener); verify(mOnWifiActivityEnergyInfoListener).onWifiActivityEnergyInfo(null); @@ -8971,7 +9038,8 @@ public class WifiServiceImplTest extends WifiBaseTest { */ @Test public void getWifiActivityEnergyInfoAsyncSuccess() throws Exception { - when(mActiveModeWarden.getSupportedFeatureSet()).thenReturn(Long.MAX_VALUE); + when(mActiveModeWarden.getSupportedFeatureSet()).thenReturn( + createCapabilityBitset(WifiManager.WIFI_FEATURE_LINK_LAYER_STATS)); setupReportActivityInfo(); mWifiServiceImpl.getWifiActivityEnergyInfoAsync(mOnWifiActivityEnergyInfoListener); mLooper.dispatchAll(); @@ -9145,9 +9213,15 @@ public class WifiServiceImplTest extends WifiBaseTest { */ @Test public void testSetWifiConnectedNetworkScorerAndVerify() throws Exception { + when(mPackageManager.getPackagesForUid(anyInt())) + .thenReturn(new String[]{EXTERNAL_SCORER_PKG_NAME}); + when(mContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenReturn(true); mLooper.startAutoDispatch(); + mWifiServiceImpl.setWifiConnectedNetworkScorer(mAppBinder, mWifiConnectedNetworkScorer); mLooper.stopAutoDispatch(); + + assertNotNull(mWifiServiceImpl.mScorerServiceConnection); verify(mActiveModeWarden).setWifiConnectedNetworkScorer( mAppBinder, mWifiConnectedNetworkScorer, myUid()); } @@ -9156,9 +9230,30 @@ public class WifiServiceImplTest extends WifiBaseTest { * Verify that clearWifiConnectedNetworkScorer clears scorer from {@link WifiScoreReport}. */ @Test + public void testClearWifiConnectedNetworkScorerUnbindService() throws Exception { + when(mPackageManager.getPackagesForUid(anyInt())) + .thenReturn(new String[]{EXTERNAL_SCORER_PKG_NAME}); + when(mContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenReturn(true); + mLooper.startAutoDispatch(); + mWifiServiceImpl.setWifiConnectedNetworkScorer(mAppBinder, mWifiConnectedNetworkScorer); + mLooper.stopAutoDispatch(); + assertNotNull(mWifiServiceImpl.mScorerServiceConnection); + + mWifiServiceImpl.clearWifiConnectedNetworkScorer(); + mLooper.dispatchAll(); + + verify(mContext).unbindService(any()); + verify(mActiveModeWarden).clearWifiConnectedNetworkScorer(); + } + + @Test public void testClearWifiConnectedNetworkScorerAndVerify() throws Exception { + mWifiServiceImpl.mScorerServiceConnection = null; + mWifiServiceImpl.clearWifiConnectedNetworkScorer(); mLooper.dispatchAll(); + + verify(mContext, never()).unbindService(any()); verify(mActiveModeWarden).clearWifiConnectedNetworkScorer(); } @@ -9229,18 +9324,20 @@ public class WifiServiceImplTest extends WifiBaseTest { } @Test - public void getSupportedFeaturesVerboseLoggingThrottled() { + public void supportedFeaturesVerboseLoggingThrottled() { mWifiServiceImpl.enableVerboseLogging( WifiManager.VERBOSE_LOGGING_LEVEL_ENABLED); // this logs when(mClock.getElapsedSinceBootMillis()).thenReturn(1000L); - when(mActiveModeWarden.getSupportedFeatureSet()).thenReturn(0x110L); - mWifiServiceImpl.getSupportedFeatures(); + when(mActiveModeWarden.getSupportedFeatureSet()).thenReturn( + createCapabilityBitset(WifiManager.WIFI_FEATURE_P2P)); + mWifiServiceImpl.isFeatureSupported(WifiManager.WIFI_FEATURE_P2P); when(mClock.getElapsedSinceBootMillis()).thenReturn(1001L); - mWifiServiceImpl.getSupportedFeatures(); // should not log + mWifiServiceImpl.isFeatureSupported(WifiManager.WIFI_FEATURE_P2P); // should not log when(mClock.getElapsedSinceBootMillis()).thenReturn(5000L); - mWifiServiceImpl.getSupportedFeatures(); - when(mActiveModeWarden.getSupportedFeatureSet()).thenReturn(0x100L); - mWifiServiceImpl.getSupportedFeatures(); + mWifiServiceImpl.isFeatureSupported(WifiManager.WIFI_FEATURE_P2P); + when(mActiveModeWarden.getSupportedFeatureSet()).thenReturn(createCapabilityBitset( + WifiManager.WIFI_FEATURE_P2P, WifiManager.WIFI_FEATURE_PASSPOINT)); + mWifiServiceImpl.isFeatureSupported(WifiManager.WIFI_FEATURE_P2P); verify(mLog, times(4)).info(any()); } @@ -9687,6 +9784,7 @@ public class WifiServiceImplTest extends WifiBaseTest { Intent intent = new Intent(Intent.ACTION_LOCALE_CHANGED); mBroadcastReceiverCaptor.getValue().onReceive(mContext, intent); + verify(mResourceCache).handleLocaleChange(); verify(mWifiNotificationManager).createNotificationChannels(); verify(mWifiNetworkSuggestionsManager).resetNotification(); verify(mWifiCarrierInfoManager).resetNotification(); @@ -10162,19 +10260,28 @@ public class WifiServiceImplTest extends WifiBaseTest { ScanResult.CHANNEL_WIDTH_20MHZ))); when(mWifiNative.getUsableChannels(eq(WIFI_BAND_60_GHZ), anyInt(), anyInt())) .thenReturn(null); - mWifiServiceImpl.mCountryCodeTracker.onDriverCountryCodeChanged(TEST_COUNTRY_CODE); mLooper.dispatchAll(); - verify(mWifiSettingsConfigStore).put( eq(WifiSettingsConfigStore.WIFI_SOFT_AP_COUNTRY_CODE), eq(TEST_COUNTRY_CODE)); verify(mWifiSettingsConfigStore).put( eq(WifiSettingsConfigStore.WIFI_AVAILABLE_SOFT_AP_FREQS_MHZ), eq("[2452,5180,5955]")); + + // Make sure CC change to world mode won't update WIFI_SOFT_AP_COUNTRY_CODE + when(mWifiSettingsConfigStore.get(WifiSettingsConfigStore.WIFI_SOFT_AP_COUNTRY_CODE)) + .thenReturn(TEST_COUNTRY_CODE); + when(mWifiCountryCode.isDriverCountryCodeWorldMode()).thenReturn(true); + String testWorldModeCountryCode = "00"; + mWifiServiceImpl.mCountryCodeTracker.onDriverCountryCodeChanged(testWorldModeCountryCode); + mLooper.dispatchAll(); + verify(mWifiSettingsConfigStore, never()).put( + eq(WifiSettingsConfigStore.WIFI_SOFT_AP_COUNTRY_CODE), + eq(testWorldModeCountryCode)); } private List<WifiConfiguration> setupMultiTypeConfigs( - long featureFlags, boolean saeAutoUpgradeEnabled, boolean oweAutoUpgradeEnabled) { + BitSet featureFlags, boolean saeAutoUpgradeEnabled, boolean oweAutoUpgradeEnabled) { when(mActiveModeWarden.getSupportedFeatureSet()).thenReturn(featureFlags); when(mWifiGlobals.isWpa3SaeUpgradeEnabled()).thenReturn(saeAutoUpgradeEnabled); when(mWifiGlobals.isOweUpgradeEnabled()).thenReturn(oweAutoUpgradeEnabled); @@ -10247,7 +10354,8 @@ public class WifiServiceImplTest extends WifiBaseTest { */ @Test public void testGetConfiguredNetworksForMultiTypeConfigs() { - long featureFlags = WifiManager.WIFI_FEATURE_WPA3_SAE | WifiManager.WIFI_FEATURE_OWE; + BitSet featureFlags = createCapabilityBitset( + WifiManager.WIFI_FEATURE_WPA3_SAE, WifiManager.WIFI_FEATURE_OWE); List<WifiConfiguration> testConfigs = setupMultiTypeConfigs( featureFlags, true, true); when(mWifiConfigManager.getSavedNetworks(anyInt())) @@ -10279,7 +10387,8 @@ public class WifiServiceImplTest extends WifiBaseTest { */ @Test public void testGetConfiguredNetworksForMultiTypeConfigsWithoutAutoUpgradeEnabled() { - long featureFlags = WifiManager.WIFI_FEATURE_WPA3_SAE | WifiManager.WIFI_FEATURE_OWE; + BitSet featureFlags = createCapabilityBitset( + WifiManager.WIFI_FEATURE_WPA3_SAE, WifiManager.WIFI_FEATURE_OWE); List<WifiConfiguration> testConfigs = setupMultiTypeConfigs( featureFlags, false, false); when(mWifiConfigManager.getSavedNetworks(anyInt())) @@ -10311,7 +10420,7 @@ public class WifiServiceImplTest extends WifiBaseTest { */ @Test public void testGetConfiguredNetworksForMultiTypeConfigsWithoutHwSupport() { - long featureFlags = 0L; + BitSet featureFlags = new BitSet(); List<WifiConfiguration> testConfigs = setupMultiTypeConfigs( featureFlags, true, true); when(mWifiConfigManager.getSavedNetworks(anyInt())) @@ -10539,7 +10648,7 @@ public class WifiServiceImplTest extends WifiBaseTest { assumeTrue(SdkLevel.isAtLeastT()); when(mWifiGlobals.isBackgroundScanSupported()).thenReturn(true); when(mActiveModeWarden.getSupportedFeatureSet()) - .thenReturn(WifiManager.WIFI_FEATURE_PNO); + .thenReturn(createCapabilityBitset(WifiManager.WIFI_FEATURE_PNO)); when(mWifiPermissionsUtil.checkRequestCompanionProfileAutomotiveProjectionPermission( anyInt())).thenReturn(true); when(mWifiPermissionsUtil.checkCallersLocationPermissionInManifest( @@ -10559,7 +10668,7 @@ public class WifiServiceImplTest extends WifiBaseTest { @Test public void testSetExternalPnoScanRequest_PnoNotSupported() throws Exception { assumeTrue(SdkLevel.isAtLeastT()); - when(mActiveModeWarden.getSupportedFeatureSet()).thenReturn(0L); + when(mActiveModeWarden.getSupportedFeatureSet()).thenReturn(new BitSet()); when(mWifiPermissionsUtil.checkRequestCompanionProfileAutomotiveProjectionPermission( anyInt())).thenReturn(true); when(mWifiPermissionsUtil.checkCallersLocationPermissionInManifest( @@ -10777,11 +10886,11 @@ public class WifiServiceImplTest extends WifiBaseTest { .setPassphrase("thisIsABadPassword", SoftApConfiguration.SECURITY_TYPE_WPA3_SAE) .build(); when(mWifiApConfigStore.generateLocalOnlyHotspotConfig( - any(), any(), any())).thenReturn(customizedConfig); + any(), any(), any(), eq(true))).thenReturn(customizedConfig); // Expect the result is registered but it should get failure because non-supported // configuration int result = mWifiServiceImpl.startLocalOnlyHotspot(mLohsCallback, TEST_PACKAGE_NAME, - TEST_FEATURE_ID, customizedConfig, mExtras); + TEST_FEATURE_ID, customizedConfig, mExtras, true); assertEquals(LocalOnlyHotspotCallback.REQUEST_REGISTERED, result); mLooper.dispatchAll(); verify(mLohsCallback).onHotspotFailed(ERROR_GENERIC); @@ -10822,6 +10931,8 @@ public class WifiServiceImplTest extends WifiBaseTest { @Test public void unregisterLohsSoftApCallbackRemovesCallback() throws Exception { assumeTrue(SdkLevel.isAtLeastT()); + AttributionSource attributionSource = mock(AttributionSource.class); + mExtras.putParcelable(WifiManager.EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE, attributionSource); registerLohsSoftApCallbackAndVerify(mClientSoftApCallback, mExtras); mWifiServiceImpl.unregisterLocalOnlyHotspotSoftApCallback(mClientSoftApCallback, mExtras); @@ -10842,6 +10953,8 @@ public class WifiServiceImplTest extends WifiBaseTest { public void unregisterLohsSoftApCallbackDoesNotRemoveCallbackIfCallbackNotMatching() throws Exception { assumeTrue(SdkLevel.isAtLeastT()); + AttributionSource attributionSource = mock(AttributionSource.class); + mExtras.putParcelable(WifiManager.EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE, attributionSource); registerLohsSoftApCallbackAndVerify(mClientSoftApCallback, mExtras); mWifiServiceImpl.unregisterLocalOnlyHotspotSoftApCallback(mAnotherSoftApCallback, mExtras); @@ -10863,6 +10976,8 @@ public class WifiServiceImplTest extends WifiBaseTest { assumeTrue(SdkLevel.isAtLeastT()); WifiClient testWifiClient = new WifiClient(MacAddress.fromString("22:33:44:55:66:77"), WIFI_IFACE_NAME2); + AttributionSource attributionSource = mock(AttributionSource.class); + mExtras.putParcelable(WifiManager.EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE, attributionSource); registerLohsSoftApCallbackAndVerify(mClientSoftApCallback, mExtras); mLooper.dispatchAll(); @@ -10908,6 +11023,8 @@ public class WifiServiceImplTest extends WifiBaseTest { @Test public void registersForBinderDeathOnRegisterLohsSoftApCallback() throws Exception { assumeTrue(SdkLevel.isAtLeastT()); + AttributionSource attributionSource = mock(AttributionSource.class); + mExtras.putParcelable(WifiManager.EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE, attributionSource); registerLohsSoftApCallbackAndVerify(mClientSoftApCallback, mExtras); verify(mAppBinder).linkToDeath(any(IBinder.DeathRecipient.class), anyInt()); } @@ -10920,6 +11037,8 @@ public class WifiServiceImplTest extends WifiBaseTest { assumeTrue(SdkLevel.isAtLeastT()); ArgumentCaptor<IBinder.DeathRecipient> drCaptor = ArgumentCaptor.forClass(IBinder.DeathRecipient.class); + AttributionSource attributionSource = mock(AttributionSource.class); + mExtras.putParcelable(WifiManager.EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE, attributionSource); registerLohsSoftApCallbackAndVerify(mClientSoftApCallback, mExtras); verify(mAppBinder).linkToDeath(drCaptor.capture(), anyInt()); @@ -10967,6 +11086,31 @@ public class WifiServiceImplTest extends WifiBaseTest { } /** + * Verifies that a LOHS SoftApCallback is ignored if its AttributionSource no longer has the + * NEARBY_WIFI_DEVICES permission + */ + @Test + public void testRegisterLocalOnlyHotspotSoftApCallbackIgnoredWhenPermissionRevoked() + throws Exception { + assumeTrue(SdkLevel.isAtLeastT()); + AttributionSource attributionSource = mock(AttributionSource.class); + mExtras.putParcelable(WifiManager.EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE, attributionSource); + registerLohsSoftApCallbackAndVerify(mClientSoftApCallback, mExtras); + + // Revoke NEARBY_WIFI_DEVICES permission + when(mWifiPermissionsUtil.checkNearbyDevicesPermission(any(), anyBoolean(), any())) + .thenReturn(false); + + // Callback should be ignored + reset(mClientSoftApCallback); + mLohsApCallback.onConnectedClientsOrInfoChanged( + mTestSoftApInfos, mTestSoftApClients, false); + mLooper.dispatchAll(); + verify(mClientSoftApCallback, never()).onConnectedClientsOrInfoChanged( + any(), any(), anyBoolean(), anyBoolean()); + } + + /** * Verify getStaConcurrencyForMultiInternetMode */ @Test @@ -12326,17 +12470,24 @@ public class WifiServiceImplTest extends WifiBaseTest { @Test public void testSetWepAllowedWithPermission() { + // Test if WIFI_WEP_ALLOWED starts with false. when(mWifiSettingsConfigStore.get(eq(WIFI_WEP_ALLOWED))).thenReturn(false); mWifiServiceImpl.checkAndStartWifi(); mLooper.dispatchAll(); verify(mWifiSettingsConfigStore).get(eq(WIFI_WEP_ALLOWED)); verify(mWifiGlobals).setWepAllowed(eq(false)); + verify(mWifiSettingsConfigStore).registerChangeListener( + eq(WIFI_WEP_ALLOWED), + mWepAllowedSettingChangedListenerCaptor.capture(), + any(Handler.class)); // verify setWepAllowed with MANAGE_WIFI_NETWORK_SETTING when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(true); mWifiServiceImpl.setWepAllowed(true); + verify(mWifiSettingsConfigStore).put(eq(WIFI_WEP_ALLOWED), eq(true)); + mWepAllowedSettingChangedListenerCaptor.getValue() + .onSettingsChanged(WIFI_WEP_ALLOWED, true); mLooper.dispatchAll(); verify(mWifiGlobals).setWepAllowed(true); - verify(mWifiSettingsConfigStore).put(eq(WIFI_WEP_ALLOWED), eq(true)); } @Test @@ -12352,10 +12503,21 @@ public class WifiServiceImplTest extends WifiBaseTest { when(mockWifiInfoWpa.getCurrentSecurityType()).thenReturn(WifiInfo.SECURITY_TYPE_PSK); when(cmmWep.getConnectionInfo()).thenReturn(mockWifiInfoWep); when(cmmWpa.getConnectionInfo()).thenReturn(mockWifiInfoWpa); + mWifiServiceImpl.checkAndStartWifi(); + mLooper.dispatchAll(); + verify(mWifiSettingsConfigStore).get(eq(WIFI_WEP_ALLOWED)); + verify(mWifiGlobals).setWepAllowed(eq(true)); + verify(mWifiSettingsConfigStore).registerChangeListener( + eq(WIFI_WEP_ALLOWED), + mWepAllowedSettingChangedListenerCaptor.capture(), + any(Handler.class)); + mWifiServiceImpl.setWepAllowed(false); + verify(mWifiSettingsConfigStore).put(eq(WIFI_WEP_ALLOWED), eq(false)); + mWepAllowedSettingChangedListenerCaptor.getValue() + .onSettingsChanged(WIFI_WEP_ALLOWED, false); mLooper.dispatchAll(); verify(mWifiGlobals).setWepAllowed(false); - verify(mWifiSettingsConfigStore).put(eq(WIFI_WEP_ALLOWED), eq(false)); // Only WEP disconnect verify(cmmWep).disconnect(); verify(cmmWpa, never()).disconnect(); @@ -12493,8 +12655,8 @@ public class WifiServiceImplTest extends WifiBaseTest { @Test public void testSetPerSsidRoamingModeByDeviceAdmin() throws RemoteException { assumeTrue(SdkLevel.isAtLeastV()); - when(mActiveModeWarden.getSupportedFeatureSet()) - .thenReturn(WifiManager.WIFI_FEATURE_AGGRESSIVE_ROAMING_MODE_SUPPORT); + when(mActiveModeWarden.getSupportedFeatureSet()).thenReturn( + createCapabilityBitset(WifiManager.WIFI_FEATURE_AGGRESSIVE_ROAMING_MODE_SUPPORT)); assertThrows(SecurityException.class, () -> mWifiServiceImpl.setPerSsidRoamingMode( WifiSsid.fromString(TEST_SSID_WITH_QUOTES), @@ -12516,8 +12678,8 @@ public class WifiServiceImplTest extends WifiBaseTest { @Test public void testSetPerSsidRoamingModeByNonAdmin() throws RemoteException { assumeTrue(SdkLevel.isAtLeastV()); - when(mActiveModeWarden.getSupportedFeatureSet()) - .thenReturn(WifiManager.WIFI_FEATURE_AGGRESSIVE_ROAMING_MODE_SUPPORT); + when(mActiveModeWarden.getSupportedFeatureSet()).thenReturn( + createCapabilityBitset(WifiManager.WIFI_FEATURE_AGGRESSIVE_ROAMING_MODE_SUPPORT)); assertThrows(SecurityException.class, () -> mWifiServiceImpl.setPerSsidRoamingMode( WifiSsid.fromString(TEST_SSID_WITH_QUOTES), @@ -12539,8 +12701,8 @@ public class WifiServiceImplTest extends WifiBaseTest { @Test public void testRemovePerSsidRoamingModeByDeviceAdmin() throws RemoteException { assumeTrue(SdkLevel.isAtLeastV()); - when(mActiveModeWarden.getSupportedFeatureSet()) - .thenReturn(WifiManager.WIFI_FEATURE_AGGRESSIVE_ROAMING_MODE_SUPPORT); + when(mActiveModeWarden.getSupportedFeatureSet()).thenReturn( + createCapabilityBitset(WifiManager.WIFI_FEATURE_AGGRESSIVE_ROAMING_MODE_SUPPORT)); assertThrows(SecurityException.class, () -> mWifiServiceImpl.removePerSsidRoamingMode( WifiSsid.fromString(TEST_SSID_WITH_QUOTES), TEST_PACKAGE_NAME)); @@ -12557,8 +12719,8 @@ public class WifiServiceImplTest extends WifiBaseTest { @Test public void testRemovePerSsidRoamingModeByNonAdmin() throws RemoteException { assumeTrue(SdkLevel.isAtLeastV()); - when(mActiveModeWarden.getSupportedFeatureSet()) - .thenReturn(WifiManager.WIFI_FEATURE_AGGRESSIVE_ROAMING_MODE_SUPPORT); + when(mActiveModeWarden.getSupportedFeatureSet()).thenReturn( + createCapabilityBitset(WifiManager.WIFI_FEATURE_AGGRESSIVE_ROAMING_MODE_SUPPORT)); assertThrows(SecurityException.class, () -> mWifiServiceImpl.removePerSsidRoamingMode( WifiSsid.fromString(TEST_SSID_WITH_QUOTES), TEST_PACKAGE_NAME)); @@ -12574,8 +12736,8 @@ public class WifiServiceImplTest extends WifiBaseTest { @Test public void testGetPerSsidRoamingModesByDeviceAdmin() throws RemoteException { assumeTrue(SdkLevel.isAtLeastV()); - when(mActiveModeWarden.getSupportedFeatureSet()) - .thenReturn(WifiManager.WIFI_FEATURE_AGGRESSIVE_ROAMING_MODE_SUPPORT); + when(mActiveModeWarden.getSupportedFeatureSet()).thenReturn( + createCapabilityBitset(WifiManager.WIFI_FEATURE_AGGRESSIVE_ROAMING_MODE_SUPPORT)); IMapListener listener = mock(IMapListener.class); InOrder inOrder = inOrder(listener); assertThrows(SecurityException.class, @@ -12602,8 +12764,8 @@ public class WifiServiceImplTest extends WifiBaseTest { @Test public void testGetPerSsidRoamingModesByNonAdmin() throws RemoteException { assumeTrue(SdkLevel.isAtLeastV()); - when(mActiveModeWarden.getSupportedFeatureSet()) - .thenReturn(WifiManager.WIFI_FEATURE_AGGRESSIVE_ROAMING_MODE_SUPPORT); + when(mActiveModeWarden.getSupportedFeatureSet()).thenReturn( + createCapabilityBitset(WifiManager.WIFI_FEATURE_AGGRESSIVE_ROAMING_MODE_SUPPORT)); IMapListener listener = mock(IMapListener.class); InOrder inOrder = inOrder(listener); assertThrows(SecurityException.class, @@ -12633,9 +12795,9 @@ public class WifiServiceImplTest extends WifiBaseTest { when(mWifiGlobals.isSwPnoEnabled()).thenReturn(isSwPnoEnabled); if (isPnoFeatureSet) { when(mActiveModeWarden.getSupportedFeatureSet()) - .thenReturn(WifiManager.WIFI_FEATURE_PNO); + .thenReturn(createCapabilityBitset(WifiManager.WIFI_FEATURE_PNO)); } else { - when(mActiveModeWarden.getSupportedFeatureSet()).thenReturn(0L); + when(mActiveModeWarden.getSupportedFeatureSet()).thenReturn(new BitSet()); } assertEquals(mWifiServiceImpl.isPnoSupported(), (isBackgroundScanSupported && isPnoFeatureSet) || isSwPnoEnabled); @@ -12881,7 +13043,8 @@ public class WifiServiceImplTest extends WifiBaseTest { @Test public void testGetWifiConfigForMatchedNetworkSuggestionsSharedWithUserForMultiTypeConfigs() { - long featureFlags = WifiManager.WIFI_FEATURE_WPA3_SAE | WifiManager.WIFI_FEATURE_OWE; + BitSet featureFlags = createCapabilityBitset( + WifiManager.WIFI_FEATURE_WPA3_SAE, WifiManager.WIFI_FEATURE_OWE); List<WifiConfiguration> testConfigs = setupMultiTypeConfigs(featureFlags, true, true); when(mWifiNetworkSuggestionsManager .getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(anyList())) @@ -12906,4 +13069,341 @@ public class WifiServiceImplTest extends WifiBaseTest { expectedConfigs, configs.getList()); } + @Test + public void testSetAutojoinDisallowedSecurityTypesWithPermission() throws RemoteException { + assumeTrue(SdkLevel.isAtLeastT()); + // No permission to call API + when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(false); + when(mWifiPermissionsUtil.checkManageWifiNetworkSelectionPermission(anyInt())) + .thenReturn(false); + assertThrows(SecurityException.class, + () -> mWifiServiceImpl.setAutojoinDisallowedSecurityTypes(0/*restrict none*/, + mExtras)); + // Has permission to call API + when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(true); + // Null argument + assertThrows(IllegalArgumentException.class, + () -> mWifiServiceImpl.setAutojoinDisallowedSecurityTypes(0/*restrict none*/, + null)); + // Invalid argument + assertThrows(IllegalArgumentException.class, + () -> mWifiServiceImpl.setAutojoinDisallowedSecurityTypes( + 0x1 << WifiInfo.SECURITY_TYPE_OWE, mExtras)); + assertThrows(IllegalArgumentException.class, + () -> mWifiServiceImpl.setAutojoinDisallowedSecurityTypes( + 0x1 << WifiInfo.SECURITY_TYPE_SAE, mExtras)); + assertThrows(IllegalArgumentException.class, + () -> mWifiServiceImpl.setAutojoinDisallowedSecurityTypes( + 0x1 << WifiInfo.SECURITY_TYPE_EAP_WPA3_ENTERPRISE, mExtras)); + // Valid argument + int restrictions = (0x1 << WifiInfo.SECURITY_TYPE_OPEN) + | (0x1 << WifiInfo.SECURITY_TYPE_OWE) | (0x1 << WifiInfo.SECURITY_TYPE_WEP); + mWifiServiceImpl.setAutojoinDisallowedSecurityTypes(restrictions, mExtras); + mLooper.dispatchAll(); + verify(mWifiConnectivityManager).setAutojoinDisallowedSecurityTypes(eq(restrictions)); + } + + @Test + public void testGetAutojoinDisallowedSecurityTypesWithPermission() throws RemoteException { + assumeTrue(SdkLevel.isAtLeastT()); + // Mock listener. + IIntegerListener listener = mock(IIntegerListener.class); + InOrder inOrder = inOrder(listener); + // No permission to call API + when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(false); + when(mWifiPermissionsUtil.checkManageWifiNetworkSelectionPermission(anyInt())) + .thenReturn(false); + assertThrows(SecurityException.class, + () -> mWifiServiceImpl.getAutojoinDisallowedSecurityTypes(listener, mExtras)); + // Null arguments + assertThrows(IllegalArgumentException.class, + () -> mWifiServiceImpl.getAutojoinDisallowedSecurityTypes(null, mExtras)); + assertThrows(IllegalArgumentException.class, + () -> mWifiServiceImpl.getAutojoinDisallowedSecurityTypes(listener, null)); + // has permission to call API + when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(true); + when(mWifiConnectivityManager.getAutojoinDisallowedSecurityTypes()).thenReturn(7); + mWifiServiceImpl.getAutojoinDisallowedSecurityTypes(listener, mExtras); + mLooper.dispatchAll(); + inOrder.verify(listener).onResult(7); + } + /** + * Verify UwbManager.AdapterStateCallback onStateChanged could update mLastUwbState in + * WifiMetrics properly + */ + @Test + public void testServiceImplAdapterStateCallback() { + assumeTrue(SdkLevel.isAtLeastT()); + UwbAdapterStateListener uwbAdapterStateListener = + mWifiServiceImpl.new UwbAdapterStateListener(); + + uwbAdapterStateListener.onStateChanged(2, 1); + verify(mWifiMetrics).setLastUwbState(2); + } + + /** + * Verify ThreadNetworkController.StateCallback onDeviceRoleChanged could update + * mLastThreadDeviceRole in WifiMetrics properly + */ + @Test + public void testServiceImplThreadStateCallback() { + assumeTrue(SdkLevel.isAtLeastV()); + ThreadStateListener threadStateListener = + mWifiServiceImpl.new ThreadStateListener(); + + threadStateListener.onDeviceRoleChanged(3); + verify(mWifiMetrics).setLastThreadDeviceRole(3); + } + + @Test + public void testSetQueryAllowedWhenWepUsageControllerSupported() { + when(mFeatureFlags.wepDisabledInApm()).thenReturn(true); + WepNetworkUsageController testWepNetworkUsageController = + mock(WepNetworkUsageController.class); + when(mWifiInjector.getWepNetworkUsageController()) + .thenReturn(testWepNetworkUsageController); + when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(true); + ConcreteClientModeManager cmmWep = mock(ConcreteClientModeManager.class); + WifiInfo mockWifiInfoWep = mock(WifiInfo.class); + List<ClientModeManager> cmms = Arrays.asList(cmmWep); + when(mActiveModeWarden.getClientModeManagers()).thenReturn(cmms); + when(mockWifiInfoWep.getCurrentSecurityType()).thenReturn(WifiInfo.SECURITY_TYPE_WEP); + when(cmmWep.getConnectionInfo()).thenReturn(mockWifiInfoWep); + mWifiServiceImpl = makeWifiServiceImpl(); + mWifiServiceImpl.checkAndStartWifi(); + mWifiServiceImpl.handleBootCompleted(); + mLooper.dispatchAll(); + // Verify boot complete go through the new design. + verify(mWifiSettingsConfigStore, never()).registerChangeListener( + eq(WIFI_WEP_ALLOWED), + mWepAllowedSettingChangedListenerCaptor.capture(), + any(Handler.class)); + verify(mWifiGlobals, never()).setWepAllowed(anyBoolean()); + verify(testWepNetworkUsageController).handleBootCompleted(); + + mWifiServiceImpl.setWepAllowed(false); + mLooper.dispatchAll(); + verify(mWifiGlobals, never()).setWepAllowed(anyBoolean()); + verify(mWifiSettingsConfigStore).put(eq(WIFI_WEP_ALLOWED), eq(false)); + // WEP disconnect logic moved to WepNetworkUsageController. + verify(cmmWep, never()).disconnect(); + verify(mWifiGlobals, never()).setWepAllowed(anyBoolean()); + } + + @Test + public void testUpdateSoftApCapabilityCheckMLOSupport() throws Exception { + lenient().when(SubscriptionManager.getActiveDataSubscriptionId()) + .thenReturn(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID); + ArgumentCaptor<SoftApCapability> capabilityArgumentCaptor = ArgumentCaptor.forClass( + SoftApCapability.class); + when(mWifiNative.isMLDApSupportMLO()).thenReturn(true); + mWifiServiceImpl.checkAndStartWifi(); + mLooper.dispatchAll(); + verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), + argThat((IntentFilter filter) -> + filter.hasAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)), + isNull(), + any(Handler.class)); + + // Send the broadcast + Intent intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); + mBroadcastReceiverCaptor.getValue().onReceive(mContext, intent); + mLooper.dispatchAll(); + verify(mActiveModeWarden).updateSoftApCapability(capabilityArgumentCaptor.capture(), + eq(WifiManager.IFACE_IP_MODE_TETHERED)); + assertTrue(capabilityArgumentCaptor.getValue() + .areFeaturesSupported(SoftApCapability.SOFTAP_FEATURE_MLO)); + } + + @Test + public void testCustomUserLohs() { + assumeTrue(Environment.isSdkAtLeastB()); + SoftApConfiguration customConfig = new SoftApConfiguration.Builder() + .setSsid("customConfig") + .build(); + assertThrows(SecurityException.class, + () -> mWifiServiceImpl.startLocalOnlyHotspot( + mLohsCallback, TEST_PACKAGE_NAME, TEST_FEATURE_ID, + customConfig, mExtras, false)); + setupLohsPermissions(); + mWifiServiceImpl.startLocalOnlyHotspot(mLohsCallback, TEST_PACKAGE_NAME, TEST_FEATURE_ID, + customConfig, mExtras, false); + } + + @Test + public void testDisallowCurrentSuggestedNetwork() { + BlockingOption blockingOption = new BlockingOption.Builder(100).build(); + WifiInfo info = new WifiInfo(); + info.setRequestingPackageName(TEST_PACKAGE_NAME); + when(mActiveModeWarden.getWifiState()).thenReturn(WIFI_STATE_ENABLED); + when(mActiveModeWarden.getConnectionInfo()).thenReturn(info); + mWifiServiceImpl.disallowCurrentSuggestedNetwork(blockingOption, TEST_PACKAGE_NAME); + mLooper.dispatchAll(); + verify(mClientModeManager).blockNetwork(eq(blockingOption)); + } + + /** + * Test add and remove listener will go to ActiveModeWarden. + */ + @Test + public void testAddRemoveWifiStateChangedListener() throws Exception { + assumeTrue(SdkLevel.isAtLeastS()); + when(mWifiStateChangedListener.asBinder()).thenReturn(mAppBinder); + mWifiServiceImpl.addWifiStateChangedListener(mWifiStateChangedListener); + mLooper.dispatchAll(); + verify(mActiveModeWarden).addWifiStateChangedListener(mWifiStateChangedListener); + mWifiServiceImpl.removeWifiStateChangedListener(mWifiStateChangedListener); + mLooper.dispatchAll(); + verify(mActiveModeWarden).removeWifiStateChangedListener(mWifiStateChangedListener); + } + + /** + * Verify that a call to addWifiStateChangedListener throws a SecurityException if the + * caller does not have the ACCESS_WIFI_STATE permission. + */ + @Test + public void testAddWifiStateChangedListenerThrowsSecurityExceptionOnMissingPermissions() { + doThrow(new SecurityException()).when(mContext) + .enforceCallingOrSelfPermission(eq(ACCESS_WIFI_STATE), + eq("WifiService")); + try { + mWifiServiceImpl.addWifiStateChangedListener(mWifiStateChangedListener); + fail("expected SecurityException"); + } catch (SecurityException expected) { } + } + + /** + * Verify that a call to removeWifiStateChangedListener throws a SecurityException if the caller + * does not have the ACCESS_WIFI_STATE permission. + */ + @Test + public void testRemoveWifiStateChangedListenerThrowsSecurityExceptionOnMissingPermissions() { + doThrow(new SecurityException()).when(mContext) + .enforceCallingOrSelfPermission(eq(ACCESS_WIFI_STATE), + eq("WifiService")); + try { + mWifiServiceImpl.addWifiStateChangedListener(mWifiStateChangedListener); + fail("expected SecurityException"); + } catch (SecurityException expected) { } + } + + @Test + public void testCustomLohs_NotExclusive5GConfigButNewRequestorLowerPriority() { + assumeTrue(Environment.isSdkAtLeastB()); + when(mFeatureFlags.publicBandsForLohs()).thenReturn(true); + when(mWorkSourceHelper.getRequestorWsPriority()) + .thenReturn(WorkSourceHelper.PRIORITY_FG_APP) + .thenReturn(WorkSourceHelper.PRIORITY_BG); + setupForCustomLohs(); + setup5GhzSupported(); + SoftApConfiguration custom5GBandConfig = new SoftApConfiguration.Builder() + .setSsid("TestAp") + .setBand(SoftApConfiguration.BAND_5GHZ) + .build(); + when(mWifiApConfigStore.generateLocalOnlyHotspotConfig( + any(), any(), any(), eq(false))).thenReturn(custom5GBandConfig); + when(mRequestInfo.getCustomConfig()).thenReturn(custom5GBandConfig); + when(mRequestInfo2.getCustomConfig()).thenReturn(null); + mLooper.startAutoDispatch(); + assertThat(mWifiServiceImpl.registerLOHSForTest(TEST_PID, mRequestInfo)) + .isEqualTo(REQUEST_REGISTERED); + stopAutoDispatchWithDispatchAllBeforeStopAndIgnoreExceptions(mLooper); + // Test second requestor gets fail since it has lower priority + assertThat(mWifiServiceImpl.registerLOHSForTest(TEST_PID2, mRequestInfo2)) + .isEqualTo(ERROR_GENERIC); + } + + @Test + public void testCustomLohs_NotExclusive2GConfigSharedEvenIfNewRequestorLowerPriority() + throws Exception { + assumeTrue(Environment.isSdkAtLeastB()); + when(mFeatureFlags.publicBandsForLohs()).thenReturn(true); + when(mWorkSourceHelper.getRequestorWsPriority()) + .thenReturn(WorkSourceHelper.PRIORITY_FG_APP) + .thenReturn(WorkSourceHelper.PRIORITY_BG); + setupForCustomLohs(); + SoftApConfiguration custom2GBandConfig = new SoftApConfiguration.Builder() + .setSsid("TestAp") + .setBand(SoftApConfiguration.BAND_2GHZ) + .build(); + when(mWifiApConfigStore.generateLocalOnlyHotspotConfig( + any(), any(), any(), eq(false))).thenReturn(custom2GBandConfig); + when(mRequestInfo.getCustomConfig()).thenReturn(custom2GBandConfig); + when(mRequestInfo2.getCustomConfig()).thenReturn(null); + mLooper.startAutoDispatch(); + assertThat(mWifiServiceImpl.registerLOHSForTest(TEST_PID, mRequestInfo)) + .isEqualTo(REQUEST_REGISTERED); + stopAutoDispatchWithDispatchAllBeforeStopAndIgnoreExceptions(mLooper); + mLooper.startAutoDispatch(); + // Test second requestor gets registered even if it has lower priority + assertThat(mWifiServiceImpl.registerLOHSForTest(TEST_PID2, mRequestInfo2)) + .isEqualTo(REQUEST_REGISTERED); + verify(mRequestInfo, never()).unlinkDeathRecipient(); + verify(mRequestInfo2).sendHotspotStartedMessage(eq(custom2GBandConfig)); + stopAutoDispatchWithDispatchAllBeforeStopAndIgnoreExceptions(mLooper); + } + + @Test + public void testCustomLohs_NotExclusive5GConfigButNewRequestorHigherPriority() + throws Exception { + assumeTrue(Environment.isSdkAtLeastB()); + when(mFeatureFlags.publicBandsForLohs()).thenReturn(true); + when(mWorkSourceHelper.getRequestorWsPriority()) + .thenReturn(WorkSourceHelper.PRIORITY_FG_APP) // first requestor + .thenReturn(WorkSourceHelper.PRIORITY_FG_APP) // new requestor + .thenReturn(WorkSourceHelper.PRIORITY_BG); // first requestor to BG + setupForCustomLohs(); + setup5GhzSupported(); + SoftApConfiguration custom5GBandConfig = new SoftApConfiguration.Builder() + .setSsid("TestAp") + .setBand(SoftApConfiguration.BAND_5GHZ) + .build(); + when(mWifiApConfigStore.generateLocalOnlyHotspotConfig( + any(), any(), any(), eq(false))).thenReturn(custom5GBandConfig); + when(mRequestInfo.getCustomConfig()).thenReturn(custom5GBandConfig); + when(mRequestInfo2.getCustomConfig()).thenReturn(null); + mLooper.startAutoDispatch(); + assertThat(mWifiServiceImpl.registerLOHSForTest(TEST_PID, mRequestInfo)) + .isEqualTo(REQUEST_REGISTERED); + stopAutoDispatchWithDispatchAllBeforeStopAndIgnoreExceptions(mLooper); + mLooper.startAutoDispatch(); + // Test second requestor gets succeeded since it has higher priority + assertThat(mWifiServiceImpl.registerLOHSForTest(TEST_PID2, mRequestInfo2)) + .isEqualTo(REQUEST_REGISTERED); + // Make sure first requestor dead since it was replaced by second requestor. + verify(mRequestInfo).sendHotspotFailedMessage(eq(ERROR_INCOMPATIBLE_MODE)); + verify(mRequestInfo).unlinkDeathRecipient(); + stopAutoDispatchWithDispatchAllBeforeStopAndIgnoreExceptions(mLooper); + } + + @Test + public void testCustomLohs_NotExclusive2GConfigSharedWhenNewRequestorHihgerPriority() + throws Exception { + assumeTrue(Environment.isSdkAtLeastB()); + when(mFeatureFlags.publicBandsForLohs()).thenReturn(true); + when(mWorkSourceHelper.getRequestorWsPriority()) + .thenReturn(WorkSourceHelper.PRIORITY_BG) + .thenReturn(WorkSourceHelper.PRIORITY_FG_APP); + setupForCustomLohs(); + SoftApConfiguration custom2GBandConfig = new SoftApConfiguration.Builder() + .setSsid("TestAp") + .setBand(SoftApConfiguration.BAND_2GHZ) + .build(); + when(mWifiApConfigStore.generateLocalOnlyHotspotConfig( + any(), any(), any(), eq(false))).thenReturn(custom2GBandConfig); + when(mRequestInfo.getCustomConfig()).thenReturn(custom2GBandConfig); + when(mRequestInfo2.getCustomConfig()).thenReturn(null); + mLooper.startAutoDispatch(); + assertThat(mWifiServiceImpl.registerLOHSForTest(TEST_PID, mRequestInfo)) + .isEqualTo(REQUEST_REGISTERED); + stopAutoDispatchWithDispatchAllBeforeStopAndIgnoreExceptions(mLooper); + mLooper.startAutoDispatch(); + // Test second requestor gets succeeded since it has higher priority + assertThat(mWifiServiceImpl.registerLOHSForTest(TEST_PID2, mRequestInfo2)) + .isEqualTo(REQUEST_REGISTERED); + // Make sure first requestor still alive since 2.4G can be shared. + verify(mRequestInfo2).sendHotspotStartedMessage(eq(custom2GBandConfig)); + verify(mRequestInfo, never()).unlinkDeathRecipient(); + stopAutoDispatchWithDispatchAllBeforeStopAndIgnoreExceptions(mLooper); + } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiShellCommandTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiShellCommandTest.java index a63c4d47b2..d972c23fe4 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiShellCommandTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiShellCommandTest.java @@ -598,7 +598,7 @@ public class WifiShellCommandTest extends WifiBaseTest { ArgumentCaptor<SoftApConfiguration> softApConfigurationCaptor = ArgumentCaptor.forClass( SoftApConfiguration.class); verify(mWifiService).startLocalOnlyHotspot(any(), eq(SHELL_PACKAGE_NAME), any(), - softApConfigurationCaptor.capture(), any()); + softApConfigurationCaptor.capture(), any(), eq(false)); assertEquals(SoftApConfiguration.BAND_5GHZ, softApConfigurationCaptor.getValue().getBand()); assertEquals(SoftApConfiguration.SECURITY_TYPE_WPA2_PSK, diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java index 0862fd7245..b1ba78ec3c 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java @@ -18,7 +18,7 @@ package com.android.server.wifi; import static com.android.server.wifi.HalDeviceManager.HDM_CREATE_IFACE_AP; import static com.android.server.wifi.HalDeviceManager.HDM_CREATE_IFACE_STA; -import static com.android.server.wifi.util.GeneralUtil.longToBitset; +import static com.android.server.wifi.TestUtil.createCapabilityBitset; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -32,7 +32,6 @@ import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyBoolean; import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.anyLong; import static org.mockito.Mockito.anyObject; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.eq; @@ -175,7 +174,7 @@ public class WifiVendorHalTest extends WifiBaseTest { }).when(mHalDeviceManager).stop(); when(mHalDeviceManager.createStaIface(any(), any(), any(), eq(mConcreteClientModeManager))) .thenReturn(mWifiStaIface); - when(mHalDeviceManager.createApIface(anyLong(), any(), any(), any(), anyBoolean(), + when(mHalDeviceManager.createApIface(any(), any(), any(), any(), anyBoolean(), eq(mSoftApManager), anyList())) .thenReturn(mWifiApIface); when(mHalDeviceManager.removeIface(any())).thenReturn(true); @@ -256,7 +255,7 @@ public class WifiVendorHalTest extends WifiBaseTest { verify(mWifiChip).registerCallback(any(WifiChip.Callback.class)); verify(mHalDeviceManager, never()).createApIface( - anyLong(), any(), any(), any(), anyBoolean(), eq(mSoftApManager), anyList()); + any(), any(), any(), any(), anyBoolean(), eq(mSoftApManager), anyList()); } /** @@ -280,7 +279,7 @@ public class WifiVendorHalTest extends WifiBaseTest { verify(mHalDeviceManager, never()).createStaIface(any(), any(), any(), eq(mConcreteClientModeManager)); verify(mHalDeviceManager, never()).createApIface( - anyLong(), any(), any(), any(), anyBoolean(), eq(mSoftApManager), anyList()); + any(), any(), any(), any(), anyBoolean(), eq(mSoftApManager), anyList()); verify(mHalDeviceManager, never()).getChip(any(WifiHal.WifiInterface.class)); verify(mWifiStaIface, never()) .registerFrameworkCallback(any(WifiStaIface.Callback.class)); @@ -303,7 +302,7 @@ public class WifiVendorHalTest extends WifiBaseTest { verify(mHalDeviceManager).stop(); verify(mHalDeviceManager, never()).createApIface( - anyLong(), any(), any(), any(), anyBoolean(), eq(mSoftApManager), anyList()); + any(), any(), any(), any(), anyBoolean(), eq(mSoftApManager), anyList()); verify(mHalDeviceManager, never()).getChip(any(WifiHal.WifiInterface.class)); verify(mWifiStaIface, never()) .registerFrameworkCallback(any(WifiStaIface.Callback.class)); @@ -327,7 +326,7 @@ public class WifiVendorHalTest extends WifiBaseTest { verify(mWifiStaIface).registerFrameworkCallback(any(WifiStaIface.Callback.class)); verify(mHalDeviceManager, never()).createApIface( - anyLong(), any(), any(), any(), anyBoolean(), eq(mSoftApManager), anyList()); + any(), any(), any(), any(), anyBoolean(), eq(mSoftApManager), anyList()); } /** @@ -349,7 +348,7 @@ public class WifiVendorHalTest extends WifiBaseTest { verify(mHalDeviceManager, never()).getChip(any(WifiHal.WifiInterface.class)); verify(mHalDeviceManager, never()).createApIface( - anyLong(), any(), any(), any(), anyBoolean(), eq(mSoftApManager), anyList()); + any(), any(), any(), any(), anyBoolean(), eq(mSoftApManager), anyList()); } /** @@ -372,7 +371,7 @@ public class WifiVendorHalTest extends WifiBaseTest { verify(mWifiChip).registerCallback(any(WifiChip.Callback.class)); verify(mHalDeviceManager, never()).createApIface( - anyLong(), any(), any(), any(), anyBoolean(), eq(mSoftApManager), anyList()); + any(), any(), any(), any(), anyBoolean(), eq(mSoftApManager), anyList()); } /** @@ -396,7 +395,7 @@ public class WifiVendorHalTest extends WifiBaseTest { verify(mHalDeviceManager, times(2)).isStarted(); verify(mHalDeviceManager, never()).createApIface( - anyLong(), any(), any(), any(), anyBoolean(), eq(mSoftApManager), anyList()); + any(), any(), any(), any(), anyBoolean(), eq(mSoftApManager), anyList()); } /** @@ -417,7 +416,7 @@ public class WifiVendorHalTest extends WifiBaseTest { verify(mHalDeviceManager).start(); verify(mHalDeviceManager).stop(); verify(mHalDeviceManager).createApIface( - anyLong(), any(), any(), any(), anyBoolean(), eq(mSoftApManager), anyList()); + any(), any(), any(), any(), anyBoolean(), eq(mSoftApManager), anyList()); verify(mHalDeviceManager).getChip(eq(mWifiApIface)); verify(mHalDeviceManager, times(2)).isReady(); verify(mHalDeviceManager, times(2)).isStarted(); @@ -475,7 +474,7 @@ public class WifiVendorHalTest extends WifiBaseTest { assertTrue(mWifiVendorHal.isHalStarted()); verify(mHalDeviceManager).start(); - verify(mHalDeviceManager).createApIface(anyLong(), + verify(mHalDeviceManager).createApIface(any(), internalListenerCaptor.capture(), any(), eq(TEST_WORKSOURCE), eq(false), eq(mSoftApManager), anyList()); verify(mHalDeviceManager).getChip(eq(mWifiApIface)); @@ -555,9 +554,9 @@ public class WifiVendorHalTest extends WifiBaseTest { */ @Test public void testGetSupportedFeatures() throws Exception { - BitSet staIfaceCaps = longToBitset( - WifiManager.WIFI_FEATURE_SCANNER | WifiManager.WIFI_FEATURE_LINK_LAYER_STATS); - BitSet chipCaps = longToBitset(WifiManager.WIFI_FEATURE_TX_POWER_LIMIT); + BitSet staIfaceCaps = createCapabilityBitset( + WifiManager.WIFI_FEATURE_SCANNER, WifiManager.WIFI_FEATURE_LINK_LAYER_STATS); + BitSet chipCaps = createCapabilityBitset(WifiManager.WIFI_FEATURE_TX_POWER_LIMIT); WifiChip.Response<BitSet> chipCapsResponse = new WifiChip.Response<>(chipCaps); chipCapsResponse.setStatusCode(WifiHal.WIFI_STATUS_SUCCESS); when(mWifiStaIface.getCapabilities()).thenReturn(staIfaceCaps); @@ -570,15 +569,13 @@ public class WifiVendorHalTest extends WifiBaseTest { when(mWifiGlobals.isWpa3SaeH2eSupported()).thenReturn(true); when(mHalDeviceManager.is24g5gDbsSupported(any())).thenReturn(true); - BitSet expectedFeatureSet = longToBitset( - WifiManager.WIFI_FEATURE_SCANNER - | WifiManager.WIFI_FEATURE_LINK_LAYER_STATS - | WifiManager.WIFI_FEATURE_TX_POWER_LIMIT - | WifiManager.WIFI_FEATURE_INFRA - | WifiManager.WIFI_FEATURE_P2P - | WifiManager.WIFI_FEATURE_SAE_H2E - | WifiManager.WIFI_FEATURE_DUAL_BAND_SIMULTANEOUS - ); + BitSet expectedFeatureSet = createCapabilityBitset(WifiManager.WIFI_FEATURE_SCANNER, + WifiManager.WIFI_FEATURE_LINK_LAYER_STATS, + WifiManager.WIFI_FEATURE_TX_POWER_LIMIT, + WifiManager.WIFI_FEATURE_INFRA, + WifiManager.WIFI_FEATURE_P2P, + WifiManager.WIFI_FEATURE_SAE_H2E, + WifiManager.WIFI_FEATURE_DUAL_BAND_SIMULTANEOUS); assertTrue(mWifiVendorHal.startVendorHalSta(mConcreteClientModeManager)); assertTrue(expectedFeatureSet.equals( mWifiVendorHal.getSupportedFeatureSet(TEST_IFACE_NAME))); @@ -606,11 +603,8 @@ public class WifiVendorHalTest extends WifiBaseTest { when(mPackageManager.hasSystemFeature(eq(PackageManager.FEATURE_WIFI_AWARE))) .thenReturn(true); - BitSet expectedFeatureSet = longToBitset( - WifiManager.WIFI_FEATURE_INFRA - | WifiManager.WIFI_FEATURE_P2P - | WifiManager.WIFI_FEATURE_AWARE - ); + BitSet expectedFeatureSet = createCapabilityBitset(WifiManager.WIFI_FEATURE_INFRA, + WifiManager.WIFI_FEATURE_P2P, WifiManager.WIFI_FEATURE_AWARE); assertTrue(expectedFeatureSet.equals( mWifiVendorHal.getSupportedFeatureSet(TEST_IFACE_NAME))); } @@ -1468,7 +1462,7 @@ public class WifiVendorHalTest extends WifiBaseTest { null, TEST_WORKSOURCE, SoftApConfiguration.BAND_2GHZ, false, mSoftApManager, new ArrayList<>())); verify(mHalDeviceManager).createApIface( - anyLong(), any(), any(), eq(TEST_WORKSOURCE), eq(false), eq(mSoftApManager), + any(), any(), any(), eq(TEST_WORKSOURCE), eq(false), eq(mSoftApManager), anyList()); } @@ -1497,7 +1491,7 @@ public class WifiVendorHalTest extends WifiBaseTest { null, TEST_WORKSOURCE, SoftApConfiguration.BAND_2GHZ, false, mSoftApManager, new ArrayList<>()); verify(mHalDeviceManager).createApIface( - anyLong(), any(), any(), eq(TEST_WORKSOURCE), eq(false), eq(mSoftApManager), + any(), any(), any(), eq(TEST_WORKSOURCE), eq(false), eq(mSoftApManager), anyList()); assertEquals(TEST_IFACE_NAME, ifaceName); assertTrue(mWifiVendorHal.removeApIface(ifaceName)); diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiVoipDetectorTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiVoipDetectorTest.java index 9741a2c8c2..1c8264a1f6 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/WifiVoipDetectorTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/WifiVoipDetectorTest.java @@ -66,6 +66,7 @@ public class WifiVoipDetectorTest extends WifiBaseTest { @Mock private TelephonyManager mTelephonyManager; @Mock private WifiNative mWifiNative; @Mock private WifiCarrierInfoManager mWifiCarrierInfoManager; + @Mock private WifiMetrics mWifiMetrics; private WifiVoipDetector mWifiVoipDetector; private TestLooper mLooper; @@ -84,13 +85,15 @@ public class WifiVoipDetectorTest extends WifiBaseTest { when(mWifiInjector.makeTelephonyManager()).thenReturn(mTelephonyManager); when(mContext.getSystemService(AudioManager.class)).thenReturn(mAudioManager); when(mWifiInjector.getWifiNative()).thenReturn(mWifiNative); + when(mWifiInjector.getWifiMetrics()).thenReturn(mWifiMetrics); when(mWifiNative.setVoipMode(anyInt())).thenReturn(true); mWifiVoipDetector = new WifiVoipDetector(mContext, new Handler(mLooper.getLooper()), mWifiInjector, mWifiCarrierInfoManager); } - private void resetWifiNativeAndReSetupforMock() { + private void resetWifiNativeAndWifiMetricsAndReSetupforMock() { reset(mWifiNative); + reset(mWifiMetrics); when(mWifiNative.setVoipMode(anyInt())).thenReturn(true); } @@ -111,38 +114,46 @@ public class WifiVoipDetectorTest extends WifiBaseTest { when(mWifiCarrierInfoManager.isWifiCallingAvailable()).thenReturn(true); mWifiVoipDetector.notifyWifiConnected(true, true, TEST_PRIMARY_INTERFACE_NAME); verify(mWifiNative).setVoipMode(WifiChip.WIFI_VOIP_MODE_VOICE); + verify(mWifiInjector.getWifiMetrics()).setVoipMode(WifiChip.WIFI_VOIP_MODE_VOICE); // Test VoWifi call off -> switch to VoLte mTelephonyCallbackCaptor.getValue().onCallAttributesChanged(TEST_LTE_CALL_ATT); verify(mWifiNative).setVoipMode(WifiChip.WIFI_VOIP_MODE_OFF); - resetWifiNativeAndReSetupforMock(); + verify(mWifiInjector.getWifiMetrics()).setVoipMode(WifiChip.WIFI_VOIP_MODE_OFF); + resetWifiNativeAndWifiMetricsAndReSetupforMock(); // Test VoWifi Call mTelephonyCallbackCaptor.getValue().onCallAttributesChanged(TEST_VOWIFI_CALL_ATT); verify(mWifiNative).setVoipMode(WifiChip.WIFI_VOIP_MODE_VOICE); - resetWifiNativeAndReSetupforMock(); + verify(mWifiInjector.getWifiMetrics()).setVoipMode(WifiChip.WIFI_VOIP_MODE_VOICE); + resetWifiNativeAndWifiMetricsAndReSetupforMock(); // Test VoWifi call off -> switch to VoLte mTelephonyCallbackCaptor.getValue().onCallAttributesChanged(TEST_LTE_CALL_ATT); verify(mWifiNative).setVoipMode(WifiChip.WIFI_VOIP_MODE_OFF); - resetWifiNativeAndReSetupforMock(); + verify(mWifiInjector.getWifiMetrics()).setVoipMode(WifiChip.WIFI_VOIP_MODE_OFF); + resetWifiNativeAndWifiMetricsAndReSetupforMock(); // Test MODE_IN_COMMUNICATION to trigger voice mode mAudioModeChangedListeneCaptor.getValue().onModeChanged(AudioManager.MODE_IN_COMMUNICATION); verify(mWifiNative).setVoipMode(WifiChip.WIFI_VOIP_MODE_VOICE); - resetWifiNativeAndReSetupforMock(); + verify(mWifiInjector.getWifiMetrics()).setVoipMode(WifiChip.WIFI_VOIP_MODE_VOICE); + resetWifiNativeAndWifiMetricsAndReSetupforMock(); // Test MODE_NORMAL off WifiChip.WIFI_VOIP_MODE_OFF mAudioModeChangedListeneCaptor.getValue().onModeChanged(AudioManager.MODE_NORMAL); verify(mWifiNative).setVoipMode(WifiChip.WIFI_VOIP_MODE_OFF); - resetWifiNativeAndReSetupforMock(); + verify(mWifiInjector.getWifiMetrics()).setVoipMode(WifiChip.WIFI_VOIP_MODE_OFF); + resetWifiNativeAndWifiMetricsAndReSetupforMock(); // Test MODE_COMMUNICATION_REDIRECT to trigger voice mode mAudioModeChangedListeneCaptor.getValue().onModeChanged( AudioManager.MODE_COMMUNICATION_REDIRECT); verify(mWifiNative).setVoipMode(WifiChip.WIFI_VOIP_MODE_VOICE); - resetWifiNativeAndReSetupforMock(); + verify(mWifiInjector.getWifiMetrics()).setVoipMode(WifiChip.WIFI_VOIP_MODE_VOICE); + resetWifiNativeAndWifiMetricsAndReSetupforMock(); // Do nothing when mode change between two OTT modes. mAudioModeChangedListeneCaptor.getValue().onModeChanged(AudioManager.MODE_IN_COMMUNICATION); verify(mWifiNative, never()).setVoipMode(anyInt()); mWifiVoipDetector.notifyWifiConnected(false, true, TEST_PRIMARY_INTERFACE_NAME); verify(mWifiNative).setVoipMode(WifiChip.WIFI_VOIP_MODE_OFF); + verify(mWifiInjector.getWifiMetrics()).setVoipMode(WifiChip.WIFI_VOIP_MODE_OFF); // Test OTT on when wifi is disconnected, should do nothing since it doesn't relate to wifi. - resetWifiNativeAndReSetupforMock(); + resetWifiNativeAndWifiMetricsAndReSetupforMock(); mAudioModeChangedListeneCaptor.getValue().onModeChanged( AudioManager.MODE_COMMUNICATION_REDIRECT); verify(mWifiNative, never()).setVoipMode(anyInt()); @@ -150,6 +161,7 @@ public class WifiVoipDetectorTest extends WifiBaseTest { when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_COMMUNICATION_REDIRECT); mWifiVoipDetector.notifyWifiConnected(true, true, TEST_PRIMARY_INTERFACE_NAME); verify(mWifiNative).setVoipMode(WifiChip.WIFI_VOIP_MODE_VOICE); + verify(mWifiInjector.getWifiMetrics()).setVoipMode(WifiChip.WIFI_VOIP_MODE_VOICE); } @Test diff --git a/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java index 809fa1a915..681db5b3d2 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareDataPathStateManagerTest.java @@ -88,6 +88,8 @@ import android.os.test.TestLooper; import androidx.test.filters.SmallTest; +import com.android.dx.mockito.inline.extended.ExtendedMockito; +import com.android.dx.mockito.inline.extended.StaticMockitoSession; import com.android.modules.utils.build.SdkLevel; import com.android.server.wifi.Clock; import com.android.server.wifi.DeviceConfigFacade; @@ -117,6 +119,7 @@ import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.Spy; +import org.mockito.quality.Strictness; import java.nio.ByteOrder; import java.util.ArrayList; @@ -172,6 +175,7 @@ public class WifiAwareDataPathStateManagerTest extends WifiBaseTest { public ErrorCollector collector = new ErrorCollector(); private MockResources mResources; private Bundle mExtras = new Bundle(); + private StaticMockitoSession mSession; /** * Initialize mocks. @@ -179,6 +183,12 @@ public class WifiAwareDataPathStateManagerTest extends WifiBaseTest { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + mSession = ExtendedMockito.mockitoSession() + .strictness(Strictness.LENIENT) + .mockStatic(WifiInjector.class) + .startMocking(); + + when(WifiInjector.getInstance()).thenReturn(mWifiInjector); mAlarmManager = new TestAlarmManager(); when(mMockContext.getSystemService(Context.ALARM_SERVICE)) @@ -244,6 +254,7 @@ public class WifiAwareDataPathStateManagerTest extends WifiBaseTest { @After public void tearDown() throws Exception { mMockNative.validateUniqueTransactionIds(); + mSession.finishMocking(); } /** diff --git a/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareServiceImplTest.java b/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareServiceImplTest.java index 8342171c42..0e06d031a7 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareServiceImplTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareServiceImplTest.java @@ -43,6 +43,7 @@ import android.Manifest; import android.content.Context; import android.content.pm.PackageManager; import android.net.wifi.OuiKeyedData; +import android.net.wifi.ScanResult; import android.net.wifi.WifiScanner; import android.net.wifi.aware.Characteristics; import android.net.wifi.aware.ConfigRequest; @@ -1064,7 +1065,7 @@ public class WifiAwareServiceImplTest extends WifiBaseTest { // constructed configs. PublishConfig publishConfig = new PublishConfig(serviceName.getBytes(), ssi, matchFilter, PublishConfig.PUBLISH_TYPE_UNSOLICITED, 0, true, false, false, - WifiScanner.WIFI_BAND_24_GHZ, null, null, false, Collections.emptyList()); + WifiScanner.WIFI_BAND_24_GHZ, null, null, false, Collections.emptyList(), false); int clientId = doConnect(); IWifiAwareDiscoverySessionCallback mockCallback = mock( IWifiAwareDiscoverySessionCallback.class); @@ -1081,7 +1082,9 @@ public class WifiAwareServiceImplTest extends WifiBaseTest { // constructed configs. SubscribeConfig subscribeConfig = new SubscribeConfig(serviceName.getBytes(), ssi, matchFilter, SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE, 0, true, false, 0, false, 0, - false, WifiScanner.WIFI_BAND_24_GHZ, null, false, Collections.emptyList()); + false, WifiScanner.WIFI_BAND_24_GHZ, null, false, Collections.emptyList(), + SubscribeConfig.PERIODIC_RANGING_INTERVAL_512TU, false, 2, 2437, 0, 0, + ScanResult.PREAMBLE_HT, 0); int clientId = doConnect(); IWifiAwareDiscoverySessionCallback mockCallback = mock( IWifiAwareDiscoverySessionCallback.class); diff --git a/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareStateManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareStateManagerTest.java index ee53e22771..c2f2e835d8 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareStateManagerTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/aware/WifiAwareStateManagerTest.java @@ -34,6 +34,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyByte; @@ -215,9 +216,11 @@ public class WifiAwareStateManagerTest extends WifiBaseTest { MockitoAnnotations.initMocks(this); mSession = ExtendedMockito.mockitoSession() .strictness(Strictness.LENIENT) + .mockStatic(WifiInjector.class) .mockStatic(WifiStatsLog.class) .startMocking(); + when(WifiInjector.getInstance()).thenReturn(mWifiInjector); mAlarmManager = new TestAlarmManager(); when(mMockContext.getSystemService(Context.ALARM_SERVICE)) .thenReturn(mAlarmManager.getAlarmManager()); @@ -276,8 +279,7 @@ public class WifiAwareStateManagerTest extends WifiBaseTest { mDut.start(mMockContext, mMockLooper.getLooper(), mAwareMetricsMock, mWifiPermissionsUtil, mPermissionsWrapperMock, new Clock(), mock(NetdWrapper.class), mInterfaceConflictManager); - verify(mMockContext, never()).registerReceiver(any(), any(IntentFilter.class), isNull(), - any(Handler.class)); + verify(mMockContext, never()).registerReceiver(any(), any(IntentFilter.class)); mDut.startLate(); mDut.enableVerboseLogging(true, true, true); mMockLooper.dispatchAll(); @@ -288,14 +290,17 @@ public class WifiAwareStateManagerTest extends WifiBaseTest { callbackArgumentCaptor.capture()); mActiveCountryCodeChangedCallback = callbackArgumentCaptor.getValue(); } - verify(mMockContext, times(3)) + verify(mMockContext) + .registerReceiverForAllUsers( + bcastRxCaptor.capture(), + argThat(filter -> filter.hasAction(LocationManager.MODE_CHANGED_ACTION)), + isNull(), isNull()); + verify(mMockContext, times(2)) .registerReceiver( bcastRxCaptor.capture(), - any(IntentFilter.class), - isNull(), - any(Handler.class)); - mPowerBcastReceiver = bcastRxCaptor.getAllValues().get(0); - mLocationModeReceiver = bcastRxCaptor.getAllValues().get(1); + any(IntentFilter.class)); + mLocationModeReceiver = bcastRxCaptor.getAllValues().get(0); + mPowerBcastReceiver = bcastRxCaptor.getAllValues().get(1); mWifiStateChangedReceiver = bcastRxCaptor.getAllValues().get(2); verify(mWifiSettingsConfigStore).registerChangeListener( eq(D2D_ALLOWED_WHEN_INFRA_STA_DISABLED), diff --git a/service/tests/wifitests/src/com/android/server/wifi/hal/WifiChipAidlImplTest.java b/service/tests/wifitests/src/com/android/server/wifi/hal/WifiChipAidlImplTest.java index 2850a1abec..f4104ab87f 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hal/WifiChipAidlImplTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hal/WifiChipAidlImplTest.java @@ -17,7 +17,7 @@ package com.android.server.wifi.hal; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; -import static com.android.server.wifi.util.GeneralUtil.longToBitset; +import static com.android.server.wifi.TestUtil.createCapabilityBitset; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -125,11 +125,8 @@ public class WifiChipAidlImplTest extends WifiBaseTest { | android.hardware.wifi.IWifiChip.FeatureSetMask.D2D_RTT | android.hardware.wifi.IWifiChip.FeatureSetMask.D2AP_RTT ); - BitSet expected = longToBitset( - WifiManager.WIFI_FEATURE_TX_POWER_LIMIT - | WifiManager.WIFI_FEATURE_D2D_RTT - | WifiManager.WIFI_FEATURE_D2AP_RTT - ); + BitSet expected = createCapabilityBitset(WifiManager.WIFI_FEATURE_TX_POWER_LIMIT, + WifiManager.WIFI_FEATURE_D2D_RTT, WifiManager.WIFI_FEATURE_D2AP_RTT); assertTrue(expected.equals(mDut.halToFrameworkChipFeatureSet(halFeatures))); } diff --git a/service/tests/wifitests/src/com/android/server/wifi/hal/WifiChipHidlImplTest.java b/service/tests/wifitests/src/com/android/server/wifi/hal/WifiChipHidlImplTest.java index 53dd5b47a5..52d505ca89 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hal/WifiChipHidlImplTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hal/WifiChipHidlImplTest.java @@ -16,7 +16,7 @@ package com.android.server.wifi.hal; -import static com.android.server.wifi.util.GeneralUtil.longToBitset; +import static com.android.server.wifi.TestUtil.createCapabilityBitset; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -156,11 +156,8 @@ public class WifiChipHidlImplTest extends WifiBaseTest { | android.hardware.wifi.V1_1.IWifiChip.ChipCapabilityMask.D2D_RTT | android.hardware.wifi.V1_1.IWifiChip.ChipCapabilityMask.D2AP_RTT ); - BitSet expected = longToBitset( - WifiManager.WIFI_FEATURE_TX_POWER_LIMIT - | WifiManager.WIFI_FEATURE_D2D_RTT - | WifiManager.WIFI_FEATURE_D2AP_RTT - ); + BitSet expected = createCapabilityBitset(WifiManager.WIFI_FEATURE_TX_POWER_LIMIT, + WifiManager.WIFI_FEATURE_D2D_RTT, WifiManager.WIFI_FEATURE_D2AP_RTT); assertTrue(expected.equals(mDut.wifiFeatureMaskFromChipCapabilities(caps))); } @@ -173,10 +170,8 @@ public class WifiChipHidlImplTest extends WifiBaseTest { android.hardware.wifi.V1_3.IWifiChip.ChipCapabilityMask.SET_LATENCY_MODE | android.hardware.wifi.V1_1.IWifiChip.ChipCapabilityMask.D2D_RTT ); - BitSet expected = longToBitset( - WifiManager.WIFI_FEATURE_LOW_LATENCY - | WifiManager.WIFI_FEATURE_D2D_RTT - ); + BitSet expected = createCapabilityBitset( + WifiManager.WIFI_FEATURE_LOW_LATENCY, WifiManager.WIFI_FEATURE_D2D_RTT); assertTrue(expected.equals(mDut.wifiFeatureMaskFromChipCapabilities_1_3(caps))); } diff --git a/service/tests/wifitests/src/com/android/server/wifi/hal/WifiRttControllerAidlImplTest.java b/service/tests/wifitests/src/com/android/server/wifi/hal/WifiRttControllerAidlImplTest.java index 199b0e3313..bae5001594 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hal/WifiRttControllerAidlImplTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hal/WifiRttControllerAidlImplTest.java @@ -259,6 +259,61 @@ public class WifiRttControllerAidlImplTest extends WifiBaseTest { } /** + * Validate IEEE 802.11az ranging request on an IEEE 802.11mc capable device. Expectation is + * RTT type has to be downgraded to 11mc and pre-amble needs to be adjusted based on the band + * of operation. + */ + @Test + public void test11azRangeRequestOn11mcCapableDevice() throws Exception { + int cmdId = 55; + RangingRequest request = RttTestUtils.getDummyRangingRequestWith11az((byte) 0); + + // update capabilities to enable 11mc only + RttCapabilities cap = getFullRttCapabilities(); + cap.ntbInitiatorSupported = false; + reset(mIWifiRttControllerMock); + when(mIWifiRttControllerMock.getCapabilities()).thenReturn(cap); + createAndInitializeDut(); + + mDut.rangeRequest(cmdId, request); + verify(mIWifiRttControllerMock).rangeRequest(eq(cmdId), mRttConfigCaptor.capture()); + RttConfig[] halRequest = mRttConfigCaptor.getValue(); + + collector.checkThat("number of entries", halRequest.length, + equalTo(request.mRttPeers.size())); + + RttConfig rttConfig = halRequest[0]; + collector.checkThat("entry 0: MAC", rttConfig.addr, + equalTo(MacAddress.fromString("00:01:02:03:04:00").toByteArray())); + collector.checkThat("entry 0: rtt type", rttConfig.type, equalTo(RttType.TWO_SIDED)); + collector.checkThat("entry 0: peer type", rttConfig.peer, equalTo(RttPeerType.AP)); + collector.checkThat("", rttConfig.preamble, equalTo(RttPreamble.VHT)); + + rttConfig = halRequest[1]; + collector.checkThat("entry 1: MAC", rttConfig.addr, + equalTo(MacAddress.fromString("0A:0B:0C:0D:0E:00").toByteArray())); + collector.checkThat("entry 1: rtt type", rttConfig.type, equalTo(RttType.ONE_SIDED)); + collector.checkThat("entry 1: peer type", rttConfig.peer, equalTo(RttPeerType.AP)); + collector.checkThat("", rttConfig.preamble, equalTo(RttPreamble.HT)); + + rttConfig = halRequest[2]; + collector.checkThat("entry 2: MAC", rttConfig.addr, + equalTo(MacAddress.fromString("08:09:08:07:06:05").toByteArray())); + collector.checkThat("entry 2: rtt type", rttConfig.type, equalTo(RttType.TWO_SIDED)); + collector.checkThat("entry 2: peer type", rttConfig.peer, equalTo(RttPeerType.NAN_TYPE)); + collector.checkThat("", rttConfig.preamble, equalTo(RttPreamble.HT)); + + rttConfig = halRequest[3]; + collector.checkThat("entry 3: MAC", rttConfig.addr, + equalTo(MacAddress.fromString("00:11:22:33:44:00").toByteArray())); + collector.checkThat("entry 3: rtt type", rttConfig.type, equalTo(RttType.TWO_SIDED_11MC)); + collector.checkThat("entry 3: peer type", rttConfig.peer, equalTo(RttPeerType.AP)); + collector.checkThat("entry 3: preamble", rttConfig.preamble, equalTo(RttPreamble.VHT)); + + verifyNoMoreInteractions(mIWifiRttControllerMock); + + } + /** * Validate successful ranging flow - with privileges access but with limited capabilities: * - Very limited BW * - Very limited Preamble diff --git a/service/tests/wifitests/src/com/android/server/wifi/hal/WifiRttControllerHidlImplTest.java b/service/tests/wifitests/src/com/android/server/wifi/hal/WifiRttControllerHidlImplTest.java index fdd63d31e1..34f8671637 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hal/WifiRttControllerHidlImplTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hal/WifiRttControllerHidlImplTest.java @@ -415,4 +415,42 @@ public class WifiRttControllerHidlImplTest extends WifiBaseTest { return cap; } + + /** + * Verify RTT burst duration with respect to different burst sizes. + */ + @Test + public void testBurstDuration() throws Exception { + int cmdId = 55; + RangingRequest request = RttTestUtils.getDummyRangingRequestMcOnly((byte) 0, 8); + mDut.rangeRequest(cmdId, request); + verify(mockRttController).rangeRequest(eq(cmdId), mRttConfigCaptor.capture()); + ArrayList<RttConfig> halRequest = mRttConfigCaptor.getValue(); + collector.checkThat("number of entries", halRequest.size(), + equalTo(request.mRttPeers.size())); + RttConfig rttConfig = halRequest.get(0); + collector.checkThat("(1) Rtt burst size", rttConfig.numFramesPerBurst, equalTo(8)); + collector.checkThat("(1) Rtt burst duration", rttConfig.burstDuration, equalTo(9)); + + cmdId = 56; + request = RttTestUtils.getDummyRangingRequestMcOnly((byte) 0, 20); + mDut.rangeRequest(cmdId, request); + verify(mockRttController).rangeRequest(eq(cmdId), mRttConfigCaptor.capture()); + halRequest = mRttConfigCaptor.getValue(); + rttConfig = halRequest.get(0); + collector.checkThat("(2) Rtt burst size", rttConfig.numFramesPerBurst, equalTo(20)); + collector.checkThat("(2) Rtt burst duration", rttConfig.burstDuration, equalTo(10)); + + cmdId = 57; + request = RttTestUtils.getDummyRangingRequestMcOnly((byte) 0, 30); + mDut.rangeRequest(cmdId, request); + verify(mockRttController).rangeRequest(eq(cmdId), mRttConfigCaptor.capture()); + halRequest = mRttConfigCaptor.getValue(); + rttConfig = halRequest.get(0); + collector.checkThat("(3) Rtt burst size", rttConfig.numFramesPerBurst, equalTo(30)); + collector.checkThat("(3) Rtt burst duration", rttConfig.burstDuration, equalTo(11)); + + verifyNoMoreInteractions(mockRttController); + } + } diff --git a/service/tests/wifitests/src/com/android/server/wifi/hal/WifiStaIfaceAidlImplTest.java b/service/tests/wifitests/src/com/android/server/wifi/hal/WifiStaIfaceAidlImplTest.java index 4a0a4e3e9f..5bd0b260f1 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hal/WifiStaIfaceAidlImplTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hal/WifiStaIfaceAidlImplTest.java @@ -16,7 +16,7 @@ package com.android.server.wifi.hal; -import static com.android.server.wifi.util.GeneralUtil.longToBitset; +import static com.android.server.wifi.TestUtil.createCapabilityBitset; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -58,6 +58,8 @@ import android.net.wifi.WifiManager; import android.net.wifi.WifiScanner.ScanData; import android.net.wifi.WifiSsid; +import com.android.dx.mockito.inline.extended.ExtendedMockito; +import com.android.dx.mockito.inline.extended.StaticMockitoSession; import com.android.server.wifi.SsidTranslator; import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.WifiLinkLayerStats; @@ -65,6 +67,7 @@ import com.android.server.wifi.WifiLoggerHal; import com.android.server.wifi.WifiNative; import com.android.wifi.resources.R; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; @@ -83,12 +86,21 @@ public class WifiStaIfaceAidlImplTest extends WifiBaseTest { @Mock private Context mContextMock; @Mock private Resources mResourcesMock; @Mock private SsidTranslator mSsidTranslatorMock; + private StaticMockitoSession mSession; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); when(mContextMock.getResources()).thenReturn(mResourcesMock); mDut = new WifiStaIfaceAidlImpl(mIWifiStaIfaceMock, mContextMock, mSsidTranslatorMock); + mSession = ExtendedMockito.mockitoSession() + .mockStatic(WifiHalAidlImpl.class) + .startMocking(); + } + + @After + public void teardown() { + mSession.finishMocking(); } /** @@ -100,9 +112,8 @@ public class WifiStaIfaceAidlImplTest extends WifiBaseTest { IWifiStaIface.FeatureSetMask.BACKGROUND_SCAN | IWifiStaIface.FeatureSetMask.LINK_LAYER_STATS ); - BitSet expected = longToBitset( - WifiManager.WIFI_FEATURE_SCANNER - | WifiManager.WIFI_FEATURE_LINK_LAYER_STATS); + BitSet expected = createCapabilityBitset( + WifiManager.WIFI_FEATURE_SCANNER, WifiManager.WIFI_FEATURE_LINK_LAYER_STATS); assertTrue(expected.equals(mDut.halToFrameworkStaFeatureSet(halFeatures))); } @@ -334,6 +345,7 @@ public class WifiStaIfaceAidlImplTest extends WifiBaseTest { */ @Test public void testGetCachedScanData() throws Exception { + when(WifiHalAidlImpl.isServiceVersionAtLeast(2)).thenReturn(true); CachedScanData halData = new CachedScanData(); CachedScanResult[] halResults = new CachedScanResult[2]; CachedScanResult halResult = new CachedScanResult(); diff --git a/service/tests/wifitests/src/com/android/server/wifi/hal/WifiStaIfaceHidlImplTest.java b/service/tests/wifitests/src/com/android/server/wifi/hal/WifiStaIfaceHidlImplTest.java index 8adb4fd171..21e8c844f7 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/hal/WifiStaIfaceHidlImplTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/hal/WifiStaIfaceHidlImplTest.java @@ -16,7 +16,7 @@ package com.android.server.wifi.hal; -import static com.android.server.wifi.util.GeneralUtil.longToBitset; +import static com.android.server.wifi.TestUtil.createCapabilityBitset; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -146,9 +146,8 @@ public class WifiStaIfaceHidlImplTest extends WifiBaseTest { IWifiStaIface.StaIfaceCapabilityMask.BACKGROUND_SCAN | IWifiStaIface.StaIfaceCapabilityMask.LINK_LAYER_STATS ); - BitSet expected = longToBitset( - WifiManager.WIFI_FEATURE_SCANNER - | WifiManager.WIFI_FEATURE_LINK_LAYER_STATS); + BitSet expected = createCapabilityBitset( + WifiManager.WIFI_FEATURE_SCANNER, WifiManager.WIFI_FEATURE_LINK_LAYER_STATS); assertTrue(expected.equals(mDut.halToFrameworkStaIfaceCapability(caps))); } diff --git a/service/tests/wifitests/src/com/android/server/wifi/mainline_supplicant/MainlineSupplicantTest.java b/service/tests/wifitests/src/com/android/server/wifi/mainline_supplicant/MainlineSupplicantTest.java new file mode 100644 index 0000000000..e27c46f5dd --- /dev/null +++ b/service/tests/wifitests/src/com/android/server/wifi/mainline_supplicant/MainlineSupplicantTest.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2024 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.mainline_supplicant; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.net.wifi.util.Environment; +import android.os.IBinder; +import android.system.wifi.mainline_supplicant.IMainlineSupplicant; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Unit tests for {@link MainlineSupplicant}. + */ +public class MainlineSupplicantTest { + private @Mock IMainlineSupplicant mIMainlineSupplicantMock; + private @Mock IBinder mIBinderMock; + private MainlineSupplicantSpy mDut; + + private ArgumentCaptor<IBinder.DeathRecipient> mDeathRecipientCaptor = + ArgumentCaptor.forClass(IBinder.DeathRecipient.class); + + // Spy version of this class allows us to override methods for testing. + private class MainlineSupplicantSpy extends MainlineSupplicant { + MainlineSupplicantSpy() { + super(); + } + + @Override + protected IMainlineSupplicant getNewServiceBinderMockable() { + return mIMainlineSupplicantMock; + } + } + + @Before + public void setUp() throws Exception { + assumeTrue(Environment.isSdkAtLeastB()); + MockitoAnnotations.initMocks(this); + when(mIMainlineSupplicantMock.asBinder()).thenReturn(mIBinderMock); + mDut = new MainlineSupplicantSpy(); + } + + private void validateServiceStart() throws Exception { + assertTrue(mDut.startService()); + verify(mIBinderMock).linkToDeath(mDeathRecipientCaptor.capture(), anyInt()); + assertTrue(mDut.isActive()); + } + + private void validateServiceStop() { + mDut.stopService(); + mDeathRecipientCaptor.getValue().binderDied(mIBinderMock); + assertFalse(mDut.isActive()); + } + + /** + * Verify that the class can be started and stopped successfully. + */ + @Test + public void testStartAndStopSuccess() throws Exception { + validateServiceStart(); + validateServiceStop(); + } + + /** + * Verify that unsolicited death notifications (ex. caused by a service crash) + * are handled correctly. + */ + @Test + public void testUnsolicitedDeathNotification() throws Exception { + validateServiceStart(); + + // Notification with an unknown binder should be ignored + IBinder otherBinder = mock(IBinder.class); + mDeathRecipientCaptor.getValue().binderDied(otherBinder); + assertTrue(mDut.isActive()); + + // Notification with the correct binder should be handled + mDeathRecipientCaptor.getValue().binderDied(mIBinderMock); + assertFalse(mDut.isActive()); + } +} diff --git a/service/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceCallbackAidlImplTest.java b/service/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceCallbackAidlImplTest.java index df9120d06b..f744dd807a 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceCallbackAidlImplTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceCallbackAidlImplTest.java @@ -17,6 +17,7 @@ package com.android.server.wifi.p2p; import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTL; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -29,43 +30,58 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.withSettings; import android.app.test.MockAnswerUtil.AnswerWithArguments; import android.hardware.wifi.common.OuiKeyedData; +import android.hardware.wifi.supplicant.KeyMgmtMask; import android.hardware.wifi.supplicant.P2pClientEapolIpAddressInfo; import android.hardware.wifi.supplicant.P2pDeviceFoundEventParams; +import android.hardware.wifi.supplicant.P2pDirInfo; import android.hardware.wifi.supplicant.P2pGoNegotiationReqEventParams; import android.hardware.wifi.supplicant.P2pGroupStartedEventParams; import android.hardware.wifi.supplicant.P2pInvitationEventParams; +import android.hardware.wifi.supplicant.P2pPairingBootstrappingMethodMask; import android.hardware.wifi.supplicant.P2pPeerClientDisconnectedEventParams; import android.hardware.wifi.supplicant.P2pPeerClientJoinedEventParams; import android.hardware.wifi.supplicant.P2pProvDiscStatusCode; import android.hardware.wifi.supplicant.P2pProvisionDiscoveryCompletedEventParams; import android.hardware.wifi.supplicant.P2pStatusCode; +import android.hardware.wifi.supplicant.P2pUsdBasedServiceDiscoveryResultParams; import android.hardware.wifi.supplicant.WpsConfigMethods; import android.hardware.wifi.supplicant.WpsDevPasswordId; import android.net.MacAddress; import android.net.wifi.ScanResult; +import android.net.wifi.WifiMigration; import android.net.wifi.WpsInfo; import android.net.wifi.p2p.WifiP2pConfig; import android.net.wifi.p2p.WifiP2pDevice; +import android.net.wifi.p2p.WifiP2pDirInfo; import android.net.wifi.p2p.WifiP2pGroup; import android.net.wifi.p2p.WifiP2pProvDiscEvent; import android.net.wifi.p2p.nsd.WifiP2pServiceResponse; +import android.net.wifi.p2p.nsd.WifiP2pUsdBasedServiceResponse; +import android.net.wifi.util.Environment; import android.os.PersistableBundle; +import android.text.TextUtils; import androidx.test.filters.SmallTest; +import com.android.dx.mockito.inline.extended.ExtendedMockito; import com.android.modules.utils.build.SdkLevel; import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.p2p.WifiP2pServiceImpl.P2pStatus; import com.android.server.wifi.util.HalAidlUtil; import com.android.server.wifi.util.NativeUtil; +import com.android.wifi.flags.Flags; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.MockitoAnnotations; +import org.mockito.MockitoSession; import java.util.ArrayList; import java.util.Arrays; @@ -81,6 +97,7 @@ public class SupplicantP2pIfaceCallbackAidlImplTest extends WifiBaseTest { private String mGroupIface = "test_p2p-p2p0-3"; private WifiP2pMonitor mMonitor; private SupplicantP2pIfaceCallbackAidlImpl mDut; + private MockitoSession mSession; private byte[] mDeviceAddressInvalid1 = { 0x00 }; private byte[] mDeviceAddressInvalid2 = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 }; @@ -100,6 +117,8 @@ public class SupplicantP2pIfaceCallbackAidlImplTest extends WifiBaseTest { private short mTestConfigMethods = 0x1234; private byte mTestCapabilities = 123; private int mTestGroupCapabilities = 456; + private static final byte[] TEST_NONCE = {10, 20, 30, 40, 50, 60, 70, 80}; + private static final byte[] TEST_DIR_TAG = {11, 22, 33, 44, 55, 66, 77, 88}; private class SupplicantP2pIfaceCallbackImplSpy extends SupplicantP2pIfaceCallbackAidlImpl { SupplicantP2pIfaceCallbackImplSpy(String iface, WifiP2pMonitor monitor, @@ -113,6 +132,18 @@ public class SupplicantP2pIfaceCallbackAidlImplTest extends WifiBaseTest { MockitoAnnotations.initMocks(this); mMonitor = mock(WifiP2pMonitor.class); initializeDut(DEFAULT_SERVICE_VERSION); + mSession = ExtendedMockito.mockitoSession() + .mockStatic(Flags.class, withSettings().lenient()) + .mockStatic(WifiMigration.class, withSettings().lenient()) + .startMocking(); + when(Flags.wifiDirectR2()).thenReturn(true); + } + + @After + public void tearDown() { + if (mSession != null) { + mSession.finishMocking(); + } } private void initializeDut(int serviceVersion) { @@ -673,6 +704,30 @@ public class SupplicantP2pIfaceCallbackAidlImplTest extends WifiBaseTest { } /** + * Verify onGroupStartedWithParams call when the parameters + * include Authentication Key Management. + */ + @Test + public void testOnGroupStartedWithKeyMgmtMask() throws Exception { + assumeTrue(Environment.isSdkAtLeastB()); + initializeDut(4 /* serviceVersion */); + when(Flags.wifiDirectR2()).thenReturn(true); + P2pGroupStartedEventParams params = new P2pGroupStartedEventParams(); + params.groupInterfaceName = "group name"; + params.ssid = new byte[] {0x30, 0x31, 0x32, 0x33}; + params.goDeviceAddress = mDeviceAddress1Bytes; + params.keyMgmtMask = KeyMgmtMask.SAE | KeyMgmtMask.WPA_PSK; + + ArgumentCaptor<WifiP2pGroup> p2pGroupCaptor = + ArgumentCaptor.forClass(WifiP2pGroup.class); + mDut.onGroupStartedWithParams(params); + verify(mMonitor).broadcastP2pGroupStarted(eq(mIface), p2pGroupCaptor.capture()); + + assertEquals(WifiP2pGroup.SECURITY_TYPE_WPA3_COMPATIBILITY, + p2pGroupCaptor.getValue().getSecurityType()); + } + + /** * Failing scenarios for onGroupStartedWithParams call. */ @Test @@ -737,15 +792,15 @@ public class SupplicantP2pIfaceCallbackAidlImplTest extends WifiBaseTest { p2pDeviceAddr, isRequest, status, configMethods, generatedPin); verify(mMonitor).broadcastP2pProvisionDiscoveryEnterPin( anyString(), discEventCaptor.capture()); - assertEquals(WifiP2pProvDiscEvent.ENTER_PIN, discEventCaptor.getValue().event); + assertEquals(WifiP2pProvDiscEvent.WPS_ENTER_PIN, discEventCaptor.getValue().event); configMethods = WpsConfigMethods.KEYPAD; mDut.onProvisionDiscoveryCompleted( p2pDeviceAddr, isRequest, status, configMethods, generatedPin); verify(mMonitor).broadcastP2pProvisionDiscoveryShowPin( anyString(), discEventCaptor.capture()); - assertEquals(WifiP2pProvDiscEvent.SHOW_PIN, discEventCaptor.getValue().event); - assertEquals(generatedPin, discEventCaptor.getValue().pin); + assertEquals(WifiP2pProvDiscEvent.WPS_SHOW_PIN, discEventCaptor.getValue().event); + assertEquals(generatedPin, discEventCaptor.getValue().wpsPin); isRequest = true; configMethods = WpsConfigMethods.KEYPAD; @@ -753,15 +808,15 @@ public class SupplicantP2pIfaceCallbackAidlImplTest extends WifiBaseTest { p2pDeviceAddr, isRequest, status, configMethods, generatedPin); verify(mMonitor, times(2)).broadcastP2pProvisionDiscoveryEnterPin( anyString(), discEventCaptor.capture()); - assertEquals(WifiP2pProvDiscEvent.ENTER_PIN, discEventCaptor.getValue().event); + assertEquals(WifiP2pProvDiscEvent.WPS_ENTER_PIN, discEventCaptor.getValue().event); configMethods = WpsConfigMethods.DISPLAY; mDut.onProvisionDiscoveryCompleted( p2pDeviceAddr, isRequest, status, configMethods, generatedPin); verify(mMonitor, times(2)).broadcastP2pProvisionDiscoveryShowPin( anyString(), discEventCaptor.capture()); - assertEquals(WifiP2pProvDiscEvent.SHOW_PIN, discEventCaptor.getValue().event); - assertEquals(generatedPin, discEventCaptor.getValue().pin); + assertEquals(WifiP2pProvDiscEvent.WPS_SHOW_PIN, discEventCaptor.getValue().event); + assertEquals(generatedPin, discEventCaptor.getValue().wpsPin); isRequest = false; configMethods = WpsConfigMethods.PUSHBUTTON; @@ -769,14 +824,14 @@ public class SupplicantP2pIfaceCallbackAidlImplTest extends WifiBaseTest { p2pDeviceAddr, isRequest, status, configMethods, generatedPin); verify(mMonitor).broadcastP2pProvisionDiscoveryPbcResponse( anyString(), discEventCaptor.capture()); - assertEquals(WifiP2pProvDiscEvent.PBC_RSP, discEventCaptor.getValue().event); + assertEquals(WifiP2pProvDiscEvent.WPS_PBC_RSP, discEventCaptor.getValue().event); isRequest = true; mDut.onProvisionDiscoveryCompleted( p2pDeviceAddr, isRequest, status, configMethods, generatedPin); verify(mMonitor).broadcastP2pProvisionDiscoveryPbcRequest( anyString(), discEventCaptor.capture()); - assertEquals(WifiP2pProvDiscEvent.PBC_REQ, discEventCaptor.getValue().event); + assertEquals(WifiP2pProvDiscEvent.WPS_PBC_REQ, discEventCaptor.getValue().event); } /** @@ -798,42 +853,42 @@ public class SupplicantP2pIfaceCallbackAidlImplTest extends WifiBaseTest { mDut.onProvisionDiscoveryCompletedEvent(params); verify(mMonitor).broadcastP2pProvisionDiscoveryEnterPin( anyString(), discEventCaptor.capture()); - assertEquals(WifiP2pProvDiscEvent.ENTER_PIN, discEventCaptor.getValue().event); + assertEquals(WifiP2pProvDiscEvent.WPS_ENTER_PIN, discEventCaptor.getValue().event); params.configMethods = WpsConfigMethods.KEYPAD; mDut.onProvisionDiscoveryCompletedEvent(params); verify(mMonitor).broadcastP2pProvisionDiscoveryShowPin( anyString(), discEventCaptor.capture()); - assertEquals(WifiP2pProvDiscEvent.SHOW_PIN, discEventCaptor.getValue().event); - assertEquals("12345678", discEventCaptor.getValue().pin); + assertEquals(WifiP2pProvDiscEvent.WPS_SHOW_PIN, discEventCaptor.getValue().event); + assertEquals("12345678", discEventCaptor.getValue().wpsPin); params.isRequest = true; params.configMethods = WpsConfigMethods.KEYPAD; mDut.onProvisionDiscoveryCompletedEvent(params); verify(mMonitor, times(2)).broadcastP2pProvisionDiscoveryEnterPin( anyString(), discEventCaptor.capture()); - assertEquals(WifiP2pProvDiscEvent.ENTER_PIN, discEventCaptor.getValue().event); + assertEquals(WifiP2pProvDiscEvent.WPS_ENTER_PIN, discEventCaptor.getValue().event); params.configMethods = WpsConfigMethods.DISPLAY; mDut.onProvisionDiscoveryCompletedEvent(params); verify(mMonitor, times(2)).broadcastP2pProvisionDiscoveryShowPin( anyString(), discEventCaptor.capture()); - assertEquals(WifiP2pProvDiscEvent.SHOW_PIN, discEventCaptor.getValue().event); - assertEquals("12345678", discEventCaptor.getValue().pin); + assertEquals(WifiP2pProvDiscEvent.WPS_SHOW_PIN, discEventCaptor.getValue().event); + assertEquals("12345678", discEventCaptor.getValue().wpsPin); params.isRequest = false; params.configMethods = WpsConfigMethods.PUSHBUTTON; mDut.onProvisionDiscoveryCompletedEvent(params); verify(mMonitor).broadcastP2pProvisionDiscoveryPbcResponse( anyString(), discEventCaptor.capture()); - assertEquals(WifiP2pProvDiscEvent.PBC_RSP, discEventCaptor.getValue().event); + assertEquals(WifiP2pProvDiscEvent.WPS_PBC_RSP, discEventCaptor.getValue().event); params.isRequest = true; params.groupInterfaceName = "group name"; mDut.onProvisionDiscoveryCompletedEvent(params); verify(mMonitor).broadcastP2pProvisionDiscoveryPbcRequest( anyString(), discEventCaptor.capture()); - assertEquals(WifiP2pProvDiscEvent.PBC_REQ, discEventCaptor.getValue().event); + assertEquals(WifiP2pProvDiscEvent.WPS_PBC_REQ, discEventCaptor.getValue().event); } /** @@ -861,7 +916,7 @@ public class SupplicantP2pIfaceCallbackAidlImplTest extends WifiBaseTest { mDut.onProvisionDiscoveryCompletedEvent(params); verify(mMonitor).broadcastP2pProvisionDiscoveryEnterPin( anyString(), discEventCaptor.capture()); - assertEquals(WifiP2pProvDiscEvent.ENTER_PIN, discEventCaptor.getValue().event); + assertEquals(WifiP2pProvDiscEvent.WPS_ENTER_PIN, discEventCaptor.getValue().event); assertEquals(frameworkVendorData, discEventCaptor.getValue().getVendorData()); } @@ -1306,6 +1361,217 @@ public class SupplicantP2pIfaceCallbackAidlImplTest extends WifiBaseTest { } /** + * Test a successful call to onUsdBasedServiceDiscoveryResult. + */ + @Test + public void testOnUsdBasedServiceDiscoveryResult() throws Exception { + assumeTrue(Environment.isSdkAtLeastB()); + + byte[] testSsi = {0x1, 0x3, 0x4, 0x7}; + P2pUsdBasedServiceDiscoveryResultParams params = + new P2pUsdBasedServiceDiscoveryResultParams(); + params.peerMacAddress = DEVICE_ADDRESS; + params.sessionId = 1; + params.peerSessionId = 2; + params.serviceProtocolType = 3; + params.serviceSpecificInfo = testSsi; + + ArgumentCaptor<List<WifiP2pServiceResponse>> respListCaptor = + ArgumentCaptor.forClass(List.class); + + mDut.onUsdBasedServiceDiscoveryResult(params); + + verify(mMonitor).broadcastP2pServiceDiscoveryResponse(anyString(), + respListCaptor.capture()); + assertNotNull(respListCaptor.getValue()); + List<WifiP2pServiceResponse> services = respListCaptor.getValue(); + WifiP2pServiceResponse resp = services.get(0); + assertNotNull(resp); + WifiP2pUsdBasedServiceResponse usdResponse = resp.getWifiP2pUsdBasedServiceResponse(); + assertNotNull(usdResponse); + assertEquals(1, resp.getUsdSessionId()); + assertEquals(3, usdResponse.getServiceProtocolType()); + assertArrayEquals(testSsi, usdResponse.getServiceSpecificInfo()); + assertNotNull(resp.getSrcDevice()); + assertEquals(DEVICE_ADDRESS_STR, resp.getSrcDevice().deviceAddress); + } + + /** + * Test a successful call to onUsdBasedServiceDiscoveryTerminated + */ + @Test + public void testOnUsdBasedServiceDiscoveryTerminated() { + assumeTrue(Environment.isSdkAtLeastB()); + mDut.onUsdBasedServiceDiscoveryTerminated(1, 3); + verify(mMonitor).broadcastUsdBasedServiceDiscoveryTerminated(eq(mIface), + eq(1), eq(3)); + } + + /** + * Test a successful call to onUsdBasedServiceAdvertisementTerminated + */ + @Test + public void testOnUsdBasedServiceAdvertisementTerminated() { + assumeTrue(Environment.isSdkAtLeastB()); + mDut.onUsdBasedServiceAdvertisementTerminated(4, 2); + verify(mMonitor).broadcastUsdBasedServiceAdvertisementTerminated(eq(mIface), + eq(4), eq(2)); + } + + /** + * Test a successful call to onDeviceFoundWithParams with P2P2 info + */ + @Test + public void testOnDeviceFoundWithP2p2Info() throws Exception { + assumeTrue(Environment.isSdkAtLeastB()); + initializeDut(4 /* serviceVersion */); + String fakeDeviceName = "test device name"; + + P2pDirInfo aidlDirInfo = new P2pDirInfo(); + aidlDirInfo.cipherVersion = P2pDirInfo.CipherVersion.DIRA_CIPHER_VERSION_128_BIT; + aidlDirInfo.deviceInterfaceMacAddress = DEVICE_ADDRESS; + aidlDirInfo.dirTag = TEST_DIR_TAG; + aidlDirInfo.nonce = TEST_NONCE; + + P2pDeviceFoundEventParams params = new P2pDeviceFoundEventParams(); + params.p2pDeviceAddress = DEVICE_ADDRESS; + params.deviceName = fakeDeviceName; + params.dirInfo = aidlDirInfo; + params.pairingBootstrappingMethods = P2pPairingBootstrappingMethodMask + .BOOTSTRAPPING_OPPORTUNISTIC | P2pPairingBootstrappingMethodMask + .BOOTSTRAPPING_DISPLAY_PINCODE; + + + mDut.onDeviceFoundWithParams(params); + ArgumentCaptor<WifiP2pDevice> p2pDeviceCaptor = + ArgumentCaptor.forClass(WifiP2pDevice.class); + verify(mMonitor).broadcastP2pDeviceFound(eq(mIface), p2pDeviceCaptor.capture()); + WifiP2pDevice device = p2pDeviceCaptor.getValue(); + assertTrue(device.isOpportunisticBootstrappingMethodSupported()); + assertTrue(device.isPinCodeDisplayBootstrappingMethodSupported()); + WifiP2pDirInfo dirInfo = device.dirInfo; + assertEquals(android.net.MacAddress.fromBytes(DEVICE_ADDRESS), dirInfo.getMacAddress()); + assertArrayEquals(TEST_DIR_TAG, dirInfo.getDirTag()); + assertArrayEquals(TEST_NONCE, dirInfo.getNonce()); + } + + /** + * Test a successful call to onProvisionDiscoveryCompletedEvent with P2P2 pairing bootstrapping + * information. + */ + @Test + public void testOnProvisionDiscoveryCompletedEventWithP2pPairingBootstrappingMethod() throws + Exception { + assumeTrue(Environment.isSdkAtLeastB()); + initializeDut(4 /* serviceVersion */); + P2pProvisionDiscoveryCompletedEventParams params = + new P2pProvisionDiscoveryCompletedEventParams(); + params.p2pDeviceAddress = DEVICE_ADDRESS; + params.isRequest = true; + params.status = P2pProvDiscStatusCode.SUCCESS; + params.pairingBootstrappingMethod = + P2pPairingBootstrappingMethodMask.BOOTSTRAPPING_OPPORTUNISTIC; + params.password = ""; + + ArgumentCaptor<WifiP2pProvDiscEvent> discEventCaptor = + ArgumentCaptor.forClass(WifiP2pProvDiscEvent.class); + mDut.onProvisionDiscoveryCompletedEvent(params); + verify(mMonitor).broadcastP2pProvisionDiscoveryPairingBootstrappingOpportunisticRequest( + anyString(), discEventCaptor.capture()); + assertEquals(WifiP2pProvDiscEvent.PAIRING_BOOTSTRAPPING_OPPORTUNISTIC_REQ, + discEventCaptor.getValue().event); + + params.isRequest = false; + mDut.onProvisionDiscoveryCompletedEvent(params); + verify(mMonitor).broadcastP2pProvisionDiscoveryPairingBootstrappingOpportunisticResponse( + anyString(), discEventCaptor.capture()); + assertEquals(WifiP2pProvDiscEvent.PAIRING_BOOTSTRAPPING_OPPORTUNISTIC_RSP, + discEventCaptor.getValue().event); + + params.isRequest = true; + params.pairingBootstrappingMethod = + P2pPairingBootstrappingMethodMask.BOOTSTRAPPING_DISPLAY_PINCODE; + params.password = "1234"; + mDut.onProvisionDiscoveryCompletedEvent(params); + verify(mMonitor).broadcastP2pProvisionDiscoveryShowPairingBootstrappingPinOrPassphrase( + anyString(), discEventCaptor.capture()); + assertEquals(WifiP2pProvDiscEvent.PAIRING_BOOTSTRAPPING_SHOW_PIN, + discEventCaptor.getValue().event); + assertEquals("1234", discEventCaptor.getValue().pairingPinOrPassphrase); + + params.isRequest = false; + mDut.onProvisionDiscoveryCompletedEvent(params); + verify(mMonitor).broadcastP2pProvisionDiscoveryEnterPairingBootstrappingPinOrPassphrase( + anyString(), discEventCaptor.capture()); + assertEquals(WifiP2pProvDiscEvent.PAIRING_BOOTSTRAPPING_ENTER_PIN, + discEventCaptor.getValue().event); + assertTrue(TextUtils.isEmpty(discEventCaptor.getValue().pairingPinOrPassphrase)); + + params.isRequest = true; + params.pairingBootstrappingMethod = + P2pPairingBootstrappingMethodMask.BOOTSTRAPPING_DISPLAY_PASSPHRASE; + params.password = "abed"; + mDut.onProvisionDiscoveryCompletedEvent(params); + verify(mMonitor, times(2)) + .broadcastP2pProvisionDiscoveryShowPairingBootstrappingPinOrPassphrase( + anyString(), discEventCaptor.capture()); + assertEquals(WifiP2pProvDiscEvent.PAIRING_BOOTSTRAPPING_SHOW_PASSPHRASE, + discEventCaptor.getValue().event); + assertEquals("abed", discEventCaptor.getValue().pairingPinOrPassphrase); + + params.isRequest = false; + mDut.onProvisionDiscoveryCompletedEvent(params); + verify(mMonitor, times(2)) + .broadcastP2pProvisionDiscoveryEnterPairingBootstrappingPinOrPassphrase( + anyString(), discEventCaptor.capture()); + assertEquals(WifiP2pProvDiscEvent.PAIRING_BOOTSTRAPPING_ENTER_PASSPHRASE, + discEventCaptor.getValue().event); + assertTrue(TextUtils.isEmpty(discEventCaptor.getValue().pairingPinOrPassphrase)); + + params.isRequest = true; + params.pairingBootstrappingMethod = + P2pPairingBootstrappingMethodMask.BOOTSTRAPPING_KEYPAD_PINCODE; + params.password = "1234"; + mDut.onProvisionDiscoveryCompletedEvent(params); + verify(mMonitor, times(3)) + .broadcastP2pProvisionDiscoveryEnterPairingBootstrappingPinOrPassphrase( + anyString(), discEventCaptor.capture()); + assertEquals(WifiP2pProvDiscEvent.PAIRING_BOOTSTRAPPING_ENTER_PIN, + discEventCaptor.getValue().event); + assertTrue(TextUtils.isEmpty(discEventCaptor.getValue().pairingPinOrPassphrase)); + + params.isRequest = false; + mDut.onProvisionDiscoveryCompletedEvent(params); + verify(mMonitor, times(3)) + .broadcastP2pProvisionDiscoveryShowPairingBootstrappingPinOrPassphrase( + anyString(), discEventCaptor.capture()); + assertEquals(WifiP2pProvDiscEvent.PAIRING_BOOTSTRAPPING_SHOW_PIN, + discEventCaptor.getValue().event); + assertEquals("1234", discEventCaptor.getValue().pairingPinOrPassphrase); + + params.isRequest = true; + params.pairingBootstrappingMethod = + P2pPairingBootstrappingMethodMask.BOOTSTRAPPING_KEYPAD_PASSPHRASE; + params.password = "abed"; + mDut.onProvisionDiscoveryCompletedEvent(params); + verify(mMonitor, times(4)) + .broadcastP2pProvisionDiscoveryEnterPairingBootstrappingPinOrPassphrase( + anyString(), discEventCaptor.capture()); + assertEquals(WifiP2pProvDiscEvent.PAIRING_BOOTSTRAPPING_ENTER_PASSPHRASE, + discEventCaptor.getValue().event); + assertTrue(TextUtils.isEmpty(discEventCaptor.getValue().pairingPinOrPassphrase)); + + params.isRequest = false; + mDut.onProvisionDiscoveryCompletedEvent(params); + verify(mMonitor, times(4)) + .broadcastP2pProvisionDiscoveryShowPairingBootstrappingPinOrPassphrase( + anyString(), discEventCaptor.capture()); + assertEquals(WifiP2pProvDiscEvent.PAIRING_BOOTSTRAPPING_SHOW_PASSPHRASE, + discEventCaptor.getValue().event); + assertEquals("abed", discEventCaptor.getValue().pairingPinOrPassphrase); + } + + /** * Helper function for comparing InformationElement lists. * * InformationElement equals() is implemented in S. List equals() cannot diff --git a/service/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceCallbackHidlImplTest.java b/service/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceCallbackHidlImplTest.java index 916fc5afaf..f9feb0abd3 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceCallbackHidlImplTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceCallbackHidlImplTest.java @@ -432,15 +432,15 @@ public class SupplicantP2pIfaceCallbackHidlImplTest extends WifiBaseTest { p2pDeviceAddr, isRequest, status, configMethods, generatedPin); verify(mMonitor).broadcastP2pProvisionDiscoveryEnterPin( anyString(), discEventCaptor.capture()); - assertEquals(WifiP2pProvDiscEvent.ENTER_PIN, discEventCaptor.getValue().event); + assertEquals(WifiP2pProvDiscEvent.WPS_ENTER_PIN, discEventCaptor.getValue().event); configMethods = WpsConfigMethods.KEYPAD; mDut.onProvisionDiscoveryCompleted( p2pDeviceAddr, isRequest, status, configMethods, generatedPin); verify(mMonitor).broadcastP2pProvisionDiscoveryShowPin( anyString(), discEventCaptor.capture()); - assertEquals(WifiP2pProvDiscEvent.SHOW_PIN, discEventCaptor.getValue().event); - assertEquals(generatedPin, discEventCaptor.getValue().pin); + assertEquals(WifiP2pProvDiscEvent.WPS_SHOW_PIN, discEventCaptor.getValue().event); + assertEquals(generatedPin, discEventCaptor.getValue().wpsPin); isRequest = true; configMethods = WpsConfigMethods.KEYPAD; @@ -448,15 +448,15 @@ public class SupplicantP2pIfaceCallbackHidlImplTest extends WifiBaseTest { p2pDeviceAddr, isRequest, status, configMethods, generatedPin); verify(mMonitor, times(2)).broadcastP2pProvisionDiscoveryEnterPin( anyString(), discEventCaptor.capture()); - assertEquals(WifiP2pProvDiscEvent.ENTER_PIN, discEventCaptor.getValue().event); + assertEquals(WifiP2pProvDiscEvent.WPS_ENTER_PIN, discEventCaptor.getValue().event); configMethods = WpsConfigMethods.DISPLAY; mDut.onProvisionDiscoveryCompleted( p2pDeviceAddr, isRequest, status, configMethods, generatedPin); verify(mMonitor, times(2)).broadcastP2pProvisionDiscoveryShowPin( anyString(), discEventCaptor.capture()); - assertEquals(WifiP2pProvDiscEvent.SHOW_PIN, discEventCaptor.getValue().event); - assertEquals(generatedPin, discEventCaptor.getValue().pin); + assertEquals(WifiP2pProvDiscEvent.WPS_SHOW_PIN, discEventCaptor.getValue().event); + assertEquals(generatedPin, discEventCaptor.getValue().wpsPin); isRequest = false; configMethods = WpsConfigMethods.PUSHBUTTON; @@ -464,14 +464,14 @@ public class SupplicantP2pIfaceCallbackHidlImplTest extends WifiBaseTest { p2pDeviceAddr, isRequest, status, configMethods, generatedPin); verify(mMonitor).broadcastP2pProvisionDiscoveryPbcResponse( anyString(), discEventCaptor.capture()); - assertEquals(WifiP2pProvDiscEvent.PBC_RSP, discEventCaptor.getValue().event); + assertEquals(WifiP2pProvDiscEvent.WPS_PBC_RSP, discEventCaptor.getValue().event); isRequest = true; mDut.onProvisionDiscoveryCompleted( p2pDeviceAddr, isRequest, status, configMethods, generatedPin); verify(mMonitor).broadcastP2pProvisionDiscoveryPbcRequest( anyString(), discEventCaptor.capture()); - assertEquals(WifiP2pProvDiscEvent.PBC_REQ, discEventCaptor.getValue().event); + assertEquals(WifiP2pProvDiscEvent.WPS_PBC_REQ, discEventCaptor.getValue().event); } private void verifyProvisionDiscoveryFailureEvent( diff --git a/service/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceHalAidlImplTest.java b/service/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceHalAidlImplTest.java index 9cb54e710f..3bbb4cfbef 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceHalAidlImplTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceHalAidlImplTest.java @@ -43,6 +43,8 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.test.MockAnswerUtil.AnswerWithArguments; import android.hardware.wifi.supplicant.FreqRange; import android.hardware.wifi.supplicant.ISupplicant; @@ -50,11 +52,14 @@ import android.hardware.wifi.supplicant.ISupplicantP2pIface; import android.hardware.wifi.supplicant.ISupplicantP2pIfaceCallback; import android.hardware.wifi.supplicant.ISupplicantP2pNetwork; import android.hardware.wifi.supplicant.IfaceInfo; +import android.hardware.wifi.supplicant.KeyMgmtMask; import android.hardware.wifi.supplicant.MacAddress; import android.hardware.wifi.supplicant.MiracastMode; import android.hardware.wifi.supplicant.P2pConnectInfo; +import android.hardware.wifi.supplicant.P2pDirInfo; import android.hardware.wifi.supplicant.P2pExtListenInfo; import android.hardware.wifi.supplicant.P2pFrameTypeMask; +import android.hardware.wifi.supplicant.P2pPairingBootstrappingMethodMask; import android.hardware.wifi.supplicant.SupplicantStatusCode; import android.hardware.wifi.supplicant.WpsProvisionMethod; import android.net.wifi.CoexUnsafeChannel; @@ -63,11 +68,17 @@ import android.net.wifi.ScanResult; import android.net.wifi.WpsInfo; import android.net.wifi.p2p.WifiP2pConfig; import android.net.wifi.p2p.WifiP2pDevice; +import android.net.wifi.p2p.WifiP2pDirInfo; import android.net.wifi.p2p.WifiP2pExtListenParams; import android.net.wifi.p2p.WifiP2pGroup; import android.net.wifi.p2p.WifiP2pGroupList; import android.net.wifi.p2p.WifiP2pManager; +import android.net.wifi.p2p.WifiP2pPairingBootstrappingConfig; +import android.net.wifi.p2p.WifiP2pUsdBasedLocalServiceAdvertisementConfig; +import android.net.wifi.p2p.WifiP2pUsdBasedServiceDiscoveryConfig; import android.net.wifi.p2p.nsd.WifiP2pServiceInfo; +import android.net.wifi.p2p.nsd.WifiP2pUsdBasedServiceConfig; +import android.net.wifi.util.Environment; import android.os.IBinder; import android.os.PersistableBundle; import android.os.RemoteException; @@ -160,6 +171,18 @@ public class SupplicantP2pIfaceHalAidlImplTest extends WifiBaseTest { final int mGroupOwnerBand = WifiP2pConfig.GROUP_OWNER_BAND_5GHZ; final boolean mIsPersistent = false; + private static final String TEST_USD_SERVICE_NAME = "test_service_name"; + private static final String TEST_GROUP_INTERFACE_NAME = "test_group_if_name"; + private static final int TEST_USD_PROTOCOL_TYPE = 4; + private static final byte[] TEST_USD_SERVICE_SPECIFIC_INFO = {10, 20, 30, 40, 50, 60}; + private static final int TEST_USD_DISCOVERY_CHANNEL_FREQUENCY_MHZ = 2437; + private static final int[] TEST_USD_DISCOVERY_CHANNEL_FREQUENCIES_MHZ = {2412, 2437, 2462}; + private static final int TEST_USD_TIMEOUT_S = 30; + private static final int TEST_USD_SESSION_ID = 3; + private static final int TEST_DEV_IK_ID = 2; + private static final byte[] TEST_NONCE = {10, 20, 30, 40, 50, 60, 70, 80}; + private static final byte[] TEST_DIR_TAG = {11, 22, 33, 44, 55, 66, 77, 88}; + private class SupplicantP2pIfaceHalSpy extends SupplicantP2pIfaceHalAidlImpl { SupplicantP2pIfaceHalSpy() { super(mWifiMonitor, mWifiInjector); @@ -1259,9 +1282,9 @@ public class SupplicantP2pIfaceHalAidlImplTest extends WifiBaseTest { public void testGroupAdd_success() throws Exception { doNothing().when(mISupplicantP2pIfaceMock).addGroup(eq(true), eq(3)); // Default value when service is not initialized. - assertFalse(mDut.groupAdd(3, true)); + assertFalse(mDut.groupAdd(3, true, false)); executeAndValidateInitializationSequence(false, false); - assertTrue(mDut.groupAdd(3, true)); + assertTrue(mDut.groupAdd(3, true, false)); } /** @@ -1272,7 +1295,7 @@ public class SupplicantP2pIfaceHalAidlImplTest extends WifiBaseTest { executeAndValidateInitializationSequence(false, false); doThrow(new ServiceSpecificException(SupplicantStatusCode.FAILURE_UNKNOWN)) .when(mISupplicantP2pIfaceMock).addGroup(anyBoolean(), anyInt()); - assertFalse(mDut.groupAdd(0, true)); + assertFalse(mDut.groupAdd(0, true, false)); // Check that service is still alive. assertTrue(mDut.isInitializationComplete()); } @@ -1285,7 +1308,7 @@ public class SupplicantP2pIfaceHalAidlImplTest extends WifiBaseTest { executeAndValidateInitializationSequence(false, false); doThrow(new RemoteException()).when(mISupplicantP2pIfaceMock) .addGroup(anyBoolean(), anyInt()); - assertFalse(mDut.groupAdd(0, true)); + assertFalse(mDut.groupAdd(0, true, false)); // Check service is dead. assertFalse(mDut.isInitializationComplete()); } @@ -1304,7 +1327,8 @@ public class SupplicantP2pIfaceHalAidlImplTest extends WifiBaseTest { eq(mPeerMacAddressBytes), anyBoolean()); // Default value when service is not initialized. - assertFalse(mDut.groupAdd(mNetworkName, mPassphrase, mIsPersistent, + assertFalse(mDut.groupAdd(mNetworkName, mPassphrase, + WifiP2pConfig.PCC_MODE_CONNECTION_TYPE_LEGACY_ONLY, mIsPersistent, mGroupOwnerBand, mPeerMacAddress, true)); verify(mISupplicantP2pIfaceMock, never()).addGroupWithConfig( any(byte[].class), anyString(), @@ -1312,7 +1336,8 @@ public class SupplicantP2pIfaceHalAidlImplTest extends WifiBaseTest { any(byte[].class), anyBoolean()); executeAndValidateInitializationSequence(false, false); - assertTrue(mDut.groupAdd(mNetworkName, mPassphrase, mIsPersistent, + assertTrue(mDut.groupAdd(mNetworkName, mPassphrase, + WifiP2pConfig.PCC_MODE_CONNECTION_TYPE_LEGACY_ONLY, mIsPersistent, mGroupOwnerBand, mPeerMacAddress, true)); verify(mISupplicantP2pIfaceMock).addGroupWithConfig( eq(NativeUtil.byteArrayFromArrayList( @@ -1325,6 +1350,35 @@ public class SupplicantP2pIfaceHalAidlImplTest extends WifiBaseTest { } /** + * Sunny day scenario for groupAdd() with config + */ + @Test + public void testGroupAddWithConfigurationParamsSuccess() throws Exception { + assumeTrue(Environment.isSdkAtLeastB()); + setCachedServiceVersion(4); + ArgumentCaptor<android.hardware.wifi.supplicant.P2pAddGroupConfigurationParams> + addGroupParamsCaptor = ArgumentCaptor.forClass(android.hardware.wifi.supplicant + .P2pAddGroupConfigurationParams.class); + + executeAndValidateInitializationSequence(false, false); + assertTrue(mDut.groupAdd(mNetworkName, mPassphrase, + WifiP2pConfig.PCC_MODE_CONNECTION_TYPE_LEGACY_OR_R2, mIsPersistent, + mGroupOwnerBand, mPeerMacAddress, true)); + verify(mISupplicantP2pIfaceMock).addGroupWithConfigurationParams( + addGroupParamsCaptor.capture()); + android.hardware.wifi.supplicant.P2pAddGroupConfigurationParams params = + addGroupParamsCaptor.getValue(); + assertArrayEquals(NativeUtil.byteArrayFromArrayList( + NativeUtil.decodeSsid("\"" + mNetworkName + "\"")), params.ssid); + assertEquals(mGroupOwnerBand, params.frequencyMHzOrBand); + assertEquals(mPassphrase, params.passphrase); + assertArrayEquals(mPeerMacAddressBytes, params.goInterfaceAddress); + assertEquals(mIsPersistent, params.isPersistent); + assertEquals(true, params.joinExistingGroup); + assertEquals(KeyMgmtMask.WPA_PSK | KeyMgmtMask.SAE, params.keyMgmtMask); + } + + /** * Verify that groupAdd with config returns false, if HAL call did not succeed. */ @Test @@ -1339,7 +1393,8 @@ public class SupplicantP2pIfaceHalAidlImplTest extends WifiBaseTest { eq(mGroupOwnerBand), eq(mPeerMacAddressBytes), anyBoolean()); - assertFalse(mDut.groupAdd(mNetworkName, mPassphrase, mIsPersistent, + assertFalse(mDut.groupAdd(mNetworkName, mPassphrase, + WifiP2pConfig.PCC_MODE_CONNECTION_TYPE_LEGACY_ONLY, mIsPersistent, mGroupOwnerBand, mPeerMacAddress, true)); verify(mISupplicantP2pIfaceMock).addGroupWithConfig( eq(NativeUtil.byteArrayFromArrayList( @@ -1369,7 +1424,8 @@ public class SupplicantP2pIfaceHalAidlImplTest extends WifiBaseTest { eq(mGroupOwnerBand), eq(mPeerMacAddressBytes), anyBoolean()); - assertFalse(mDut.groupAdd(mNetworkName, mPassphrase, mIsPersistent, + assertFalse(mDut.groupAdd(mNetworkName, mPassphrase, + WifiP2pConfig.PCC_MODE_CONNECTION_TYPE_LEGACY_ONLY, mIsPersistent, mGroupOwnerBand, mPeerMacAddress, true)); verify(mISupplicantP2pIfaceMock).addGroupWithConfig( eq(NativeUtil.byteArrayFromArrayList( @@ -2688,24 +2744,22 @@ public class SupplicantP2pIfaceHalAidlImplTest extends WifiBaseTest { * Test that getSupportedFeatures returns the expected feature sets. */ @Test - public void testGetSupportedFeatures() { + public void testGetSupportedFeatures() throws Exception { WifiSettingsConfigStore mockConfigStore = mock(WifiSettingsConfigStore.class); when(mWifiInjector.getSettingsConfigStore()).thenReturn(mockConfigStore); - - // If the service version cannot be retrieved, expect the default feature set. when(mockConfigStore.get(any())).thenReturn(-1); - long defaultFeatureSet = mDut.getSupportedFeatures(); - verify(mockConfigStore).get(any()); + when(mISupplicantP2pIfaceMock.getFeatureSet()) + .thenReturn(mISupplicantP2pIfaceMock.P2P_FEATURE_V2); + executeAndValidateInitializationSequence(false, false); - // Full feature set can be retrieved once we have the service version. - when(mockConfigStore.get(any())).thenReturn(2); - long fullFeatureSet = mDut.getSupportedFeatures(); - assertNotEquals(defaultFeatureSet, fullFeatureSet); + // getSupportedFeatures() HAL API is supported only on version 4 or later. + assertEquals(0, mDut.getSupportedFeatures()); verify(mockConfigStore, times(2)).get(any()); - // Service version should be cached on subsequent calls. - assertEquals(fullFeatureSet, mDut.getSupportedFeatures()); - verifyNoMoreInteractions(mockConfigStore); + // Feature set can be retrieved only on service version set to at least version 4. + when(mockConfigStore.get(any())).thenReturn(4); + assertNotEquals(mISupplicantP2pIfaceMock.P2P_FEATURE_V2, mDut.getSupportedFeatures()); + verify(mockConfigStore, times(3)).get(any()); } /** @@ -2759,6 +2813,24 @@ public class SupplicantP2pIfaceHalAidlImplTest extends WifiBaseTest { /** * Create new placeholder WifiP2pConfig instance. */ + private WifiP2pConfig createP2pConfigWithBootstrappingMethod(@NonNull byte[] macAddress, + @WifiP2pPairingBootstrappingConfig.PairingBootstrappingMethod int method, + @Nullable String password, boolean authorize) { + WifiP2pPairingBootstrappingConfig pairingBootstrappingConfig = + new WifiP2pPairingBootstrappingConfig(method, password); + WifiP2pConfig config = new WifiP2pConfig.Builder() + .setDeviceAddress(NativeUtil.getMacAddressOrNull( + NativeUtil.macAddressFromByteArray(macAddress))) + .setPairingBootstrappingConfig(pairingBootstrappingConfig) + .setAuthorizeConnectionFromPeerEnabled(authorize) + .build(); + config.groupOwnerIntent = WifiP2pServiceImpl.DEFAULT_GROUP_OWNER_INTENT; + return config; + } + + /** + * Create new placeholder WifiP2pConfig instance. + */ private WifiP2pConfig createPlaceholderP2pConfig(String peerAddress, int wpsProvMethod, String pin) { WifiP2pConfig config = new WifiP2pConfig(); @@ -2836,4 +2908,515 @@ public class SupplicantP2pIfaceHalAidlImplTest extends WifiBaseTest { assertFalse(mDut.isInitializationComplete()); verify(mSupplicantHalDeathHandler, never()).onDeath(); } + + /** + * Test the handling of start an Un-synchronized Service Discovery (USD) based service + * discovery. + */ + @Test + public void testStartUsdBasedServiceDiscoverySuccess() throws Exception { + assumeTrue(Environment.isSdkAtLeastB()); + setCachedServiceVersion(4); + ArgumentCaptor<android.hardware.wifi.supplicant.P2pUsdBasedServiceDiscoveryConfig> + usdBasedServiceDiscoveryConfigCaptor = ArgumentCaptor.forClass( + android.hardware.wifi.supplicant.P2pUsdBasedServiceDiscoveryConfig.class); + when(mISupplicantP2pIfaceMock.startUsdBasedServiceDiscovery(any())) + .thenReturn(TEST_USD_SESSION_ID); + + WifiP2pUsdBasedServiceConfig usdConfig = new WifiP2pUsdBasedServiceConfig.Builder( + TEST_USD_SERVICE_NAME) + .setServiceProtocolType(TEST_USD_PROTOCOL_TYPE) + .setServiceSpecificInfo(TEST_USD_SERVICE_SPECIFIC_INFO).build(); + WifiP2pUsdBasedServiceDiscoveryConfig serviceDiscoveryConfig = + new WifiP2pUsdBasedServiceDiscoveryConfig.Builder() + .setFrequenciesMhz(TEST_USD_DISCOVERY_CHANNEL_FREQUENCIES_MHZ).build(); + + executeAndValidateInitializationSequence(false, false); + + assertEquals(TEST_USD_SESSION_ID, mDut.startUsdBasedServiceDiscovery(usdConfig, + serviceDiscoveryConfig, TEST_USD_TIMEOUT_S)); + + verify(mISupplicantP2pIfaceMock).startUsdBasedServiceDiscovery( + usdBasedServiceDiscoveryConfigCaptor.capture()); + android.hardware.wifi.supplicant.P2pUsdBasedServiceDiscoveryConfig aidlUsdConfig = + usdBasedServiceDiscoveryConfigCaptor.getValue(); + + assertEquals(TEST_USD_SERVICE_NAME, aidlUsdConfig.serviceName); + assertEquals(TEST_USD_PROTOCOL_TYPE, aidlUsdConfig.serviceProtocolType); + assertArrayEquals(TEST_USD_SERVICE_SPECIFIC_INFO, aidlUsdConfig.serviceSpecificInfo); + assertEquals(0, aidlUsdConfig.bandMask); + assertArrayEquals(TEST_USD_DISCOVERY_CHANNEL_FREQUENCIES_MHZ, + aidlUsdConfig.frequencyListMhz); + assertEquals(TEST_USD_TIMEOUT_S, aidlUsdConfig.timeoutInSeconds); + } + + /** + * Test the handling of stop an Un-synchronized Service Discovery (USD) based service discovery. + */ + @Test + public void testStopUsdBasedServiceDiscoverySuccess() throws Exception { + assumeTrue(Environment.isSdkAtLeastB()); + setCachedServiceVersion(4); + + doNothing().when(mISupplicantP2pIfaceMock).stopUsdBasedServiceDiscovery(anyInt()); + + executeAndValidateInitializationSequence(false, false); + + mDut.stopUsdBasedServiceDiscovery(TEST_USD_SESSION_ID); + verify(mISupplicantP2pIfaceMock).stopUsdBasedServiceDiscovery(eq(TEST_USD_SESSION_ID)); + } + + /** + * Test the handling of start an Un-synchronized Service Discovery (USD) based service + * advertisement. + */ + @Test + public void testStartUsdBasedServiceAdvertisementSuccess() throws Exception { + assumeTrue(Environment.isSdkAtLeastB()); + setCachedServiceVersion(4); + ArgumentCaptor<android.hardware.wifi.supplicant.P2pUsdBasedServiceAdvertisementConfig> + usdBasedServiceAdvertisementConfigCaptor = ArgumentCaptor.forClass( + android.hardware.wifi.supplicant.P2pUsdBasedServiceAdvertisementConfig.class); + when(mISupplicantP2pIfaceMock.startUsdBasedServiceAdvertisement(any())) + .thenReturn(TEST_USD_SESSION_ID); + + WifiP2pUsdBasedServiceConfig usdConfig = new WifiP2pUsdBasedServiceConfig.Builder( + TEST_USD_SERVICE_NAME) + .setServiceProtocolType(TEST_USD_PROTOCOL_TYPE) + .setServiceSpecificInfo(TEST_USD_SERVICE_SPECIFIC_INFO).build(); + WifiP2pUsdBasedLocalServiceAdvertisementConfig serviceAdvertisementConfig = + new WifiP2pUsdBasedLocalServiceAdvertisementConfig.Builder() + .setFrequencyMhz(TEST_USD_DISCOVERY_CHANNEL_FREQUENCY_MHZ).build(); + + executeAndValidateInitializationSequence(false, false); + + assertEquals(TEST_USD_SESSION_ID, mDut.startUsdBasedServiceAdvertisement(usdConfig, + serviceAdvertisementConfig, TEST_USD_TIMEOUT_S)); + + verify(mISupplicantP2pIfaceMock).startUsdBasedServiceAdvertisement( + usdBasedServiceAdvertisementConfigCaptor.capture()); + android.hardware.wifi.supplicant.P2pUsdBasedServiceAdvertisementConfig aidlUsdConfig = + usdBasedServiceAdvertisementConfigCaptor.getValue(); + + assertEquals(TEST_USD_SERVICE_NAME, aidlUsdConfig.serviceName); + assertEquals(TEST_USD_PROTOCOL_TYPE, aidlUsdConfig.serviceProtocolType); + assertArrayEquals(TEST_USD_SERVICE_SPECIFIC_INFO, aidlUsdConfig.serviceSpecificInfo); + assertEquals(TEST_USD_DISCOVERY_CHANNEL_FREQUENCY_MHZ, aidlUsdConfig.frequencyMHz); + assertEquals(TEST_USD_TIMEOUT_S, aidlUsdConfig.timeoutInSeconds); + } + + /** + * Test the handling of stop an Un-synchronized Service Discovery (USD) based service + * advertisement. + */ + @Test + public void testStopUsdBasedServiceAdvertisementSuccess() throws Exception { + assumeTrue(Environment.isSdkAtLeastB()); + setCachedServiceVersion(4); + + doNothing().when(mISupplicantP2pIfaceMock).stopUsdBasedServiceAdvertisement(anyInt()); + + executeAndValidateInitializationSequence(false, false); + + mDut.stopUsdBasedServiceAdvertisement(TEST_USD_SESSION_ID); + verify(mISupplicantP2pIfaceMock).stopUsdBasedServiceAdvertisement(eq(TEST_USD_SESSION_ID)); + } + + /** + * Test the handling of getting the Device Identity Resolution (DIR) Information. + */ + @Test + public void testGetDirInfoSuccess() throws Exception { + assumeTrue(Environment.isSdkAtLeastB()); + setCachedServiceVersion(4); + + P2pDirInfo aidlDirInfo = new P2pDirInfo(); + aidlDirInfo.cipherVersion = P2pDirInfo.CipherVersion.DIRA_CIPHER_VERSION_128_BIT; + aidlDirInfo.deviceInterfaceMacAddress = mPeerMacAddressBytes; + aidlDirInfo.dirTag = TEST_DIR_TAG; + aidlDirInfo.nonce = TEST_NONCE; + + doReturn(aidlDirInfo).when(mISupplicantP2pIfaceMock).getDirInfo(); + + executeAndValidateInitializationSequence(false, false); + + WifiP2pDirInfo dirInfo = mDut.getDirInfo(); + assertNotNull(dirInfo); + assertEquals(android.net.MacAddress.fromBytes(mPeerMacAddressBytes), + dirInfo.getMacAddress()); + assertArrayEquals(TEST_DIR_TAG, dirInfo.getDirTag()); + assertArrayEquals(TEST_NONCE, dirInfo.getNonce()); + } + + /** + * Test the handling of Validating the Device Identity Resolution (DIR) Information + */ + @Test + public void testValidateDirInfoSuccess() throws Exception { + assumeTrue(Environment.isSdkAtLeastB()); + setCachedServiceVersion(4); + + WifiP2pDirInfo dirInfo = new WifiP2pDirInfo( + android.net.MacAddress.fromBytes(mPeerMacAddressBytes), TEST_NONCE, TEST_DIR_TAG); + + + ArgumentCaptor<android.hardware.wifi.supplicant.P2pDirInfo> + p2pDirInfoCaptor = ArgumentCaptor.forClass( + android.hardware.wifi.supplicant.P2pDirInfo.class); + when(mISupplicantP2pIfaceMock.validateDirInfo(any())).thenReturn(TEST_DEV_IK_ID); + + executeAndValidateInitializationSequence(false, false); + + assertEquals(TEST_DEV_IK_ID, mDut.validateDirInfo(dirInfo)); + + verify(mISupplicantP2pIfaceMock).validateDirInfo( + p2pDirInfoCaptor.capture()); + android.hardware.wifi.supplicant.P2pDirInfo aidlDirInfo = p2pDirInfoCaptor.getValue(); + assertEquals(P2pDirInfo.CipherVersion.DIRA_CIPHER_VERSION_128_BIT, + aidlDirInfo.cipherVersion); + assertArrayEquals(mPeerMacAddressBytes, aidlDirInfo.deviceInterfaceMacAddress); + assertArrayEquals(TEST_DIR_TAG, aidlDirInfo.dirTag); + assertArrayEquals(TEST_NONCE, aidlDirInfo.nonce); + } + + /** + * Test the handling of groupAdd with P2P version. + */ + @Test + public void testCreateGroupOwnerSuccess() throws Exception { + assumeTrue(Environment.isSdkAtLeastB()); + setCachedServiceVersion(4); + doNothing().when(mISupplicantP2pIfaceMock).addGroup(anyBoolean(), anyInt()); + doNothing().when(mISupplicantP2pIfaceMock).createGroupOwner(any()); + ArgumentCaptor<android.hardware.wifi.supplicant.P2pCreateGroupOwnerInfo> + p2pGroupInfoCaptor = ArgumentCaptor.forClass( + android.hardware.wifi.supplicant.P2pCreateGroupOwnerInfo.class); + + executeAndValidateInitializationSequence(false, false); + + assertTrue(mDut.groupAdd(-1, false, true)); + verify(mISupplicantP2pIfaceMock, never()).addGroup(anyBoolean(), anyInt()); + verify(mISupplicantP2pIfaceMock).createGroupOwner( + p2pGroupInfoCaptor.capture()); + android.hardware.wifi.supplicant.P2pCreateGroupOwnerInfo aidlGroupInfo = + p2pGroupInfoCaptor.getValue(); + assertFalse(aidlGroupInfo.persistent); + assertEquals(-1, aidlGroupInfo.persistentNetworkId); + assertTrue(aidlGroupInfo.isP2pV2); + } + + /** + * Test the handling of ProvisionDiscovery with pairing bootstrapping method. + */ + @Test + public void testProvisionDiscoveryWithBootStrappingMethodSuccess() throws Exception { + assumeTrue(Environment.isSdkAtLeastB()); + setCachedServiceVersion(4); + doNothing().when(mISupplicantP2pIfaceMock).provisionDiscoveryWithParams(any()); + ArgumentCaptor<android.hardware.wifi.supplicant.P2pProvisionDiscoveryParams> + p2pProvDiscParamsCaptor = ArgumentCaptor.forClass( + android.hardware.wifi.supplicant.P2pProvisionDiscoveryParams.class); + + executeAndValidateInitializationSequence(false, false); + + WifiP2pConfig config = createP2pConfigWithBootstrappingMethod(mPeerMacAddressBytes, + WifiP2pPairingBootstrappingConfig.PAIRING_BOOTSTRAPPING_METHOD_OPPORTUNISTIC, + "", false); + assertTrue(mDut.provisionDiscovery(config)); + verify(mISupplicantP2pIfaceMock).provisionDiscoveryWithParams( + p2pProvDiscParamsCaptor.capture()); + android.hardware.wifi.supplicant.P2pProvisionDiscoveryParams aidlProvDiscParams = + p2pProvDiscParamsCaptor.getValue(); + assertArrayEquals(mPeerMacAddressBytes, aidlProvDiscParams.peerMacAddress); + assertEquals(WpsProvisionMethod.NONE, aidlProvDiscParams.provisionMethod); + assertEquals(P2pPairingBootstrappingMethodMask.BOOTSTRAPPING_OPPORTUNISTIC, + aidlProvDiscParams.pairingBootstrappingMethod); + } + + /** + * Test the mapping of user provided pairing bootstrapping method to the method filled in + * provision discovery request packet. + */ + @Test + public void testProvisionDiscoveryBootstrappingMethodMapping() throws Exception { + assumeTrue(Environment.isSdkAtLeastB()); + setCachedServiceVersion(4); + doNothing().when(mISupplicantP2pIfaceMock).provisionDiscoveryWithParams(any()); + ArgumentCaptor<android.hardware.wifi.supplicant.P2pProvisionDiscoveryParams> + p2pProvDiscParamsCaptor = ArgumentCaptor.forClass( + android.hardware.wifi.supplicant.P2pProvisionDiscoveryParams.class); + + executeAndValidateInitializationSequence(false, false); + + // DISPLAY PIN -> ENTER PIN + WifiP2pConfig config = createP2pConfigWithBootstrappingMethod(mPeerMacAddressBytes, + WifiP2pPairingBootstrappingConfig.PAIRING_BOOTSTRAPPING_METHOD_DISPLAY_PINCODE, + "1234", false); + assertTrue(mDut.provisionDiscovery(config)); + verify(mISupplicantP2pIfaceMock, times(1)) + .provisionDiscoveryWithParams(p2pProvDiscParamsCaptor.capture()); + android.hardware.wifi.supplicant.P2pProvisionDiscoveryParams aidlProvDiscParams = + p2pProvDiscParamsCaptor.getValue(); + assertEquals(P2pPairingBootstrappingMethodMask.BOOTSTRAPPING_KEYPAD_PINCODE, + aidlProvDiscParams.pairingBootstrappingMethod); + + // DISPLAY PASSPHRASE -> ENTER PASSPHRASE + config = createP2pConfigWithBootstrappingMethod(mPeerMacAddressBytes, + WifiP2pPairingBootstrappingConfig.PAIRING_BOOTSTRAPPING_METHOD_DISPLAY_PASSPHRASE, + "abed", false); + assertTrue(mDut.provisionDiscovery(config)); + verify(mISupplicantP2pIfaceMock, times(2)) + .provisionDiscoveryWithParams(p2pProvDiscParamsCaptor.capture()); + aidlProvDiscParams = p2pProvDiscParamsCaptor.getValue(); + assertEquals(P2pPairingBootstrappingMethodMask.BOOTSTRAPPING_KEYPAD_PASSPHRASE, + aidlProvDiscParams.pairingBootstrappingMethod); + + // ENTER PIN -> DISPLAY PIN + config = createP2pConfigWithBootstrappingMethod(mPeerMacAddressBytes, + WifiP2pPairingBootstrappingConfig.PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PINCODE, + "", false); + assertTrue(mDut.provisionDiscovery(config)); + verify(mISupplicantP2pIfaceMock, times(3)) + .provisionDiscoveryWithParams(p2pProvDiscParamsCaptor.capture()); + aidlProvDiscParams = p2pProvDiscParamsCaptor.getValue(); + assertEquals(P2pPairingBootstrappingMethodMask.BOOTSTRAPPING_DISPLAY_PINCODE, + aidlProvDiscParams.pairingBootstrappingMethod); + + // ENTER PASSPHRASE -> DISPLAY PASSPHRASE + config = createP2pConfigWithBootstrappingMethod(mPeerMacAddressBytes, + WifiP2pPairingBootstrappingConfig.PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PASSPHRASE, + "", false); + assertTrue(mDut.provisionDiscovery(config)); + verify(mISupplicantP2pIfaceMock, times(4)) + .provisionDiscoveryWithParams(p2pProvDiscParamsCaptor.capture()); + aidlProvDiscParams = p2pProvDiscParamsCaptor.getValue(); + assertEquals(P2pPairingBootstrappingMethodMask.BOOTSTRAPPING_DISPLAY_PASSPHRASE, + aidlProvDiscParams.pairingBootstrappingMethod); + + } + + /** + * Test that a ProvisionDiscovery request with pairing bootstrapping method Out Of Band fails. + */ + @Test + public void testProvisionDiscoveryForOobBootstrappingFails() throws Exception { + assumeTrue(Environment.isSdkAtLeastB()); + setCachedServiceVersion(4); + doNothing().when(mISupplicantP2pIfaceMock).provisionDiscoveryWithParams(any()); + + executeAndValidateInitializationSequence(false, false); + + WifiP2pConfig config = createP2pConfigWithBootstrappingMethod(mPeerMacAddressBytes, + WifiP2pPairingBootstrappingConfig.PAIRING_BOOTSTRAPPING_METHOD_OUT_OF_BAND, + "1234", false); + assertFalse(mDut.provisionDiscovery(config)); + } + + /** + * Test the handling of connect with pairing bootstrapping method: Opportunistic. + */ + @Test + public void testConnectWithBootStrappingMethodOpportunisticSuccess() throws Exception { + assumeTrue(Environment.isSdkAtLeastB()); + setCachedServiceVersion(4); + doReturn("").when(mISupplicantP2pIfaceMock).connectWithParams(any()); + + ArgumentCaptor<android.hardware.wifi.supplicant.P2pConnectInfo> + connectInfoCaptor = ArgumentCaptor.forClass( + android.hardware.wifi.supplicant.P2pConnectInfo.class); + + executeAndValidateInitializationSequence(false, false); + + WifiP2pConfig config = createP2pConfigWithBootstrappingMethod(mPeerMacAddressBytes, + WifiP2pPairingBootstrappingConfig.PAIRING_BOOTSTRAPPING_METHOD_OPPORTUNISTIC, + "", false); + assertTrue(mDut.connect(config, false).isEmpty()); + verify(mISupplicantP2pIfaceMock).connectWithParams(connectInfoCaptor.capture()); + android.hardware.wifi.supplicant.P2pConnectInfo aidlConnectInfo = + connectInfoCaptor.getValue(); + assertArrayEquals(mPeerMacAddressBytes, aidlConnectInfo.peerAddress); + assertEquals(WpsProvisionMethod.NONE, aidlConnectInfo.provisionMethod); + assertEquals(P2pPairingBootstrappingMethodMask.BOOTSTRAPPING_OPPORTUNISTIC, + aidlConnectInfo.pairingBootstrappingMethod); + assertTrue(TextUtils.isEmpty(aidlConnectInfo.password)); + assertEquals(0, aidlConnectInfo.frequencyMHz); + assertFalse(aidlConnectInfo.authorizeConnectionFromPeer); + assertNull(aidlConnectInfo.groupInterfaceName); + } + + /** + * Test the handling of connect with pairing bootstrapping method: display pin-code. + */ + @Test + public void testConnectWithBootStrappingMethodDisplayPinCodeSuccess() throws Exception { + assumeTrue(Environment.isSdkAtLeastB()); + setCachedServiceVersion(4); + doReturn("").when(mISupplicantP2pIfaceMock).connectWithParams(any()); + ArgumentCaptor<android.hardware.wifi.supplicant.P2pConnectInfo> + connectInfoCaptor = ArgumentCaptor.forClass( + android.hardware.wifi.supplicant.P2pConnectInfo.class); + + executeAndValidateInitializationSequence(false, false); + + WifiP2pConfig config = createP2pConfigWithBootstrappingMethod(mPeerMacAddressBytes, + WifiP2pPairingBootstrappingConfig.PAIRING_BOOTSTRAPPING_METHOD_DISPLAY_PINCODE, + "1234", false); + assertTrue(mDut.connect(config, false).isEmpty()); + verify(mISupplicantP2pIfaceMock).connectWithParams(connectInfoCaptor.capture()); + android.hardware.wifi.supplicant.P2pConnectInfo aidlConnectInfo = + connectInfoCaptor.getValue(); + assertArrayEquals(mPeerMacAddressBytes, aidlConnectInfo.peerAddress); + assertEquals(WpsProvisionMethod.NONE, aidlConnectInfo.provisionMethod); + assertEquals(P2pPairingBootstrappingMethodMask.BOOTSTRAPPING_DISPLAY_PINCODE, + aidlConnectInfo.pairingBootstrappingMethod); + assertEquals("1234", aidlConnectInfo.password); + assertEquals(0, aidlConnectInfo.frequencyMHz); + assertFalse(aidlConnectInfo.authorizeConnectionFromPeer); + assertNull(aidlConnectInfo.groupInterfaceName); + } + + /** + * Test the handling of connect with pairing bootstrapping method: display passphrase. + */ + @Test + public void testConnectWithBootStrappingMethodDisplayPassphraseSuccess() throws Exception { + assumeTrue(Environment.isSdkAtLeastB()); + setCachedServiceVersion(4); + doReturn("").when(mISupplicantP2pIfaceMock).connectWithParams(any()); + ArgumentCaptor<android.hardware.wifi.supplicant.P2pConnectInfo> + connectInfoCaptor = ArgumentCaptor.forClass( + android.hardware.wifi.supplicant.P2pConnectInfo.class); + + executeAndValidateInitializationSequence(false, false); + + WifiP2pConfig config = createP2pConfigWithBootstrappingMethod(mPeerMacAddressBytes, + WifiP2pPairingBootstrappingConfig.PAIRING_BOOTSTRAPPING_METHOD_DISPLAY_PASSPHRASE, + "abed", false); + assertTrue(mDut.connect(config, false).isEmpty()); + verify(mISupplicantP2pIfaceMock).connectWithParams(connectInfoCaptor.capture()); + android.hardware.wifi.supplicant.P2pConnectInfo aidlConnectInfo = + connectInfoCaptor.getValue(); + assertArrayEquals(mPeerMacAddressBytes, aidlConnectInfo.peerAddress); + assertEquals(WpsProvisionMethod.NONE, aidlConnectInfo.provisionMethod); + assertEquals(P2pPairingBootstrappingMethodMask.BOOTSTRAPPING_DISPLAY_PASSPHRASE, + aidlConnectInfo.pairingBootstrappingMethod); + assertEquals("abed", aidlConnectInfo.password); + } + + /** + * Test the handling of connect with pairing bootstrapping method: keypad pin-code. + */ + @Test + public void testConnectWithBootStrappingMethodKeypadPinCodeSuccess() throws Exception { + assumeTrue(Environment.isSdkAtLeastB()); + setCachedServiceVersion(4); + doReturn("").when(mISupplicantP2pIfaceMock).connectWithParams(any()); + ArgumentCaptor<android.hardware.wifi.supplicant.P2pConnectInfo> + connectInfoCaptor = ArgumentCaptor.forClass( + android.hardware.wifi.supplicant.P2pConnectInfo.class); + + executeAndValidateInitializationSequence(false, false); + + WifiP2pConfig config = createP2pConfigWithBootstrappingMethod(mPeerMacAddressBytes, + WifiP2pPairingBootstrappingConfig.PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PINCODE, + "", false); + config.getPairingBootstrappingConfig().setPairingBootstrappingPassword("1234"); + assertTrue(mDut.connect(config, false).isEmpty()); + verify(mISupplicantP2pIfaceMock).connectWithParams(connectInfoCaptor.capture()); + android.hardware.wifi.supplicant.P2pConnectInfo aidlConnectInfo = + connectInfoCaptor.getValue(); + assertArrayEquals(mPeerMacAddressBytes, aidlConnectInfo.peerAddress); + assertEquals(WpsProvisionMethod.NONE, aidlConnectInfo.provisionMethod); + assertEquals(P2pPairingBootstrappingMethodMask.BOOTSTRAPPING_KEYPAD_PINCODE, + aidlConnectInfo.pairingBootstrappingMethod); + assertEquals("1234", aidlConnectInfo.password); + } + + /** + * Test the handling of connect with pairing bootstrapping method: keypad passphrase. + */ + @Test + public void testConnectWithBootStrappingMethodKeypadPassphraseSuccess() throws Exception { + assumeTrue(Environment.isSdkAtLeastB()); + setCachedServiceVersion(4); + doReturn("").when(mISupplicantP2pIfaceMock).connectWithParams(any()); + ArgumentCaptor<android.hardware.wifi.supplicant.P2pConnectInfo> + connectInfoCaptor = ArgumentCaptor.forClass( + android.hardware.wifi.supplicant.P2pConnectInfo.class); + + executeAndValidateInitializationSequence(false, false); + + WifiP2pConfig config = createP2pConfigWithBootstrappingMethod(mPeerMacAddressBytes, + WifiP2pPairingBootstrappingConfig.PAIRING_BOOTSTRAPPING_METHOD_KEYPAD_PASSPHRASE, + "", false); + config.getPairingBootstrappingConfig().setPairingBootstrappingPassword("abed"); + assertTrue(mDut.connect(config, false).isEmpty()); + verify(mISupplicantP2pIfaceMock).connectWithParams(connectInfoCaptor.capture()); + android.hardware.wifi.supplicant.P2pConnectInfo aidlConnectInfo = + connectInfoCaptor.getValue(); + assertArrayEquals(mPeerMacAddressBytes, aidlConnectInfo.peerAddress); + assertEquals(WpsProvisionMethod.NONE, aidlConnectInfo.provisionMethod); + assertEquals(P2pPairingBootstrappingMethodMask.BOOTSTRAPPING_KEYPAD_PASSPHRASE, + aidlConnectInfo.pairingBootstrappingMethod); + assertEquals("abed", aidlConnectInfo.password); + } + + /** + * Test the handling of connect with pairing bootstrapping method: out of band + */ + @Test + public void testConnectWithBootStrappingMethodOutOfBandSuccess() throws Exception { + assumeTrue(Environment.isSdkAtLeastB()); + setCachedServiceVersion(4); + doReturn("").when(mISupplicantP2pIfaceMock).connectWithParams(any()); + ArgumentCaptor<android.hardware.wifi.supplicant.P2pConnectInfo> + connectInfoCaptor = ArgumentCaptor.forClass( + android.hardware.wifi.supplicant.P2pConnectInfo.class); + + executeAndValidateInitializationSequence(false, false); + + WifiP2pConfig config = createP2pConfigWithBootstrappingMethod(mPeerMacAddressBytes, + WifiP2pPairingBootstrappingConfig.PAIRING_BOOTSTRAPPING_METHOD_OUT_OF_BAND, + "", false); + config.getPairingBootstrappingConfig().setPairingBootstrappingPassword("abed"); + assertTrue(mDut.connect(config, false).isEmpty()); + verify(mISupplicantP2pIfaceMock).connectWithParams(connectInfoCaptor.capture()); + android.hardware.wifi.supplicant.P2pConnectInfo aidlConnectInfo = + connectInfoCaptor.getValue(); + assertArrayEquals(mPeerMacAddressBytes, aidlConnectInfo.peerAddress); + assertEquals(WpsProvisionMethod.NONE, aidlConnectInfo.provisionMethod); + assertEquals(P2pPairingBootstrappingMethodMask.BOOTSTRAPPING_OUT_OF_BAND, + aidlConnectInfo.pairingBootstrappingMethod); + assertEquals("abed", aidlConnectInfo.password); + } + + /** + * Test the handling of authorize connection request to an existing group owner. + */ + @Test + public void testAuthorizeConnectRequestOnGroupOwner() throws Exception { + assumeTrue(Environment.isSdkAtLeastB()); + setCachedServiceVersion(4); + doReturn("").when(mISupplicantP2pIfaceMock).connectWithParams(any()); + ArgumentCaptor<android.hardware.wifi.supplicant.P2pConnectInfo> + connectInfoCaptor = ArgumentCaptor.forClass( + android.hardware.wifi.supplicant.P2pConnectInfo.class); + + executeAndValidateInitializationSequence(false, false); + + WifiP2pConfig config = createP2pConfigWithBootstrappingMethod(mPeerMacAddressBytes, + WifiP2pPairingBootstrappingConfig.PAIRING_BOOTSTRAPPING_METHOD_OUT_OF_BAND, + "1234", true); + assertTrue(mDut.authorizeConnectRequestOnGroupOwner(config, + TEST_GROUP_INTERFACE_NAME)); + verify(mISupplicantP2pIfaceMock).connectWithParams(connectInfoCaptor.capture()); + android.hardware.wifi.supplicant.P2pConnectInfo aidlConnectInfo = + connectInfoCaptor.getValue(); + assertArrayEquals(mPeerMacAddressBytes, aidlConnectInfo.peerAddress); + assertEquals(WpsProvisionMethod.NONE, aidlConnectInfo.provisionMethod); + assertEquals(P2pPairingBootstrappingMethodMask.BOOTSTRAPPING_OUT_OF_BAND, + aidlConnectInfo.pairingBootstrappingMethod); + assertEquals(TEST_GROUP_INTERFACE_NAME, aidlConnectInfo.groupInterfaceName); + assertTrue(aidlConnectInfo.authorizeConnectionFromPeer); + } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceHalHidlImplTest.java b/service/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceHalHidlImplTest.java index 2520a7b01e..35edd1ed3c 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceHalHidlImplTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceHalHidlImplTest.java @@ -1312,9 +1312,9 @@ public class SupplicantP2pIfaceHalHidlImplTest extends WifiBaseTest { when(mISupplicantP2pIfaceMock.addGroup(eq(true), eq(3))) .thenReturn(mStatusSuccess); // Default value when service is not initialized. - assertFalse(mDut.groupAdd(3, true)); + assertFalse(mDut.groupAdd(3, true, false)); executeAndValidateInitializationSequence(false, false, false); - assertTrue(mDut.groupAdd(3, true)); + assertTrue(mDut.groupAdd(3, true, false)); } /** @@ -1325,7 +1325,7 @@ public class SupplicantP2pIfaceHalHidlImplTest extends WifiBaseTest { executeAndValidateInitializationSequence(false, false, false); when(mISupplicantP2pIfaceMock.addGroup(anyBoolean(), anyInt())) .thenReturn(mStatusFailure); - assertFalse(mDut.groupAdd(0, true)); + assertFalse(mDut.groupAdd(0, true, false)); // Check that service is still alive. assertTrue(mDut.isInitializationComplete()); } @@ -1338,7 +1338,7 @@ public class SupplicantP2pIfaceHalHidlImplTest extends WifiBaseTest { executeAndValidateInitializationSequence(false, false, false); when(mISupplicantP2pIfaceMock.addGroup(anyBoolean(), anyInt())) .thenThrow(mRemoteException); - assertFalse(mDut.groupAdd(0, true)); + assertFalse(mDut.groupAdd(0, true, false)); // Check service is dead. assertFalse(mDut.isInitializationComplete()); } @@ -1358,7 +1358,8 @@ public class SupplicantP2pIfaceHalHidlImplTest extends WifiBaseTest { anyBoolean())) .thenReturn(mStatusSuccess); // Default value when service is not initialized. - assertFalse(mDut.groupAdd(mNetworkName, mPassphrase, mIsPersistent, + assertFalse(mDut.groupAdd(mNetworkName, mPassphrase, + WifiP2pConfig.PCC_MODE_CONNECTION_TYPE_LEGACY_ONLY, mIsPersistent, mGroupOwnerBand, mPeerMacAddress, true)); verify(mISupplicantP2pIfaceMockV12, never()).addGroup_1_2( any(ArrayList.class), anyString(), @@ -1366,7 +1367,8 @@ public class SupplicantP2pIfaceHalHidlImplTest extends WifiBaseTest { any(byte[].class), anyBoolean()); executeAndValidateInitializationSequence(false, false, false); - assertTrue(mDut.groupAdd(mNetworkName, mPassphrase, mIsPersistent, + assertTrue(mDut.groupAdd(mNetworkName, mPassphrase, + WifiP2pConfig.PCC_MODE_CONNECTION_TYPE_LEGACY_ONLY, mIsPersistent, mGroupOwnerBand, mPeerMacAddress, true)); verify(mISupplicantP2pIfaceMockV12).addGroup_1_2( eq(NativeUtil.decodeSsid("\"" + mNetworkName + "\"")), @@ -1388,7 +1390,8 @@ public class SupplicantP2pIfaceHalHidlImplTest extends WifiBaseTest { anyBoolean(), anyInt(), any(byte[].class), anyBoolean())) .thenReturn(mStatusFailure); - assertFalse(mDut.groupAdd(mNetworkName, mPassphrase, mIsPersistent, + assertFalse(mDut.groupAdd(mNetworkName, mPassphrase, + WifiP2pConfig.PCC_MODE_CONNECTION_TYPE_LEGACY_ONLY, mIsPersistent, mGroupOwnerBand, mPeerMacAddress, true)); verify(mISupplicantP2pIfaceMockV12).addGroup_1_2( eq(NativeUtil.decodeSsid("\"" + mNetworkName + "\"")), @@ -1415,7 +1418,8 @@ public class SupplicantP2pIfaceHalHidlImplTest extends WifiBaseTest { // disable 1.2 interface to simulator since older revision cannot be casted to v1.2 mISupplicantV12Enabled = false; - assertFalse(mDut.groupAdd(mNetworkName, mPassphrase, mIsPersistent, + assertFalse(mDut.groupAdd(mNetworkName, mPassphrase, + WifiP2pConfig.PCC_MODE_CONNECTION_TYPE_LEGACY_ONLY, mIsPersistent, mGroupOwnerBand, mPeerMacAddress, true)); verify(mISupplicantP2pIfaceMockV12, never()).addGroup_1_2( any(ArrayList.class), anyString(), @@ -1437,7 +1441,8 @@ public class SupplicantP2pIfaceHalHidlImplTest extends WifiBaseTest { anyBoolean(), anyInt(), any(byte[].class), anyBoolean())) .thenThrow(mRemoteException); - assertFalse(mDut.groupAdd(mNetworkName, mPassphrase, mIsPersistent, + assertFalse(mDut.groupAdd(mNetworkName, mPassphrase, + WifiP2pConfig.PCC_MODE_CONNECTION_TYPE_LEGACY_ONLY, mIsPersistent, mGroupOwnerBand, mPeerMacAddress, true)); verify(mISupplicantP2pIfaceMockV12).addGroup_1_2( eq(NativeUtil.decodeSsid("\"" + mNetworkName + "\"")), diff --git a/service/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceHalTest.java b/service/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceHalTest.java index bf7194bec5..667cbf9808 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceHalTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/p2p/SupplicantP2pIfaceHalTest.java @@ -71,6 +71,7 @@ public class SupplicantP2pIfaceHalTest extends WifiBaseTest { private static final String RESPONSE = "blahblahblah"; private static final String PIN = "5678"; private static final boolean ENABLE = true; + private static final boolean DISABLE = false; private static final int NETWORK_ID = 2; private static final int CHANNEL = 3; @@ -423,9 +424,9 @@ public class SupplicantP2pIfaceHalTest extends WifiBaseTest { @Test public void testGroupAdd() { initializeWithAidlImpl(true); - when(mP2pIfaceHalAidlMock.groupAdd(anyInt(), anyBoolean())).thenReturn(true); - assertTrue(mDut.groupAdd(NETWORK_ID, ENABLE)); - verify(mP2pIfaceHalAidlMock).groupAdd(eq(NETWORK_ID), eq(ENABLE)); + when(mP2pIfaceHalAidlMock.groupAdd(anyInt(), anyBoolean(), anyBoolean())).thenReturn(true); + assertTrue(mDut.groupAdd(NETWORK_ID, ENABLE, DISABLE)); + verify(mP2pIfaceHalAidlMock).groupAdd(eq(NETWORK_ID), eq(ENABLE), eq(DISABLE)); } /** @@ -434,9 +435,9 @@ public class SupplicantP2pIfaceHalTest extends WifiBaseTest { @Test public void testGroupAddWrapper() { initializeWithAidlImpl(true); - when(mP2pIfaceHalAidlMock.groupAdd(anyInt(), anyBoolean())).thenReturn(true); - assertTrue(mDut.groupAdd(ENABLE)); - verify(mP2pIfaceHalAidlMock).groupAdd(eq(-1) /* set by wrapper */, eq(ENABLE)); + when(mP2pIfaceHalAidlMock.groupAdd(anyInt(), anyBoolean(), anyBoolean())).thenReturn(true); + assertTrue(mDut.groupAdd(ENABLE, DISABLE)); + verify(mP2pIfaceHalAidlMock).groupAdd(eq(-1) /* set by wrapper */, eq(ENABLE), eq(DISABLE)); } /** @@ -447,13 +448,17 @@ public class SupplicantP2pIfaceHalTest extends WifiBaseTest { initializeWithAidlImpl(true); String networkName = "someName"; String passphrase = "somePassword"; + @WifiP2pConfig.PccModeConnectionType int connectionType = + WifiP2pConfig.PCC_MODE_CONNECTION_TYPE_LEGACY_ONLY; boolean persistent = true; boolean join = true; int freq = 10; - when(mP2pIfaceHalAidlMock.groupAdd(anyString(), anyString(), + when(mP2pIfaceHalAidlMock.groupAdd(anyString(), anyString(), anyInt(), anyBoolean(), anyInt(), anyString(), anyBoolean())).thenReturn(true); - assertTrue(mDut.groupAdd(networkName, passphrase, persistent, freq, BSSID, join)); - verify(mP2pIfaceHalAidlMock).groupAdd(eq(networkName), eq(passphrase), eq(persistent), + assertTrue(mDut.groupAdd(networkName, passphrase, connectionType, persistent, + freq, BSSID, join)); + verify(mP2pIfaceHalAidlMock).groupAdd(eq(networkName), eq(passphrase), + eq(connectionType), eq(persistent), eq(freq), eq(BSSID), eq(join)); } diff --git a/service/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pNativeInterfaceManagementTest.java b/service/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pNativeInterfaceManagementTest.java index 7080a1d6a5..a217d7e7a1 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pNativeInterfaceManagementTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pNativeInterfaceManagementTest.java @@ -30,6 +30,8 @@ import android.hardware.wifi.V1_0.IWifiP2pIface; import android.hardware.wifi.V1_0.WifiStatus; import android.hardware.wifi.V1_0.WifiStatusCode; import android.net.wifi.nl80211.WifiNl80211Manager; +import android.net.wifi.p2p.WifiP2pManager; +import android.net.wifi.util.Environment; import android.os.Handler; import android.os.WorkSource; @@ -44,6 +46,7 @@ import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.WifiInjector; import com.android.server.wifi.WifiMetrics; import com.android.server.wifi.WifiNative; +import com.android.server.wifi.WifiSettingsConfigStore; import com.android.server.wifi.WifiVendorHal; import com.android.server.wifi.hal.WifiHal; import com.android.wifi.flags.FeatureFlags; @@ -78,6 +81,7 @@ public class WifiP2pNativeInterfaceManagementTest extends WifiBaseTest { @Mock private WifiInjector mWifiInjector; @Mock private DeviceConfigFacade mDeviceConfigFacade; @Mock private FeatureFlags mFeatureFlags; + private @Mock WifiSettingsConfigStore mWifiSettingsConfigStore; private WifiP2pNative mWifiP2pNative; private WifiStatus mWifiStatusSuccess; @@ -108,6 +112,20 @@ public class WifiP2pNativeInterfaceManagementTest extends WifiBaseTest { .thenReturn(TEST_P2P_IFACE_NAME); when(mWifiInjector.getDeviceConfigFacade()).thenReturn(mDeviceConfigFacade); when(mDeviceConfigFacade.getFeatureFlags()).thenReturn(mFeatureFlags); + when(mWifiInjector.getSettingsConfigStore()).thenReturn(mWifiSettingsConfigStore); + when(mWifiSettingsConfigStore + .get(eq(WifiSettingsConfigStore.SUPPLICANT_HAL_AIDL_SERVICE_VERSION))) + .thenReturn(2); + if (Environment.isSdkAtLeastB()) { + when(mWifiSettingsConfigStore + .get(eq(WifiSettingsConfigStore.WIFI_P2P_SUPPORTED_FEATURES))) + .thenReturn(WifiP2pManager.FEATURE_WIFI_DIRECT_R2 + | WifiP2pManager.FEATURE_PCC_MODE_ALLOW_LEGACY_AND_R2_CONNECTION); + } else { + when(mWifiSettingsConfigStore + .get(eq(WifiSettingsConfigStore.WIFI_P2P_SUPPORTED_FEATURES))) + .thenReturn(0L); + } mWifiP2pNative = new WifiP2pNative(mWifiNl80211Manager, mWifiNative, mWifiMetrics, mWifiVendorHal, mSupplicantP2pIfaceHal, mHalDeviceManager, mPropertyService, mWifiInjector); diff --git a/service/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pNativeTest.java b/service/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pNativeTest.java index d27157087e..eafe0c77fa 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pNativeTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pNativeTest.java @@ -19,6 +19,7 @@ package com.android.server.wifi.p2p; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; @@ -32,6 +33,7 @@ import static org.mockito.Mockito.withSettings; import android.app.test.MockAnswerUtil.AnswerWithArguments; import android.hardware.wifi.V1_0.IWifiP2pIface; +import android.net.wifi.WifiMigration; import android.net.wifi.nl80211.WifiNl80211Manager; import android.net.wifi.p2p.WifiP2pConfig; import android.net.wifi.p2p.WifiP2pDevice; @@ -40,6 +42,7 @@ import android.net.wifi.p2p.WifiP2pGroupList; import android.net.wifi.p2p.WifiP2pManager; import android.net.wifi.p2p.nsd.WifiP2pDnsSdServiceInfo; import android.net.wifi.p2p.nsd.WifiP2pServiceInfo; +import android.net.wifi.util.Environment; import android.os.Handler; import android.os.WorkSource; @@ -53,9 +56,11 @@ import com.android.server.wifi.WifiBaseTest; import com.android.server.wifi.WifiInjector; import com.android.server.wifi.WifiMetrics; import com.android.server.wifi.WifiNative; +import com.android.server.wifi.WifiSettingsConfigStore; import com.android.server.wifi.WifiVendorHal; import com.android.server.wifi.hal.WifiHal; import com.android.wifi.flags.FeatureFlags; +import com.android.wifi.flags.Flags; import org.junit.After; import org.junit.Before; @@ -111,6 +116,7 @@ public class WifiP2pNativeTest extends WifiBaseTest { @Mock private WifiInjector mWifiInjector; @Mock private DeviceConfigFacade mDeviceConfigFacade; @Mock private FeatureFlags mFeatureFlags; + private @Mock WifiSettingsConfigStore mWifiSettingsConfigStore; private MockitoSession mSession; private WifiP2pNative mWifiP2pNative; @@ -135,14 +141,46 @@ public class WifiP2pNativeTest extends WifiBaseTest { mSession = ExtendedMockito.mockitoSession() .mockStatic(HalDeviceManager.class, withSettings().lenient()) .strictness(Strictness.LENIENT) + .mockStatic(Flags.class, withSettings().lenient()) + .mockStatic(WifiMigration.class, withSettings().lenient()) .startMocking(); + when(Flags.wifiDirectR2()).thenReturn(false); mWifiClientInterfaceNames.add("wlan0"); mWifiClientInterfaceNames.add("wlan1"); when(mWifiInjector.getDeviceConfigFacade()).thenReturn(mDeviceConfigFacade); when(mDeviceConfigFacade.getFeatureFlags()).thenReturn(mFeatureFlags); + when(mWifiInjector.getSettingsConfigStore()).thenReturn(mWifiSettingsConfigStore); + when(mWifiSettingsConfigStore + .get(eq(WifiSettingsConfigStore.SUPPLICANT_HAL_AIDL_SERVICE_VERSION))) + .thenReturn(2); + if (Environment.isSdkAtLeastB()) { + when(mWifiSettingsConfigStore + .get(eq(WifiSettingsConfigStore.WIFI_P2P_SUPPORTED_FEATURES))) + .thenReturn(WifiP2pManager.FEATURE_WIFI_DIRECT_R2 + | WifiP2pManager.FEATURE_PCC_MODE_ALLOW_LEGACY_AND_R2_CONNECTION); + } else { + when(mWifiSettingsConfigStore + .get(eq(WifiSettingsConfigStore.WIFI_P2P_SUPPORTED_FEATURES))) + .thenReturn(0L); + } mWifiP2pNative = new WifiP2pNative(mWifiCondManager, mWifiNative, mWifiMetrics, mWifiVendorHalMock, mSupplicantP2pIfaceHalMock, mHalDeviceManagerMock, mPropertyServiceMock, mWifiInjector); + if (Environment.isSdkAtLeastB()) { + assertEquals(WifiP2pManager.FEATURE_SET_VENDOR_ELEMENTS + | WifiP2pManager.FEATURE_FLEXIBLE_DISCOVERY + | WifiP2pManager.FEATURE_GROUP_CLIENT_REMOVAL + | WifiP2pManager.FEATURE_GROUP_OWNER_IPV6_LINK_LOCAL_ADDRESS_PROVIDED + | WifiP2pManager.FEATURE_WIFI_DIRECT_R2 + | WifiP2pManager.FEATURE_PCC_MODE_ALLOW_LEGACY_AND_R2_CONNECTION, + mWifiP2pNative.getSupportedFeatures()); + } else { + assertEquals(WifiP2pManager.FEATURE_SET_VENDOR_ELEMENTS + | WifiP2pManager.FEATURE_FLEXIBLE_DISCOVERY + | WifiP2pManager.FEATURE_GROUP_CLIENT_REMOVAL + | WifiP2pManager.FEATURE_GROUP_OWNER_IPV6_LINK_LOCAL_ADDRESS_PROVIDED, + mWifiP2pNative.getSupportedFeatures()); + } when(mWifiNative.getClientInterfaceNames()).thenReturn(mWifiClientInterfaceNames); @@ -713,9 +751,9 @@ public class WifiP2pNativeTest extends WifiBaseTest { */ @Test public void testJoinGroup() { - when(mSupplicantP2pIfaceHalMock.groupAdd(anyBoolean())).thenReturn(true); - assertTrue(mWifiP2pNative.p2pGroupAdd(true)); - verify(mSupplicantP2pIfaceHalMock).groupAdd(eq(true)); + when(mSupplicantP2pIfaceHalMock.groupAdd(anyBoolean(), anyBoolean())).thenReturn(true); + assertTrue(mWifiP2pNative.p2pGroupAdd(true, false)); + verify(mSupplicantP2pIfaceHalMock).groupAdd(eq(true), eq(false)); } /** @@ -723,9 +761,10 @@ public class WifiP2pNativeTest extends WifiBaseTest { */ @Test public void testJoinGroupWithNetworkId() { - when(mSupplicantP2pIfaceHalMock.groupAdd(anyInt(), anyBoolean())).thenReturn(true); - assertTrue(mWifiP2pNative.p2pGroupAdd(5)); - verify(mSupplicantP2pIfaceHalMock).groupAdd(eq(5), eq(true)); + when(mSupplicantP2pIfaceHalMock.groupAdd(anyInt(), anyBoolean(), anyBoolean())) + .thenReturn(true); + assertTrue(mWifiP2pNative.p2pGroupAdd(5, false)); + verify(mSupplicantP2pIfaceHalMock).groupAdd(eq(5), eq(true), eq(false)); } /** @@ -733,8 +772,9 @@ public class WifiP2pNativeTest extends WifiBaseTest { */ @Test public void testJoinGroupWithConfig() { + when(Flags.wifiDirectR2()).thenReturn(true); when(mSupplicantP2pIfaceHalMock.groupAdd( - anyString(), anyString(), anyBoolean(), + anyString(), anyString(), anyInt(), anyBoolean(), anyInt(), anyString(), anyBoolean())).thenReturn(true); WifiP2pConfig config = new WifiP2pConfig.Builder() .setNetworkName(TEST_NETWORK_NAME) @@ -748,13 +788,90 @@ public class WifiP2pNativeTest extends WifiBaseTest { verify(mWifiCondManager).abortScan(eq(intf)); } + if (!Environment.isSdkAtLeastB()) { + verify(mSupplicantP2pIfaceHalMock).groupAdd( + eq(TEST_NETWORK_NAME), + eq(TEST_PASSPHRASE), + eq(WifiP2pConfig.PCC_MODE_CONNECTION_TYPE_LEGACY_ONLY), + eq(true), + eq(TEST_GROUP_FREQ), + eq(config.deviceAddress), + eq(true)); + } else { + verify(mSupplicantP2pIfaceHalMock).groupAdd( + eq(TEST_NETWORK_NAME), + eq(TEST_PASSPHRASE), + eq(WifiP2pConfig.PCC_MODE_CONNECTION_TYPE_LEGACY_OR_R2), + eq(true), + eq(TEST_GROUP_FREQ), + eq(config.deviceAddress), + eq(true)); + } + } + + /** + * Verifies joining p2p group with Pcc Mode config. + */ + @Test + public void testJoinGroupWithPccModeConfig() { + assumeTrue(Environment.isSdkAtLeastB()); + when(Flags.wifiDirectR2()).thenReturn(true); + when(mSupplicantP2pIfaceHalMock.groupAdd( + anyString(), anyString(), anyInt(), anyBoolean(), + anyInt(), anyString(), anyBoolean())).thenReturn(true); + + /* Check if we are upgrading LEGACY to R1/R2 compatible mode */ + WifiP2pConfig config = new WifiP2pConfig.Builder() + .setNetworkName(TEST_NETWORK_NAME) + .setPassphrase(TEST_PASSPHRASE) + .setPccModeConnectionType(WifiP2pConfig.PCC_MODE_CONNECTION_TYPE_LEGACY_ONLY) + .setGroupOperatingFrequency(TEST_GROUP_FREQ) + .build(); + assertTrue(mWifiP2pNative.p2pGroupAdd(config, true)); + verify(mSupplicantP2pIfaceHalMock).groupAdd( eq(TEST_NETWORK_NAME), eq(TEST_PASSPHRASE), - eq(true), + eq(WifiP2pConfig.PCC_MODE_CONNECTION_TYPE_LEGACY_OR_R2), + eq(false), eq(TEST_GROUP_FREQ), eq(config.deviceAddress), eq(true)); + + /* Check the 6GHz configuration success case */ + config = new WifiP2pConfig.Builder() + .setNetworkName(TEST_NETWORK_NAME) + .setPassphrase(TEST_PASSPHRASE) + .setPccModeConnectionType(WifiP2pConfig.PCC_MODE_CONNECTION_TYPE_R2_ONLY) + .setGroupOperatingBand(WifiP2pConfig.GROUP_OWNER_BAND_6GHZ) + .build(); + assertTrue(mWifiP2pNative.p2pGroupAdd(config, true)); + verify(mSupplicantP2pIfaceHalMock).groupAdd( + eq(TEST_NETWORK_NAME), + eq(TEST_PASSPHRASE), + eq(WifiP2pConfig.PCC_MODE_CONNECTION_TYPE_R2_ONLY), + eq(false), + eq(6), + eq(config.deviceAddress), + eq(true)); + + /* Check the 6GHz request fails in legacy mode */ + config = new WifiP2pConfig.Builder() + .setNetworkName(TEST_NETWORK_NAME) + .setPassphrase(TEST_PASSPHRASE) + .setPccModeConnectionType(WifiP2pConfig.PCC_MODE_CONNECTION_TYPE_LEGACY_ONLY) + .setGroupOperatingBand(WifiP2pConfig.GROUP_OWNER_BAND_6GHZ) + .build(); + assertFalse(mWifiP2pNative.p2pGroupAdd(config, true)); + + /* Check the 6GHz request fails in R1/R2 compatible mode */ + config = new WifiP2pConfig.Builder() + .setNetworkName(TEST_NETWORK_NAME) + .setPassphrase(TEST_PASSPHRASE) + .setPccModeConnectionType(WifiP2pConfig.PCC_MODE_CONNECTION_TYPE_LEGACY_OR_R2) + .setGroupOperatingBand(WifiP2pConfig.GROUP_OWNER_BAND_6GHZ) + .build(); + assertFalse(mWifiP2pNative.p2pGroupAdd(config, true)); } /** @@ -1083,4 +1200,28 @@ public class WifiP2pNativeTest extends WifiBaseTest { mWifiP2pNative.stopP2pSupplicantIfNecessary(); verify(mSupplicantP2pIfaceHalMock).terminate(); } + + /** + * Verifies that the supported features retrieved from wpa_supplicant is cached in the + * config store + */ + @Test + public void testSupportedFeatures() throws Exception { + when(mSupplicantP2pIfaceHalMock.initialize()).thenReturn(true); + when(mSupplicantP2pIfaceHalMock.isInitializationComplete()).thenReturn(true); + when(mSupplicantP2pIfaceHalMock.setupIface(eq(TEST_IFACE))).thenReturn(true); + when(mSupplicantP2pIfaceHalMock.registerDeathHandler(any())).thenReturn(true); + when(mSupplicantP2pIfaceHalMock.getSupportedFeatures()) + .thenReturn(WifiP2pManager.FEATURE_WIFI_DIRECT_R2 + | WifiP2pManager.FEATURE_PCC_MODE_ALLOW_LEGACY_AND_R2_CONNECTION); + assertEquals(TEST_IFACE, mWifiP2pNative.setupInterface( + mDestroyedListenerMock, mHandlerMock, mWorkSourceMock)); + assertEquals(WifiP2pManager.FEATURE_WIFI_DIRECT_R2 + | WifiP2pManager.FEATURE_PCC_MODE_ALLOW_LEGACY_AND_R2_CONNECTION + | WifiP2pManager.FEATURE_SET_VENDOR_ELEMENTS + | WifiP2pManager.FEATURE_FLEXIBLE_DISCOVERY + | WifiP2pManager.FEATURE_GROUP_CLIENT_REMOVAL + | WifiP2pManager.FEATURE_GROUP_OWNER_IPV6_LINK_LOCAL_ADDRESS_PROVIDED, + mWifiP2pNative.getSupportedFeatures()); + } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pServiceImplTest.java b/service/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pServiceImplTest.java index 2ec2e59152..b5509e0c2e 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pServiceImplTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/p2p/WifiP2pServiceImplTest.java @@ -29,7 +29,6 @@ import static com.android.server.wifi.WifiSettingsConfigStore.WIFI_P2P_DEVICE_AD import static com.android.server.wifi.WifiSettingsConfigStore.WIFI_P2P_DEVICE_NAME; import static com.android.server.wifi.WifiSettingsConfigStore.WIFI_P2P_PENDING_FACTORY_RESET; import static com.android.server.wifi.WifiSettingsConfigStore.WIFI_VERBOSE_LOGGING_ENABLED; -import static com.android.server.wifi.p2p.WifiP2pServiceImpl.IPC_DHCP_RESULTS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -79,7 +78,6 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.location.LocationManager; import android.net.ConnectivityManager; -import android.net.DhcpResultsParcelable; import android.net.InetAddresses; import android.net.LinkAddress; import android.net.MacAddress; @@ -100,6 +98,7 @@ import android.net.wifi.p2p.IWifiP2pListener; import android.net.wifi.p2p.WifiP2pConfig; import android.net.wifi.p2p.WifiP2pDevice; import android.net.wifi.p2p.WifiP2pDeviceList; +import android.net.wifi.p2p.WifiP2pDirInfo; import android.net.wifi.p2p.WifiP2pExtListenParams; import android.net.wifi.p2p.WifiP2pGroup; import android.net.wifi.p2p.WifiP2pGroupList; @@ -109,6 +108,7 @@ import android.net.wifi.p2p.WifiP2pProvDiscEvent; import android.net.wifi.p2p.WifiP2pWfdInfo; import android.net.wifi.p2p.nsd.WifiP2pServiceInfo; import android.net.wifi.p2p.nsd.WifiP2pServiceRequest; +import android.net.wifi.util.Environment; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -204,7 +204,6 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { private static final String thisDeviceName = "thisDeviceName"; private static final String ANONYMIZED_DEVICE_ADDRESS = "02:00:00:00:00:00"; private static final String TEST_PACKAGE_NAME = "com.p2p.test"; - private static final String TEST_PACKAGE2_NAME = "com.p2p.test2"; private static final String TEST_NETWORK_NAME = "DIRECT-xy-NEW"; private static final String TEST_ANDROID_ID = "314Deadbeef"; private static final String[] TEST_REQUIRED_PERMISSIONS_T = @@ -221,6 +220,9 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { private static final int P2P_PEER_AUTH_TIMEOUT_MS = 1000; private static final int P2P_EXT_LISTEN_PERIOD_MS = 250; private static final int P2P_EXT_LISTEN_INTERVAL_MS = 450; + private static final String TEST_DEVICE_MAC_ADDRESS_STRING = "00:11:22:33:44:55"; + private static final byte[] TEST_NONCE = {10, 20, 30, 40, 50, 60, 70, 80}; + private static final byte[] TEST_DIR_TAG = {11, 22, 33, 44, 55, 66, 77, 88}; private ArgumentCaptor<BroadcastReceiver> mBcastRxCaptor = ArgumentCaptor.forClass( BroadcastReceiver.class); @@ -233,10 +235,8 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { private BroadcastReceiver mTetherStateReceiver; private BroadcastReceiver mUserRestrictionReceiver; private Handler mClientHandler; - private Handler mClient2Handler; private Messenger mP2pStateMachineMessenger; private Messenger mClientMessenger; - private Messenger mClient2Messenger; private WifiP2pServiceImpl mWifiP2pServiceImpl; private TestLooper mClientHanderLooper; private TestLooper mLooper; @@ -257,7 +257,6 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { private TetheringManager.TetheringEventCallback mTetheringEventCallback; private Bundle mExtras = new Bundle(); private IWifiP2pListener mP2pListener = mock(IWifiP2pListener.class); - private IWifiP2pListener mP2pListener2 = mock(IWifiP2pListener.class); private ArgumentCaptor<WifiSettingsConfigStore.OnSettingsChangedListener> mD2DAllowedSettingsCallbackCaptor = ArgumentCaptor.forClass(WifiSettingsConfigStore.OnSettingsChangedListener.class); @@ -658,10 +657,9 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { /** * Send WifiP2pMonitor.P2P_GROUP_REMOVED_EVENT. */ - private void sendGroupRemovedMsg(WifiP2pGroup group) throws Exception { + private void sendGroupRemovedMsg() throws Exception { Message msg = Message.obtain(); msg.what = WifiP2pMonitor.P2P_GROUP_REMOVED_EVENT; - msg.obj = group; mP2pStateMachineMessenger.send(Message.obtain(msg)); mLooper.dispatchAll(); } @@ -1390,9 +1388,7 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { generatorTestData(); mClientHanderLooper = new TestLooper(); mClientHandler = spy(new Handler(mClientHanderLooper.getLooper())); - mClient2Handler = spy(new Handler(mClientHanderLooper.getLooper())); mClientMessenger = new Messenger(mClientHandler); - mClient2Messenger = new Messenger(mClient2Handler); mLooper = new TestLooper(); when(mContext.getSystemService(Context.ALARM_SERVICE)) @@ -1456,7 +1452,7 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { when(mWifiInjector.getWifiP2pConnection()).thenReturn(mWifiP2pConnection); when(mWifiDialogManager.createP2pInvitationReceivedDialog(any(), anyBoolean(), any(), - anyInt(), any(), any())).thenReturn(mDialogHandle); + anyInt(), anyInt(), any(), any())).thenReturn(mDialogHandle); when(mWifiDialogManager.createP2pInvitationSentDialog(any(), any(), anyInt())) .thenReturn(mDialogHandle); when(mWifiInjector.getClock()).thenReturn(mClock); @@ -1526,7 +1522,6 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { when(mDeviceConfigFacade.isP2pFailureBugreportEnabled()).thenReturn(false); when(mContext.getSystemService(TetheringManager.class)).thenReturn(mTetheringManager); when(mP2pListener.asBinder()).thenReturn(mock(IBinder.class)); - when(mP2pListener2.asBinder()).thenReturn(mock(IBinder.class)); mWifiP2pServiceImpl = new WifiP2pServiceImpl(mContext, mWifiInjector); if (supported) { @@ -1535,20 +1530,24 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { // * LocationManager.MODE_CHANGED_ACTION -- always // * TetheringManager.ACTION_TETHER_STATE_CHANGED -- < S // * UserManager.ACTION_USER_RESTRICTIONS_CHANGED -- >= T + verify(mContext).registerReceiverForAllUsers( + mBcastRxCaptor.capture(), + argThat(filter -> filter.hasAction(LocationManager.MODE_CHANGED_ACTION)), + eq(null), any(Handler.class)); if (SdkLevel.isAtLeastT()) { - verify(mContext, times(3)).registerReceiver(mBcastRxCaptor.capture(), + verify(mContext, times(2)).registerReceiver(mBcastRxCaptor.capture(), any(IntentFilter.class)); mUserRestrictionReceiver = mBcastRxCaptor.getAllValues().get(2); } else if (SdkLevel.isAtLeastS()) { - verify(mContext, times(2)).registerReceiver(mBcastRxCaptor.capture(), + verify(mContext).registerReceiver(mBcastRxCaptor.capture(), any(IntentFilter.class)); } else { - verify(mContext, times(3)).registerReceiver(mBcastRxCaptor.capture(), + verify(mContext, times(2)).registerReceiver(mBcastRxCaptor.capture(), any(IntentFilter.class)); mTetherStateReceiver = mBcastRxCaptor.getAllValues().get(2); } - mWifiStateChangedReceiver = mBcastRxCaptor.getAllValues().get(0); - mLocationModeReceiver = mBcastRxCaptor.getAllValues().get(1); + mWifiStateChangedReceiver = mBcastRxCaptor.getAllValues().get(1); + mLocationModeReceiver = mBcastRxCaptor.getAllValues().get(0); verify(mWifiSettingsConfigStore).registerChangeListener( eq(D2D_ALLOWED_WHEN_INFRA_STA_DISABLED), mD2DAllowedSettingsCallbackCaptor.capture(), any()); @@ -1568,9 +1567,11 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { mStaticMockSession = mockitoSession() .mockStatic(NetworkInterface.class) .mockStatic(Process.class) + .mockStatic(WifiInjector.class) .startMocking(); lenient().when(NetworkInterface.getByName(eq(IFACE_NAME_P2P))) .thenReturn(mP2pNetworkInterface); + when(WifiInjector.getInstance()).thenReturn(mWifiInjector); when(mLayoutInflater.cloneInContext(any())).thenReturn(mLayoutInflater); when(mLayoutInflater.inflate(anyInt(), any())).thenReturn(mView); when(mLayoutInflater.inflate(anyInt(), any(), anyBoolean())).thenReturn(mView); @@ -1589,7 +1590,6 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { mWifiP2pServiceImpl.handleBootCompleted(); if (SdkLevel.isAtLeastT()) { mWifiP2pServiceImpl.registerWifiP2pListener(mP2pListener, TEST_PACKAGE_NAME, mExtras); - mWifiP2pServiceImpl.registerWifiP2pListener(mP2pListener2, TEST_PACKAGE2_NAME, mExtras); } } @@ -2072,7 +2072,7 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { sendCreateGroupMsgWithConfigValidAsGroup(mClientMessenger); assertTrue(mClientHandler.hasMessages(WifiP2pManager.CREATE_GROUP_FAILED)); - verify(mWifiNative, never()).p2pGroupAdd(anyBoolean()); + verify(mWifiNative, never()).p2pGroupAdd(anyBoolean(), anyBoolean()); verify(mWifiNative, never()).p2pGroupAdd(any(), anyBoolean()); } @@ -2089,7 +2089,7 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { sendChannelInfoUpdateMsg("wrongpkg", "testFeature", mClient1, mClientMessenger); sendCreateGroupMsgWithConfigValidAsGroup(mClientMessenger); assertTrue(mClientHandler.hasMessages(WifiP2pManager.CREATE_GROUP_FAILED)); - verify(mWifiNative, never()).p2pGroupAdd(anyBoolean()); + verify(mWifiNative, never()).p2pGroupAdd(anyBoolean(), anyBoolean()); verify(mWifiNative, never()).p2pGroupAdd(any(), anyBoolean()); } @@ -2112,7 +2112,7 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { } sendCreateGroupMsgWithConfigValidAsGroup(mClientMessenger); assertTrue(mClientHandler.hasMessages(WifiP2pManager.CREATE_GROUP_FAILED)); - verify(mWifiNative, never()).p2pGroupAdd(anyBoolean()); + verify(mWifiNative, never()).p2pGroupAdd(anyBoolean(), anyBoolean()); verify(mWifiNative, never()).p2pGroupAdd(any(), anyBoolean()); if (SdkLevel.isAtLeastT()) { verify(mWifiPermissionsUtil, atLeastOnce()).checkNearbyDevicesPermission( @@ -2967,407 +2967,6 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { anyInt(), anyInt(), anyString(), eq(true)); } - /** - * Sets up the environment for P2P Ownership test where Client1 is the group owner. - */ - private void groupOwnershipConfigTestSetup() throws Exception { - assumeTrue(SdkLevel.isAtLeastV()); - when(mFeatureFlags.p2pOwnership()).thenReturn(true); - forceP2pEnabled(mClient1); - sendChannelInfoUpdateMsg(TEST_PACKAGE2_NAME, "testFeature", mClient2, mClient2Messenger); - - // group created by client1 - when(mWifiNative.p2pGroupAdd(any(), eq(false))).thenReturn(true); - sendCreateGroupMsgWithConfigValidAsGroup(mClientMessenger); - verify(mWifiNative).p2pGroupAdd(any(), eq(false)); - assertTrue(mClientHandler.hasMessages(WifiP2pManager.CREATE_GROUP_SUCCEEDED)); - assertFalse(mClient2Handler.hasMessages(WifiP2pManager.CREATE_GROUP_SUCCEEDED)); - - WifiP2pGroup group = new WifiP2pGroup(); - group.setNetworkName("DIRECT-test"); - group.setOwner(new WifiP2pDevice("thisDeviceMac")); - group.setIsGroupOwner(true); - group.setInterface(IFACE_NAME_P2P); - sendGroupStartedMsg(group); - simulateTetherReady(); - reset(mClientHandler); - } - - /** - * Sets up the environment for P2P Ownership test with WPS group. - */ - private void groupOwnershipWpsTestSetup() throws Exception { - assumeTrue(SdkLevel.isAtLeastV()); - mWifiP2pServiceImpl.registerWifiP2pListener(mP2pListener2, TEST_PACKAGE2_NAME, mExtras); - forceP2pEnabled(mClient1); - sendChannelInfoUpdateMsg(TEST_PACKAGE2_NAME, "testFeature", mClient2, mClient2Messenger); - - // WPS group created - WifiP2pGroup group = new WifiP2pGroup(); - group.setNetworkId(WifiP2pGroup.NETWORK_ID_PERSISTENT); - group.setNetworkName(TEST_NETWORK_NAME); - group.setOwner(new WifiP2pDevice("thisDeviceMac")); - group.setIsGroupOwner(true); - group.setInterface(IFACE_NAME_P2P); - sendGroupStartedMsg(group); - simulateTetherReady(); - } - - /** Verify that only the group owner can send an invitation connection */ - @Test - public void testGroupOwnershipConfigJoinInvite() throws Exception { - groupOwnershipConfigTestSetup(); - - // client2 cannot send an invitation to join - sendConnectMsg(mClient2Messenger, mTestWifiP2pPeerConfig); - verify(mClient2Handler).sendMessage(mMessageCaptor.capture()); - Message message = mMessageCaptor.getValue(); - assertEquals(WifiP2pManager.CONNECT_FAILED, message.what); - assertEquals(WifiP2pManager.BUSY, message.arg1); - - // client1 can send an invitation to join - when(mWifiNative.p2pInvite(any(), any())).thenReturn(true); - mockPeersList(); - sendConnectMsg(mClientMessenger, mTestWifiP2pPeerConfig); - verify(mClientHandler).sendMessage(mMessageCaptor.capture()); - message = mMessageCaptor.getValue(); - assertEquals(WifiP2pManager.CONNECT_SUCCEEDED, message.what); - } - - /** Verify that any client can send an invitation connection */ - @Test - public void testGroupOwnershipWpsJoinInvite() throws Exception { - groupOwnershipWpsTestSetup(); - - // client1 can send an invitation to join - when(mWifiNative.p2pInvite(any(), any())).thenReturn(true); - mockPeersList(); - sendConnectMsg(mClientMessenger, mTestWifiP2pPeerConfig); - verify(mClientHandler).sendMessage(mMessageCaptor.capture()); - Message message = mMessageCaptor.getValue(); - assertEquals(WifiP2pManager.CONNECT_SUCCEEDED, message.what); - - // client2 can send an invitation to join - sendConnectMsg(mClient2Messenger, mTestWifiP2pPeerConfig); - verify(mClient2Handler).sendMessage(mMessageCaptor.capture()); - message = mMessageCaptor.getValue(); - assertEquals(WifiP2pManager.CONNECT_SUCCEEDED, message.what); - } - - /** Verify that only the group owner can cancel an invitation connection */ - @Test - public void testGroupOwnershipConfigCancelInvitationConnect() throws Exception { - groupOwnershipConfigTestSetup(); - - // client2 cannot cancel invitation connection - sendSimpleMsg(mClient2Messenger, WifiP2pManager.CANCEL_CONNECT); - verify(mClient2Handler).sendMessage(mMessageCaptor.capture()); - Message message = mMessageCaptor.getValue(); - assertEquals(WifiP2pManager.CANCEL_CONNECT_FAILED, message.what); - assertEquals(WifiP2pManager.BUSY, message.arg1); - - // client1 can cancel invitation connection - sendSimpleMsg(mClientMessenger, WifiP2pManager.CANCEL_CONNECT); - verify(mClientHandler).sendMessage(mMessageCaptor.capture()); - message = mMessageCaptor.getValue(); - assertEquals(WifiP2pManager.CANCEL_CONNECT_SUCCEEDED, message.what); - } - - /** Verify that any client can cancel an invitation connection */ - @Test - public void testGroupOwnershipWpsCancelInvitationConnect() throws Exception { - groupOwnershipWpsTestSetup(); - - // client1 can cancel invitation connection - sendSimpleMsg(mClientMessenger, WifiP2pManager.CANCEL_CONNECT); - verify(mClientHandler).sendMessage(mMessageCaptor.capture()); - Message message = mMessageCaptor.getValue(); - assertEquals(WifiP2pManager.CANCEL_CONNECT_SUCCEEDED, message.what); - - // client2 can cancel invitation connection - sendSimpleMsg(mClient2Messenger, WifiP2pManager.CANCEL_CONNECT); - verify(mClient2Handler).sendMessage(mMessageCaptor.capture()); - message = mMessageCaptor.getValue(); - assertEquals(WifiP2pManager.CANCEL_CONNECT_SUCCEEDED, message.what); - } - - /** Verify that only the client that initiated the connection can cancel it */ - @Test - public void testGroupOwnershipConfigCancelConnect() throws Exception { - assumeTrue(SdkLevel.isAtLeastV()); - when(mFeatureFlags.p2pOwnership()).thenReturn(true); - forceP2pEnabled(mClient1); - sendChannelInfoUpdateMsg(TEST_PACKAGE2_NAME, "testFeature", mClient2, mClient2Messenger); - - // group created by client1 - when(mWifiNative.p2pGroupAdd(any(), eq(false))).thenReturn(true); - sendCreateGroupMsgWithConfigValidAsGroup(mClientMessenger); - verify(mWifiNative).p2pGroupAdd(any(), eq(false)); - reset(mClientHandler); - - // client2 cannot cancel connection - sendSimpleMsg(mClient2Messenger, WifiP2pManager.CANCEL_CONNECT); - verify(mClient2Handler).sendMessage(mMessageCaptor.capture()); - Message message = mMessageCaptor.getValue(); - assertEquals(WifiP2pManager.CANCEL_CONNECT_FAILED, message.what); - assertEquals(WifiP2pManager.BUSY, message.arg1); - - // client1 can cancel connection - sendSimpleMsg(mClientMessenger, WifiP2pManager.CANCEL_CONNECT); - verify(mClientHandler).sendMessage(mMessageCaptor.capture()); - message = mMessageCaptor.getValue(); - assertEquals(WifiP2pManager.CANCEL_CONNECT_SUCCEEDED, message.what); - } - - /** Verify that any client can cancel an ongoing WPS connection */ - @Test - public void testGroupOwnershipWpsCancelConnect() throws Exception { - assumeTrue(SdkLevel.isAtLeastV()); - when(mFeatureFlags.p2pOwnership()).thenReturn(true); - forceP2pEnabled(mClient1); - sendChannelInfoUpdateMsg(TEST_PACKAGE2_NAME, "testFeature", mClient2, mClient2Messenger); - - // connection initiated by client1 - when(mWifiNative.p2pGroupAdd(anyBoolean())).thenReturn(true); - mockEnterGroupNegotiationState(); - - // client2 can cancel connection - sendSimpleMsg(mClient2Messenger, WifiP2pManager.CANCEL_CONNECT); - verify(mClient2Handler).sendMessage(mMessageCaptor.capture()); - Message message = mMessageCaptor.getValue(); - assertEquals(WifiP2pManager.CANCEL_CONNECT_SUCCEEDED, message.what); - } - - /** Verify that only the group owner can remove the group */ - @Test - public void testGroupOwnershipConfigRemoveGroup() throws Exception { - groupOwnershipConfigTestSetup(); - - // client2 cannot remove group - sendSimpleMsg(mClient2Messenger, WifiP2pManager.REMOVE_GROUP); - verify(mClient2Handler).sendMessage(mMessageCaptor.capture()); - Message message = mMessageCaptor.getValue(); - assertEquals(WifiP2pManager.REMOVE_GROUP_FAILED, message.what); - assertEquals(WifiP2pManager.BUSY, message.arg1); - - // client1 can remove group - when(mWifiNative.p2pGroupRemove(eq(IFACE_NAME_P2P))).thenReturn(true); - sendSimpleMsg(mClientMessenger, WifiP2pManager.REMOVE_GROUP); - verify(mWifiNative).p2pGroupRemove(eq(IFACE_NAME_P2P)); - verify(mClientHandler).sendMessage(mMessageCaptor.capture()); - message = mMessageCaptor.getValue(); - assertEquals(WifiP2pManager.REMOVE_GROUP_SUCCEEDED, message.what); - } - - /** Verify that any client can remove the group */ - @Test - public void testGroupOwnershipWpsRemoveGroup() throws Exception { - groupOwnershipWpsTestSetup(); - - // client2 can remove group - when(mWifiNative.p2pGroupRemove(eq(IFACE_NAME_P2P))).thenReturn(true); - sendSimpleMsg(mClient2Messenger, WifiP2pManager.REMOVE_GROUP); - verify(mWifiNative).p2pGroupRemove(eq(IFACE_NAME_P2P)); - verify(mClient2Handler).sendMessage(mMessageCaptor.capture()); - Message message = mMessageCaptor.getValue(); - assertEquals(WifiP2pManager.REMOVE_GROUP_SUCCEEDED, message.what); - } - - /** Verify that only the group owner can get group info */ - @Test - public void testGroupOwnershipConfigGetGroupInformation() throws Exception { - groupOwnershipConfigTestSetup(); - - // client2 cannot get P2P group info - sendRequestGroupInfoMsg(mClient2Messenger); - verify(mClient2Handler).sendMessage(mMessageCaptor.capture()); - assertEquals(WifiP2pManager.RESPONSE_GROUP_INFO, mMessageCaptor.getValue().what); - WifiP2pGroup wifiP2pGroup = (WifiP2pGroup) mMessageCaptor.getValue().obj; - assertNull(wifiP2pGroup); - - // client1 can get P2P group info - sendRequestGroupInfoMsg(mClientMessenger); - verify(mClientHandler).sendMessage(mMessageCaptor.capture()); - assertEquals(WifiP2pManager.RESPONSE_GROUP_INFO, mMessageCaptor.getValue().what); - wifiP2pGroup = (WifiP2pGroup) mMessageCaptor.getValue().obj; - assertNotNull(wifiP2pGroup); - } - - /** Verify that any client can get group info */ - @Test - public void testGroupOwnershipWpsGetGroupInformation() throws Exception { - groupOwnershipWpsTestSetup(); - - // client1 can get P2P group info - sendRequestGroupInfoMsg(mClientMessenger); - verify(mClientHandler).sendMessage(mMessageCaptor.capture()); - assertEquals(WifiP2pManager.RESPONSE_GROUP_INFO, mMessageCaptor.getValue().what); - WifiP2pGroup wifiP2pGroup = (WifiP2pGroup) mMessageCaptor.getValue().obj; - assertNotNull(wifiP2pGroup); - - // client2 can get P2P group info - sendRequestGroupInfoMsg(mClient2Messenger); - verify(mClient2Handler).sendMessage(mMessageCaptor.capture()); - assertEquals(WifiP2pManager.RESPONSE_GROUP_INFO, mMessageCaptor.getValue().what); - wifiP2pGroup = (WifiP2pGroup) mMessageCaptor.getValue().obj; - assertNotNull(wifiP2pGroup); - } - - /** Verify that only the group owner can get connection info */ - @Test - public void testGroupOwnershipConfigGetConnectionInfo() throws Exception { - groupOwnershipConfigTestSetup(); - - // client2 cannot get P2P connection info - sendSimpleMsg(mClient2Messenger, WifiP2pManager.REQUEST_CONNECTION_INFO); - verify(mClient2Handler).sendMessage(mMessageCaptor.capture()); - Message message = mMessageCaptor.getValue(); - WifiP2pInfo info = (WifiP2pInfo) message.obj; - assertEquals(WifiP2pManager.RESPONSE_CONNECTION_INFO, message.what); - assertEquals((new WifiP2pInfo()).toString(), info.toString()); - - // client1 can get P2P connection info - sendSimpleMsg(mClientMessenger, WifiP2pManager.REQUEST_CONNECTION_INFO); - verify(mClientHandler).sendMessage(mMessageCaptor.capture()); - message = mMessageCaptor.getValue(); - info = (WifiP2pInfo) message.obj; - assertEquals(WifiP2pManager.RESPONSE_CONNECTION_INFO, mMessageCaptor.getValue().what); - assertTrue(info.groupFormed); - } - - /** Verify that any client can get connection info */ - @Test - public void testGroupOwnershipWpsGetConnectionInfo() throws Exception { - groupOwnershipWpsTestSetup(); - - // client1 can get P2P connection info - sendSimpleMsg(mClientMessenger, WifiP2pManager.REQUEST_CONNECTION_INFO); - verify(mClientHandler).sendMessage(mMessageCaptor.capture()); - Message message = mMessageCaptor.getValue(); - WifiP2pInfo info = (WifiP2pInfo) message.obj; - assertEquals(WifiP2pManager.RESPONSE_CONNECTION_INFO, mMessageCaptor.getValue().what); - assertTrue(info.groupFormed); - - // client2 can get P2P connection info - sendSimpleMsg(mClient2Messenger, WifiP2pManager.REQUEST_CONNECTION_INFO); - verify(mClient2Handler).sendMessage(mMessageCaptor.capture()); - message = mMessageCaptor.getValue(); - info = (WifiP2pInfo) message.obj; - assertEquals(WifiP2pManager.RESPONSE_CONNECTION_INFO, mMessageCaptor.getValue().what); - assertTrue(info.groupFormed); - } - - /** Verify that only the group owner receives the connection changed broadcast */ - @Test - public void testGroupOwnershipConfigBroadcast() throws Exception { - groupOwnershipConfigTestSetup(); - - ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); - verify(mContext, atLeastOnce()).sendBroadcast(intentCaptor.capture(), any(), any()); - - ArrayList<Intent> intentArrayList = new ArrayList<>(); - for (int i = 0; i < intentCaptor.getAllValues().size(); i++) { - Intent intent = intentCaptor.getAllValues().get(i); - if (intent.getAction().equals(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)) { - intentArrayList.add(intent); - } - } - // Connection changed broadcast is sent 3 times: - // 1. Entering P2pEnabledState - // 2. Entering GroupCreatingState - // 3. Entering GroupCreatedState - // Each time, sendBroadcast is called twice (refer sendBroadcastWithExcludedPermissions) - // Verify group created broadcast only sent to client1 - Intent intent = intentArrayList.get(4); - assertEquals(TEST_PACKAGE_NAME, intent.getPackage()); - // Verify broadcast sent to NEARBY_WIFI_DEVICES apps do not have the package name set - intent = intentArrayList.get(5); - assertNull(intent.getPackage()); - } - - /** Verify that all clients receive the connection changed broadcast */ - @Test - public void testGroupOwnershipWpsBroadcast() throws Exception { - groupOwnershipWpsTestSetup(); - - WifiP2pDevice connectedClientDevice = new WifiP2pDevice(mTestWifiP2pDevice); - connectedClientDevice.setInterfaceMacAddress(MacAddress.fromString(PEER_INTERFACE_ADDRESS)); - sendApStaConnectedEvent(connectedClientDevice); - ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); - verify(mContext, atLeastOnce()).sendBroadcast(intentCaptor.capture(), any(), any()); - - ArrayList<Intent> intentArrayList = new ArrayList<>(); - for (int i = 0; i < intentCaptor.getAllValues().size(); i++) { - Intent intent = intentCaptor.getAllValues().get(i); - if (intent.getAction().equals(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)) { - intentArrayList.add(intent); - } - } - // Connection changed broadcast is sent 3 times: - // 1. Entering P2pEnabledState - // 2. Entering GroupCreatingState - // 3. Peer connection in GroupCreatedState - // Each time, sendBroadcast is called twice (refer sendBroadcastWithExcludedPermissions) - // Verify group created broadcast sent to all clients - Intent intent = intentArrayList.get(4); - assertNull(intent.getPackage()); - } - - /** Verify that only the group owner receives the group related callback */ - @Test - public void testGroupOwnershipConfigP2pListener() throws Exception { - groupOwnershipConfigTestSetup(); - - verify(mP2pListener).onGroupCreated(any(), any()); - verify(mP2pListener2, never()).onGroupCreated(any(), any()); - - WifiP2pDevice peerClientDevice = new WifiP2pDevice(); - peerClientDevice.deviceName = "peerClientDeviceName"; - peerClientDevice.deviceAddress = "11:22:33:aa:bb:cc"; - peerClientDevice.setInterfaceMacAddress(MacAddress.fromString(PEER_INTERFACE_ADDRESS)); - sendSimpleMsg(null, WifiP2pMonitor.AP_STA_CONNECTED_EVENT, peerClientDevice); - verify(mP2pListener).onPeerClientJoined(any(), any()); - verify(mP2pListener2, never()).onPeerClientJoined(any(), any()); - - sendSimpleMsg(null, WifiP2pMonitor.AP_STA_DISCONNECTED_EVENT, peerClientDevice); - verify(mP2pListener).onPeerClientDisconnected(any(), any()); - verify(mP2pListener2, never()).onPeerClientDisconnected(any(), any()); - - sendSimpleMsg(null, WifiP2pMonitor.P2P_FREQUENCY_CHANGED_EVENT, TEST_GROUP_FREQUENCY); - verify(mP2pListener).onFrequencyChanged(any(), any()); - verify(mP2pListener2, never()).onFrequencyChanged(any(), any()); - } - - /** Verify that all clients receive the group related callback */ - @Test - public void testGroupOwnershipWpsP2pListener() throws Exception { - groupOwnershipWpsTestSetup(); - - DhcpResultsParcelable dhcpResults = new DhcpResultsParcelable(); - dhcpResults.serverAddress = P2P_GO_IP; - sendSimpleMsg(mClientMessenger, IPC_DHCP_RESULTS, dhcpResults); - verify(mP2pListener).onGroupCreated(any(), any()); - verify(mP2pListener2).onGroupCreated(any(), any()); - - WifiP2pDevice connectedClientDevice = new WifiP2pDevice(mTestWifiP2pDevice); - connectedClientDevice.setInterfaceMacAddress(MacAddress.fromString(PEER_INTERFACE_ADDRESS)); - sendApStaConnectedEvent(connectedClientDevice); - verify(mP2pListener).onPeerClientJoined(any(), any()); - verify(mP2pListener2).onPeerClientJoined(any(), any()); - - // Need to connect a second peer device before testing disconnect to avoid removing group - WifiP2pDevice secondPeerDevice = new WifiP2pDevice(connectedClientDevice); - secondPeerDevice.deviceAddress = "11:22:33:aa:bb:cc"; - sendApStaConnectedEvent(secondPeerDevice); - sendSimpleMsg(null, WifiP2pMonitor.AP_STA_DISCONNECTED_EVENT, connectedClientDevice); - verify(mP2pListener).onPeerClientDisconnected(any(), any()); - verify(mP2pListener2).onPeerClientDisconnected(any(), any()); - - sendSimpleMsg(null, WifiP2pMonitor.P2P_FREQUENCY_CHANGED_EVENT, TEST_GROUP_FREQUENCY); - verify(mP2pListener).onFrequencyChanged(any(), any()); - verify(mP2pListener2).onFrequencyChanged(any(), any()); - } - /** Verify the p2p randomized MAC feature is enabled if OEM supports it. */ @Test public void testP2pRandomMacWithOemSupport() throws Exception { @@ -3580,7 +3179,7 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { WifiP2pGroup groupCaptured = groupCaptor.getValue(); assertEquals(mTestWifiP2pNewPersistentGoGroup.toString(), groupCaptured.toString()); - sendGroupRemovedMsg(groupCaptured); + sendGroupRemovedMsg(); verify(mWifiP2pMetrics).endGroupEvent(); } @@ -3624,7 +3223,7 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { public void testStartReinvokeConnectionEventWhenSendConnect() throws Exception { setTargetSdkGreaterThanT(); forceP2pEnabled(mClient1); - when(mWifiNative.p2pGroupAdd(anyInt())) + when(mWifiNative.p2pGroupAdd(anyInt(), anyBoolean())) .thenReturn(true); when(mTestWifiP2pDevice.isGroupOwner()).thenReturn(true); when(mWifiNative.p2pGetSsid(eq(mTestWifiP2pDevice.deviceAddress))) @@ -4017,7 +3616,7 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { public void testEndConnectionEventWhenTimeout() throws Exception { setTargetSdkGreaterThanT(); forceP2pEnabled(mClient1); - when(mWifiNative.p2pGroupAdd(anyBoolean())).thenReturn(true); + when(mWifiNative.p2pGroupAdd(anyBoolean(), anyBoolean())).thenReturn(true); mockEnterGroupNegotiationState(); if (SdkLevel.isAtLeastT()) { @@ -4043,7 +3642,7 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { @Test public void testAcceptFrequencyConflictDialogSendsDisconnectWifiRequest() throws Exception { forceP2pEnabled(mClient1); - when(mWifiNative.p2pGroupAdd(anyBoolean())).thenReturn(true); + when(mWifiNative.p2pGroupAdd(anyBoolean(), anyBoolean())).thenReturn(true); AsyncChannel wifiChannel = mAsyncChannel; sendChannelHalfConnectedEvent(mClientMessenger, wifiChannel); WifiDialogManager.DialogHandle dialogHandle = mock(WifiDialogManager.DialogHandle.class); @@ -4112,7 +3711,7 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { @Test public void testDeclineFrequencyConflictDialogEndsP2pConnectionEvent() throws Exception { forceP2pEnabled(mClient1); - when(mWifiNative.p2pGroupAdd(anyBoolean())).thenReturn(true); + when(mWifiNative.p2pGroupAdd(anyBoolean(), anyBoolean())).thenReturn(true); AsyncChannel wifiChannel = mock(AsyncChannel.class); sendChannelHalfConnectedEvent(mClientMessenger, wifiChannel); WifiDialogManager.DialogHandle dialogHandle = mock(WifiDialogManager.DialogHandle.class); @@ -4151,7 +3750,7 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { @Test public void testFrequencyConflictDialogDismissedOnStateExit() throws Exception { forceP2pEnabled(mClient1); - when(mWifiNative.p2pGroupAdd(anyBoolean())).thenReturn(true); + when(mWifiNative.p2pGroupAdd(anyBoolean(), anyBoolean())).thenReturn(true); AsyncChannel wifiChannel = mock(AsyncChannel.class); sendChannelHalfConnectedEvent(mClientMessenger, wifiChannel); WifiDialogManager.DialogHandle dialogHandle = mock(WifiDialogManager.DialogHandle.class); @@ -4192,7 +3791,7 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { public void testEndConnectionEventWhenCancel() throws Exception { setTargetSdkGreaterThanT(); forceP2pEnabled(mClient1); - when(mWifiNative.p2pGroupAdd(anyBoolean())).thenReturn(true); + when(mWifiNative.p2pGroupAdd(anyBoolean(), anyBoolean())).thenReturn(true); mockEnterGroupNegotiationState(); if (SdkLevel.isAtLeastT()) { @@ -4218,7 +3817,7 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { public void testEndConnectionEventWhenProvDiscFailure() throws Exception { setTargetSdkGreaterThanT(); forceP2pEnabled(mClient1); - when(mWifiNative.p2pGroupAdd(anyBoolean())).thenReturn(true); + when(mWifiNative.p2pGroupAdd(anyBoolean(), anyBoolean())).thenReturn(true); mockEnterProvisionDiscoveryState(); if (SdkLevel.isAtLeastT()) { @@ -4247,7 +3846,7 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { public void testEndConnectionEventWhenGroupRemoval() throws Exception { setTargetSdkGreaterThanT(); forceP2pEnabled(mClient1); - when(mWifiNative.p2pGroupAdd(anyBoolean())).thenReturn(true); + when(mWifiNative.p2pGroupAdd(anyBoolean(), anyBoolean())).thenReturn(true); mockEnterGroupNegotiationState(); if (SdkLevel.isAtLeastT()) { @@ -4312,7 +3911,7 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { public void testEndConnectionEventWhenInvitationFailure() throws Exception { setTargetSdkGreaterThanT(); forceP2pEnabled(mClient1); - when(mWifiNative.p2pGroupAdd(anyBoolean())).thenReturn(true); + when(mWifiNative.p2pGroupAdd(anyBoolean(), anyBoolean())).thenReturn(true); mockEnterGroupNegotiationState(); if (SdkLevel.isAtLeastT()) { @@ -4338,7 +3937,7 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { public void testEndConnectionEventWhenPeerRejectTheConnectRequestInNegotiationFlow() throws Exception { forceP2pEnabled(mClient1); - when(mWifiNative.p2pGroupAdd(anyBoolean())).thenReturn(true); + when(mWifiNative.p2pGroupAdd(anyBoolean(), anyBoolean())).thenReturn(true); mockEnterGroupNegotiationState(); WifiP2pProvDiscEvent pdEvent = new WifiP2pProvDiscEvent(); @@ -6736,7 +6335,7 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { WifiP2pProvDiscEvent pdEvent = new WifiP2pProvDiscEvent(); pdEvent.device = mTestWifiP2pDevice; - pdEvent.pin = "pin"; + pdEvent.wpsPin = "pin"; sendSimpleMsg(null, WifiP2pMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT, pdEvent); @@ -6746,7 +6345,7 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { verify(mAlertDialog).show(); } else { verify(mWifiDialogManager).createP2pInvitationSentDialog( - pdEvent.device.deviceName, pdEvent.pin, Display.DEFAULT_DISPLAY); + pdEvent.device.deviceName, pdEvent.wpsPin, Display.DEFAULT_DISPLAY); verify(mDialogHandle).launchDialog(); } } @@ -6766,7 +6365,7 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { sendDeviceFoundEventMsg(mTestWifiP2pDevice); WifiP2pProvDiscEvent pdEvent = new WifiP2pProvDiscEvent(); pdEvent.device = mTestWifiP2pDevice; - pdEvent.pin = "pin"; + pdEvent.wpsPin = "pin"; sendSimpleMsg(null, WifiP2pMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT, pdEvent); @@ -6775,8 +6374,8 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { verify(mAlertDialog).show(); } else { verify(mWifiDialogManager).createP2pInvitationReceivedDialog( - eq(pdEvent.device.deviceName), eq(false), eq(pdEvent.pin), - anyInt(), any(), any()); + eq(pdEvent.device.deviceName), eq(false), eq(pdEvent.wpsPin), + anyInt(), anyInt(), any(), any()); verify(mDialogHandle).launchDialog(); } } @@ -6918,7 +6517,7 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { */ @Test public void testGroupCreatingFailureDueToTethering() throws Exception { - when(mWifiNative.p2pGroupAdd(anyBoolean())).thenReturn(true); + when(mWifiNative.p2pGroupAdd(anyBoolean(), anyBoolean())).thenReturn(true); when(mWifiNative.p2pGroupRemove(eq(IFACE_NAME_P2P))).thenReturn(true); when(mWifiPermissionsUtil.checkCanAccessWifiDirect(eq(TEST_PACKAGE_NAME), eq("testFeature"), anyInt(), anyBoolean())).thenReturn(true); @@ -7300,8 +6899,9 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { verify(mAlertDialog).show(); } else { verify(mWifiDialogManager).createP2pInvitationReceivedDialog(anyString(), anyBoolean(), - any(), eq(Display.DEFAULT_DISPLAY), any(), any()); - verify(mDialogHandle).launchDialog(P2P_INVITATION_RECEIVED_TIMEOUT_MS); + any(), eq(P2P_INVITATION_RECEIVED_TIMEOUT_MS), eq(Display.DEFAULT_DISPLAY), + any(), any()); + verify(mDialogHandle).launchDialog(); } } @@ -7328,8 +6928,9 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { sendNegotiationRequestEvent(config); verify(mWifiDialogManager).createP2pInvitationReceivedDialog(anyString(), - anyBoolean(), any(), eq(someNonDefaultDisplayId), any(), any()); - verify(mDialogHandle).launchDialog(P2P_INVITATION_RECEIVED_TIMEOUT_MS); + anyBoolean(), any(), eq(P2P_INVITATION_RECEIVED_TIMEOUT_MS), + eq(someNonDefaultDisplayId), any(), any()); + verify(mDialogHandle).launchDialog(); } /** @@ -7356,8 +6957,9 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { // "simple" client connect (no display ID) sendNegotiationRequestEvent(config); verify(mWifiDialogManager).createP2pInvitationReceivedDialog(anyString(), anyBoolean(), - any(), eq(Display.DEFAULT_DISPLAY), any(), any()); - verify(mDialogHandle).launchDialog(P2P_INVITATION_RECEIVED_TIMEOUT_MS); + any(), eq(P2P_INVITATION_RECEIVED_TIMEOUT_MS), eq(Display.DEFAULT_DISPLAY), any(), + any()); + verify(mDialogHandle).launchDialog(); } private void verifySetVendorElement(boolean isP2pActivated, boolean shouldSucceed, @@ -7622,7 +7224,7 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { WifiP2pProvDiscEvent pdEvent = new WifiP2pProvDiscEvent(); pdEvent.device = mTestWifiP2pDevice; - pdEvent.pin = "pin"; + pdEvent.wpsPin = "pin"; sendSimpleMsg(null, WifiP2pMonitor.P2P_PROV_DISC_SHOW_PIN_EVENT, pdEvent); @@ -7903,7 +7505,7 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { @Test public void testProvDiscRejectEventForProvDisc() throws Exception { forceP2pEnabled(mClient1); - when(mWifiNative.p2pGroupAdd(anyBoolean())).thenReturn(true); + when(mWifiNative.p2pGroupAdd(anyBoolean(), anyBoolean())).thenReturn(true); mockEnterProvisionDiscoveryState(); @@ -7926,7 +7528,7 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { @Test public void testSendP2pRejectWhenCancelRequest() throws Exception { forceP2pEnabled(mClient1); - when(mWifiNative.p2pGroupAdd(anyBoolean())).thenReturn(true); + when(mWifiNative.p2pGroupAdd(anyBoolean(), anyBoolean())).thenReturn(true); mockEnterProvisionDiscoveryState(); @@ -7941,7 +7543,7 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { */ @Test public void testSendP2pRejectOnRejectRequest() throws Exception { - when(mWifiNative.p2pGroupAdd(anyBoolean())).thenReturn(true); + when(mWifiNative.p2pGroupAdd(anyBoolean(), anyBoolean())).thenReturn(true); forceP2pEnabled(mClient1); mockEnterUserAuthorizingNegotiationRequestState(WpsInfo.PBC); @@ -7960,7 +7562,7 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { */ @Test public void testBroadcastDisconnectedStateOnRejectRequest() throws Exception { - when(mWifiNative.p2pGroupAdd(anyBoolean())).thenReturn(true); + when(mWifiNative.p2pGroupAdd(anyBoolean(), anyBoolean())).thenReturn(true); forceP2pEnabled(mClient1); mockEnterUserAuthorizingNegotiationRequestState(WpsInfo.PBC); @@ -7974,7 +7576,7 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { @Test public void testDismissDialogOnReceiveProvDiscFailureEvent() throws Exception { assumeTrue(SdkLevel.isAtLeastT()); - when(mWifiNative.p2pGroupAdd(anyBoolean())).thenReturn(true); + when(mWifiNative.p2pGroupAdd(anyBoolean(), anyBoolean())).thenReturn(true); forceP2pEnabled(mClient1); mockEnterUserAuthorizingNegotiationRequestState(WpsInfo.PBC); @@ -7990,7 +7592,7 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { @Test public void testDismissDialogOnReceiveProvDiscFailureEventPreT() throws Exception { assumeFalse(SdkLevel.isAtLeastT()); - when(mWifiNative.p2pGroupAdd(anyBoolean())).thenReturn(true); + when(mWifiNative.p2pGroupAdd(anyBoolean(), anyBoolean())).thenReturn(true); forceP2pEnabled(mClient1); mockEnterUserAuthorizingNegotiationRequestState(WpsInfo.PBC); @@ -8132,7 +7734,7 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { mockEnterGroupCreatedState(); - // The first provision discvoery request triggers the dialog. + // The first provision discovery request triggers the dialog. WifiP2pProvDiscEvent pdEvent = new WifiP2pProvDiscEvent(); pdEvent.device = mTestWifiP2pDevice; sendSimpleMsg(null, WifiP2pMonitor.P2P_PROV_DISC_PBC_REQ_EVENT, @@ -8140,8 +7742,8 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { verify(mWifiDialogManager).createP2pInvitationReceivedDialog( eq(mTestWifiP2pDevice.deviceAddress), anyBoolean(), - any(), anyInt(), any(), any()); - verify(mDialogHandle).launchDialog(P2P_INVITATION_RECEIVED_TIMEOUT_MS); + any(), eq(P2P_INVITATION_RECEIVED_TIMEOUT_MS), anyInt(), any(), any()); + verify(mDialogHandle).launchDialog(); // Handle it programmatically. sendSimpleMsg(null, WifiP2pServiceImpl.PEER_CONNECTION_USER_REJECT); @@ -8154,10 +7756,10 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { reset(mWifiDialogManager); reset(mDialogHandle); verify(mWifiDialogManager, never()).createP2pInvitationReceivedDialog( - any(), anyBoolean(), any(), anyInt(), any(), any()); + any(), anyBoolean(), any(), anyInt(), anyInt(), any(), any()); when(mWifiDialogManager.createP2pInvitationReceivedDialog(any(), anyBoolean(), any(), - anyInt(), any(), any())).thenReturn(mDialogHandle); + anyInt(), anyInt(), any(), any())).thenReturn(mDialogHandle); when(mWifiDialogManager.createP2pInvitationSentDialog(any(), any(), anyInt())) .thenReturn(mDialogHandle); when(mClock.getElapsedSinceBootMillis()).thenReturn(P2P_PEER_AUTH_TIMEOUT_MS + 1L); @@ -8169,8 +7771,8 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { // Another dialog should be triggered. verify(mWifiDialogManager).createP2pInvitationReceivedDialog( eq(mTestWifiP2pDevice.deviceAddress), anyBoolean(), - any(), anyInt(), any(), any()); - verify(mDialogHandle).launchDialog(P2P_INVITATION_RECEIVED_TIMEOUT_MS); + any(), eq(P2P_INVITATION_RECEIVED_TIMEOUT_MS), anyInt(), any(), any()); + verify(mDialogHandle).launchDialog(); } @Test @@ -8224,7 +7826,7 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { verify(mWifiP2pMetrics).startGroupEvent(group); verify(mWifiNative).p2pStopFind(); verify(mWifiNative).p2pExtListen(eq(false), anyInt(), anyInt(), eq(null)); - sendGroupRemovedMsg(group); + sendGroupRemovedMsg(); //force to back disabled state mockEnterDisabledState(); @@ -8239,7 +7841,7 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { lenient().when(Process.myUid()).thenReturn(Process.SYSTEM_UID); when(mWifiNative.p2pFind(anyInt())).thenReturn(true); forceP2pEnabled(mClient1); - when(mWifiNative.p2pGroupAdd(anyBoolean())).thenReturn(true); + when(mWifiNative.p2pGroupAdd(anyBoolean(), anyBoolean())).thenReturn(true); mockEnterProvisionDiscoveryState(); @@ -8284,7 +7886,7 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { when(mWifiNative.p2pGetSsid(eq(mTestWifiP2pDevice.deviceAddress))) .thenReturn(mTestWifiP2pGroup.getNetworkName() + "unknown"); sendSimpleMsg(null, WifiP2pServiceImpl.PEER_CONNECTION_USER_ACCEPT); - verify(mWifiNative, never()).p2pGroupAdd(anyInt()); + verify(mWifiNative, never()).p2pGroupAdd(anyInt(), anyBoolean()); verify(mWifiNative).p2pConnect(any(), anyBoolean()); } @@ -8295,7 +7897,7 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { when(mWifiNative.p2pGetSsid(eq(mTestWifiP2pDevice.deviceAddress))) .thenReturn(mTestWifiP2pGroup.getNetworkName()); sendSimpleMsg(null, WifiP2pServiceImpl.PEER_CONNECTION_USER_ACCEPT); - verify(mWifiNative).p2pGroupAdd(anyInt()); + verify(mWifiNative).p2pGroupAdd(anyInt(), anyBoolean()); } @Test @@ -8564,7 +8166,7 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { p2pGroupCaptor.capture()); assertEquals(TEST_GROUP_FREQUENCY, p2pGroupCaptor.getValue().getFrequency()); - sendGroupRemovedMsg(p2pGroup); + sendGroupRemovedMsg(); mockEnterDisabledState(); mLooper.dispatchAll(); verify(mP2pListener).onGroupRemoved(); @@ -8627,4 +8229,68 @@ public class WifiP2pServiceImplTest extends WifiBaseTest { verify(mWifiNative).teardownInterface(); verify(mWifiMonitor).stopMonitoring(anyString()); } + + /** + * Verify {@link WifiP2pManager#GET_DIR_INFO} message. + */ + @Test + public void testGetDirInfo() throws Exception { + assumeTrue(Environment.isSdkAtLeastB()); + when(mWifiNative.getSupportedFeatures()).thenReturn( + WifiP2pManager.FEATURE_WIFI_DIRECT_R2); + when(mFeatureFlags.wifiDirectR2()).thenReturn(true); + forceP2pEnabled(mClient1); + when(mWifiPermissionsUtil.checkNearbyDevicesPermission(any(), anyBoolean(), any())) + .thenReturn(false); + sendSimpleMsg(mClientMessenger, WifiP2pManager.GET_DIR_INFO); + assertTrue(mClientHandler.hasMessages(WifiP2pManager.GET_DIR_INFO_FAILED)); + + when(mWifiPermissionsUtil.checkNearbyDevicesPermission(any(), anyBoolean(), any())) + .thenReturn(true); + sendSimpleMsg(mClientMessenger, WifiP2pManager.GET_DIR_INFO); + + verify(mClientHandler, times(2)).sendMessage(mMessageCaptor.capture()); + List<Message> messages = mMessageCaptor.getAllValues(); + assertEquals(WifiP2pManager.GET_DIR_INFO_FAILED, messages.get(0).what); + assertEquals(WifiP2pManager.RESPONSE_GET_DIR_INFO, messages.get(1).what); + } + + /** + * Verify {@link WifiP2pManager#VALIDATE_DIR_INFO} message. + */ + @Test + public void testValidateDirInfo() throws Exception { + assumeTrue(Environment.isSdkAtLeastB()); + when(mWifiNative.getSupportedFeatures()).thenReturn( + WifiP2pManager.FEATURE_WIFI_DIRECT_R2); + when(mFeatureFlags.wifiDirectR2()).thenReturn(true); + + WifiP2pDirInfo dirInfo = new WifiP2pDirInfo( + MacAddress.fromString(TEST_DEVICE_MAC_ADDRESS_STRING), TEST_NONCE, TEST_DIR_TAG); + Message msg = Message.obtain(); + msg.what = WifiP2pManager.VALIDATE_DIR_INFO; + msg.replyTo = mClientMessenger; + msg.obj = new AttributionSource(1000, TEST_PACKAGE_NAME, null); + Bundle extras = new Bundle(); + extras.putParcelable(WifiP2pManager.EXTRA_PARAM_KEY_DIR_INFO, dirInfo); + msg.getData().putBundle(WifiP2pManager.EXTRA_PARAM_KEY_BUNDLE, extras); + + + forceP2pEnabled(mClient1); + when(mWifiPermissionsUtil.checkNearbyDevicesPermission(any(), anyBoolean(), any())) + .thenReturn(false); + mP2pStateMachineMessenger.send(Message.obtain(msg)); + mLooper.dispatchAll(); + assertTrue(mClientHandler.hasMessages(WifiP2pManager.VALIDATE_DIR_INFO_FAILED)); + + when(mWifiPermissionsUtil.checkNearbyDevicesPermission(any(), anyBoolean(), any())) + .thenReturn(true); + mP2pStateMachineMessenger.send(Message.obtain(msg)); + mLooper.dispatchAll(); + + verify(mClientHandler, times(2)).sendMessage(mMessageCaptor.capture()); + List<Message> messages = mMessageCaptor.getAllValues(); + assertEquals(WifiP2pManager.VALIDATE_DIR_INFO_FAILED, messages.get(0).what); + assertEquals(WifiP2pManager.RESPONSE_VALIDATE_DIR_INFO, messages.get(1).what); + } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/rtt/RttServiceImplTest.java b/service/tests/wifitests/src/com/android/server/wifi/rtt/RttServiceImplTest.java index c96116914d..cc570c77c7 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/rtt/RttServiceImplTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/rtt/RttServiceImplTest.java @@ -32,6 +32,7 @@ import static org.junit.Assume.assumeTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.doAnswer; @@ -51,7 +52,6 @@ import android.content.AttributionSource; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.content.pm.PackageManager; import android.location.LocationManager; import android.net.MacAddress; @@ -252,8 +252,11 @@ public class RttServiceImplTest extends WifiBaseTest { mMockLooper.dispatchAll(); ArgumentCaptor<BroadcastReceiver> bcastRxCaptor = ArgumentCaptor.forClass( BroadcastReceiver.class); - verify(mockContext, times(2)).registerReceiver(bcastRxCaptor.capture(), - any(IntentFilter.class)); + verify(mockContext).registerReceiver(bcastRxCaptor.capture(), + argThat(filter -> filter.hasAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED))); + verify(mockContext).registerReceiverForAllUsers(bcastRxCaptor.capture(), + argThat(filter -> filter.hasAction(LocationManager.MODE_CHANGED_ACTION)), + eq(null), any(Handler.class)); mPowerBcastReceiver = bcastRxCaptor.getAllValues().get(0); mLocationModeReceiver = bcastRxCaptor.getAllValues().get(1); diff --git a/service/tests/wifitests/src/com/android/server/wifi/rtt/RttTestUtils.java b/service/tests/wifitests/src/com/android/server/wifi/rtt/RttTestUtils.java index 1e7df0db7a..edcbaec843 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/rtt/RttTestUtils.java +++ b/service/tests/wifitests/src/com/android/server/wifi/rtt/RttTestUtils.java @@ -85,26 +85,50 @@ public class RttTestUtils { */ public static RangingRequest getDummyRangingRequestWith11az(byte lastMacByte) { RangingRequest.Builder builder = new RangingRequest.Builder(); + ScanResult.InformationElement vhtCap = new ScanResult.InformationElement(); + vhtCap.id = ScanResult.InformationElement.EID_VHT_CAPABILITIES; - ScanResult scan1 = new ScanResult(); - scan1.BSSID = "00:01:02:03:04:" + String.format("%02d", lastMacByte); - scan1.setFlag(ScanResult.FLAG_80211mc_RESPONDER); - scan1.channelWidth = ScanResult.CHANNEL_WIDTH_40MHZ; + ScanResult.InformationElement heCap = new ScanResult.InformationElement(); + heCap.id = ScanResult.InformationElement.EID_EXTENSION_PRESENT; + heCap.idExt = ScanResult.InformationElement.EID_EXT_HE_CAPABILITIES; + + ScanResult.InformationElement ehtCap = new ScanResult.InformationElement(); + ehtCap.id = ScanResult.InformationElement.EID_EXTENSION_PRESENT; + ehtCap.idExt = ScanResult.InformationElement.EID_EXT_EHT_CAPABILITIES; + + ScanResult.InformationElement[] ie = new ScanResult.InformationElement[3]; + ie[0] = vhtCap; + ie[1] = heCap; + ie[2] = ehtCap; + + // peer 0: 11mc only + ScanResult scan1 = new ScanResult.Builder() + .setBssid("00:01:02:03:04:" + String.format("%02d", lastMacByte)) + .setIs80211McRTTResponder(true) + .setChannelWidth(ScanResult.CHANNEL_WIDTH_40MHZ) + .setFrequency(5200) + .build(); + scan1.informationElements = ie; + builder.addAccessPoint(scan1); + // peer 1: one-sided only ScanResult scan2 = new ScanResult(); scan2.BSSID = "0A:0B:0C:0D:0E:" + String.format("%02d", lastMacByte); scan2.channelWidth = ScanResult.CHANNEL_WIDTH_20MHZ; MacAddress mac1 = MacAddress.fromString("08:09:08:07:06:05"); - - builder.addAccessPoint(scan1); builder.addNon80211mcCapableAccessPoint(scan2); - // Changing default RTT burst size to a valid, but maximum, value + // peer 2: Aware builder.setRttBurstSize(RangingRequest.getMaxRttBurstSize()); builder.addWifiAwarePeer(mac1); - // Add 11az & 11mc supported AP - scan1.BSSID = "00:11:22:33:44:" + String.format("%02d", lastMacByte); - scan1.setFlag(ScanResult.FLAG_80211mc_RESPONDER); - scan1.setFlag(ScanResult.FLAG_80211az_NTB_RESPONDER); - scan1.channelWidth = ScanResult.CHANNEL_WIDTH_40MHZ; + // peer 3: 11az & 11mc supported AP. Since the device supports 11mc only, the expectation is + // preamble will be adjusted as VHT since ranging request is in 5 Ghz. + scan1 = new ScanResult.Builder() + .setBssid("00:11:22:33:44:" + String.format("%02d", lastMacByte)) + .setIs80211McRTTResponder(true) + .setIs80211azNtbRTTResponder(true) + .setChannelWidth(ScanResult.CHANNEL_WIDTH_40MHZ) + .setFrequency(5200) + .build(); + scan1.informationElements = ie; builder.addAccessPoint(scan1); return builder.build(); } diff --git a/service/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java b/service/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java index 971722a82c..a4711cb567 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/scanner/WifiScanningServiceTest.java @@ -461,6 +461,7 @@ public class WifiScanningServiceTest extends WifiBaseTest { String serviceDump = dumpService(); Pattern logLineRegex = Pattern.compile("^.+" + type + ": ClientInfo\\[uid=\\d+, package=" + TEST_PACKAGE_NAME + + ", attributionTag=" + TEST_FEATURE_ID + ", Mock for Stub, hashCode: \\d+\\]", Pattern.MULTILINE); assertTrue("dump did not contain log with {" + logLineRegex @@ -473,6 +474,7 @@ public class WifiScanningServiceTest extends WifiBaseTest { String extraPattern = extra == null ? "" : "," + extra; Pattern logLineRegex = Pattern.compile("^.+" + callback + ": ClientInfo\\[uid=\\d+, package=" + TEST_PACKAGE_NAME + + ", attributionTag=" + TEST_FEATURE_ID + ", Mock for Stub, hashCode: \\d+\\]" + extraPattern + "$", Pattern.MULTILINE); assertTrue("dump did not contain callback log with callback {" + logLineRegex @@ -2178,7 +2180,7 @@ public class WifiScanningServiceTest extends WifiBaseTest { verifyNoMoreInteractions(client.listener); verifyNoMoreInteractions(client1.listener); assertDumpContainsRequestLog("registerScanListener"); - assertDumpContainsCallbackLog("singleScanResults", + assertDumpContainsCallbackLog("singleScanResults listener", "results=" + results.getScanData().getResults().length); } @@ -2186,7 +2188,7 @@ public class WifiScanningServiceTest extends WifiBaseTest { * Register a single scan listener and do a single scan */ @Test - public void deregisterScanListener() throws Exception { + public void testDeregisterScanListener() throws Exception { WifiScanner.ScanSettings requestSettings = createRequest(WifiScanner.WIFI_BAND_BOTH, 0, 0, 20, WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN); WifiNative.ScanSettings nativeSettings = computeSingleScanNativeSettings(requestSettings); @@ -2232,6 +2234,21 @@ public class WifiScanningServiceTest extends WifiBaseTest { } /** + * Verify back to back calls to registerScanListener and deregisterScanListener works + */ + @Test + public void testRegisterAndDeregisterListener() throws Exception { + startServiceAndLoadDriver(); + + TestClient client = new TestClient(); + client.registerScanListener(); + client.deregisterScanListener(); + mLooper.dispatchAll(); + client.verifySuccessfulResponse(); + client.verifyUnlinkedToDeath(); + } + + /** * Send a single scan request and then two more before the first completes. Neither are * satisfied by the first scan. Verify that the first completes and the second two are merged. */ @@ -2314,9 +2331,9 @@ public class WifiScanningServiceTest extends WifiBaseTest { client.verifyScanResultsReceived(results2and3.getScanData()); assertDumpContainsRequestLog("registerScanListener"); - assertDumpContainsCallbackLog("singleScanResults", + assertDumpContainsCallbackLog("singleScanResults listener", "results=" + results1.getRawScanResults().length); - assertDumpContainsCallbackLog("singleScanResults", + assertDumpContainsCallbackLog("singleScanResults listener", "results=" + results2and3.getRawScanResults().length); } @@ -2754,6 +2771,7 @@ public class WifiScanningServiceTest extends WifiBaseTest { Pattern logLineRegex = Pattern.compile( "^.+" + "Successfully stopped all requests for client " + "ClientInfo\\[uid=\\d+, package=" + TEST_PACKAGE_NAME + + ", attributionTag=" + TEST_FEATURE_ID + ", Mock for Stub, hashCode: \\d+\\]", Pattern.MULTILINE); assertTrue("dump did not contain log with [" + logLineRegex + "]\n" + serviceDump + "\n", diff --git a/service/tests/wifitests/src/com/android/server/wifi/usd/UsdRequestManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/usd/UsdRequestManagerTest.java new file mode 100644 index 0000000000..dfd3033fc6 --- /dev/null +++ b/service/tests/wifitests/src/com/android/server/wifi/usd/UsdRequestManagerTest.java @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2024 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.usd; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +import android.app.AlarmManager; +import android.net.wifi.usd.Characteristics; +import android.net.wifi.usd.Config; +import android.net.wifi.usd.IPublishSessionCallback; +import android.net.wifi.usd.ISubscribeSessionCallback; +import android.net.wifi.usd.PublishConfig; +import android.net.wifi.usd.SubscribeConfig; +import android.os.IBinder; +import android.os.RemoteException; + +import androidx.test.filters.SmallTest; + +import com.android.server.wifi.Clock; +import com.android.server.wifi.SupplicantStaIfaceHal; +import com.android.server.wifi.SupplicantStaIfaceHal.UsdCapabilitiesInternal; +import com.android.server.wifi.WifiBaseTest; +import com.android.server.wifi.WifiNative; +import com.android.server.wifi.WifiThreadRunner; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.InOrder; +import org.mockito.Mock; + +import java.util.ArrayList; +import java.util.List; + +/** + * Unit test for {@link UsdRequestManager}. + */ +@SmallTest +public class UsdRequestManagerTest extends WifiBaseTest { + private UsdRequestManager mUsdRequestManager; + private UsdNativeManager mUsdNativeManager; + @Mock + private WifiThreadRunner mWifiThreadRunner; + private static final String USD_INTERFACE_NAME = "wlan0"; + private static final int USD_REQUEST_COMMAND_ID = 100; + private static final String USD_TEST_SERVICE_NAME = "UsdTest"; + private static final int USD_TEST_PERIOD_MILLIS = 200; + private static final int USD_TTL_SEC = 3000; + @Mock + private Clock mClock; + @Mock + ISubscribeSessionCallback mSubscribeSessionCallback; + @Mock + IPublishSessionCallback mPublishSessionCallback; + + private SupplicantStaIfaceHal.UsdCapabilitiesInternal mUsdCapabilities; + @Mock + private WifiNative mWifiNative; + @Mock + private IBinder mAppBinder; + private InOrder mInOrderAppBinder; + @Mock + private AlarmManager mAlarmManager; + private byte[] mSsi = new byte[]{1, 2, 3}; + private int[] mFreqs = new int[]{2437}; + private List<byte[]> mFilter; + + @Before + public void setUp() throws Exception { + initMocks(this); + mUsdCapabilities = getMockUsdCapabilities(); + mUsdNativeManager = new UsdNativeManager(mWifiNative); + when(mWifiNative.getUsdCapabilities()).thenReturn(mUsdCapabilities); + mUsdRequestManager = new UsdRequestManager(mUsdNativeManager, mWifiThreadRunner, + USD_INTERFACE_NAME, mClock, mAlarmManager); + UsdCapabilitiesInternal mockUsdCapabilities = getMockUsdCapabilities(); + mFilter = new ArrayList<>(); + mFilter.add(new byte[]{10, 11}); + mFilter.add(new byte[]{12, 13, 14}); + mInOrderAppBinder = inOrder(mAppBinder); + when(mUsdNativeManager.getUsdCapabilities()).thenReturn(mockUsdCapabilities); + + } + + private UsdCapabilitiesInternal getMockUsdCapabilities() { + return new UsdCapabilitiesInternal(true, true, 1024, 255, + 255, 1, 1); + } + + /** + * Test {@link UsdRequestManager#getCharacteristics()}. + */ + @Test + public void testUsdGetCharacteristics() { + Characteristics characteristics = mUsdRequestManager.getCharacteristics(); + assertEquals(mUsdCapabilities.maxNumSubscribeSessions, + characteristics.getMaxNumberOfSubscribeSessions()); + assertEquals(mUsdCapabilities.maxNumPublishSessions, + characteristics.getMaxNumberOfPublishSessions()); + assertEquals(mUsdCapabilities.maxServiceNameLengthBytes, + characteristics.getMaxServiceNameLength()); + assertEquals(mUsdCapabilities.maxMatchFilterLengthBytes, + characteristics.getMaxMatchFilterLength()); + assertEquals(mUsdCapabilities.maxLocalSsiLengthBytes, + characteristics.getMaxServiceSpecificInfoLength()); + } + + /** + * Test USD subscribe. + */ + @Test + public void testUsdSubscribe() throws RemoteException { + SubscribeConfig subscribeConfig = new SubscribeConfig.Builder(USD_TEST_SERVICE_NAME) + .setQueryPeriodMillis(USD_TEST_PERIOD_MILLIS) + .setOperatingFrequenciesMhz(mFreqs) + .setRxMatchFilter(mFilter) + .setTxMatchFilter(mFilter) + .setSubscribeType(SubscribeConfig.SUBSCRIBE_TYPE_ACTIVE) + .setServiceSpecificInfo(mSsi) + .setServiceProtoType(Config.SERVICE_PROTO_TYPE_CSA_MATTER) + .setQueryPeriodMillis(USD_TEST_PERIOD_MILLIS) + .setTtlSeconds(USD_TTL_SEC).build(); + when(mSubscribeSessionCallback.asBinder()).thenReturn(mAppBinder); + when(mWifiNative.startUsdSubscribe(USD_INTERFACE_NAME, USD_REQUEST_COMMAND_ID, + subscribeConfig)).thenReturn(true); + mUsdRequestManager.subscribe(subscribeConfig, mSubscribeSessionCallback); + mInOrderAppBinder.verify(mAppBinder).linkToDeath(any(IBinder.DeathRecipient.class), + anyInt()); + verify(mWifiNative).startUsdSubscribe(USD_INTERFACE_NAME, USD_REQUEST_COMMAND_ID, + subscribeConfig); + } + + /** + * Test USD publish. + */ + @Test + public void testUsdPublish() throws RemoteException { + PublishConfig publishConfig = new PublishConfig.Builder(USD_TEST_SERVICE_NAME) + .setAnnouncementPeriodMillis(USD_TEST_PERIOD_MILLIS) + .setEventsEnabled(true) + .setOperatingFrequenciesMhz(mFreqs) + .setRxMatchFilter(mFilter) + .setTxMatchFilter(mFilter) + .setServiceProtoType(Config.SERVICE_PROTO_TYPE_CSA_MATTER) + .setServiceSpecificInfo(mSsi) + .setSolicitedTransmissionType(Config.TRANSMISSION_TYPE_UNICAST) + .setTtlSeconds(USD_TTL_SEC) + .build(); + when(mPublishSessionCallback.asBinder()).thenReturn(mAppBinder); + when(mWifiNative.startUsdPublish(USD_INTERFACE_NAME, USD_REQUEST_COMMAND_ID, + publishConfig)).thenReturn(true); + mUsdRequestManager.publish(publishConfig, mPublishSessionCallback); + mInOrderAppBinder.verify(mAppBinder).linkToDeath(any(IBinder.DeathRecipient.class), + anyInt()); + verify(mWifiNative).startUsdPublish(USD_INTERFACE_NAME, USD_REQUEST_COMMAND_ID, + publishConfig); + } +} diff --git a/service/tests/wifitests/src/com/android/server/wifi/util/ApConfigUtilTest.java b/service/tests/wifitests/src/com/android/server/wifi/util/ApConfigUtilTest.java index 749d05ef8d..39eca8f05c 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/util/ApConfigUtilTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/util/ApConfigUtilTest.java @@ -39,8 +39,10 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.validateMockitoUsage; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.mockito.Mockito.withSettings; import android.net.MacAddress; import android.net.wifi.CoexUnsafeChannel; @@ -59,18 +61,23 @@ import android.util.SparseIntArray; import androidx.test.filters.SmallTest; +import com.android.dx.mockito.inline.extended.ExtendedMockito; import com.android.modules.utils.build.SdkLevel; import com.android.server.wifi.SoftApManager; import com.android.server.wifi.WifiBaseTest; +import com.android.server.wifi.WifiInjector; import com.android.server.wifi.WifiNative; import com.android.server.wifi.WifiSettingsConfigStore; import com.android.server.wifi.coex.CoexManager; +import com.android.wifi.flags.Flags; import com.android.wifi.resources.R; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.MockitoSession; import java.util.ArrayList; import java.util.Arrays; @@ -173,15 +180,23 @@ public class ApConfigUtilTest extends WifiBaseTest { @Mock WifiSettingsConfigStore mConfigStore; @Mock DeviceWiphyCapabilities mDeviceWiphyCapabilities; + @Mock WifiInjector mWifiInjector; private SoftApCapability mCapability; private boolean mApBridgeIfaceCobinationSupported = false; private boolean mApBridgeWithStaIfaceCobinationSupported = false; + private MockitoSession mSession; + /** * Setup test. */ @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + // Mock WifiMigration to avoid calling into its static methods + mSession = ExtendedMockito.mockitoSession() + .mockStatic(Flags.class, withSettings().lenient()) + .mockStatic(WifiInjector.class, withSettings().lenient()) + .startMocking(); final long testFeatures = SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT | SoftApCapability.SOFTAP_FEATURE_BAND_6G_SUPPORTED | SoftApCapability.SOFTAP_FEATURE_BAND_60G_SUPPORTED; @@ -189,6 +204,8 @@ public class ApConfigUtilTest extends WifiBaseTest { mCapability.setSupportedChannelList(SoftApConfiguration.BAND_2GHZ, ALLOWED_2G_CHANS); mCapability.setSupportedChannelList(SoftApConfiguration.BAND_5GHZ, ALLOWED_5G_CHANS); mCapability.setSupportedChannelList(SoftApConfiguration.BAND_60GHZ, ALLOWED_60G_CHANS); + when(WifiInjector.getInstance()).thenReturn(mWifiInjector); + when(mWifiInjector.getContext()).thenReturn(mContext); when(mContext.getResourceCache()).thenReturn(mResources); when(mResources.getBoolean(R.bool.config_wifi24ghzSupport)).thenReturn(true); when(mResources.getBoolean(R.bool.config_wifi5ghzSupport)).thenReturn(true); @@ -221,6 +238,17 @@ public class ApConfigUtilTest extends WifiBaseTest { } /** + * Called after each test + */ + @After + public void cleanup() { + validateMockitoUsage(); + if (mSession != null) { + mSession.finishMocking(); + } + } + + /** * Verify Bridge AP support when Iface combination for AP bridge is allowed. */ @Test @@ -1377,7 +1405,7 @@ public class ApConfigUtilTest extends WifiBaseTest { .thenReturn(TEST_5G_DFS_FREQS); when(mWifiNative.getChannelsForBand(anyInt())).thenReturn(new int[0]); List<Integer> result = ApConfigUtil.getAvailableChannelFreqsForBand( - SoftApConfiguration.BAND_5GHZ, mWifiNative, mResources, true); + SoftApConfiguration.BAND_5GHZ, mWifiNative, null, true); // make sure we try to get dfs channel. verify(mWifiNative).getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY); for (int freq : result) { @@ -1412,7 +1440,7 @@ public class ApConfigUtilTest extends WifiBaseTest { when(mWifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ)) .thenReturn(ALLOWED_5G_FREQS); List<Integer> result = ApConfigUtil.getAvailableChannelFreqsForBand( - SoftApConfiguration.BAND_5GHZ, mWifiNative, mResources, true); + SoftApConfiguration.BAND_5GHZ, mWifiNative, null, true); // make sure we try to get available channels from wificond. verify(mWifiNative).getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ); verify(mWifiNative, never()).getUsableChannels(anyInt(), anyInt(), anyInt()); @@ -1432,7 +1460,7 @@ public class ApConfigUtilTest extends WifiBaseTest { .thenReturn(ALLOWED_5G_FREQS); when(mWifiNative.getUsableChannels(anyInt(), anyInt(), anyInt())).thenReturn(null); List<Integer> result = ApConfigUtil.getAvailableChannelFreqsForBand( - SoftApConfiguration.BAND_5GHZ, mWifiNative, mResources, true); + SoftApConfiguration.BAND_5GHZ, mWifiNative, null, true); // make sure we try to get available channels from HAL and fallback to wificond. verify(mWifiNative).getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ); verify(mWifiNative).getUsableChannels(eq(WifiScanner.WIFI_BAND_5_GHZ), anyInt(), anyInt()); @@ -1455,7 +1483,8 @@ public class ApConfigUtilTest extends WifiBaseTest { .thenReturn(false); /* 11be is disallowed when IEEE80211_BE feature is not supported */ assertFalse(ApConfigUtil.is11beAllowedForThisConfiguration(mDeviceWiphyCapabilities, - mContext, config, true)); + mContext, config, true, 2 /* maximumSupportedMLD */, 0 /* currentExistingMLD */, + false)); when(mResources.getBoolean(R.bool.config_wifiSoftapIeee80211beSupported)) .thenReturn(true); @@ -1465,14 +1494,42 @@ public class ApConfigUtilTest extends WifiBaseTest { .thenReturn(true); /* 11be is allowed if chip supports single link MLO in bridged mode */ assertTrue(ApConfigUtil.is11beAllowedForThisConfiguration(mDeviceWiphyCapabilities, - mContext, config, true)); + mContext, config, true, 2 /* maximumSupportedMLD */, 0 /* currentExistingMLD */, + false)); /* 11be is not allowed if chip doesn't support single link MLO in bridged mode */ when(mResources.getBoolean(R.bool.config_wifiSoftApSingleLinkMloInBridgedModeSupported)) .thenReturn(false); assertFalse(ApConfigUtil.is11beAllowedForThisConfiguration(mDeviceWiphyCapabilities, - mContext, config, true)); + mContext, config, true, 1 /* maximumSupportedMLD */, 0 /* currentExistingMLD */, + false)); + + when(Flags.mloSap()).thenReturn(true); + // two MLDs supported, allow 11be on bridged mode. + when(mResources.getInteger(R.integer.config_wifiSoftApMaxNumberMLDSupported)) + .thenReturn(2); + assertTrue(ApConfigUtil.is11beAllowedForThisConfiguration(mDeviceWiphyCapabilities, + mContext, config, true, 2 /* maximumSupportedMLD */, 0 /* currentExistingMLD */, + false)); + + // One MLD supported only, disallow 11be on bridged AP. + when(mResources.getInteger(R.integer.config_wifiSoftApMaxNumberMLDSupported)) + .thenReturn(1); + assertFalse(ApConfigUtil.is11beAllowedForThisConfiguration(mDeviceWiphyCapabilities, + mContext, config, true, 1 /* maximumSupportedMLD */, 0 /* currentExistingMLD */, + false)); + + // One MLD supported only, disallow 11be when there is existing 11be AP. + assertFalse(ApConfigUtil.is11beAllowedForThisConfiguration(mDeviceWiphyCapabilities, + mContext, config, false, 1 /* maximumSupportedMLD */, 1 /* currentExistingMLD */, + false)); + + // One MLD supported only but chip support MultilinksOnMLD, allow 11be on bridged AP. + assertTrue(ApConfigUtil.is11beAllowedForThisConfiguration(mDeviceWiphyCapabilities, + mContext, config, true, 1 /* maximumSupportedMLD */, 0 /* currentExistingMLD */, + true)); } + @Test public void testIs11beDisabledForSecurityType() throws Exception { assertTrue(ApConfigUtil.is11beDisabledForSecurityType(SECURITY_TYPE_OPEN)); @@ -1482,4 +1539,41 @@ public class ApConfigUtilTest extends WifiBaseTest { assertFalse(ApConfigUtil.is11beDisabledForSecurityType(SECURITY_TYPE_WPA3_OWE)); assertTrue(ApConfigUtil.is11beDisabledForSecurityType(SECURITY_TYPE_WPA3_OWE_TRANSITION)); } + + @Test + public void testGetMaximumSupportedMLD() throws Exception { + // Old overlay, no MLD number is configured + when(mResources.getInteger(R.integer.config_wifiSoftApMaxNumberMLDSupported)) + .thenReturn(0); + // 1 MLD supported only no matter whether multiple MLD supported. + when(mResources.getBoolean(R.bool.config_wifiSoftApSingleLinkMloInBridgedModeSupported)) + .thenReturn(false); + assertEquals(1, ApConfigUtil.getMaximumSupportedMLD(mContext, + false /* isMultipleMLMDSupportedOnSap */)); + assertEquals(1, ApConfigUtil.getMaximumSupportedMLD(mContext, + true /* isMultipleMLMDSupportedOnSap */)); + + // 2 MLDs supported when overlay is true and no matter whether multiple MLD supported. + when(mResources.getBoolean(R.bool.config_wifiSoftApSingleLinkMloInBridgedModeSupported)) + .thenReturn(true); + assertEquals(2, ApConfigUtil.getMaximumSupportedMLD(mContext, + false /* isMultipleMLMDSupportedOnSap */)); + assertEquals(2, ApConfigUtil.getMaximumSupportedMLD(mContext, + true /* isMultipleMLMDSupportedOnSap */)); + + // New overlay, MLD number is configured. It will check multiple MLD supported value. + when(Flags.multipleMldOnSapSupported()).thenReturn(true); + when(mResources.getInteger(R.integer.config_wifiSoftApMaxNumberMLDSupported)) + .thenReturn(2); + assertEquals(1, ApConfigUtil.getMaximumSupportedMLD(mContext, + false /* isMultipleMLMDSupportedOnSap */)); + assertEquals(2, ApConfigUtil.getMaximumSupportedMLD(mContext, + true /* isMultipleMLMDSupportedOnSap */)); + + // Make sure it uses overlay value even though chip supports multiple MLD. + when(mResources.getInteger(R.integer.config_wifiSoftApMaxNumberMLDSupported)) + .thenReturn(1); + assertEquals(1, ApConfigUtil.getMaximumSupportedMLD(mContext, + true /* isMultipleMLMDSupportedOnSap */)); + } } diff --git a/service/tests/wifitests/src/com/android/server/wifi/util/FeatureBitsetUtilsTest.java b/service/tests/wifitests/src/com/android/server/wifi/util/FeatureBitsetUtilsTest.java new file mode 100644 index 0000000000..52c3a3ead1 --- /dev/null +++ b/service/tests/wifitests/src/com/android/server/wifi/util/FeatureBitsetUtilsTest.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2024 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.util; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertTrue; + +import android.net.wifi.WifiManager; + +import org.junit.Test; + +import java.util.BitSet; + +/** + * Unit tests for {@link FeatureBitsetUtils} + */ +public class FeatureBitsetUtilsTest { + /** + * Verify that formatting a null or empty BitSet produces the default value. + */ + @Test + public void testNoFeatures() { + assertEquals("[]", FeatureBitsetUtils.formatSupportedFeatures(null)); + assertEquals("[]", FeatureBitsetUtils.formatSupportedFeatures(new BitSet())); + } + + /** + * Verify that a simple BitSet can be formatted successfully. + */ + @Test + public void testSuccessfulFormatting() { + BitSet features = new BitSet(); + features.set(WifiManager.WIFI_FEATURE_AP_STA); + features.set(WifiManager.WIFI_FEATURE_AWARE); + String formatted = FeatureBitsetUtils.formatSupportedFeatures(features); + assertTrue(formatted.contains("WIFI_FEATURE_AP_STA")); + assertTrue(formatted.contains("WIFI_FEATURE_AWARE")); + } + + /** + * Verify that the newest feature is formatted successfully. + */ + @Test + public void testNewestFeatureFormatting() { + BitSet features = new BitSet(); + features.set(FeatureBitsetUtils.NEWEST_FEATURE_INDEX); + String formatted = FeatureBitsetUtils.formatSupportedFeatures(features); + String newestFeatureName = (String) FeatureBitsetUtils.ALL_FEATURES.get( + FeatureBitsetUtils.NEWEST_FEATURE_INDEX); + assertTrue(formatted.contains(newestFeatureName)); + } + + /** + * Verify that an unrecognized feature produces the expected warning text. + */ + @Test + public void testUnrecognizedFeature() { + BitSet features = new BitSet(); + features.set(FeatureBitsetUtils.NEWEST_FEATURE_INDEX + 1); + String formatted = FeatureBitsetUtils.formatSupportedFeatures(features); + assertTrue(formatted.contains("UNRECOGNIZED FEATURE")); + } +} diff --git a/service/tests/wifitests/src/com/android/server/wifi/util/RssiUtilTest.java b/service/tests/wifitests/src/com/android/server/wifi/util/RssiUtilTest.java index 3e924173c0..6089651d3f 100644 --- a/service/tests/wifitests/src/com/android/server/wifi/util/RssiUtilTest.java +++ b/service/tests/wifitests/src/com/android/server/wifi/util/RssiUtilTest.java @@ -30,7 +30,6 @@ import android.net.wifi.IWifiManager; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.Build; -import android.os.Looper; import androidx.test.filters.SmallTest; @@ -117,7 +116,7 @@ public class RssiUtilTest extends WifiBaseTest { when(context.getApplicationInfo()).thenReturn(applicationInfo); when(context.getOpPackageName()).thenReturn("TestPackage"); WifiManager wifiManager = new WifiManager( - context, iWifiManager, mock(Looper.class)); + context, iWifiManager); int level = wifiManager.getMaxSignalLevel(); assertThat(level).isEqualTo(4); diff --git a/tests/hostsidetests/multidevices/com.google.snippet.wifi/Android.bp b/tests/hostsidetests/multidevices/com.google.snippet.wifi/Android.bp index bcbcb8532c..84c90e3ab1 100644 --- a/tests/hostsidetests/multidevices/com.google.snippet.wifi/Android.bp +++ b/tests/hostsidetests/multidevices/com.google.snippet.wifi/Android.bp @@ -7,17 +7,16 @@ package { android_test { name: "wifi_mobly_snippet", sdk_version: "system_current", - srcs: [ - "aware/*.java", - "direct/*.java", - ], + srcs: ["**/*.java"], manifest: "AndroidManifest.xml", static_libs: [ "androidx.test.runner", "compatibility-device-util-axt", + "gson", "guava", "mobly-snippet-lib", "mobly-bundled-snippets-lib", + "wifi_aconfig_flags_lib", ], min_sdk_version: "31", target_sdk_version: "35", diff --git a/tests/hostsidetests/multidevices/com.google.snippet.wifi/AndroidManifest.xml b/tests/hostsidetests/multidevices/com.google.snippet.wifi/AndroidManifest.xml index b8e9f824c8..719d3fe6cb 100644 --- a/tests/hostsidetests/multidevices/com.google.snippet.wifi/AndroidManifest.xml +++ b/tests/hostsidetests/multidevices/com.google.snippet.wifi/AndroidManifest.xml @@ -25,6 +25,8 @@ android:value="com.google.snippet.wifi.aware.WifiAwareManagerSnippet, com.google.snippet.wifi.aware.ConnectivityManagerSnippet, com.google.snippet.wifi.direct.WifiP2pManagerSnippet, + com.google.snippet.wifi.softap.TetheringManagerSnippet, + com.google.snippet.wifi.WifiManagerSnippet, com.google.android.mobly.snippet.bundled.WifiManagerSnippet"/> <meta-data android:name="mobly-object-converter" diff --git a/tests/hostsidetests/multidevices/com.google.snippet.wifi/WifiJsonConverter.java b/tests/hostsidetests/multidevices/com.google.snippet.wifi/WifiJsonConverter.java new file mode 100644 index 0000000000..47c408268d --- /dev/null +++ b/tests/hostsidetests/multidevices/com.google.snippet.wifi/WifiJsonConverter.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2024 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.google.snippet.wifi; + +import android.net.wifi.SoftApConfiguration; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import org.json.JSONException; +import org.json.JSONObject; + +/** + * The converter class that allows users to use custom type as snippet RPC arguments and return + * values. + */ +public final class WifiJsonConverter { + private static final Gson GSON = new GsonBuilder().serializeNulls().create(); + + /** + * Remove the extra quotation marks from the beginning and the end of a string. + * + * <p>This is useful for strings like the SSID field of Android's Wi-Fi configuration. + */ + public static String trimQuotationMarks(String originalString) { + String result = originalString; + if (originalString.length() > 2 + && originalString.charAt(0) == '"' + && originalString.charAt(originalString.length() - 1) == '"') { + result = originalString.substring(1, originalString.length() - 1); + } + return result; + } + + /** + * Serializes a complex type object to {@link JSONObject}. + ** + * @param object The object to convert to "serialize". + * @return A JSONObject representation of the input object. + * @throws JSONException if there is an error serializing the object. + */ + public static JSONObject serialize(Object object) throws JSONException { + // If the RPC method requires a custom return type with special serialization + // considerations we need to define it here. + if (object instanceof SoftApConfiguration) { + return serializeSoftApConfiguration((SoftApConfiguration) object); + } + + // By default, depends on Gson to serialize correctly. + return new JSONObject(GSON.toJson(object)); + } + + private static JSONObject serializeSoftApConfiguration(SoftApConfiguration data) + throws JSONException { + JSONObject result = new JSONObject(GSON.toJson(data)); + result.put("SSID", trimQuotationMarks(data.getWifiSsid().toString())); + return result; + } + + private WifiJsonConverter() {} +} diff --git a/tests/hostsidetests/multidevices/com.google.snippet.wifi/WifiManagerSnippet.java b/tests/hostsidetests/multidevices/com.google.snippet.wifi/WifiManagerSnippet.java new file mode 100644 index 0000000000..3a0ce0d57b --- /dev/null +++ b/tests/hostsidetests/multidevices/com.google.snippet.wifi/WifiManagerSnippet.java @@ -0,0 +1,288 @@ +/* + * Copyright (C) 2024 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.google.snippet.wifi; + +import static android.net.wifi.DeauthenticationReasonCode.REASON_UNKNOWN; + +import android.content.Context; +import android.net.wifi.SoftApConfiguration; +import android.net.wifi.SoftApInfo; +import android.net.wifi.WifiClient; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiManager; +import android.os.Handler; +import android.os.HandlerThread; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.compatibility.common.util.PollingCheck; +import com.android.wifi.flags.Flags; + +import com.google.android.mobly.snippet.Snippet; +import com.google.android.mobly.snippet.event.EventCache; +import com.google.android.mobly.snippet.event.SnippetEvent; +import com.google.android.mobly.snippet.rpc.AsyncRpc; +import com.google.android.mobly.snippet.rpc.Rpc; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** Snippet class for WifiManager. */ +public class WifiManagerSnippet implements Snippet { + + private static final String TAG = "WifiManagerSnippet"; + private static final long POLLING_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(10); + + private final WifiManager mWifiManager; + private final Handler mHandler; + private final Object mLock = new Object(); + + private WifiManagerSnippet.SnippetSoftApCallback mSoftApCallback; + private WifiManager.LocalOnlyHotspotReservation mLocalOnlyHotspotReservation; + + /** Callback to listen in and verify events to SoftAp. */ + private static class SnippetSoftApCallback implements WifiManager.SoftApCallback { + private final String mCallbackId; + + SnippetSoftApCallback(String callbackId) { + mCallbackId = callbackId; + } + + @Override + public void onConnectedClientsChanged(@NonNull SoftApInfo info, + @NonNull List<WifiClient> clients) { + Log.d(TAG, "onConnectedClientsChanged, info=" + info + ", clients=" + clients); + SnippetEvent event = new SnippetEvent(mCallbackId, "onConnectedClientsChanged"); + event.getData().putInt("connectedClientsCount", clients.size()); + String macAddress = null; + if (!clients.isEmpty()) { + // In our Mobly test cases, there is only ever one other device. + WifiClient client = clients.getFirst(); + macAddress = client.getMacAddress().toString(); + } + event.getData().putString("clientMacAddress", macAddress); + EventCache.getInstance().postEvent(event); + } + + @Override + public void onClientsDisconnected(@NonNull SoftApInfo info, + @NonNull List<WifiClient> clients) { + Log.d(TAG, "onClientsDisconnected, info=" + info + ", clients=" + clients); + SnippetEvent event = new SnippetEvent(mCallbackId, "onClientsDisconnected"); + event.getData().putInt("disconnectedClientsCount", clients.size()); + String macAddress = null; + int disconnectReason = REASON_UNKNOWN; + if (!clients.isEmpty()) { + // In our Mobly test cases, there is only ever one other device. + WifiClient client = clients.getFirst(); + macAddress = client.getMacAddress().toString(); + disconnectReason = client.getDisconnectReason(); + } + event.getData().putString("clientMacAddress", macAddress); + event.getData().putInt("clientDisconnectReason", disconnectReason); + EventCache.getInstance().postEvent(event); + } + } + + /** Callback class to get the results of local hotspot start. */ + private class SnippetLocalOnlyHotspotCallback extends WifiManager.LocalOnlyHotspotCallback { + private final String mCallbackId; + + SnippetLocalOnlyHotspotCallback(String callbackId) { + mCallbackId = callbackId; + } + + @Override + public void onStarted(WifiManager.LocalOnlyHotspotReservation reservation) { + Log.d(TAG, "Local-only hotspot onStarted"); + synchronized (mLock) { + mLocalOnlyHotspotReservation = reservation; + } + SoftApConfiguration currentConfiguration = reservation.getSoftApConfiguration(); + SnippetEvent event = new SnippetEvent(mCallbackId, "onStarted"); + event.getData().putString("ssid", + WifiJsonConverter.trimQuotationMarks( + currentConfiguration.getWifiSsid().toString())); + event.getData() + .putString( + "passphrase", + currentConfiguration.getPassphrase()); + EventCache.getInstance().postEvent(event); + } + } + + public WifiManagerSnippet() { + Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); + mWifiManager = context.getSystemService(WifiManager.class); + HandlerThread handlerThread = new HandlerThread(getClass().getSimpleName()); + handlerThread.start(); + mHandler = new Handler(handlerThread.getLooper()); + } + + /** + * Starts local-only hotspot. + * + * @param callbackId A unique identifier assigned automatically by Mobly. + */ + @AsyncRpc(description = "Call to start local-only hotspot.") + public void wifiStartLocalOnlyHotspot(String callbackId) { + mWifiManager.startLocalOnlyHotspot(new SnippetLocalOnlyHotspotCallback(callbackId), + mHandler); + } + + /** + * Stop local-only hotspot. + */ + @Rpc(description = "Call to stop local-only hotspot.") + public void wifiStopLocalOnlyHotspot() { + synchronized (mLock) { + if (mLocalOnlyHotspotReservation == null) { + Log.w(TAG, "Requested to stop local-only hotspot which was already stopped."); + return; + } + + mLocalOnlyHotspotReservation.close(); + mLocalOnlyHotspotReservation = null; + } + } + + /** + * Registers a callback for Soft AP. + * + * @param callbackId A unique identifier assigned automatically by Mobly. + */ + @AsyncRpc(description = "Call to register SoftApCallback.") + public void wifiRegisterSoftApCallback(String callbackId) { + if (mSoftApCallback == null) { + mSoftApCallback = new SnippetSoftApCallback(callbackId); + mWifiManager.registerSoftApCallback(mHandler::post, mSoftApCallback); + } + } + + + /** + * Registers a callback for local-only hotspot. + * + * @param callbackId A unique identifier assigned automatically by Mobly. + */ + @AsyncRpc(description = "Call to register SoftApCallback for local-only hotspot.") + public void wifiRegisterLocalOnlyHotspotSoftApCallback(String callbackId) { + if (mSoftApCallback == null) { + mSoftApCallback = new SnippetSoftApCallback(callbackId); + mWifiManager.registerLocalOnlyHotspotSoftApCallback(mHandler::post, + mSoftApCallback); + } + } + + /** + * Checks if the device supports portable hotspot. + * + * @return {@code true} if the device supports portable hotspot, {@code false} otherwise. + */ + @Rpc(description = "Check if the device supports portable hotspot.") + public boolean wifiIsPortableHotspotSupported() { + return mWifiManager.isPortableHotspotSupported(); + } + + /** + * Unregisters soft AP callback function. + */ + @Rpc(description = "Unregister soft AP callback function.") + public void wifiUnregisterSoftApCallback() { + if (mSoftApCallback == null) { + return; + } + + mWifiManager.unregisterSoftApCallback(mSoftApCallback); + mSoftApCallback = null; + } + + /** + * Unregisters soft AP callback function. + */ + @Rpc(description = "Unregister soft AP callback function.") + public void wifiUnregisterLocalOnlyHotspotSoftApCallback() { + if (mSoftApCallback == null) { + return; + } + + mWifiManager.unregisterLocalOnlyHotspotSoftApCallback(mSoftApCallback); + mSoftApCallback = null; + } + + /** + * Enables all saved networks. + */ + @Rpc(description = "Enable all saved networks.") + public void wifiEnableAllSavedNetworks() { + for (WifiConfiguration savedNetwork : mWifiManager.getConfiguredNetworks()) { + mWifiManager.enableNetwork(savedNetwork.networkId, false); + } + } + + /** + * Disables all saved networks. + */ + @Rpc(description = "Disable all saved networks.") + public void wifiDisableAllSavedNetworks() { + for (WifiConfiguration savedNetwork : mWifiManager.getConfiguredNetworks()) { + mWifiManager.disableNetwork(savedNetwork.networkId); + } + } + + /** + * Checks the softap_disconnect_reason flag. + * + * @return {@code true} if the softap_disconnect_reason flag is enabled, {@code false} + * otherwise. + */ + @Rpc(description = "Checks SoftApDisconnectReason flag.") + public boolean wifiCheckSoftApDisconnectReasonFlag() { + return Flags.softapDisconnectReason(); + } + + /** + * Gets the Wi-Fi tethered AP Configuration. + * + * @return AP details in {@link SoftApConfiguration} as JSON format. + */ + @Rpc(description = "Get current SoftApConfiguration.") + public JSONObject wifiGetSoftApConfiguration() throws JSONException { + return WifiJsonConverter.serialize(mWifiManager.getSoftApConfiguration()); + } + + /** + * Waits for tethering to be disabled. + * + * @return {@code true} if tethering is disabled within the timeout, {@code false} otherwise. + */ + @Rpc(description = "Call to wait for tethering to be disabled.") + public boolean wifiWaitForTetheringDisabled() { + try { + PollingCheck.check("Tethering NOT disabled", POLLING_TIMEOUT_MS, + () -> !mWifiManager.isWifiApEnabled()); + } catch (Exception e) { + return false; + } + return true; + } +} diff --git a/tests/hostsidetests/multidevices/com.google.snippet.wifi/softap/TetheringManagerSnippet.java b/tests/hostsidetests/multidevices/com.google.snippet.wifi/softap/TetheringManagerSnippet.java new file mode 100644 index 0000000000..ce1bc5f61e --- /dev/null +++ b/tests/hostsidetests/multidevices/com.google.snippet.wifi/softap/TetheringManagerSnippet.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2024 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.google.snippet.wifi.softap; + +import android.content.Context; +import android.net.TetheringManager; +import android.net.TetheringManager.TetheringRequest; +import android.os.Handler; +import android.os.HandlerThread; +import android.util.Log; + +import androidx.test.platform.app.InstrumentationRegistry; + +import com.google.android.mobly.snippet.Snippet; +import com.google.android.mobly.snippet.event.EventCache; +import com.google.android.mobly.snippet.event.SnippetEvent; +import com.google.android.mobly.snippet.rpc.AsyncRpc; +import com.google.android.mobly.snippet.rpc.Rpc; + +/** Snippet class for TetheringManager. */ +public class TetheringManagerSnippet implements Snippet { + private static final String TAG = "TetheringManagerSnippet"; + + private final TetheringManager mTetheringManager; + private final Handler mHandler; + + + /** Callback class to get the results of tethering start. */ + private static class SnippetStartTetheringCallback implements + TetheringManager.StartTetheringCallback { + private final String mCallbackId; + + SnippetStartTetheringCallback(String callbackId) { + mCallbackId = callbackId; + } + + @Override + public void onTetheringStarted() { + Log.d(TAG, "onTetheringStarted"); + SnippetEvent event = new SnippetEvent(mCallbackId, "onTetheringStarted"); + EventCache.getInstance().postEvent(event); + } + + @Override + public void onTetheringFailed(final int error) { + Log.d(TAG, "onTetheringFailed, error=" + error); + SnippetEvent event = new SnippetEvent(mCallbackId, "onTetheringFailed"); + event.getData().putInt("error", error); + EventCache.getInstance().postEvent(event); + } + } + + public TetheringManagerSnippet() { + Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); + mTetheringManager = context.getSystemService(TetheringManager.class); + HandlerThread handlerThread = new HandlerThread(getClass().getSimpleName()); + handlerThread.start(); + mHandler = new Handler(handlerThread.getLooper()); + } + + /** + * Starts tethering. + * + * @param callbackId A unique identifier assigned automatically by Mobly. + */ + @AsyncRpc(description = "Call to start tethering.") + public void tetheringStartTethering(String callbackId) { + TetheringRequest request = + new TetheringRequest.Builder(TetheringManager.TETHERING_WIFI).build(); + + SnippetStartTetheringCallback callback = new SnippetStartTetheringCallback(callbackId); + mTetheringManager.startTethering(request, mHandler::post, callback); + } + + /** + * Stop tethering. + */ + @Rpc(description = "Call to stop tethering.") + public void tetheringStopTethering() { + mTetheringManager.stopTethering(TetheringManager.TETHERING_WIFI); + } +} diff --git a/tests/hostsidetests/multidevices/test/Android.bp b/tests/hostsidetests/multidevices/test/Android.bp index 81c7eb8b7b..c177cc7303 100644 --- a/tests/hostsidetests/multidevices/test/Android.bp +++ b/tests/hostsidetests/multidevices/test/Android.bp @@ -39,7 +39,7 @@ python_defaults { "mobly", ], test_suites: [ - "cts", + "cts-interactive", "general-tests", ], version: { @@ -57,7 +57,6 @@ python_test_host { device_common_data: [ // Package the snippet with the mobly test ":wifi_aware_snippet", - "requirements.txt", ], test_options: { unit_test: false, @@ -65,10 +64,10 @@ python_test_host { defaults: ["CtsWifiMultiDevicePythonDefaults"], } -// TODO: Rename this to CtsWifiAwareTestCases when adding the CTS tag. python_test_host { - name: "WifiAwareManagerTestCases", + name: "CtsWifiAwareTests", main: "aware/cts_wifi_aware_test_suite.py", + test_config: "aware/AndroidTestNew.xml", srcs: [ "aware/cts_wifi_aware_test_suite.py", "aware/wifi_aware_network_test.py", @@ -85,9 +84,12 @@ python_test_host { device_common_data: [":wifi_mobly_snippet"], test_options: { unit_test: false, - tags: ["mobly"], + runner: "mobly", }, - test_suites: ["general-tests"], + test_suites: [ + "general-tests", + "cts-v-host", + ], version: { py3: { embedded_launcher: true, @@ -131,7 +133,7 @@ python_test_host { } python_test_host { - name: "CtsWifiDirectTestCases", + name: "CtsWifiDirectTests", main: "direct/cts_wifi_direct_test_suite.py", srcs: [ "direct/group_owner_negotiation_test.py", @@ -140,6 +142,7 @@ python_test_host { "direct/service_discovery_test.py", "direct/cts_wifi_direct_test_suite.py", ], + test_config: "direct/AndroidTest.xml", libs: [ "mobly", "wifi_direct_constants", @@ -150,9 +153,42 @@ python_test_host { device_common_data: [":wifi_mobly_snippet"], test_options: { unit_test: false, - tags: ["mobly"], + runner: "mobly", }, - test_suites: ["general-tests"], + test_suites: [ + "general-tests", + "cts-v-host", + ], + version: { + py3: { + embedded_launcher: true, + }, + }, +} + +python_library_host { + name: "wifi_softap_constants", + srcs: ["softap/constants.py"], +} + +python_test_host { + name: "CtsWifiSoftApTestCases", + main: "softap/wifi_softap_test.py", + srcs: ["softap/wifi_softap_test.py"], + test_config: "softap/AndroidTest.xml", + libs: [ + "mobly", + "wifi_softap_constants", + ], + device_common_data: [":wifi_mobly_snippet"], + test_options: { + unit_test: false, + runner: "mobly", + }, + test_suites: [ + "general-tests", + "cts-v-host", + ], version: { py3: { embedded_launcher: true, diff --git a/tests/hostsidetests/multidevices/test/aware/AndroidTestNew.xml b/tests/hostsidetests/multidevices/test/aware/AndroidTestNew.xml new file mode 100644 index 0000000000..244257efec --- /dev/null +++ b/tests/hostsidetests/multidevices/test/aware/AndroidTestNew.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2025 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. +--> +<configuration description="Test config for Wi-Fi Aware multi-device CTS tests"> + <option name="test-suite-tag" value="cts-v-host" /> + <option name="config-descriptor:metadata" key="component" value="wifi" /> + <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> + <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> + <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> + + <device name="AndroidDevice"> + <target_preparer class="AndroidDeviceFeaturesCheckDecorator"> + <option name="required_feature" value="android.hardware.wifi.aware" /> + </target_preparer> + <target_preparer class="AndroidMainlineModulesCheckDecorator"> + <option name="mainline_module_package_name" value="com.google.android.wifi" /> + </target_preparer> + <target_preparer class="AndroidInstallAppsDecorator" /> + </device> + <device name="AndroidDevice"> + <target_preparer class="AndroidDeviceFeaturesCheckDecorator"> + <option name="required_feature" value="android.hardware.wifi.aware" /> + </target_preparer> + <target_preparer class="AndroidMainlineModulesCheckDecorator"> + <option name="mainline_module_package_name" value="com.google.android.wifi" /> + </target_preparer> + <target_preparer class="AndroidInstallAppsDecorator" /> + </device> + + <test class="MoblyAospPackageTest" /> + + <option name="mobly_pkg" key="file" value="CtsWifiAwareTests" /> + <option name="build_apk" key="file" value="wifi_mobly_snippet.apk" /> +</configuration>
\ No newline at end of file diff --git a/tests/hostsidetests/multidevices/test/direct/AndroidTest.xml b/tests/hostsidetests/multidevices/test/direct/AndroidTest.xml new file mode 100644 index 0000000000..63f3af4ada --- /dev/null +++ b/tests/hostsidetests/multidevices/test/direct/AndroidTest.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2025 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. +--> +<configuration description="Test config for Wi-Fi Direct multi-device CTS tests"> + <option name="test-suite-tag" value="cts-v-host" /> + <option name="config-descriptor:metadata" key="component" value="wifi" /> + <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> + <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> + <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> + + <device name="AndroidDevice"> + <target_preparer class="AndroidDeviceFeaturesCheckDecorator"> + <option name="required_feature" value="android.hardware.wifi.direct" /> + </target_preparer> + <target_preparer class="AndroidMainlineModulesCheckDecorator"> + <option name="mainline_module_package_name" value="com.google.android.wifi" /> + </target_preparer> + <target_preparer class="AndroidInstallAppsDecorator" /> + </device> + <device name="AndroidDevice"> + <target_preparer class="AndroidDeviceFeaturesCheckDecorator"> + <option name="required_feature" value="android.hardware.wifi.direct" /> + </target_preparer> + <target_preparer class="AndroidMainlineModulesCheckDecorator"> + <option name="mainline_module_package_name" value="com.google.android.wifi" /> + </target_preparer> + <target_preparer class="AndroidInstallAppsDecorator" /> + </device> + + <test class="MoblyAospPackageTest" /> + + <option name="mobly_pkg" key="file" value="CtsWifiDirectTests" /> + <option name="build_apk" key="file" value="wifi_mobly_snippet.apk" /> +</configuration>
\ No newline at end of file diff --git a/tests/hostsidetests/multidevices/test/requirements.txt b/tests/hostsidetests/multidevices/test/requirements.txt deleted file mode 100644 index e69de29bb2..0000000000 --- a/tests/hostsidetests/multidevices/test/requirements.txt +++ /dev/null diff --git a/tests/hostsidetests/multidevices/test/softap/AndroidTest.xml b/tests/hostsidetests/multidevices/test/softap/AndroidTest.xml new file mode 100644 index 0000000000..19da4cca39 --- /dev/null +++ b/tests/hostsidetests/multidevices/test/softap/AndroidTest.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2025 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. +--> +<configuration description="Wifi SoftAp multi-device CTS tests"> + <option name="test-suite-tag" value="cts-v-host" /> + <option name="config-descriptor:metadata" key="component" value="wifi" /> + <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> + <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> + <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> + + <device name="AndroidDevice"> + <target_preparer class="AndroidDeviceFeaturesCheckDecorator"> + <option name="required_feature" value="android.hardware.wifi" /> + </target_preparer> + <target_preparer class="AndroidMainlineModulesCheckDecorator"> + <option name="mainline_module_package_name" value="com.google.android.wifi" /> + </target_preparer> + <target_preparer class="AndroidInstallAppsDecorator" /> + </device> + <device name="AndroidDevice"> + <target_preparer class="AndroidDeviceFeaturesCheckDecorator"> + <option name="required_feature" value="android.hardware.wifi" /> + </target_preparer> + <target_preparer class="AndroidMainlineModulesCheckDecorator"> + <option name="mainline_module_package_name" value="com.google.android.wifi" /> + </target_preparer> + <target_preparer class="AndroidInstallAppsDecorator" /> + </device> + + <test class="MoblyAospPackageTest" /> + + <option name="mobly_pkg" key="file" value="CtsWifiSoftApTestCases" /> + <option name="build_apk" key="file" value="wifi_mobly_snippet.apk" /> +</configuration>
\ No newline at end of file diff --git a/tests/hostsidetests/multidevices/test/softap/constants.py b/tests/hostsidetests/multidevices/test/softap/constants.py new file mode 100644 index 0000000000..535b30c109 --- /dev/null +++ b/tests/hostsidetests/multidevices/test/softap/constants.py @@ -0,0 +1,87 @@ +# Copyright (C) 2024 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. + +# Lint as: python3 +"""Constants for SoftAp Mobly test.""" + +import datetime +import enum + + +# Timeout duration for receiving callbacks +CALLBACK_TIMEOUT = datetime.timedelta(seconds=10) +# Wi-Fi scan result interval +WIFI_SCAN_INTERVAL_SEC = datetime.timedelta(seconds=5) + + +@enum.unique +class LocalOnlyHotspotCallbackEventName(enum.StrEnum): + """Event names for WifiManager#LocalOnlyHotspotCallback.""" + + ON_STARTED = 'onStarted' + ON_FAILED = 'onFailed' + + +@enum.unique +class LocalOnlyOnStartedDataKey(enum.StrEnum): + """Data keys received from LocalOnlyHotspotCallback#onStarted.""" + + SSID = 'ssid' + PASSPHRASE = 'passphrase' + + +@enum.unique +class LocalOnlyOnFailedDataKey(enum.StrEnum): + """Data keys received from LocalOnlyHotspotCallback#onFailed.""" + + REASON = 'reason' + + +@enum.unique +class StartTetheringCallbackEventName(enum.StrEnum): + """Event names for TetheringManager#StartTetheringCallback.""" + + ON_TETHERING_STARTED = 'onTetheringStarted' + ON_TETHERING_FAILED = 'onTetheringFailed' + + +@enum.unique +class TetheringOnTetheringFailedDataKey(enum.StrEnum): + """Data keys received from the StartTetheringCallback#onTetheringFailed.""" + + ERROR = 'error' + + +@enum.unique +class SoftApCallbackEventName(enum.StrEnum): + """Event names for WifiManager#SoftApCallback.""" + + ON_CONNECTED_CLIENTS_CHANGED = 'onConnectedClientsChanged' + ON_CLIENTS_DISCONNECTED = 'onClientsDisconnected' + + +@enum.unique +class SoftApOnConnectedClientsChangedDataKey(enum.StrEnum): + """Data keys received from SoftApCallback#onConnectedClientsChanged.""" + + CONNECTED_CLIENTS_COUNT = 'connectedClientsCount' + CLIENT_MAC_ADDRESS = 'clientMacAddress' + + +@enum.unique +class SoftApOnClientsDisconnectedDataKey(enum.StrEnum): + """Data keys received from SoftApCallback#onClientsDisconnected.""" + + DISCONNECTED_CLIENTS_COUNT = 'disconnectedClientsCount' + CLIENT_MAC_ADDRESS = 'clientMacAddress' diff --git a/tests/hostsidetests/multidevices/test/softap/wifi_softap_test.py b/tests/hostsidetests/multidevices/test/softap/wifi_softap_test.py new file mode 100644 index 0000000000..cbd592fcd1 --- /dev/null +++ b/tests/hostsidetests/multidevices/test/softap/wifi_softap_test.py @@ -0,0 +1,320 @@ +# Copyright (C) 2024 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. + +# Lint as: python3 +import time + +from mobly import asserts +from mobly import base_test +from mobly import test_runner +from mobly import utils +from mobly.controllers import android_device +from mobly.controllers.android_device_lib import callback_handler_v2 +from mobly.snippet import errors +from softap import constants + +_CALLBACK_TIMEOUT = constants.CALLBACK_TIMEOUT.total_seconds() +_WIFI_SCAN_INTERVAL_SEC = constants.WIFI_SCAN_INTERVAL_SEC.total_seconds() + + +class WifiSoftApTest(base_test.BaseTestClass): + """SoftAp test class. + + Attributes: + host: Android device providing Wi-Fi hotspot + client: Android device connecting to host provided hotspot + """ + + def setup_class(self) -> None: + self.ads = self.register_controller(android_device, min_number=2) + self.host = self.ads[0] + self.client = self.ads[1] + utils.concurrent_exec( + self._setup_device, + param_list=[[ad] for ad in self.ads], + raise_on_exception=True, + ) + + asserts.abort_class_if( + not self.host.wifi.wifiIsPortableHotspotSupported(), + 'Hotspot is not supported on host, abort remaining tests.', + ) + + def _setup_device(self, ad: android_device.AndroidDevice) -> None: + ad.load_snippet('wifi', 'com.google.snippet.wifi') + + def setup_test(self): + for ad in self.ads: + self._stop_tethering(self.host) + ad.wifi.wifiDisableAllSavedNetworks() + ad.wifi.wifiEnable() + + def teardown_class(self): + for ad in self.ads: + ad.wifi.wifiEnableAllSavedNetworks() + ad.wifi.wifiEnable() + + def _test_softap_disconnect(self, use_local_only: bool): + """Tests SoftApCallback#onClientsDisconnected callback. + + Steps: + 1. Start host's hotspot and verify it is enabled. + 2. Register SoftApCallback + 3. Add client to the hotspot and verify connection. + 4. Disconnect the client and verify disconnection. + + Args: + use_local_only: True to test local-only hotspot, False to test tethering. + """ + asserts.skip_if( + not self.host.wifi.wifiCheckSoftApDisconnectReasonFlag(), + 'Skipping because softap_disconnect_reason flag is not enabled.', + ) + + # Start host's hotspot and verify that it is enabled + # and register SoftApCallback. + if use_local_only: + callback = self.host.wifi.wifiStartLocalOnlyHotspot() + ssid, password = self._wait_for_local_only_on_started(callback) + softap_callback = ( + self.host.wifi.wifiRegisterLocalOnlyHotspotSoftApCallback() + ) + else: + callback = self.host.wifi.tetheringStartTethering() + ssid, password = self._wait_for_on_tethering_started(callback) + softap_callback = self.host.wifi.wifiRegisterSoftApCallback() + + # Add client to the hotspot and verify connection. + asserts.assert_true( + self._check_wifi_scan_result_for_ssid(self.client, ssid), + 'Network could not be found in Wi-Fi scan results', + ) + self.client.wifi.wifiConnectSimple(ssid, password) + client_mac_address = self._wait_for_on_connected_clients_changed( + softap_callback + ) + + # Disconnect the client and verify disconnection. + self.client.wifi.wifiDisable() + self._wait_for_on_clients_disconnected(client_mac_address, softap_callback) + + if use_local_only: + self.host.wifi.wifiStopLocalOnlyHotspot() + self.host.wifi.wifiUnregisterLocalOnlyHotspotSoftApCallback() + else: + self.host.wifi.tetheringStopTethering() + self.host.wifi.wifiUnregisterSoftApCallback() + + def test_local_only_softap_disconnect(self): + """Tests local-only SoftAp disconnection.""" + self._test_softap_disconnect(use_local_only=True) + + def test_tethering_softap_disconnect(self): + """Tests tethering SoftAp disconnection.""" + self._test_softap_disconnect(use_local_only=False) + + def _check_wifi_scan_result_for_ssid( + self, ad: android_device.AndroidDevice, ssid: str + ) -> bool: + """Scan Wi-Fi networks for the network with the given ssid. + + Args: + ad: The Android device object. + ssid: SSID of the AP. + + Returns: + True if SSID is found in Wi-Fi scan results, False otherwise. + """ + # Due to restriction of "scan four times in a 2-minute period" in + # https://developer.android.com/guide/topics/connectivity/wifi-scan#wifi-scan-restrictions + # this function only scans 4 times looking for the ssid, 5 second + # interval between each scan. + for _ in range(1, 5): + scanned_results = ad.wifi.wifiScanAndGetResults() + scanned_ssids = sorted( + [scan_result['SSID'] for scan_result in scanned_results] + ) + if ssid in scanned_ssids: + return True + time.sleep(_WIFI_SCAN_INTERVAL_SEC) + + return False + + def _wait_for_local_only_on_started( + self, + local_only_cb: callback_handler_v2.CallbackHandlerV2, + ) -> tuple[str, str]: + """Waits for onStarted callback to be received. + + If local-only hotspot failed to start, the current test is skipped. + + Args: + local_only_cb: The LocalOnlyHotspot callback identifier. + + Returns: + A tuple containing the credentials of the hotspot session. The + tuple is in the form of (ssid of network, passphrase of network). + """ + try: + on_started_event = local_only_cb.waitAndGet( + event_name=constants.LocalOnlyHotspotCallbackEventName.ON_STARTED, + timeout=_CALLBACK_TIMEOUT, + ) + except errors.CallbackHandlerTimeoutError: + on_failed_event = local_only_cb.waitAndGet( + event_name=constants.LocalOnlyHotspotCallbackEventName.ON_FAILED, + timeout=_CALLBACK_TIMEOUT, + ) + failure_reason = on_failed_event.data[ + constants.LocalOnlyOnFailedDataKey.REASON + ] + asserts.skip( + 'Skipping this test because the local-only hotspot ' + f'could not be started. Reason: {failure_reason}' + ) + + ssid = on_started_event.data[constants.LocalOnlyOnStartedDataKey.SSID] + passphrase = on_started_event.data[ + constants.LocalOnlyOnStartedDataKey.PASSPHRASE + ] + return ssid, passphrase + + def _wait_for_on_tethering_started( + self, + tethering_cb: callback_handler_v2.CallbackHandlerV2, + ) -> tuple[str, str]: + """Waits for onTetheringStarted callback to be received. + + If tethering failed to start, the current test is skipped. + + Args: + tethering_cb: The StartTethering callback identifier. + + Returns: + A tuple containing the credentials of the tethering session. The tuple + is in the form of (ssid of network, passphrase of network). + """ + try: + tethering_cb.waitAndGet( + event_name=constants.StartTetheringCallbackEventName.ON_TETHERING_STARTED, + timeout=_CALLBACK_TIMEOUT, + ) + except errors.CallbackHandlerTimeoutError: + on_tethering_failure_event = tethering_cb.waitAndGet( + event_name=constants.StartTetheringCallbackEventName.ON_TETHERING_FAILED, + timeout=_CALLBACK_TIMEOUT, + ) + failure_error = on_tethering_failure_event.data[ + constants.TetheringOnTetheringFailedDataKey.ERROR + ] + asserts.skip( + 'Skipping this test because tethering could not be started. Reason:' + f' {failure_error}' + ) + + current_configuration = self.host.wifi.wifiGetSoftApConfiguration() + ssid = current_configuration['SSID'] + passphrase = current_configuration['mPassphrase'] + return ssid, passphrase + + def _stop_tethering(self, ad: android_device.AndroidDevice) -> bool: + """Stops any ongoing tethering sessions on the android device. + + Args: + ad: The Android device object. + + Returns: + True if tethering is disabled successfully, False otherwise. + """ + if not ad.wifi.wifiIsApEnabled(): + return True + + ad.wifi.tetheringStopTethering() + return ad.wifi.wifiWaitForTetheringDisabled() + + def _wait_for_on_connected_clients_changed( + self, softap_callback: callback_handler_v2.CallbackHandlerV2 + ) -> str: + """Wait for SoftApCallback#onConnectedClientsChanged to be received. + + The test fails if the callback is never received. If successful, the + mac address of the connected client is returned. + + Args: + softap_callback: The SoftApCallback callback identifier. + + Returns: + The string mac address of the connected client. + """ + try: + on_connected_clients_changed_event = softap_callback.waitAndGet( + event_name=( + constants.SoftApCallbackEventName.ON_CONNECTED_CLIENTS_CHANGED + ), + timeout=_CALLBACK_TIMEOUT, + ) + except errors.CallbackHandlerTimeoutError: + asserts.fail('Connection could not be established.') + + # In our test cases, there is only one other device involved + # so we can confirm that the client has connected. + asserts.assert_equal( + on_connected_clients_changed_event.data[ + constants.SoftApOnConnectedClientsChangedDataKey.CONNECTED_CLIENTS_COUNT + ], + 1, + ) + + return on_connected_clients_changed_event.data[ + constants.SoftApOnConnectedClientsChangedDataKey.CLIENT_MAC_ADDRESS + ] + + def _wait_for_on_clients_disconnected( + self, + expected_mac_address: str, + softap_callback: callback_handler_v2.CallbackHandlerV2, + ) -> None: + """Wait for SoftApCallback#onClientsDisconnected to be received. + + The test fails if the callback is never received. + + Args: + expected_mac_address: Expected mac address of disconnected client + softap_callback: The SoftApCallback callback identifier. + """ + try: + on_clients_disconnected_event = softap_callback.waitAndGet( + event_name='onClientsDisconnected', timeout=_CALLBACK_TIMEOUT + ) + except errors.CallbackHandlerTimeoutError: + asserts.fail('No client disconnected.') + + # In our test cases, there is only one other device involved + # so we can confirm that the client has disconnected. + asserts.assert_equal( + on_clients_disconnected_event.data[ + constants.SoftApOnClientsDisconnectedDataKey.DISCONNECTED_CLIENTS_COUNT + ], + 1, + ) + asserts.assert_equal( + on_clients_disconnected_event.data[ + constants.SoftApOnClientsDisconnectedDataKey.CLIENT_MAC_ADDRESS + ], + expected_mac_address, + ) + + +if __name__ == '__main__': + test_runner.main() |