blob: f0b7f17827d2366da22319290415c20487e19137 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.wifi.aware;
import static android.Manifest.permission.ACCESS_WIFI_STATE;
import static android.net.wifi.WifiAvailableChannel.OP_MODE_WIFI_AWARE;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.AttributionSource;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.wifi.V1_0.NanStatusType;
import android.hardware.wifi.V1_0.WifiStatusCode;
import android.location.LocationManager;
import android.net.MacAddress;
import android.net.wifi.WifiAvailableChannel;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiScanner;
import android.net.wifi.aware.AwareParams;
import android.net.wifi.aware.AwareResources;
import android.net.wifi.aware.Characteristics;
import android.net.wifi.aware.ConfigRequest;
import android.net.wifi.aware.IWifiAwareDiscoverySessionCallback;
import android.net.wifi.aware.IWifiAwareEventCallback;
import android.net.wifi.aware.IWifiAwareMacAddressProvider;
import android.net.wifi.aware.MacAddrMapping;
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.aware.WifiAwareManager;
import android.net.wifi.aware.WifiAwareNetworkSpecifier;
import android.net.wifi.util.HexEncoding;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.WorkSource;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.MessageUtils;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.internal.util.WakeupMessage;
import com.android.modules.utils.BasicShellCommandHandler;
import com.android.modules.utils.HandlerExecutor;
import com.android.modules.utils.build.SdkLevel;
import com.android.server.wifi.Clock;
import com.android.server.wifi.HalDeviceManager;
import com.android.server.wifi.InterfaceConflictManager;
import com.android.server.wifi.WifiInjector;
import com.android.server.wifi.util.NetdWrapper;
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.resources.R;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Manages the state of the Wi-Fi Aware system service.
*/
public class WifiAwareStateManager implements WifiAwareShellCommand.DelegatedShellCommand {
private static final String TAG = "WifiAwareStateManager";
private static final boolean VDBG = false; // STOPSHIP if true - for detailed state machine
private boolean mDbg = false;
@VisibleForTesting
public static final String HAL_COMMAND_TIMEOUT_TAG = TAG + " HAL Command Timeout";
@VisibleForTesting
public static final String HAL_SEND_MESSAGE_TIMEOUT_TAG = TAG + " HAL Send Message Timeout";
@VisibleForTesting
public static final String HAL_DATA_PATH_CONFIRM_TIMEOUT_TAG =
TAG + " HAL Data Path Confirm Timeout";
public static final int INSTANT_MODE_DISABLED = 0;
public static final int INSTANT_MODE_24GHZ = 1;
public static final int INSTANT_MODE_5GHZ = 3;
/*
* State machine message types. There are sub-types for the messages (except for TIMEOUTs).
* Format:
* - Message.arg1: contains message sub-type
* - Message.arg2: contains transaction ID for RESPONSE & RESPONSE_TIMEOUT
*/
private static final int MESSAGE_TYPE_COMMAND = 1;
private static final int MESSAGE_TYPE_RESPONSE = 2;
private static final int MESSAGE_TYPE_NOTIFICATION = 3;
private static final int MESSAGE_TYPE_RESPONSE_TIMEOUT = 4;
private static final int MESSAGE_TYPE_SEND_MESSAGE_TIMEOUT = 5;
private static final int MESSAGE_TYPE_DATA_PATH_TIMEOUT = 6;
/*
* Message sub-types:
*/
private static final int COMMAND_TYPE_CONNECT = 100;
private static final int COMMAND_TYPE_DISCONNECT = 101;
private static final int COMMAND_TYPE_TERMINATE_SESSION = 102;
private static final int COMMAND_TYPE_PUBLISH = 103;
private static final int COMMAND_TYPE_UPDATE_PUBLISH = 104;
private static final int COMMAND_TYPE_SUBSCRIBE = 105;
private static final int COMMAND_TYPE_UPDATE_SUBSCRIBE = 106;
private static final int COMMAND_TYPE_ENQUEUE_SEND_MESSAGE = 107;
private static final int COMMAND_TYPE_ENABLE_USAGE = 108;
private static final int COMMAND_TYPE_DISABLE_USAGE = 109;
private static final int COMMAND_TYPE_GET_CAPABILITIES = 111;
private static final int COMMAND_TYPE_DELETE_ALL_DATA_PATH_INTERFACES = 113;
private static final int COMMAND_TYPE_CREATE_DATA_PATH_INTERFACE = 114;
private static final int COMMAND_TYPE_DELETE_DATA_PATH_INTERFACE = 115;
private static final int COMMAND_TYPE_INITIATE_DATA_PATH_SETUP = 116;
private static final int COMMAND_TYPE_RESPOND_TO_DATA_PATH_SETUP_REQUEST = 117;
private static final int COMMAND_TYPE_END_DATA_PATH = 118;
private static final int COMMAND_TYPE_TRANSMIT_NEXT_MESSAGE = 119;
private static final int COMMAND_TYPE_RECONFIGURE = 120;
private static final int COMMAND_TYPE_DELAYED_INITIALIZATION = 121;
private static final int COMMAND_TYPE_GET_AWARE = 122;
private static final int COMMAND_TYPE_RELEASE_AWARE = 123;
private static final int COMMAND_TYPE_DISABLE = 124;
private static final int RESPONSE_TYPE_ON_CONFIG_SUCCESS = 200;
private static final int RESPONSE_TYPE_ON_CONFIG_FAIL = 201;
private static final int RESPONSE_TYPE_ON_SESSION_CONFIG_SUCCESS = 202;
private static final int RESPONSE_TYPE_ON_SESSION_CONFIG_FAIL = 203;
private static final int RESPONSE_TYPE_ON_MESSAGE_SEND_QUEUED_SUCCESS = 204;
private static final int RESPONSE_TYPE_ON_MESSAGE_SEND_QUEUED_FAIL = 205;
private static final int RESPONSE_TYPE_ON_CAPABILITIES_UPDATED = 206;
private static final int RESPONSE_TYPE_ON_CREATE_INTERFACE = 207;
private static final int RESPONSE_TYPE_ON_DELETE_INTERFACE = 208;
private static final int RESPONSE_TYPE_ON_INITIATE_DATA_PATH_SUCCESS = 209;
private static final int RESPONSE_TYPE_ON_INITIATE_DATA_PATH_FAIL = 210;
private static final int RESPONSE_TYPE_ON_RESPOND_TO_DATA_PATH_SETUP_REQUEST = 211;
private static final int RESPONSE_TYPE_ON_END_DATA_PATH = 212;
private static final int RESPONSE_TYPE_ON_DISABLE = 213;
private static final int NOTIFICATION_TYPE_INTERFACE_CHANGE = 301;
private static final int NOTIFICATION_TYPE_CLUSTER_CHANGE = 302;
private static final int NOTIFICATION_TYPE_MATCH = 303;
private static final int NOTIFICATION_TYPE_SESSION_TERMINATED = 304;
private static final int NOTIFICATION_TYPE_MESSAGE_RECEIVED = 305;
private static final int NOTIFICATION_TYPE_AWARE_DOWN = 306;
private static final int NOTIFICATION_TYPE_ON_MESSAGE_SEND_SUCCESS = 307;
private static final int NOTIFICATION_TYPE_ON_MESSAGE_SEND_FAIL = 308;
private static final int NOTIFICATION_TYPE_ON_DATA_PATH_REQUEST = 309;
private static final int NOTIFICATION_TYPE_ON_DATA_PATH_CONFIRM = 310;
private static final int NOTIFICATION_TYPE_ON_DATA_PATH_END = 311;
private static final int NOTIFICATION_TYPE_ON_DATA_PATH_SCHED_UPDATE = 312;
private static final int NOTIFICATION_TYPE_MATCH_EXPIRED = 313;
private static final SparseArray<String> sSmToString = MessageUtils.findMessageNames(
new Class[]{WifiAwareStateManager.class},
new String[]{"MESSAGE_TYPE", "COMMAND_TYPE", "RESPONSE_TYPE", "NOTIFICATION_TYPE"});
/*
* Keys used when passing (some) arguments to the Handler thread (too many
* arguments to pass in the short-cut Message members).
*/
private static final String MESSAGE_BUNDLE_KEY_SESSION_TYPE = "session_type";
private static final String MESSAGE_BUNDLE_KEY_SESSION_ID = "session_id";
private static final String MESSAGE_BUNDLE_KEY_CONFIG = "config";
private static final String MESSAGE_BUNDLE_KEY_MESSAGE = "message";
private static final String MESSAGE_BUNDLE_KEY_MESSAGE_PEER_ID = "message_peer_id";
private static final String MESSAGE_BUNDLE_KEY_MESSAGE_ID = "message_id";
private static final String MESSAGE_BUNDLE_KEY_SSI_DATA = "ssi_data";
private static final String MESSAGE_BUNDLE_KEY_FILTER_DATA = "filter_data";
private static final String MESSAGE_BUNDLE_KEY_MAC_ADDRESS = "mac_address";
private static final String MESSAGE_BUNDLE_KEY_MESSAGE_DATA = "message_data";
private static final String MESSAGE_BUNDLE_KEY_REQ_INSTANCE_ID = "req_instance_id";
private static final String MESSAGE_BUNDLE_KEY_SEND_MESSAGE_ENQUEUE_TIME = "message_queue_time";
private static final String MESSAGE_BUNDLE_KEY_RETRY_COUNT = "retry_count";
private static final String MESSAGE_BUNDLE_KEY_SUCCESS_FLAG = "success_flag";
private static final String MESSAGE_BUNDLE_KEY_STATUS_CODE = "status_code";
private static final String MESSAGE_BUNDLE_KEY_INTERFACE_NAME = "interface_name";
private static final String MESSAGE_BUNDLE_KEY_CHANNEL_REQ_TYPE = "channel_request_type";
private static final String MESSAGE_BUNDLE_KEY_CHANNEL = "channel";
private static final String MESSAGE_BUNDLE_KEY_PEER_ID = "peer_id";
private static final String MESSAGE_BUNDLE_KEY_UID = "uid";
private static final String MESSAGE_BUNDLE_KEY_PID = "pid";
private static final String MESSAGE_BUNDLE_KEY_CALLING_PACKAGE = "calling_package";
private static final String MESSAGE_BUNDLE_KEY_CALLING_FEATURE_ID = "calling_feature_id";
private static final String MESSAGE_BUNDLE_KEY_SENT_MESSAGE = "send_message";
private static final String MESSAGE_BUNDLE_KEY_MESSAGE_ARRIVAL_SEQ = "message_arrival_seq";
private static final String MESSAGE_BUNDLE_KEY_NOTIFY_IDENTITY_CHANGE = "notify_identity_chg";
private static final String MESSAGE_BUNDLE_KEY_SCID = "scid";
private static final String MESSAGE_BUNDLE_KEY_CIPHER_SUITE = "cipher_suite";
private static final String MESSAGE_BUNDLE_KEY_OOB = "out_of_band";
private static final String MESSAGE_RANGING_INDICATION = "ranging_indication";
private static final String MESSAGE_RANGE_MM = "range_mm";
private static final String MESSAGE_BUNDLE_KEY_NDP_IDS = "ndp_ids";
private static final String MESSAGE_BUNDLE_KEY_APP_INFO = "app_info";
private static final String MESSAGE_BUNDLE_KEY_ACCEPT_STATE = "accept_state";
private WifiAwareNativeApi mWifiAwareNativeApi;
private WifiAwareNativeManager mWifiAwareNativeManager;
/*
* Asynchronous access with no lock
*/
private volatile boolean mUsageEnabled = false;
/*
* Synchronous access: state is only accessed through the state machine
* handler thread: no need to use a lock.
*/
private Context mContext;
private WifiAwareMetrics mAwareMetrics;
private WifiPermissionsUtil mWifiPermissionsUtil;
private volatile Capabilities mCapabilities;
private volatile Characteristics mCharacteristics = null;
private WifiAwareStateMachine mSm;
public WifiAwareDataPathStateManager mDataPathMgr;
private PowerManager mPowerManager;
private LocationManager mLocationManager;
private InterfaceConflictManager mInterfaceConflictMgr;
private WifiManager mWifiManager;
private Handler mHandler;
private final WifiInjector mWifiInjector;
private final SparseArray<WifiAwareClientState> mClients = new SparseArray<>();
private ConfigRequest mCurrentAwareConfiguration = null;
private boolean mCurrentIdentityNotification = false;
private boolean mCurrentRangingEnabled = false;
private boolean mInstantCommModeGlobalEnable = false;
private int mInstantCommModeClientRequest = INSTANT_MODE_DISABLED;
private static final int AWARE_BAND_2_INSTANT_COMMUNICATION_CHANNEL_FREQ = 2437; // Channel 6
private int mAwareBand5InstantCommunicationChannelFreq = -1; // -1 is not set, 0 is unsupported.
private static final int AWARE_BAND_5_INSTANT_COMMUNICATION_CHANNEL_FREQ_CHANNEL_149 = 5745;
private static final int AWARE_BAND_5_INSTANT_COMMUNICATION_CHANNEL_FREQ_CHANNEL_44 = 5220;
private static final byte[] ALL_ZERO_MAC = new byte[] {0, 0, 0, 0, 0, 0};
private byte[] mCurrentDiscoveryInterfaceMac = ALL_ZERO_MAC;
// Flag to help defer the connect request when disable Aware is not finished, to prevent race
// condition.
private boolean mAwareIsDisabling = false;
public WifiAwareStateManager(WifiInjector wifiInjector) {
mWifiInjector = wifiInjector;
onReset();
}
/**
* Enable verbose logging.
*/
public void enableVerboseLogging(boolean verbose) {
mDbg = verbose | VDBG;
}
/**
* Inject references to other manager objects. Needed to resolve
* circular dependencies and to allow mocking.
*/
public void setNative(WifiAwareNativeManager wifiAwareNativeManager,
WifiAwareNativeApi wifiAwareNativeApi) {
mWifiAwareNativeManager = wifiAwareNativeManager;
mWifiAwareNativeApi = wifiAwareNativeApi;
}
/*
* parameters settable through shell command
*/
public static final String PARAM_ON_IDLE_DISABLE_AWARE = "on_idle_disable_aware";
public static final int PARAM_ON_IDLE_DISABLE_AWARE_DEFAULT = 1; // 0 = false, 1 = true
private Map<String, Integer> mSettableParameters = new HashMap<>();
/**
* Interpreter of adb shell command 'adb shell wifiaware native_api ...'.
*
* @return -1 if parameter not recognized or invalid value, 0 otherwise.
*/
@Override
public int onCommand(BasicShellCommandHandler parentShell) {
final PrintWriter pw_err = parentShell.getErrPrintWriter();
final PrintWriter pw_out = parentShell.getOutPrintWriter();
String subCmd = parentShell.getNextArgRequired();
switch (subCmd) {
case "set": {
String name = parentShell.getNextArgRequired();
if (!mSettableParameters.containsKey(name)) {
pw_err.println("Unknown parameter name -- '" + name + "'");
return -1;
}
String valueStr = parentShell.getNextArgRequired();
int value;
try {
value = Integer.valueOf(valueStr);
} catch (NumberFormatException e) {
pw_err.println("Can't convert value to integer -- '" + valueStr + "'");
return -1;
}
mSettableParameters.put(name, value);
return 0;
}
case "get": {
String name = parentShell.getNextArgRequired();
if (!mSettableParameters.containsKey(name)) {
pw_err.println("Unknown parameter name -- '" + name + "'");
return -1;
}
pw_out.println((int) mSettableParameters.get(name));
return 0;
}
case "get_capabilities": {
JSONObject j = new JSONObject();
if (mCapabilities != null) {
try {
j.put("maxConcurrentAwareClusters",
mCapabilities.maxConcurrentAwareClusters);
j.put("maxPublishes", mCapabilities.maxPublishes);
j.put("maxSubscribes", mCapabilities.maxSubscribes);
j.put("maxServiceNameLen", mCapabilities.maxServiceNameLen);
j.put("maxMatchFilterLen", mCapabilities.maxMatchFilterLen);
j.put("maxTotalMatchFilterLen", mCapabilities.maxTotalMatchFilterLen);
j.put("maxServiceSpecificInfoLen", mCapabilities.maxServiceSpecificInfoLen);
j.put("maxExtendedServiceSpecificInfoLen",
mCapabilities.maxExtendedServiceSpecificInfoLen);
j.put("maxNdiInterfaces", mCapabilities.maxNdiInterfaces);
j.put("maxNdpSessions", mCapabilities.maxNdpSessions);
j.put("maxAppInfoLen", mCapabilities.maxAppInfoLen);
j.put("maxQueuedTransmitMessages", mCapabilities.maxQueuedTransmitMessages);
j.put("maxSubscribeInterfaceAddresses",
mCapabilities.maxSubscribeInterfaceAddresses);
j.put("supportedCipherSuites", mCapabilities.supportedCipherSuites);
j.put("isInstantCommunicationModeSupported",
mCapabilities.isInstantCommunicationModeSupported);
} catch (JSONException e) {
Log.e(TAG, "onCommand: get_capabilities e=" + e);
}
}
pw_out.println(j.toString());
return 0;
}
case "get_aware_resources": {
if (!SdkLevel.isAtLeastS()) {
return -1;
}
JSONObject j = new JSONObject();
AwareResources resources = getAvailableAwareResources();
if (resources != null) {
try {
j.put("numOfAvailableNdps", resources.getAvailableDataPathsCount());
j.put("numOfAvailablePublishSessions",
resources.getAvailablePublishSessionsCount());
j.put("numOfAvailableSubscribeSessions",
resources.getAvailableSubscribeSessionsCount());
} catch (JSONException e) {
Log.e(TAG, "onCommand: get_aware_resources e=" + e);
}
}
pw_out.println(j.toString());
return 0;
}
case "allow_ndp_any": {
String flag = parentShell.getNextArgRequired();
if (mDataPathMgr == null) {
pw_err.println("Null Aware data-path manager - can't configure");
return -1;
}
if (TextUtils.equals("true", flag)) {
mDataPathMgr.mAllowNdpResponderFromAnyOverride = true;
return 0;
} else if (TextUtils.equals("false", flag)) {
mDataPathMgr.mAllowNdpResponderFromAnyOverride = false;
return 0;
} else {
pw_err.println(
"Unknown configuration flag for 'allow_ndp_any' - true|false expected"
+ " -- '"
+ flag + "'");
return -1;
}
}
case "get_instant_communication_channel": {
String arg = parentShell.getNextArgRequired();
int band;
if (TextUtils.equals(arg, "2G")) {
band = WifiScanner.WIFI_BAND_24_GHZ;
} else if (TextUtils.equals(arg, "5G")) {
band = WifiScanner.WIFI_BAND_5_GHZ;
} else {
pw_err.println("Unknown band -- " + arg);
return -1;
}
List<WifiAvailableChannel> channels = mWifiInjector.getWifiThreadRunner().call(
() -> mWifiInjector.getWifiNative().getUsableChannels(band,
OP_MODE_WIFI_AWARE,
WifiAvailableChannel.FILTER_NAN_INSTANT_MODE), null);
StringBuilder out = new StringBuilder();
for (WifiAvailableChannel channel : channels) {
out.append(channel.toString());
out.append(", ");
}
pw_out.println(out.toString());
return 0;
}
default:
pw_err.println("Unknown 'wifiaware state_mgr <cmd>'");
}
return -1;
}
@Override
public void onReset() {
mSettableParameters.put(PARAM_ON_IDLE_DISABLE_AWARE, PARAM_ON_IDLE_DISABLE_AWARE_DEFAULT);
if (mDataPathMgr != null) {
mDataPathMgr.mAllowNdpResponderFromAnyOverride = false;
}
}
@Override
public void onHelp(String command, BasicShellCommandHandler parentShell) {
final PrintWriter pw = parentShell.getOutPrintWriter();
pw.println(" " + command);
pw.println(" set <name> <value>: sets named parameter to value. Names: "
+ mSettableParameters.keySet());
pw.println(" get <name>: gets named parameter value. Names: "
+ mSettableParameters.keySet());
pw.println(" get_capabilities: prints out the capabilities as a JSON string");
pw.println(
" allow_ndp_any true|false: configure whether Responders can be specified to "
+ "accept requests from ANY requestor (null peer spec)");
pw.println(" get_instant_communication_channel 2G|5G: get instant communication mode "
+ "channel available for the target band");
}
/**
* Initialize the handler of the state manager with the specified thread
* looper.
*
* @param looper Thread looper on which to run the handler.
*/
public void start(Context context, Looper looper, WifiAwareMetrics awareMetrics,
WifiPermissionsUtil wifiPermissionsUtil, WifiPermissionsWrapper permissionsWrapper,
Clock clock, NetdWrapper netdWrapper, InterfaceConflictManager interfaceConflictMgr) {
Log.i(TAG, "start()");
mContext = context;
mAwareMetrics = awareMetrics;
mWifiPermissionsUtil = wifiPermissionsUtil;
mInterfaceConflictMgr = interfaceConflictMgr;
mSm = new WifiAwareStateMachine(TAG, looper);
mSm.setDbg(VDBG);
mSm.start();
mHandler = new Handler(looper);
mDataPathMgr = new WifiAwareDataPathStateManager(this, clock);
mDataPathMgr.start(mContext, mSm.getHandler().getLooper(), awareMetrics,
wifiPermissionsUtil, permissionsWrapper, netdWrapper);
mPowerManager = mContext.getSystemService(PowerManager.class);
mLocationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
intentFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (mDbg) Log.v(TAG, "BroadcastReceiver: action=" + action);
if (action.equals(Intent.ACTION_SCREEN_ON)
|| action.equals(Intent.ACTION_SCREEN_OFF)) {
reconfigure();
}
if (action.equals(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)) {
if (mSettableParameters.get(PARAM_ON_IDLE_DISABLE_AWARE) != 0) {
if (mPowerManager.isDeviceIdleMode()) {
disableUsage(false);
} else {
enableUsage();
}
} else {
reconfigure();
}
}
}
}, intentFilter);
intentFilter = new IntentFilter();
intentFilter.addAction(LocationManager.MODE_CHANGED_ACTION);
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (mDbg) Log.v(TAG, "onReceive: MODE_CHANGED_ACTION: intent=" + intent);
if (wifiPermissionsUtil.isLocationModeEnabled()) {
enableUsage();
} else {
if (SdkLevel.isAtLeastT()) {
handleLocationModeDisabled();
} else {
disableUsage(false);
}
}
}
}, intentFilter);
intentFilter = new IntentFilter();
intentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (mDbg) Log.v(TAG, "onReceive: WIFI_STATE_CHANGED_ACTION: intent=" + intent);
boolean isEnabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED;
if (isEnabled) {
enableUsage();
} else {
disableUsage(false);
}
}
}, intentFilter);
}
private class CountryCodeChangeCallback implements
WifiManager.ActiveCountryCodeChangedCallback {
@Override
public void onActiveCountryCodeChanged(@androidx.annotation.NonNull String countryCode) {
mAwareBand5InstantCommunicationChannelFreq = -1;
reconfigure();
}
@Override
public void onCountryCodeInactive() {
// Ignore.
}
}
/**
* Initialize the late-initialization sub-services: depend on other services already existing.
*/
public void startLate() {
delayedInitialization();
}
/**
* Try to get capability if it is null.
*/
public void tryToGetAwareCapability() {
if (mCapabilities != null) return;
// Internal request for fetching capabilities.
getAwareInterface(new WorkSource(Process.WIFI_UID));
queryCapabilities();
releaseAwareInterface();
}
/**
* Get the client state for the specified ID (or null if none exists).
*/
/* package */ WifiAwareClientState getClient(int clientId) {
return mClients.get(clientId);
}
/**
* Get the capabilities.
*/
public Capabilities getCapabilities() {
return mCapabilities;
}
/**
* Get the available aware resources.
*/
public AwareResources getAvailableAwareResources() {
if (mCapabilities == null) {
if (mDbg) {
Log.v(TAG, "Aware capability hasn't loaded, resources is unknown.");
}
return null;
}
Pair<Integer, Integer> numOfDiscoverySessions = getNumOfDiscoverySessions();
int numOfAvailableNdps = mCapabilities.maxNdpSessions - mDataPathMgr.getNumOfNdps();
int numOfAvailablePublishSessions =
mCapabilities.maxPublishes - numOfDiscoverySessions.first;
int numOfAvailableSubscribeSessions =
mCapabilities.maxSubscribes - numOfDiscoverySessions.second;
if (numOfAvailableNdps < 0) {
Log.w(TAG, "Available NDPs number is negative, wrong capability?");
}
if (numOfAvailablePublishSessions < 0) {
Log.w(TAG, "Available publish session number is negative, wrong capability?");
}
if (numOfAvailableSubscribeSessions < 0) {
Log.w(TAG, "Available subscribe session number is negative, wrong capability?");
}
return new AwareResources(numOfAvailableNdps, numOfAvailablePublishSessions,
numOfAvailableSubscribeSessions);
}
private Pair<Integer, Integer> getNumOfDiscoverySessions() {
int numOfPub = 0;
int numOfSub = 0;
for (int i = 0; i < mClients.size(); i++) {
WifiAwareClientState clientState = mClients.valueAt(i);
for (int j = 0; j < clientState.getSessions().size(); j++) {
WifiAwareDiscoverySessionState session = clientState.getSessions().valueAt(j);
if (session.isPublishSession()) {
numOfPub++;
} else {
numOfSub++;
}
}
}
return Pair.create(numOfPub, numOfSub);
}
/**
* Get the public characteristics derived from the capabilities. Use lazy initialization.
*/
public Characteristics getCharacteristics() {
if (mCharacteristics == null && mCapabilities != null) {
mCharacteristics = mCapabilities.toPublicCharacteristics();
}
return mCharacteristics;
}
/**
* Check if there is any active attach session
*/
public boolean isDeviceAttached() {
return mClients != null && mClients.size() > 0;
}
/*
* Cross-service API: synchronized but independent of state machine
*/
/**
* Translate (and return in the callback) the peerId to its MAC address representation.
*/
public void requestMacAddresses(int uid, int[] peerIds,
IWifiAwareMacAddressProvider callback) {
mSm.getHandler().post(() -> {
if (VDBG) Log.v(TAG, "requestMacAddresses: uid=" + uid + ", peerIds=" + peerIds);
Map<Integer, MacAddrMapping> peerIdToMacMap = new HashMap<>();
for (int i = 0; i < mClients.size(); ++i) {
WifiAwareClientState client = mClients.valueAt(i);
if (client.getUid() != uid) {
continue;
}
SparseArray<WifiAwareDiscoverySessionState> sessions = client.getSessions();
for (int j = 0; j < sessions.size(); ++j) {
WifiAwareDiscoverySessionState session = sessions.valueAt(j);
for (int peerId : peerIds) {
WifiAwareDiscoverySessionState.PeerInfo peerInfo = session.getPeerInfo(
peerId);
if (peerInfo != null) {
MacAddrMapping mapping = new MacAddrMapping();
mapping.peerId = peerId;
mapping.macAddress = peerInfo.mMac;
peerIdToMacMap.put(peerId, mapping);
}
}
}
}
try {
MacAddrMapping[] peerIdToMacList = peerIdToMacMap.values()
.toArray(new MacAddrMapping[0]);
if (mDbg) {
Log.v(TAG, "requestMacAddresses: peerIdToMacList begin");
for (MacAddrMapping mapping : peerIdToMacList) {
Log.v(TAG, " " + mapping.peerId + ": "
+ MacAddress.fromBytes(mapping.macAddress));
}
Log.v(TAG, "requestMacAddresses: peerIdToMacList end");
}
callback.macAddress(peerIdToMacList);
} catch (RemoteException e) {
Log.e(TAG, "requestMacAddress (sync): exception on callback -- " + e);
}
});
}
/*
* COMMANDS
*/
/**
* Place a request for delayed start operation on the state machine queue.
*/
public void delayedInitialization() {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
msg.arg1 = COMMAND_TYPE_DELAYED_INITIALIZATION;
mSm.sendMessage(msg);
}
/**
* Place a request to get the Wi-Fi Aware interface (before which no HAL command can be
* executed).
*/
public void getAwareInterface(@NonNull WorkSource requestorWs) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
msg.arg1 = COMMAND_TYPE_GET_AWARE;
msg.obj = requestorWs;
mSm.sendMessage(msg);
}
/**
* Place a request to release the Wi-Fi Aware interface (after which no HAL command can be
* executed).
*/
public void releaseAwareInterface() {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
msg.arg1 = COMMAND_TYPE_RELEASE_AWARE;
mSm.sendMessage(msg);
}
/**
* Enable instant communication mode if supported.
* @param enabled true for enable, false for disable.
*/
public void enableInstantCommunicationMode(boolean enabled) {
if (mCapabilities == null) {
if (mDbg) {
Log.v(TAG, "Aware capability is not loaded.");
}
return;
}
if (!mCapabilities.isInstantCommunicationModeSupported) {
if (mDbg) {
Log.v(TAG, "Device does not support instant communication mode.");
}
return;
}
boolean changed = mInstantCommModeGlobalEnable != enabled;
mInstantCommModeGlobalEnable = enabled;
if (!changed) {
return;
}
reconfigure();
}
/**
* Get if instant communication mode is currently enabled.
* @return true if enabled, false otherwise.
*/
public boolean isInstantCommModeGlobalEnable() {
return mInstantCommModeGlobalEnable;
}
/**
* Get if set channel on data-path request is supported.
* @return true if supported, false otherwise.
*/
public boolean isSetChannelOnDataPathSupported() {
return mContext.getResources()
.getBoolean(R.bool.config_wifiSupportChannelOnDataPath);
}
/**
* Accept using parameter from external to config the Aware,
* @see WifiAwareManager#setAwareParams(AwareParams)
*/
public void setAwareParams(AwareParams parameters) {
mHandler.post(() -> {
mWifiAwareNativeApi.setAwareParams(parameters);
reconfigure();
});
}
/**
* Place a request for a new client connection on the state machine queue.
*/
public void connect(int clientId, int uid, int pid, String callingPackage,
@Nullable String callingFeatureId, IWifiAwareEventCallback callback,
ConfigRequest configRequest, boolean notifyOnIdentityChanged, Bundle extra) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
msg.arg1 = COMMAND_TYPE_CONNECT;
msg.arg2 = clientId;
Pair<IWifiAwareEventCallback, Object> callbackAndAttributionSource = new Pair<>(
callback, extra.getParcelable(WifiManager.EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE));
msg.obj = callbackAndAttributionSource;
msg.getData().putParcelable(MESSAGE_BUNDLE_KEY_CONFIG, configRequest);
msg.getData().putInt(MESSAGE_BUNDLE_KEY_UID, uid);
msg.getData().putInt(MESSAGE_BUNDLE_KEY_PID, pid);
msg.getData().putString(MESSAGE_BUNDLE_KEY_CALLING_PACKAGE, callingPackage);
msg.getData().putString(MESSAGE_BUNDLE_KEY_CALLING_FEATURE_ID, callingFeatureId);
msg.getData().putBoolean(MESSAGE_BUNDLE_KEY_NOTIFY_IDENTITY_CHANGE,
notifyOnIdentityChanged);
mSm.sendMessage(msg);
}
/**
* Place a request to disconnect (destroy) an existing client on the state
* machine queue.
*/
public void disconnect(int clientId) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
msg.arg1 = COMMAND_TYPE_DISCONNECT;
msg.arg2 = clientId;
mSm.sendMessage(msg);
}
/**
* Place a request to defer Disable Aware on the state machine queue.
*/
private void deferDisableAware() {
mAwareIsDisabling = true;
Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
msg.arg1 = COMMAND_TYPE_DISABLE;
mSm.sendMessage(msg);
}
/**
* Place a request to reconfigure Aware. No additional input - intended to use current
* power settings when executed. Thus possibly entering or exiting power saving mode if
* needed (or do nothing if Aware is not active).
*/
public void reconfigure() {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
msg.arg1 = COMMAND_TYPE_RECONFIGURE;
mSm.sendMessage(msg);
}
/**
* Place a request to stop a discovery session on the state machine queue.
*/
public void terminateSession(int clientId, int sessionId) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
msg.arg1 = COMMAND_TYPE_TERMINATE_SESSION;
msg.arg2 = clientId;
msg.obj = sessionId;
mSm.sendMessage(msg);
}
/**
* Place a request to start a new publish discovery session on the state
* machine queue.
*/
public void publish(int clientId, PublishConfig publishConfig,
IWifiAwareDiscoverySessionCallback callback) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
msg.arg1 = COMMAND_TYPE_PUBLISH;
msg.arg2 = clientId;
msg.obj = callback;
msg.getData().putParcelable(MESSAGE_BUNDLE_KEY_CONFIG, publishConfig);
mSm.sendMessage(msg);
}
/**
* Place a request to modify an existing publish discovery session on the
* state machine queue.
*/
public void updatePublish(int clientId, int sessionId, PublishConfig publishConfig) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
msg.arg1 = COMMAND_TYPE_UPDATE_PUBLISH;
msg.arg2 = clientId;
msg.getData().putParcelable(MESSAGE_BUNDLE_KEY_CONFIG, publishConfig);
msg.getData().putInt(MESSAGE_BUNDLE_KEY_SESSION_ID, sessionId);
mSm.sendMessage(msg);
}
/**
* Place a request to start a new subscribe discovery session on the state
* machine queue.
*/
public void subscribe(int clientId, SubscribeConfig subscribeConfig,
IWifiAwareDiscoverySessionCallback callback) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
msg.arg1 = COMMAND_TYPE_SUBSCRIBE;
msg.arg2 = clientId;
msg.obj = callback;
msg.getData().putParcelable(MESSAGE_BUNDLE_KEY_CONFIG, subscribeConfig);
mSm.sendMessage(msg);
}
/**
* Place a request to modify an existing subscribe discovery session on the
* state machine queue.
*/
public void updateSubscribe(int clientId, int sessionId, SubscribeConfig subscribeConfig) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
msg.arg1 = COMMAND_TYPE_UPDATE_SUBSCRIBE;
msg.arg2 = clientId;
msg.getData().putParcelable(MESSAGE_BUNDLE_KEY_CONFIG, subscribeConfig);
msg.getData().putInt(MESSAGE_BUNDLE_KEY_SESSION_ID, sessionId);
mSm.sendMessage(msg);
}
/**
* Place a request to send a message on a discovery session on the state
* machine queue.
*/
public void sendMessage(int uid, int clientId, int sessionId, int peerId, byte[] message,
int messageId, int retryCount) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
msg.arg1 = COMMAND_TYPE_ENQUEUE_SEND_MESSAGE;
msg.arg2 = clientId;
msg.getData().putInt(MESSAGE_BUNDLE_KEY_SESSION_ID, sessionId);
msg.getData().putInt(MESSAGE_BUNDLE_KEY_MESSAGE_PEER_ID, peerId);
msg.getData().putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE, message);
msg.getData().putInt(MESSAGE_BUNDLE_KEY_MESSAGE_ID, messageId);
msg.getData().putInt(MESSAGE_BUNDLE_KEY_RETRY_COUNT, retryCount);
msg.getData().putInt(MESSAGE_BUNDLE_KEY_UID, uid);
mSm.sendMessage(msg);
}
/**
* Enable usage of Aware. Doesn't actually turn on Aware (form clusters) - that
* only happens when a connection is created.
*/
public void enableUsage() {
if (mSettableParameters.get(PARAM_ON_IDLE_DISABLE_AWARE) != 0
&& mPowerManager.isDeviceIdleMode()) {
if (mDbg) Log.d(TAG, "enableUsage(): while device is in IDLE mode - ignoring");
return;
}
if (!SdkLevel.isAtLeastT() && !mWifiPermissionsUtil.isLocationModeEnabled()) {
if (mDbg) Log.d(TAG, "enableUsage(): while location is disabled - ignoring");
return;
}
if (mWifiManager.getWifiState() != WifiManager.WIFI_STATE_ENABLED) {
if (mDbg) Log.d(TAG, "enableUsage(): while Wi-Fi is disabled - ignoring");
return;
}
Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
msg.arg1 = COMMAND_TYPE_ENABLE_USAGE;
mSm.sendMessage(msg);
}
/**
* Disable usage of Aware. Terminates all existing clients with onAwareDown().
* @param markAsAvailable mark the Aware service as available to all app or not.
*/
public void disableUsage(boolean markAsAvailable) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
msg.arg1 = COMMAND_TYPE_DISABLE_USAGE;
msg.arg2 = markAsAvailable ? 1 : 0;
mSm.sendMessage(msg);
}
/**
* Checks whether Aware usage is enabled (not necessarily that Aware is up right
* now) or disabled.
*
* @return A boolean indicating whether Aware usage is enabled (true) or
* disabled (false).
*/
public boolean isUsageEnabled() {
return mUsageEnabled;
}
/**
* Get the capabilities of the current Aware firmware.
*/
public void queryCapabilities() {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
msg.arg1 = COMMAND_TYPE_GET_CAPABILITIES;
mSm.sendMessage(msg);
}
/**
* delete all Aware data path interfaces.
*/
public void deleteAllDataPathInterfaces() {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
msg.arg1 = COMMAND_TYPE_DELETE_ALL_DATA_PATH_INTERFACES;
mSm.sendMessage(msg);
}
/**
* Create the specified data-path interface. Doesn't actually creates a data-path.
*/
public void createDataPathInterface(String interfaceName) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
msg.arg1 = COMMAND_TYPE_CREATE_DATA_PATH_INTERFACE;
msg.obj = interfaceName;
mSm.sendMessage(msg);
}
/**
* Deletes the specified data-path interface.
*/
public void deleteDataPathInterface(String interfaceName) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
msg.arg1 = COMMAND_TYPE_DELETE_DATA_PATH_INTERFACE;
msg.obj = interfaceName;
mSm.sendMessage(msg);
}
/**
* Command to initiate a data-path (executed by the initiator).
*/
public void initiateDataPathSetup(WifiAwareNetworkSpecifier networkSpecifier, int peerId,
int channelRequestType, int channel, byte[] peer, String interfaceName,
boolean isOutOfBand, byte[] appInfo) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
msg.arg1 = COMMAND_TYPE_INITIATE_DATA_PATH_SETUP;
msg.obj = networkSpecifier;
msg.getData().putInt(MESSAGE_BUNDLE_KEY_PEER_ID, peerId);
msg.getData().putInt(MESSAGE_BUNDLE_KEY_CHANNEL_REQ_TYPE, channelRequestType);
msg.getData().putInt(MESSAGE_BUNDLE_KEY_CHANNEL, channel);
msg.getData().putByteArray(MESSAGE_BUNDLE_KEY_MAC_ADDRESS, peer);
msg.getData().putString(MESSAGE_BUNDLE_KEY_INTERFACE_NAME, interfaceName);
msg.getData().putBoolean(MESSAGE_BUNDLE_KEY_OOB, isOutOfBand);
msg.getData().putByteArray(MESSAGE_BUNDLE_KEY_APP_INFO, appInfo);
mSm.sendMessage(msg);
}
/**
* Command to respond to the data-path request (executed by the responder).
*/
public void respondToDataPathRequest(boolean accept, int ndpId, String interfaceName,
byte[] appInfo, boolean isOutOfBand,
WifiAwareDataPathSecurityConfig securityConfig) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
msg.arg1 = COMMAND_TYPE_RESPOND_TO_DATA_PATH_SETUP_REQUEST;
msg.arg2 = ndpId;
msg.obj = securityConfig;
msg.getData().putBoolean(MESSAGE_BUNDLE_KEY_ACCEPT_STATE, accept);
msg.getData().putString(MESSAGE_BUNDLE_KEY_INTERFACE_NAME, interfaceName);
msg.getData().putByteArray(MESSAGE_BUNDLE_KEY_APP_INFO, appInfo);
msg.getData().putBoolean(MESSAGE_BUNDLE_KEY_OOB, isOutOfBand);
mSm.sendMessage(msg);
}
/**
* Command to terminate the specified data-path.
*/
public void endDataPath(int ndpId) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
msg.arg1 = COMMAND_TYPE_END_DATA_PATH;
msg.arg2 = ndpId;
mSm.sendMessage(msg);
}
/**
* Aware follow-on messages (L2 messages) are queued by the firmware for transmission
* on-the-air. The firmware has limited queue depth. The host queues all messages and doles
* them out to the firmware when possible. This command removes the next messages for
* transmission from the host queue and attempts to send it through the firmware. The queues
* are inspected when the command is executed - not when the command is placed on the handler
* (i.e. not evaluated here).
*/
private void transmitNextMessage() {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND);
msg.arg1 = COMMAND_TYPE_TRANSMIT_NEXT_MESSAGE;
mSm.sendMessage(msg);
}
/*
* RESPONSES
*/
/**
* Place a callback request on the state machine queue: configuration
* request completed (successfully).
*/
public void onConfigSuccessResponse(short transactionId) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_RESPONSE);
msg.arg1 = RESPONSE_TYPE_ON_CONFIG_SUCCESS;
msg.arg2 = transactionId;
mSm.sendMessage(msg);
}
/**
* Place a callback request on the state machine queue: configuration
* request failed.
*/
public void onConfigFailedResponse(short transactionId, int reason) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_RESPONSE);
msg.arg1 = RESPONSE_TYPE_ON_CONFIG_FAIL;
msg.arg2 = transactionId;
msg.obj = reason;
mSm.sendMessage(msg);
}
/**
* Place a callback request on the stage machine queue: disable request finished
* (with the provided reason code).
*/
public void onDisableResponse(short transactionId, int reason) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_RESPONSE);
msg.arg1 = RESPONSE_TYPE_ON_DISABLE;
msg.arg2 = transactionId;
msg.obj = reason;
mSm.sendMessage(msg);
}
/**
* Place a callback request on the state machine queue: session
* configuration (new or update) request succeeded.
*/
public void onSessionConfigSuccessResponse(short transactionId, boolean isPublish,
byte pubSubId) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_RESPONSE);
msg.arg1 = RESPONSE_TYPE_ON_SESSION_CONFIG_SUCCESS;
msg.arg2 = transactionId;
msg.obj = pubSubId;
msg.getData().putBoolean(MESSAGE_BUNDLE_KEY_SESSION_TYPE, isPublish);
mSm.sendMessage(msg);
}
/**
* Place a callback request on the state machine queue: session
* configuration (new or update) request failed.
*/
public void onSessionConfigFailResponse(short transactionId, boolean isPublish, int reason) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_RESPONSE);
msg.arg1 = RESPONSE_TYPE_ON_SESSION_CONFIG_FAIL;
msg.arg2 = transactionId;
msg.obj = reason;
msg.getData().putBoolean(MESSAGE_BUNDLE_KEY_SESSION_TYPE, isPublish);
mSm.sendMessage(msg);
}
/**
* Place a callback request on the state machine queue: message has been queued successfully.
*/
public void onMessageSendQueuedSuccessResponse(short transactionId) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_RESPONSE);
msg.arg1 = RESPONSE_TYPE_ON_MESSAGE_SEND_QUEUED_SUCCESS;
msg.arg2 = transactionId;
mSm.sendMessage(msg);
}
/**
* Place a callback request on the state machine queue: attempt to queue the message failed.
*/
public void onMessageSendQueuedFailResponse(short transactionId, int reason) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_RESPONSE);
msg.arg1 = RESPONSE_TYPE_ON_MESSAGE_SEND_QUEUED_FAIL;
msg.arg2 = transactionId;
msg.obj = reason;
mSm.sendMessage(msg);
}
/**
* Place a callback request on the state machine queue: update vendor
* capabilities of the Aware stack.
*/
public void onCapabilitiesUpdateResponse(short transactionId,
Capabilities capabilities) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_RESPONSE);
msg.arg1 = RESPONSE_TYPE_ON_CAPABILITIES_UPDATED;
msg.arg2 = transactionId;
msg.obj = capabilities;
mSm.sendMessage(msg);
}
/**
* Places a callback request on the state machine queue: data-path interface creation command
* completed.
*/
public void onCreateDataPathInterfaceResponse(short transactionId, boolean success,
int reasonOnFailure) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_RESPONSE);
msg.arg1 = RESPONSE_TYPE_ON_CREATE_INTERFACE;
msg.arg2 = transactionId;
msg.getData().putBoolean(MESSAGE_BUNDLE_KEY_SUCCESS_FLAG, success);
msg.getData().putInt(MESSAGE_BUNDLE_KEY_STATUS_CODE, reasonOnFailure);
mSm.sendMessage(msg);
}
/**
* Places a callback request on the state machine queue: data-path interface deletion command
* completed.
*/
public void onDeleteDataPathInterfaceResponse(short transactionId, boolean success,
int reasonOnFailure) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_RESPONSE);
msg.arg1 = RESPONSE_TYPE_ON_DELETE_INTERFACE;
msg.arg2 = transactionId;
msg.getData().putBoolean(MESSAGE_BUNDLE_KEY_SUCCESS_FLAG, success);
msg.getData().putInt(MESSAGE_BUNDLE_KEY_STATUS_CODE, reasonOnFailure);
mSm.sendMessage(msg);
}
/**
* Response from firmware to initiateDataPathSetup(...). Indicates that command has started
* succesfully (not completed!).
*/
public void onInitiateDataPathResponseSuccess(short transactionId, int ndpId) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_RESPONSE);
msg.arg1 = RESPONSE_TYPE_ON_INITIATE_DATA_PATH_SUCCESS;
msg.arg2 = transactionId;
msg.obj = ndpId;
mSm.sendMessage(msg);
}
/**
* Response from firmware to initiateDataPathSetup(...).
* Indicates that command has failed.
*/
public void onInitiateDataPathResponseFail(short transactionId, int reason) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_RESPONSE);
msg.arg1 = RESPONSE_TYPE_ON_INITIATE_DATA_PATH_FAIL;
msg.arg2 = transactionId;
msg.obj = reason;
mSm.sendMessage(msg);
}
/**
* Response from firmware to
* {@link #respondToDataPathRequest(boolean, int, String, byte[], boolean, WifiAwareDataPathSecurityConfig)}
*/
public void onRespondToDataPathSetupRequestResponse(short transactionId, boolean success,
int reasonOnFailure) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_RESPONSE);
msg.arg1 = RESPONSE_TYPE_ON_RESPOND_TO_DATA_PATH_SETUP_REQUEST;
msg.arg2 = transactionId;
msg.getData().putBoolean(MESSAGE_BUNDLE_KEY_SUCCESS_FLAG, success);
msg.getData().putInt(MESSAGE_BUNDLE_KEY_STATUS_CODE, reasonOnFailure);
mSm.sendMessage(msg);
}
/**
* Response from firmware to {@link #endDataPath(int)}.
*/
public void onEndDataPathResponse(short transactionId, boolean success, int reasonOnFailure) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_RESPONSE);
msg.arg1 = RESPONSE_TYPE_ON_END_DATA_PATH;
msg.arg2 = transactionId;
msg.getData().putBoolean(MESSAGE_BUNDLE_KEY_SUCCESS_FLAG, success);
msg.getData().putInt(MESSAGE_BUNDLE_KEY_STATUS_CODE, reasonOnFailure);
mSm.sendMessage(msg);
}
/*
* NOTIFICATIONS
*/
/**
* Place a callback request on the state machine queue: the discovery
* interface has changed.
*/
public void onInterfaceAddressChangeNotification(byte[] mac) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_NOTIFICATION);
msg.arg1 = NOTIFICATION_TYPE_INTERFACE_CHANGE;
msg.obj = mac;
mSm.sendMessage(msg);
}
/**
* Place a callback request on the state machine queue: the cluster
* membership has changed (e.g. due to starting a new cluster or joining
* another cluster).
*/
public void onClusterChangeNotification(int flag, byte[] clusterId) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_NOTIFICATION);
msg.arg1 = NOTIFICATION_TYPE_CLUSTER_CHANGE;
msg.arg2 = flag;
msg.obj = clusterId;
mSm.sendMessage(msg);
}
/**
* Place a callback request on the state machine queue: a discovery match
* has occurred - e.g. our subscription discovered someone else publishing a
* matching service (to the one we were looking for).
*/
public void onMatchNotification(int pubSubId, int requestorInstanceId, byte[] peerMac,
byte[] serviceSpecificInfo, byte[] matchFilter, int rangingIndication, int rangeMm,
byte[] scid, int peerCipherSuite) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_NOTIFICATION);
msg.arg1 = NOTIFICATION_TYPE_MATCH;
msg.arg2 = pubSubId;
msg.getData().putInt(MESSAGE_BUNDLE_KEY_REQ_INSTANCE_ID, requestorInstanceId);
msg.getData().putByteArray(MESSAGE_BUNDLE_KEY_MAC_ADDRESS, peerMac);
msg.getData().putByteArray(MESSAGE_BUNDLE_KEY_SSI_DATA, serviceSpecificInfo);
msg.getData().putByteArray(MESSAGE_BUNDLE_KEY_FILTER_DATA, matchFilter);
msg.getData().putInt(MESSAGE_RANGING_INDICATION, rangingIndication);
msg.getData().putInt(MESSAGE_RANGE_MM, rangeMm);
msg.getData().putInt(MESSAGE_BUNDLE_KEY_CIPHER_SUITE, peerCipherSuite);
msg.getData().putByteArray(MESSAGE_BUNDLE_KEY_SCID, scid);
mSm.sendMessage(msg);
}
/**
* Place a callback request on the state machine queue: a discovered session
* has expired - e.g. some discovered peer is no longer visible.
*/
public void onMatchExpiredNotification(int pubSubId, int requestorInstanceId) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_NOTIFICATION);
msg.arg1 = NOTIFICATION_TYPE_MATCH_EXPIRED;
msg.arg2 = pubSubId;
msg.getData().putInt(MESSAGE_BUNDLE_KEY_REQ_INSTANCE_ID, requestorInstanceId);
mSm.sendMessage(msg);
}
/**
* Place a callback request on the state machine queue: a session (publish
* or subscribe) has terminated (per plan or due to an error).
*/
public void onSessionTerminatedNotification(int pubSubId, int reason, boolean isPublish) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_NOTIFICATION);
msg.arg1 = NOTIFICATION_TYPE_SESSION_TERMINATED;
msg.arg2 = pubSubId;
msg.obj = reason;
msg.getData().putBoolean(MESSAGE_BUNDLE_KEY_SESSION_TYPE, isPublish);
mSm.sendMessage(msg);
}
/**
* Place a callback request on the state machine queue: a message has been
* received as part of a discovery session.
*/
public void onMessageReceivedNotification(int pubSubId, int requestorInstanceId, byte[] peerMac,
byte[] message) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_NOTIFICATION);
msg.arg1 = NOTIFICATION_TYPE_MESSAGE_RECEIVED;
msg.arg2 = pubSubId;
msg.obj = requestorInstanceId;
msg.getData().putByteArray(MESSAGE_BUNDLE_KEY_MAC_ADDRESS, peerMac);
msg.getData().putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE_DATA, message);
mSm.sendMessage(msg);
}
/**
* Place a callback request on the state machine queue: Aware is going down.
*/
public void onAwareDownNotification(int reason) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_NOTIFICATION);
msg.arg1 = NOTIFICATION_TYPE_AWARE_DOWN;
msg.arg2 = reason;
mSm.sendMessage(msg);
}
/**
* Notification that a message has been sent successfully (i.e. an ACK has been received).
*/
public void onMessageSendSuccessNotification(short transactionId) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_NOTIFICATION);
msg.arg1 = NOTIFICATION_TYPE_ON_MESSAGE_SEND_SUCCESS;
msg.arg2 = transactionId;
mSm.sendMessage(msg);
}
/**
* Notification that a message transmission has failed due to the indicated reason - e.g. no ACK
* was received.
*/
public void onMessageSendFailNotification(short transactionId, int reason) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_NOTIFICATION);
msg.arg1 = NOTIFICATION_TYPE_ON_MESSAGE_SEND_FAIL;
msg.arg2 = transactionId;
msg.obj = reason;
mSm.sendMessage(msg);
}
/**
* Place a callback request on the state machine queue: data-path request (from peer) received.
*/
public void onDataPathRequestNotification(int pubSubId, byte[] mac, int ndpId, byte[] message) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_NOTIFICATION);
msg.arg1 = NOTIFICATION_TYPE_ON_DATA_PATH_REQUEST;
msg.arg2 = pubSubId;
msg.obj = ndpId;
msg.getData().putByteArray(MESSAGE_BUNDLE_KEY_MAC_ADDRESS, mac);
msg.getData().putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE, message);
mSm.sendMessage(msg);
}
/**
* Place a callback request on the state machine queue: data-path confirmation received - i.e.
* data-path is now up.
*/
public void onDataPathConfirmNotification(int ndpId, byte[] mac, boolean accept, int reason,
byte[] message, List<WifiAwareChannelInfo> channelInfo) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_NOTIFICATION);
msg.arg1 = NOTIFICATION_TYPE_ON_DATA_PATH_CONFIRM;
msg.arg2 = ndpId;
msg.getData().putByteArray(MESSAGE_BUNDLE_KEY_MAC_ADDRESS, mac);
msg.getData().putBoolean(MESSAGE_BUNDLE_KEY_SUCCESS_FLAG, accept);
msg.getData().putInt(MESSAGE_BUNDLE_KEY_STATUS_CODE, reason);
msg.getData().putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE_DATA, message);
msg.obj = channelInfo;
mSm.sendMessage(msg);
}
/**
* Place a callback request on the state machine queue: the specified data-path has been
* terminated.
*/
public void onDataPathEndNotification(int ndpId) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_NOTIFICATION);
msg.arg1 = NOTIFICATION_TYPE_ON_DATA_PATH_END;
msg.arg2 = ndpId;
mSm.sendMessage(msg);
}
/**
* Place a callback request on the state machine queue: schedule update for the specified
* data-paths.
*/
public void onDataPathScheduleUpdateNotification(byte[] peerMac, ArrayList<Integer> ndpIds,
List<WifiAwareChannelInfo> channelInfo) {
Message msg = mSm.obtainMessage(MESSAGE_TYPE_NOTIFICATION);
msg.arg1 = NOTIFICATION_TYPE_ON_DATA_PATH_SCHED_UPDATE;
msg.getData().putByteArray(MESSAGE_BUNDLE_KEY_MAC_ADDRESS, peerMac);
msg.getData().putIntegerArrayList(MESSAGE_BUNDLE_KEY_NDP_IDS, ndpIds);
msg.obj = channelInfo;
mSm.sendMessage(msg);
}
/**
* State machine.
*/
@VisibleForTesting
class WifiAwareStateMachine extends StateMachine {
private static final int TRANSACTION_ID_IGNORE = 0;
private DefaultState mDefaultState = new DefaultState();
private WaitState mWaitState = new WaitState();
private WaitForResponseState mWaitForResponseState = new WaitForResponseState();
private WaitingState mWaitingState = new WaitingState(this);
private short mNextTransactionId = 1;
public int mNextSessionId = 1;
private Message mCurrentCommand;
private short mCurrentTransactionId = TRANSACTION_ID_IGNORE;
private static final long AWARE_SEND_MESSAGE_TIMEOUT = 10_000;
private static final int MESSAGE_QUEUE_DEPTH_PER_UID = 50;
private int mSendArrivalSequenceCounter = 0;
private boolean mSendQueueBlocked = false;
private final SparseArray<Message> mHostQueuedSendMessages = new SparseArray<>();
private final Map<Short, Message> mFwQueuedSendMessages = new LinkedHashMap<>();
private WakeupMessage mSendMessageTimeoutMessage = new WakeupMessage(mContext, getHandler(),
HAL_SEND_MESSAGE_TIMEOUT_TAG, MESSAGE_TYPE_SEND_MESSAGE_TIMEOUT);
private static final long AWARE_WAIT_FOR_DP_CONFIRM_TIMEOUT = 20_000;
private final SparseArray<WakeupMessage>
mDataPathConfirmTimeoutMessages = new SparseArray<>();
WifiAwareStateMachine(String name, Looper looper) {
super(name, looper);
addState(mDefaultState);
/* --> */ addState(mWaitState, mDefaultState);
/* ----> */ addState(mWaitingState, mWaitState);
/* --> */ addState(mWaitForResponseState, mDefaultState);
setInitialState(mWaitState);
}
public void onAwareDownCleanupSendQueueState() {
mSendQueueBlocked = false;
mHostQueuedSendMessages.clear();
mFwQueuedSendMessages.clear();
}
private class DefaultState extends State {
@Override
public boolean processMessage(Message msg) {
if (VDBG) {
Log.v(TAG, getName() + msg.toString());
}
switch (msg.what) {
case MESSAGE_TYPE_NOTIFICATION:
processNotification(msg);
return HANDLED;
case MESSAGE_TYPE_SEND_MESSAGE_TIMEOUT:
processSendMessageTimeout();
return HANDLED;
case MESSAGE_TYPE_DATA_PATH_TIMEOUT: {
int ndpId = msg.arg1;
if (mDbg) {
Log.v(TAG, "MESSAGE_TYPE_DATA_PATH_TIMEOUT: ndpId="
+ ndpId);
}
mDataPathMgr.handleDataPathTimeout(ndpId);
mDataPathConfirmTimeoutMessages.remove(ndpId);
return HANDLED;
}
default:
/* fall-through */
}
Log.wtf(TAG,
"DefaultState: should not get non-NOTIFICATION in this state: msg=" + msg);
return NOT_HANDLED;
}
}
private class WaitState extends State {
@Override
public boolean processMessage(Message msg) {
if (VDBG) {
Log.v(TAG, getName() + msg.toString());
}
switch (msg.what) {
case MESSAGE_TYPE_COMMAND:
if (processCommand(msg)) {
transitionTo(mWaitForResponseState);
}
return HANDLED;
case MESSAGE_TYPE_RESPONSE:
/* fall-through */
case MESSAGE_TYPE_RESPONSE_TIMEOUT:
/*
* remnants/delayed/out-of-sync messages - but let
* WaitForResponseState deal with them (identified as
* out-of-date by transaction ID).
*/
deferMessage(msg);
return HANDLED;
default:
/* fall-through */
}
return NOT_HANDLED;
}
}
private class WaitForResponseState extends State {
private static final long AWARE_COMMAND_TIMEOUT = 5_000;
private WakeupMessage mTimeoutMessage;
@Override
public void enter() {
mTimeoutMessage = new WakeupMessage(mContext, getHandler(), HAL_COMMAND_TIMEOUT_TAG,
MESSAGE_TYPE_RESPONSE_TIMEOUT, mCurrentCommand.arg1, mCurrentTransactionId);
mTimeoutMessage.schedule(SystemClock.elapsedRealtime() + AWARE_COMMAND_TIMEOUT);
}
@Override
public void exit() {
mTimeoutMessage.cancel();
}
@Override
public boolean processMessage(Message msg) {
if (VDBG) {
Log.v(TAG, getName() + msg.toString());
}
switch (msg.what) {
case MESSAGE_TYPE_COMMAND:
/*
* don't want COMMANDs in this state - defer until back
* in WaitState
*/
deferMessage(msg);
return HANDLED;
case MESSAGE_TYPE_RESPONSE:
if (msg.arg2 == mCurrentTransactionId) {
processResponse(msg);
transitionTo(mWaitState);
} else {
Log.w(TAG,
"WaitForResponseState: processMessage: non-matching "
+ "transaction ID on RESPONSE (a very late "
+ "response) -- msg=" + msg);
/* no transition */
}
return HANDLED;
case MESSAGE_TYPE_RESPONSE_TIMEOUT:
if (msg.arg2 == mCurrentTransactionId) {
processTimeout(msg);
transitionTo(mWaitState);
} else {
Log.w(TAG, "WaitForResponseState: processMessage: non-matching "
+ "transaction ID on RESPONSE_TIMEOUT (either a non-cancelled "
+ "timeout or a race condition with cancel) -- msg=" + msg);
/* no transition */
}
return HANDLED;
default:
/* fall-through */
}
return NOT_HANDLED;
}
}
private void processNotification(Message msg) {
if (VDBG) {
Log.v(TAG, "processNotification: msg=" + msg);
}
switch (msg.arg1) {
case NOTIFICATION_TYPE_INTERFACE_CHANGE: {
byte[] mac = (byte[]) msg.obj;
onInterfaceAddressChangeLocal(mac);
break;
}
case NOTIFICATION_TYPE_CLUSTER_CHANGE: {
int flag = msg.arg2;
byte[] clusterId = (byte[]) msg.obj;
onClusterChangeLocal(flag, clusterId);
break;
}
case NOTIFICATION_TYPE_MATCH: {
int pubSubId = msg.arg2;
int requestorInstanceId = msg.getData()
.getInt(MESSAGE_BUNDLE_KEY_REQ_INSTANCE_ID);
byte[] peerMac = msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MAC_ADDRESS);
byte[] serviceSpecificInfo = msg.getData()
.getByteArray(MESSAGE_BUNDLE_KEY_SSI_DATA);
byte[] matchFilter = msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_FILTER_DATA);
int rangingIndication = msg.getData().getInt(MESSAGE_RANGING_INDICATION);
int rangeMm = msg.getData().getInt(MESSAGE_RANGE_MM);
int cipherSuite = msg.getData().getInt(MESSAGE_BUNDLE_KEY_CIPHER_SUITE);
byte[] scid = msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_SCID);
onMatchLocal(pubSubId, requestorInstanceId, peerMac, serviceSpecificInfo,
matchFilter, rangingIndication, rangeMm, cipherSuite, scid);
break;
}
case NOTIFICATION_TYPE_MATCH_EXPIRED: {
int pubSubId = msg.arg2;
int requestorInstanceId = msg.getData()
.getInt(MESSAGE_BUNDLE_KEY_REQ_INSTANCE_ID);
onMatchExpiredLocal(pubSubId, requestorInstanceId);
break;
}
case NOTIFICATION_TYPE_SESSION_TERMINATED: {
int pubSubId = msg.arg2;
int reason = (Integer) msg.obj;
boolean isPublish = msg.getData().getBoolean(MESSAGE_BUNDLE_KEY_SESSION_TYPE);
onSessionTerminatedLocal(pubSubId, isPublish, reason);
break;
}
case NOTIFICATION_TYPE_MESSAGE_RECEIVED: {
int pubSubId = msg.arg2;
int requestorInstanceId = (Integer) msg.obj;
byte[] peerMac = msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MAC_ADDRESS);
byte[] message = msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE_DATA);
onMessageReceivedLocal(pubSubId, requestorInstanceId, peerMac, message);
break;
}
case NOTIFICATION_TYPE_AWARE_DOWN: {
int reason = msg.arg2;
/*
* TODO: b/28615938. Use reason code to determine whether or not need clean-up
* local state (only needed if AWARE_DOWN is due to internal firmware reason,
* e.g. concurrency, rather than due to a requested shutdown).
*/
onAwareDownLocal();
if (reason != NanStatusType.SUCCESS) {
sendAwareStateChangedBroadcast(false);
}
break;
}
case NOTIFICATION_TYPE_ON_MESSAGE_SEND_SUCCESS: {
short transactionId = (short) msg.arg2;
Message queuedSendCommand = mFwQueuedSendMessages.get(transactionId);
if (VDBG) {
Log.v(TAG, "NOTIFICATION_TYPE_ON_MESSAGE_SEND_SUCCESS: queuedSendCommand="
+ queuedSendCommand);
}
if (queuedSendCommand == null) {
Log.w(TAG,
"processNotification: NOTIFICATION_TYPE_ON_MESSAGE_SEND_SUCCESS:"
+ " transactionId=" + transactionId
+ " - no such queued send command (timed-out?)");
} else {
mFwQueuedSendMessages.remove(transactionId);
updateSendMessageTimeout();
onMessageSendSuccessLocal(queuedSendCommand);
}
mSendQueueBlocked = false;
transmitNextMessage();
break;
}
case NOTIFICATION_TYPE_ON_MESSAGE_SEND_FAIL: {
short transactionId = (short) msg.arg2;
int reason = (Integer) msg.obj;
Message sentMessage = mFwQueuedSendMessages.get(transactionId);
if (VDBG) {
Log.v(TAG, "NOTIFICATION_TYPE_ON_MESSAGE_SEND_FAIL: sentMessage="
+ sentMessage);
}
if (sentMessage == null) {
Log.w(TAG,
"processNotification: NOTIFICATION_TYPE_ON_MESSAGE_SEND_FAIL:"
+ " transactionId=" + transactionId
+ " - no such queued send command (timed-out?)");
} else {
mFwQueuedSendMessages.remove(transactionId);
updateSendMessageTimeout();
int retryCount = sentMessage.getData()
.getInt(MESSAGE_BUNDLE_KEY_RETRY_COUNT);
if (retryCount > 0 && reason == NanStatusType.NO_OTA_ACK) {
if (mDbg) {
Log.v(TAG,
"NOTIFICATION_TYPE_ON_MESSAGE_SEND_FAIL: transactionId="
+ transactionId + ", reason=" + reason
+ ": retransmitting - retryCount=" + retryCount);
}
sentMessage.getData().putInt(MESSAGE_BUNDLE_KEY_RETRY_COUNT,
retryCount - 1);
int arrivalSeq = sentMessage.getData().getInt(
MESSAGE_BUNDLE_KEY_MESSAGE_ARRIVAL_SEQ);
mHostQueuedSendMessages.put(arrivalSeq, sentMessage);
} else {
onMessageSendFailLocal(sentMessage, reason);
}
mSendQueueBlocked = false;
transmitNextMessage();
}
break;
}
case NOTIFICATION_TYPE_ON_DATA_PATH_REQUEST: {
int ndpId = (int) msg.obj;
boolean success = mDataPathMgr.onDataPathRequest(
msg.arg2, msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MAC_ADDRESS),
ndpId, msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE));
if (success) {
WakeupMessage timeout = new WakeupMessage(mContext, getHandler(),
HAL_DATA_PATH_CONFIRM_TIMEOUT_TAG, MESSAGE_TYPE_DATA_PATH_TIMEOUT,
ndpId);
mDataPathConfirmTimeoutMessages.put(ndpId, timeout);
timeout.schedule(
SystemClock.elapsedRealtime() + AWARE_WAIT_FOR_DP_CONFIRM_TIMEOUT);
}
break;
}
case NOTIFICATION_TYPE_ON_DATA_PATH_CONFIRM: {
int ndpId = msg.arg2;
boolean success = mDataPathMgr.onDataPathConfirm(
ndpId, msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MAC_ADDRESS),
msg.getData().getBoolean(MESSAGE_BUNDLE_KEY_SUCCESS_FLAG),
msg.getData().getInt(MESSAGE_BUNDLE_KEY_STATUS_CODE),
msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE_DATA),
(List<WifiAwareChannelInfo>) msg.obj);
if (success) {
WakeupMessage timeout = mDataPathConfirmTimeoutMessages.get(ndpId);
if (timeout != null) {
mDataPathConfirmTimeoutMessages.remove(ndpId);
timeout.cancel();
}
}
break;
}
case NOTIFICATION_TYPE_ON_DATA_PATH_END:
mDataPathMgr.onDataPathEnd(msg.arg2);
sendAwareResourcesChangedBroadcast();
break;
case NOTIFICATION_TYPE_ON_DATA_PATH_SCHED_UPDATE:
mDataPathMgr.onDataPathSchedUpdate(
msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MAC_ADDRESS),
msg.getData().getIntegerArrayList(MESSAGE_BUNDLE_KEY_NDP_IDS),
(List<WifiAwareChannelInfo>) msg.obj);
break;
default:
Log.wtf(TAG, "processNotification: this isn't a NOTIFICATION -- msg=" + msg);
return;
}
}
/**
* Execute the command specified by the input Message. Returns a true if
* need to wait for a RESPONSE, otherwise a false. We may not have to
* wait for a RESPONSE if there was an error in the state (so no command
* is sent to HAL) OR if we choose not to wait for response - e.g. for
* disconnected/terminate commands failure is not possible.
*/
private boolean processCommand(Message msg) {
if (VDBG) {
Log.v(TAG, "processCommand: msg=" + msg);
}
if (mCurrentCommand != null) {
Log.wtf(TAG,
"processCommand: receiving a command (msg=" + msg
+ ") but current (previous) command isn't null (prev_msg="
+ mCurrentCommand + ")");
mCurrentCommand = null;
}
mCurrentTransactionId = mNextTransactionId++;
boolean waitForResponse = true;
switch (msg.arg1) {
case COMMAND_TYPE_CONNECT: {
if (mAwareIsDisabling) {
deferMessage(msg);
waitForResponse = false;
break;
}
int clientId = msg.arg2;
Pair<IWifiAwareEventCallback, Object> callbackAndAttributionSource =
(Pair<IWifiAwareEventCallback, Object>) msg.obj;
IWifiAwareEventCallback callback = callbackAndAttributionSource.first;
ConfigRequest configRequest = (ConfigRequest) msg.getData()
.getParcelable(MESSAGE_BUNDLE_KEY_CONFIG);
int uid = msg.getData().getInt(MESSAGE_BUNDLE_KEY_UID);
int pid = msg.getData().getInt(MESSAGE_BUNDLE_KEY_PID);
String callingPackage = msg.getData().getString(
MESSAGE_BUNDLE_KEY_CALLING_PACKAGE);
String callingFeatureId = msg.getData().getString(
MESSAGE_BUNDLE_KEY_CALLING_FEATURE_ID);
boolean notifyIdentityChange = msg.getData().getBoolean(
MESSAGE_BUNDLE_KEY_NOTIFY_IDENTITY_CHANGE);
int proceedWithOperation =
mInterfaceConflictMgr.manageInterfaceConflictForStateMachine(TAG, msg,
this, mWaitingState, mWaitState,
HalDeviceManager.HDM_CREATE_IFACE_NAN,
new WorkSource(uid, callingPackage));
if (proceedWithOperation == InterfaceConflictManager.ICM_ABORT_COMMAND) {
// handling user rejection or possible conflict (pending command)
try {
callback.onConnectFail(
NanStatusType.NO_RESOURCES_AVAILABLE);
mAwareMetrics.recordAttachStatus(
NanStatusType.NO_RESOURCES_AVAILABLE);
} catch (RemoteException e) {
Log.w(TAG, "displayUserApprovalDialog user refusal: RemoteException "
+ "(FYI): " + e);
}
waitForResponse = false;
} else if (proceedWithOperation
== InterfaceConflictManager.ICM_EXECUTE_COMMAND) {
waitForResponse = connectLocal(mCurrentTransactionId, clientId, uid, pid,
callingPackage, callingFeatureId, callback, configRequest,
notifyIdentityChange, callbackAndAttributionSource.second);
} else { // InterfaceConflictManager.ICM_SKIP_COMMAND_WAIT_FOR_USER
waitForResponse = false;
}
break;
}
case COMMAND_TYPE_DISCONNECT: {
int clientId = msg.arg2;
waitForResponse = disconnectLocal(mCurrentTransactionId, clientId);
break;
}
case COMMAND_TYPE_DISABLE: {
mAwareIsDisabling = false;
// Must trigger a state transition to execute the deferred connect command
if (!mWifiAwareNativeApi.disable(mCurrentTransactionId)) {
onDisableResponse(mCurrentTransactionId, WifiStatusCode.ERROR_UNKNOWN);
}
break;
}
case COMMAND_TYPE_RECONFIGURE:
waitForResponse = reconfigureLocal(mCurrentTransactionId);
break;
case COMMAND_TYPE_TERMINATE_SESSION: {
int clientId = msg.arg2;
int sessionId = (Integer) msg.obj;
terminateSessionLocal(clientId, sessionId);
waitForResponse = false;
break;
}
case COMMAND_TYPE_PUBLISH: {
int clientId = msg.arg2;
IWifiAwareDiscoverySessionCallback callback =
(IWifiAwareDiscoverySessionCallback) msg.obj;
PublishConfig publishConfig = (PublishConfig) msg.getData()
.getParcelable(MESSAGE_BUNDLE_KEY_CONFIG);
waitForResponse = publishLocal(mCurrentTransactionId, clientId, publishConfig,
callback);
break;
}
case COMMAND_TYPE_UPDATE_PUBLISH: {
int clientId = msg.arg2;
int sessionId = msg.getData().getInt(MESSAGE_BUNDLE_KEY_SESSION_ID);
PublishConfig publishConfig = (PublishConfig) msg.getData()
.getParcelable(MESSAGE_BUNDLE_KEY_CONFIG);
waitForResponse = updatePublishLocal(mCurrentTransactionId, clientId, sessionId,
publishConfig);
break;
}
case COMMAND_TYPE_SUBSCRIBE: {
int clientId = msg.arg2;
IWifiAwareDiscoverySessionCallback callback =
(IWifiAwareDiscoverySessionCallback) msg.obj;
SubscribeConfig subscribeConfig = (SubscribeConfig) msg.getData()
.getParcelable(MESSAGE_BUNDLE_KEY_CONFIG);
waitForResponse = subscribeLocal(mCurrentTransactionId, clientId,
subscribeConfig, callback);
break;
}
case COMMAND_TYPE_UPDATE_SUBSCRIBE: {
int clientId = msg.arg2;
int sessionId = msg.getData().getInt(MESSAGE_BUNDLE_KEY_SESSION_ID);
SubscribeConfig subscribeConfig = (SubscribeConfig) msg.getData()
.getParcelable(MESSAGE_BUNDLE_KEY_CONFIG);
waitForResponse = updateSubscribeLocal(mCurrentTransactionId, clientId,
sessionId, subscribeConfig);
break;
}
case COMMAND_TYPE_ENQUEUE_SEND_MESSAGE: {
if (VDBG) {
Log.v(TAG, "processCommand: ENQUEUE_SEND_MESSAGE - messageId="
+ msg.getData().getInt(MESSAGE_BUNDLE_KEY_MESSAGE_ID)
+ ", mSendArrivalSequenceCounter=" + mSendArrivalSequenceCounter);
}
int uid = msg.getData().getInt(MESSAGE_BUNDLE_KEY_UID);
if (isUidExceededMessageQueueDepthLimit(uid)) {
if (mDbg) {
Log.v(TAG, "message queue limit exceeded for uid=" + uid
+ " at messageId="
+ msg.getData().getInt(MESSAGE_BUNDLE_KEY_MESSAGE_ID));
}
onMessageSendFailLocal(msg, NanStatusType.INTERNAL_FAILURE);
waitForResponse = false;
break;
}
Message sendMsg = obtainMessage(msg.what);
sendMsg.copyFrom(msg);
sendMsg.getData().putInt(MESSAGE_BUNDLE_KEY_MESSAGE_ARRIVAL_SEQ,
mSendArrivalSequenceCounter);
mHostQueuedSendMessages.put(mSendArrivalSequenceCounter, sendMsg);
mSendArrivalSequenceCounter++;
waitForResponse = false;
if (!mSendQueueBlocked) {
transmitNextMessage();
}
break;
}
case COMMAND_TYPE_TRANSMIT_NEXT_MESSAGE: {
if (mSendQueueBlocked || mHostQueuedSendMessages.size() == 0) {
if (VDBG) {
Log.v(TAG, "processCommand: SEND_TOP_OF_QUEUE_MESSAGE - blocked or "
+ "empty host queue");
}
waitForResponse = false;
} else {
if (VDBG) {
Log.v(TAG, "processCommand: SEND_TOP_OF_QUEUE_MESSAGE - "
+ "sendArrivalSequenceCounter="
+ mHostQueuedSendMessages.keyAt(0));
}
Message sendMessage = mHostQueuedSendMessages.valueAt(0);
mHostQueuedSendMessages.removeAt(0);
Bundle data = sendMessage.getData();
int clientId = sendMessage.arg2;
int sessionId = sendMessage.getData().getInt(MESSAGE_BUNDLE_KEY_SESSION_ID);
int peerId = data.getInt(MESSAGE_BUNDLE_KEY_MESSAGE_PEER_ID);
byte[] message = data.getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE);
int messageId = data.getInt(MESSAGE_BUNDLE_KEY_MESSAGE_ID);
msg.getData().putParcelable(MESSAGE_BUNDLE_KEY_SENT_MESSAGE, sendMessage);
waitForResponse = sendFollowonMessageLocal(mCurrentTransactionId, clientId,
sessionId, peerId, message, messageId);
}
break;
}
case COMMAND_TYPE_ENABLE_USAGE:
enableUsageLocal();
waitForResponse = false;
break;
case COMMAND_TYPE_DISABLE_USAGE:
disableUsageLocal(mCurrentTransactionId, msg.arg2 == 1);
waitForResponse = false;
break;
case COMMAND_TYPE_GET_CAPABILITIES:
if (mCapabilities == null) {
waitForResponse = mWifiAwareNativeApi.getCapabilities(
mCurrentTransactionId);
} else {
if (VDBG) {
Log.v(TAG, "COMMAND_TYPE_GET_CAPABILITIES: already have capabilities - "
+ "skipping");
}
waitForResponse = false;
}
break;
case COMMAND_TYPE_DELETE_ALL_DATA_PATH_INTERFACES:
mDataPathMgr.deleteAllInterfaces();
waitForResponse = false;
break;
case COMMAND_TYPE_CREATE_DATA_PATH_INTERFACE:
waitForResponse = mWifiAwareNativeApi.createAwareNetworkInterface(
mCurrentTransactionId, (String) msg.obj);
break;
case COMMAND_TYPE_DELETE_DATA_PATH_INTERFACE:
waitForResponse = mWifiAwareNativeApi.deleteAwareNetworkInterface(
mCurrentTransactionId, (String) msg.obj);
break;
case COMMAND_TYPE_INITIATE_DATA_PATH_SETUP: {
Bundle data = msg.getData();
WifiAwareNetworkSpecifier networkSpecifier =
(WifiAwareNetworkSpecifier) msg.obj;
int peerId = data.getInt(MESSAGE_BUNDLE_KEY_PEER_ID);
int channelRequestType = data.getInt(MESSAGE_BUNDLE_KEY_CHANNEL_REQ_TYPE);
int channel = data.getInt(MESSAGE_BUNDLE_KEY_CHANNEL);
byte[] peer = data.getByteArray(MESSAGE_BUNDLE_KEY_MAC_ADDRESS);
String interfaceName = data.getString(MESSAGE_BUNDLE_KEY_INTERFACE_NAME);
boolean isOutOfBand = data.getBoolean(MESSAGE_BUNDLE_KEY_OOB);
byte[] appInfo = data.getByteArray(MESSAGE_BUNDLE_KEY_APP_INFO);
waitForResponse = initiateDataPathSetupLocal(mCurrentTransactionId,
networkSpecifier, peerId, channelRequestType, channel, peer,
interfaceName, isOutOfBand, appInfo);
break;
}
case COMMAND_TYPE_RESPOND_TO_DATA_PATH_SETUP_REQUEST: {
Bundle data = msg.getData();
int ndpId = msg.arg2;
WifiAwareDataPathSecurityConfig securityConfig =
(WifiAwareDataPathSecurityConfig) msg.obj;
boolean accept = data.getBoolean(MESSAGE_BUNDLE_KEY_ACCEPT_STATE);
String interfaceName = data.getString(MESSAGE_BUNDLE_KEY_INTERFACE_NAME);
byte[] appInfo = data.getByteArray(MESSAGE_BUNDLE_KEY_APP_INFO);
boolean isOutOfBand = data.getBoolean(MESSAGE_BUNDLE_KEY_OOB);
waitForResponse = respondToDataPathRequestLocal(mCurrentTransactionId, accept,
ndpId, interfaceName, appInfo, isOutOfBand, securityConfig);
break;
}
case COMMAND_TYPE_END_DATA_PATH:
waitForResponse = endDataPathLocal(mCurrentTransactionId, msg.arg2);
break;
case COMMAND_TYPE_DELAYED_INITIALIZATION:
mWifiManager.registerActiveCountryCodeChangedCallback(
new HandlerExecutor(mHandler), new CountryCodeChangeCallback());
mWifiAwareNativeManager.start(getHandler());
waitForResponse = false;
break;
case COMMAND_TYPE_GET_AWARE:
WorkSource requestorWs = (WorkSource) msg.obj;
mWifiAwareNativeManager.tryToGetAware(requestorWs);
waitForResponse = false;
break;
case COMMAND_TYPE_RELEASE_AWARE:
mWifiAwareNativeManager.releaseAware();
waitForResponse = false;
break;
default:
waitForResponse = false;
Log.wtf(TAG, "processCommand: this isn't a COMMAND -- msg=" + msg);
/* fall-through */
}
if (!waitForResponse) {
mCurrentTransactionId = TRANSACTION_ID_IGNORE;
} else {
mCurrentCommand = obtainMessage(msg.what);
mCurrentCommand.copyFrom(msg);
}
return waitForResponse;
}
private void processResponse(Message msg) {
if (VDBG) {
Log.v(TAG, "processResponse: msg=" + msg);
}
if (mCurrentCommand == null) {
Log.wtf(TAG, "processResponse: no existing command stored!? msg=" + msg);
mCurrentTransactionId = TRANSACTION_ID_IGNORE;
return;
}
switch (msg.arg1) {
case RESPONSE_TYPE_ON_CONFIG_SUCCESS:
onConfigCompletedLocal(mCurrentCommand);
break;
case RESPONSE_TYPE_ON_CONFIG_FAIL: {
int reason = (Integer) msg.obj;
onConfigFailedLocal(mCurrentCommand, reason);
break;
}
case RESPONSE_TYPE_ON_SESSION_CONFIG_SUCCESS: {
byte pubSubId = (Byte) msg.obj;
boolean isPublish = msg.getData().getBoolean(MESSAGE_BUNDLE_KEY_SESSION_TYPE);
onSessionConfigSuccessLocal(mCurrentCommand, pubSubId, isPublish);
break;
}
case RESPONSE_TYPE_ON_SESSION_CONFIG_FAIL: {
int reason = (Integer) msg.obj;
boolean isPublish = msg.getData().getBoolean(MESSAGE_BUNDLE_KEY_SESSION_TYPE);
onSessionConfigFailLocal(mCurrentCommand, isPublish, reason);
break;
}
case RESPONSE_TYPE_ON_MESSAGE_SEND_QUEUED_SUCCESS: {
Message sentMessage = mCurrentCommand.getData().getParcelable(
MESSAGE_BUNDLE_KEY_SENT_MESSAGE);
sentMessage.getData().putLong(MESSAGE_BUNDLE_KEY_SEND_MESSAGE_ENQUEUE_TIME,
SystemClock.elapsedRealtime());
mFwQueuedSendMessages.put(mCurrentTransactionId, sentMessage);
updateSendMessageTimeout();
if (!mSendQueueBlocked) {
transmitNextMessage();
}
if (VDBG) {
Log.v(TAG, "processResponse: ON_MESSAGE_SEND_QUEUED_SUCCESS - arrivalSeq="
+ sentMessage.getData().getInt(
MESSAGE_BUNDLE_KEY_MESSAGE_ARRIVAL_SEQ));
}
break;
}
case RESPONSE_TYPE_ON_MESSAGE_SEND_QUEUED_FAIL: {
if (VDBG) {
Log.v(TAG, "processResponse: ON_MESSAGE_SEND_QUEUED_FAIL - blocking!");
}
int reason = (Integer) msg.obj;
if (reason == NanStatusType.FOLLOWUP_TX_QUEUE_FULL) {
Message sentMessage = mCurrentCommand.getData().getParcelable(
MESSAGE_BUNDLE_KEY_SENT_MESSAGE);
int arrivalSeq = sentMessage.getData().getInt(
MESSAGE_BUNDLE_KEY_MESSAGE_ARRIVAL_SEQ);
mHostQueuedSendMessages.put(arrivalSeq, sentMessage);
mSendQueueBlocked = true;
if (VDBG) {
Log.v(TAG, "processResponse: ON_MESSAGE_SEND_QUEUED_FAIL - arrivalSeq="
+ arrivalSeq + " -- blocking");
}
} else {
Message sentMessage = mCurrentCommand.getData().getParcelable(
MESSAGE_BUNDLE_KEY_SENT_MESSAGE);
onMessageSendFailLocal(sentMessage, NanStatusType.INTERNAL_FAILURE);
if (!mSendQueueBlocked) {
transmitNextMessage();
}
}
break;
}
case RESPONSE_TYPE_ON_CAPABILITIES_UPDATED: {
onCapabilitiesUpdatedResponseLocal((Capabilities) msg.obj);
break;
}
case RESPONSE_TYPE_ON_CREATE_INTERFACE:
onCreateDataPathInterfaceResponseLocal(mCurrentCommand,
msg.getData().getBoolean(MESSAGE_BUNDLE_KEY_SUCCESS_FLAG),
msg.getData().getInt(MESSAGE_BUNDLE_KEY_STATUS_CODE));
break;
case RESPONSE_TYPE_ON_DELETE_INTERFACE:
onDeleteDataPathInterfaceResponseLocal(mCurrentCommand,
msg.getData().getBoolean(MESSAGE_BUNDLE_KEY_SUCCESS_FLAG),
msg.getData().getInt(MESSAGE_BUNDLE_KEY_STATUS_CODE));
break;
case RESPONSE_TYPE_ON_INITIATE_DATA_PATH_SUCCESS:
int ndpId = (int) msg.obj;
boolean success = onInitiateDataPathResponseSuccessLocal(mCurrentCommand,
ndpId);
if (success) {
WakeupMessage timeout = new WakeupMessage(mContext, getHandler(),
HAL_DATA_PATH_CONFIRM_TIMEOUT_TAG, MESSAGE_TYPE_DATA_PATH_TIMEOUT,
ndpId);
mDataPathConfirmTimeoutMessages.put(ndpId, timeout);
timeout.schedule(
SystemClock.elapsedRealtime() + AWARE_WAIT_FOR_DP_CONFIRM_TIMEOUT);
sendAwareResourcesChangedBroadcast();
}
break;
case RESPONSE_TYPE_ON_INITIATE_DATA_PATH_FAIL:
onInitiateDataPathResponseFailLocal(mCurrentCommand, (int) msg.obj);
break;
case RESPONSE_TYPE_ON_RESPOND_TO_DATA_PATH_SETUP_REQUEST:
onRespondToDataPathSetupRequestResponseLocal(mCurrentCommand,
msg.getData().getBoolean(MESSAGE_BUNDLE_KEY_SUCCESS_FLAG),
msg.getData().getInt(MESSAGE_BUNDLE_KEY_STATUS_CODE));
break;
case RESPONSE_TYPE_ON_END_DATA_PATH:
onEndPathEndResponseLocal(mCurrentCommand,
msg.getData().getBoolean(MESSAGE_BUNDLE_KEY_SUCCESS_FLAG),
msg.getData().getInt(MESSAGE_BUNDLE_KEY_STATUS_CODE));
break;
case RESPONSE_TYPE_ON_DISABLE:
onDisableResponseLocal(mCurrentCommand, (Integer) msg.obj);
break;
default:
Log.wtf(TAG, "processResponse: this isn't a RESPONSE -- msg=" + msg);
mCurrentCommand = null;
mCurrentTransactionId = TRANSACTION_ID_IGNORE;
return;
}
mCurrentCommand = null;
mCurrentTransactionId = TRANSACTION_ID_IGNORE;
}
private void processTimeout(Message msg) {
if (mDbg) {
Log.v(TAG, "processTimeout: msg=" + msg);
}
if (mCurrentCommand == null) {
Log.wtf(TAG, "processTimeout: no existing command stored!? msg=" + msg);
mCurrentTransactionId = TRANSACTION_ID_IGNORE;
return;
}
/*
* Only have to handle those COMMANDs which wait for a response.
*/
switch (msg.arg1) {
case COMMAND_TYPE_CONNECT:
case COMMAND_TYPE_DISCONNECT:
onConfigFailedLocal(mCurrentCommand, NanStatusType.INTERNAL_FAILURE);
break;
case COMMAND_TYPE_RECONFIGURE:
/*
* Reconfigure timed-out. There is nothing to do but log the issue - which
* will be done in the callback.
*/
onConfigFailedLocal(mCurrentCommand, NanStatusType.INTERNAL_FAILURE);
break;
case COMMAND_TYPE_TERMINATE_SESSION: {
Log.wtf(TAG, "processTimeout: TERMINATE_SESSION - shouldn't be waiting!");
break;
}
case COMMAND_TYPE_PUBLISH:
case COMMAND_TYPE_UPDATE_PUBLISH: {
onSessionConfigFailLocal(mCurrentCommand, true, NanStatusType.INTERNAL_FAILURE);
break;
}
case COMMAND_TYPE_SUBSCRIBE:
case COMMAND_TYPE_UPDATE_SUBSCRIBE: {
onSessionConfigFailLocal(mCurrentCommand, false,
NanStatusType.INTERNAL_FAILURE);
break;
}
case COMMAND_TYPE_ENQUEUE_SEND_MESSAGE: {
Log.wtf(TAG, "processTimeout: ENQUEUE_SEND_MESSAGE - shouldn't be waiting!");
break;
}
case COMMAND_TYPE_TRANSMIT_NEXT_MESSAGE: {
Message sentMessage = mCurrentCommand.getData().getParcelable(
MESSAGE_BUNDLE_KEY_SENT_MESSAGE);
onMessageSendFailLocal(sentMessage, NanStatusType.INTERNAL_FAILURE);
mSendQueueBlocked = false;
transmitNextMessage();
break;
}
case COMMAND_TYPE_ENABLE_USAGE:
Log.wtf(TAG, "processTimeout: ENABLE_USAGE - shouldn't be waiting!");
break;
case COMMAND_TYPE_DISABLE_USAGE:
Log.wtf(TAG, "processTimeout: DISABLE_USAGE - shouldn't be waiting!");
break;
case COMMAND_TYPE_GET_CAPABILITIES:
Log.e(TAG,
"processTimeout: GET_CAPABILITIES timed-out - strange, will try again"
+ " when next enabled!?");
break;
case COMMAND_TYPE_DELETE_ALL_DATA_PATH_INTERFACES:
Log.wtf(TAG,
"processTimeout: DELETE_ALL_DATA_PATH_INTERFACES - shouldn't be "
+ "waiting!");
break;
case COMMAND_TYPE_CREATE_DATA_PATH_INTERFACE:
// TODO: fix status: timeout
onCreateDataPathInterfaceResponseLocal(mCurrentCommand, false, 0);
break;
case COMMAND_TYPE_DELETE_DATA_PATH_INTERFACE:
// TODO: fix status: timeout
onDeleteDataPathInterfaceResponseLocal(mCurrentCommand, false, 0);
break;
case COMMAND_TYPE_INITIATE_DATA_PATH_SETUP:
// TODO: fix status: timeout
onInitiateDataPathResponseFailLocal(mCurrentCommand, 0);
break;
case COMMAND_TYPE_RESPOND_TO_DATA_PATH_SETUP_REQUEST:
// TODO: fix status: timeout
onRespondToDataPathSetupRequestResponseLocal(mCurrentCommand, false, 0);
break;
case COMMAND_TYPE_END_DATA_PATH:
// TODO: fix status: timeout
onEndPathEndResponseLocal(mCurrentCommand, false, 0);
break;
case COMMAND_TYPE_DELAYED_INITIALIZATION:
Log.wtf(TAG,
"processTimeout: COMMAND_TYPE_DELAYED_INITIALIZATION - shouldn't be "
+ "waiting!");
break;
case COMMAND_TYPE_GET_AWARE:
Log.wtf(TAG,
"processTimeout: COMMAND_TYPE_GET_AWARE - shouldn't be waiting!");
break;
case COMMAND_TYPE_RELEASE_AWARE:
Log.wtf(TAG,
"processTimeout: COMMAND_TYPE_RELEASE_AWARE - shouldn't be waiting!");
break;
case COMMAND_TYPE_DISABLE:
mAwareMetrics.recordDisableAware();
break;
default:
Log.wtf(TAG, "processTimeout: this isn't a COMMAND -- msg=" + msg);
/* fall-through */
}
mCurrentCommand = null;
mCurrentTransactionId = TRANSACTION_ID_IGNORE;
}
private void updateSendMessageTimeout() {
if (VDBG) {
Log.v(TAG, "updateSendMessageTimeout: mHostQueuedSendMessages.size()="
+ mHostQueuedSendMessages.size() + ", mFwQueuedSendMessages.size()="
+ mFwQueuedSendMessages.size() + ", mSendQueueBlocked="
+ mSendQueueBlocked);
}
Iterator<Message> it = mFwQueuedSendMessages.values().iterator();
if (it.hasNext()) {
/*
* Schedule timeout based on the first message in the queue (which is the earliest
* submitted message). Timeout = queuing time + timeout constant.
*/
Message msg = it.next();
mSendMessageTimeoutMessage.schedule(
msg.getData().getLong(MESSAGE_BUNDLE_KEY_SEND_MESSAGE_ENQUEUE_TIME)
+ AWARE_SEND_MESSAGE_TIMEOUT);
} else {
mSendMessageTimeoutMessage.cancel();
}
}
private void processSendMessageTimeout() {
if (mDbg) {
Log.v(TAG, "processSendMessageTimeout: mHostQueuedSendMessages.size()="
+ mHostQueuedSendMessages.size() + ", mFwQueuedSendMessages.size()="
+ mFwQueuedSendMessages.size() + ", mSendQueueBlocked="
+ mSendQueueBlocked);
}
/*
* Note: using 'first' to always time-out (remove) at least 1 notification (partially)
* due to test code needs: there's no way to mock elapsedRealtime(). TODO: replace with
* injected getClock() once moved off of mmwd.
*/
boolean first = true;
long currentTime = SystemClock.elapsedRealtime();
Iterator<Map.Entry<Short, Message>> it = mFwQueuedSendMessages.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Short, Message> entry = it.next();
short transactionId = entry.getKey();
Message message = entry.getValue();
long messageEnqueueTime = message.getData().getLong(
MESSAGE_BUNDLE_KEY_SEND_MESSAGE_ENQUEUE_TIME);
if (first || messageEnqueueTime + AWARE_SEND_MESSAGE_TIMEOUT <= currentTime) {
if (mDbg) {
Log.v(TAG, "processSendMessageTimeout: expiring - transactionId="
+ transactionId + ", message=" + message
+ ", due to messageEnqueueTime=" + messageEnqueueTime
+ ", currentTime=" + currentTime);
}
onMessageSendFailLocal(message, NanStatusType.INTERNAL_FAILURE);
it.remove();
first = false;
} else {
break;
}
}
updateSendMessageTimeout();
mSendQueueBlocked = false;
transmitNextMessage();
}
private boolean isUidExceededMessageQueueDepthLimit(int uid) {
int size = mHostQueuedSendMessages.size();
int numOfMessages = 0;
if (size < MESSAGE_QUEUE_DEPTH_PER_UID) {
return false;
}
for (int i = 0; i < size; ++i) {
if (mHostQueuedSendMessages.valueAt(i).getData()
.getInt(MESSAGE_BUNDLE_KEY_UID) == uid) {
numOfMessages++;
if (numOfMessages >= MESSAGE_QUEUE_DEPTH_PER_UID) {
return true;
}
}
}
return false;
}
@Override
protected String getLogRecString(Message msg) {
StringBuilder sb = new StringBuilder(WifiAwareStateManager.messageToString(msg));
if (msg.what == MESSAGE_TYPE_COMMAND
&& mCurrentTransactionId != TRANSACTION_ID_IGNORE) {
sb.append(" (Transaction ID=").append(mCurrentTransactionId).append(")");
}
return sb.toString();
}
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("WifiAwareStateMachine:");
pw.println(" mNextTransactionId: " + mNextTransactionId);
pw.println(" mNextSessionId: " + mNextSessionId);
pw.println(" mCurrentCommand: " + mCurrentCommand);
pw.println(" mCurrentTransaction: " + mCurrentTransactionId);
pw.println(" mSendQueueBlocked: " + mSendQueueBlocked);
pw.println(" mSendArrivalSequenceCounter: " + mSendArrivalSequenceCounter);
pw.println(" mHostQueuedSendMessages: [" + mHostQueuedSendMessages + "]");
pw.println(" mFwQueuedSendMessages: [" + mFwQueuedSendMessages + "]");
super.dump(fd, pw, args);
}
}
private void sendAwareStateChangedBroadcast(boolean enabled) {
if (VDBG) {
Log.v(TAG, "sendAwareStateChangedBroadcast: enabled=" + enabled);
}
final Intent intent = new Intent(WifiAwareManager.ACTION_WIFI_AWARE_STATE_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
private void sendAwareResourcesChangedBroadcast() {
if (!SdkLevel.isAtLeastT()) {
return;
}
if (VDBG) {
Log.v(TAG, "sendAwareResourcesChangedBroadcast");
}
final Intent intent = new Intent(WifiAwareManager.ACTION_WIFI_AWARE_RESOURCE_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
intent.putExtra(WifiAwareManager.EXTRA_AWARE_RESOURCES, getAvailableAwareResources());
mContext.sendBroadcastAsUser(intent, UserHandle.ALL, ACCESS_WIFI_STATE);
}
/*
* COMMANDS
*/
private boolean connectLocal(short transactionId, int clientId, int uid, int pid,
String callingPackage, @Nullable String callingFeatureId,
IWifiAwareEventCallback callback, ConfigRequest configRequest,
boolean notifyIdentityChange, Object attributionSource) {
if (VDBG) {
Log.v(TAG, "connectLocal(): transactionId=" + transactionId + ", clientId=" + clientId
+ ", uid=" + uid + ", pid=" + pid + ", callingPackage=" + callingPackage
+ ", callback=" + callback + ", configRequest=" + configRequest
+ ", notifyIdentityChange=" + notifyIdentityChange);
}
if (!mUsageEnabled) {
Log.w(TAG, "connect(): called with mUsageEnabled=false");
try {
callback.onConnectFail(NanStatusType.INTERNAL_FAILURE);
mAwareMetrics.recordAttachStatus(NanStatusType.INTERNAL_FAILURE);
} catch (RemoteException e) {
Log.w(TAG, "connectLocal onConnectFail(): RemoteException (FYI): " + e);
}
return false;
}
if (mClients.get(clientId) != null) {
Log.e(TAG, "connectLocal: entry already exists for clientId=" + clientId);
}
if (VDBG) {
Log.v(TAG, "mCurrentAwareConfiguration=" + mCurrentAwareConfiguration
+ ", mCurrentIdentityNotification=" + mCurrentIdentityNotification);
}
ConfigRequest merged = mergeConfigRequests(configRequest);
if (merged == null) {
Log.e(TAG, "connectLocal: requested configRequest=" + configRequest
+ ", incompatible with current configurations");
try {
callback.onConnectFail(NanStatusType.INTERNAL_FAILURE);
mAwareMetrics.recordAttachStatus(NanStatusType.INTERNAL_FAILURE);
} catch (RemoteException e) {
Log.w(TAG, "connectLocal onConnectFail(): RemoteException (FYI): " + e);
}
return false;
} else if (VDBG) {
Log.v(TAG, "connectLocal: merged=" + merged);
}
if (mCurrentAwareConfiguration != null && mCurrentAwareConfiguration.equals(merged)
&& (mCurrentIdentityNotification || !notifyIdentityChange)) {
try {
callback.onConnectSuccess(clientId);
} catch (RemoteException e) {
Log.w(TAG, "connectLocal onConnectSuccess(): RemoteException (FYI): " + e);
}
WifiAwareClientState client = new WifiAwareClientState(mContext, clientId, uid, pid,
callingPackage, callingFeatureId, callback, configRequest, notifyIdentityChange,
SystemClock.elapsedRealtime(), mWifiPermissionsUtil, attributionSource);
client.enableVerboseLogging(mDbg);
client.onInterfaceAddressChange(mCurrentDiscoveryInterfaceMac);
mClients.append(clientId, client);
mAwareMetrics.recordAttachSession(uid, notifyIdentityChange, mClients);
if (!mWifiAwareNativeManager.replaceRequestorWs(createMergedRequestorWs())) {
Log.w(TAG, "Failed to replace requestorWs");
}
return false;
}
boolean notificationRequired =
doesAnyClientNeedIdentityChangeNotifications() || notifyIdentityChange;
boolean rangingRequired = doesAnyClientNeedRanging();
int instantMode = getInstantModeFromAllClients();
boolean enableInstantMode = false;
int instantModeChannel = 0;
if (instantMode != INSTANT_MODE_DISABLED || mInstantCommModeGlobalEnable) {
enableInstantMode = true;
instantModeChannel = getAwareInstantCommunicationChannel(instantMode);
}
if (mCurrentAwareConfiguration == null) {
mWifiAwareNativeManager.tryToGetAware(new WorkSource(uid, callingPackage));
}
boolean success = mWifiAwareNativeApi.enableAndConfigure(transactionId, merged,
notificationRequired, mCurrentAwareConfiguration == null,
mPowerManager.isInteractive(), mPowerManager.isDeviceIdleMode(),
rangingRequired, enableInstantMode, instantModeChannel);
if (!success) {
try {
callback.onConnectFail(NanStatusType.INTERNAL_FAILURE);
mAwareMetrics.recordAttachStatus(NanStatusType.INTERNAL_FAILURE);
} catch (RemoteException e) {
Log.w(TAG, "connectLocal onConnectFail(): RemoteException (FYI): " + e);
}
}
return success;
}
private boolean disconnectLocal(short transactionId, int clientId) {
if (VDBG) {
Log.v(TAG,
"disconnectLocal(): transactionId=" + transactionId + ", clientId=" + clientId);
}
WifiAwareClientState client = mClients.get(clientId);
if (client == null) {
Log.e(TAG, "disconnectLocal: no entry for clientId=" + clientId);
return false;
}
mClients.delete(clientId);
mAwareMetrics.recordAttachSessionDuration(client.getCreationTime());
SparseArray<WifiAwareDiscoverySessionState> sessions = client.getSessions();
for (int i = 0; i < sessions.size(); ++i) {
mAwareMetrics.recordDiscoverySessionDuration(sessions.valueAt(i).getCreationTime(),
sessions.valueAt(i).isPublishSession());
}
client.destroy();
if (mClients.size() == 0) {
mCurrentAwareConfiguration = null;
mDataPathMgr.deleteAllInterfaces();
mCurrentRangingEnabled = false;
mCurrentIdentityNotification = false;
mInstantCommModeClientRequest = INSTANT_MODE_DISABLED;
deferDisableAware();
return false;
}
if (!mWifiAwareNativeManager.replaceRequestorWs(createMergedRequestorWs())) {
Log.w(TAG, "Failed to replace requestorWs");
}
ConfigRequest merged = mergeConfigRequests(null);
if (merged == null) {
Log.wtf(TAG, "disconnectLocal: got an incompatible merge on remaining configs!?");
return false;
}
boolean notificationReqs = doesAnyClientNeedIdentityChangeNotifications();
boolean rangingEnabled = doesAnyClientNeedRanging();
int instantMode = getInstantModeFromAllClients();
boolean enableInstantMode = false;
int instantModeChannel = 0;
if (instantMode != INSTANT_MODE_DISABLED || mInstantCommModeGlobalEnable) {
enableInstantMode = true;
instantModeChannel = getAwareInstantCommunicationChannel(instantMode);
}
if (merged.equals(mCurrentAwareConfiguration)
&& mCurrentIdentityNotification == notificationReqs
&& mCurrentRangingEnabled == rangingEnabled
&& mInstantCommModeClientRequest == instantMode) {
return false;
}
return mWifiAwareNativeApi.enableAndConfigure(transactionId, merged, notificationReqs,
false, mPowerManager.isInteractive(), mPowerManager.isDeviceIdleMode(),
rangingEnabled, enableInstantMode, instantModeChannel);
}
private boolean reconfigureLocal(short transactionId) {
if (VDBG) Log.v(TAG, "reconfigureLocal(): transactionId=" + transactionId);
if (mClients.size() == 0) {
// no clients - Aware is not enabled, nothing to reconfigure
return false;
}
boolean notificationReqs = doesAnyClientNeedIdentityChangeNotifications();
boolean rangingEnabled = doesAnyClientNeedRanging();
int instantMode = getInstantModeFromAllClients();
boolean enableInstantMode = false;
int instantModeChannel = 0;
if (instantMode != INSTANT_MODE_DISABLED || mInstantCommModeGlobalEnable) {
enableInstantMode = true;
instantModeChannel = getAwareInstantCommunicationChannel(instantMode);
}
return mWifiAwareNativeApi.enableAndConfigure(transactionId, mCurrentAwareConfiguration,
notificationReqs, false, mPowerManager.isInteractive(),
mPowerManager.isDeviceIdleMode(), rangingEnabled,
enableInstantMode, instantModeChannel);
}
private void terminateSessionLocal(int clientId, int sessionId) {
if (VDBG) {
Log.v(TAG,
"terminateSessionLocal(): clientId=" + clientId + ", sessionId=" + sessionId);
}
WifiAwareClientState client = mClients.get(clientId);
if (client == null) {
Log.e(TAG, "terminateSession: no client exists for clientId=" + clientId);
return;
}
WifiAwareDiscoverySessionState session = client.terminateSession(sessionId);
// If Ranging enabled or instant mode require changes, reconfigure.
if (mCurrentRangingEnabled != doesAnyClientNeedRanging()
|| mInstantCommModeClientRequest != getInstantModeFromAllClients()) {
reconfigure();
}
if (session != null) {
mAwareMetrics.recordDiscoverySessionDuration(session.getCreationTime(),
session.isPublishSession());
}
sendAwareResourcesChangedBroadcast();
}
private boolean publishLocal(short transactionId, int clientId, PublishConfig publishConfig,
IWifiAwareDiscoverySessionCallback callback) {
if (VDBG) {
Log.v(TAG, "publishLocal(): transactionId=" + transactionId + ", clientId=" + clientId
+ ", publishConfig=" + publishConfig + ", callback=" + callback);
}
WifiAwareClientState client = mClients.get(clientId);
if (client == null) {
Log.e(TAG, "publishLocal: no client exists for clientId=" + clientId);
try {
callback.onSessionConfigFail(NanStatusType.INTERNAL_FAILURE);
} catch (RemoteException e) {
Log.w(TAG, "publishLocal onSessionConfigFail(): RemoteException (FYI): " + e);
}
return false;
}
boolean success = mWifiAwareNativeApi.publish(transactionId, (byte) 0, publishConfig);
if (!success) {
try {
callback.onSessionConfigFail(NanStatusType.INTERNAL_FAILURE);
} catch (RemoteException e) {
Log.w(TAG, "publishLocal onSessionConfigFail(): RemoteException (FYI): " + e);
}
mAwareMetrics.recordDiscoveryStatus(client.getUid(), NanStatusType.INTERNAL_FAILURE,
true);
}
return success;
}
private boolean updatePublishLocal(short transactionId, int clientId, int sessionId,
PublishConfig publishConfig) {
if (VDBG) {
Log.v(TAG, "updatePublishLocal(): transactionId=" + transactionId + ", clientId="
+ clientId + ", sessionId=" + sessionId + ", publishConfig=" + publishConfig);
}
WifiAwareClientState client = mClients.get(clientId);
if (client == null) {
Log.e(TAG, "updatePublishLocal: no client exists for clientId=" + clientId);
return false;
}
WifiAwareDiscoverySessionState session = client.getSession(sessionId);
if (session == null) {
Log.e(TAG, "updatePublishLocal: no session exists for clientId=" + clientId
+ ", sessionId=" + sessionId);
return false;
}
boolean status = session.updatePublish(transactionId, publishConfig);
if (!status) {
mAwareMetrics.recordDiscoveryStatus(client.getUid(), NanStatusType.INTERNAL_FAILURE,
true);
}
return status;
}
private boolean subscribeLocal(short transactionId, int clientId,
SubscribeConfig subscribeConfig, IWifiAwareDiscoverySessionCallback callback) {
if (VDBG) {
Log.v(TAG, "subscribeLocal(): transactionId=" + transactionId + ", clientId=" + clientId
+ ", subscribeConfig=" + subscribeConfig + ", callback=" + callback);
}
WifiAwareClientState client = mClients.get(clientId);
if (client == null) {
try {
callback.onSessionConfigFail(NanStatusType.INTERNAL_FAILURE);
} catch (RemoteException e) {
Log.w(TAG, "subscribeLocal onSessionConfigFail(): RemoteException (FYI): " + e);
}
Log.e(TAG, "subscribeLocal: no client exists for clientId=" + clientId);
return false;
}
boolean success = mWifiAwareNativeApi.subscribe(transactionId, (byte) 0, subscribeConfig);
if (!success) {
try {
callback.onSessionConfigFail(NanStatusType.INTERNAL_FAILURE);
} catch (RemoteException e) {
Log.w(TAG, "subscribeLocal onSessionConfigFail(): RemoteException (FYI): " + e);
}
mAwareMetrics.recordDiscoveryStatus(client.getUid(), NanStatusType.INTERNAL_FAILURE,
false);
}
return success;
}
private boolean updateSubscribeLocal(short transactionId, int clientId, int sessionId,
SubscribeConfig subscribeConfig) {
if (VDBG) {
Log.v(TAG,
"updateSubscribeLocal(): transactionId=" + transactionId + ", clientId="
+ clientId + ", sessionId=" + sessionId + ", subscribeConfig="
+ subscribeConfig);
}
WifiAwareClientState client = mClients.get(clientId);
if (client == null) {
Log.e(TAG, "updateSubscribeLocal: no client exists for clientId=" + clientId);
return false;
}
WifiAwareDiscoverySessionState session = client.getSession(sessionId);
if (session == null) {
Log.e(TAG, "updateSubscribeLocal: no session exists for clientId=" + clientId
+ ", sessionId=" + sessionId);
return false;
}
boolean status = session.updateSubscribe(transactionId, subscribeConfig);
if (!status) {
mAwareMetrics.recordDiscoveryStatus(client.getUid(), NanStatusType.INTERNAL_FAILURE,
false);
}
return status;
}
private boolean sendFollowonMessageLocal(short transactionId, int clientId, int sessionId,
int peerId, byte[] message, int messageId) {
if (VDBG) {
Log.v(TAG,
"sendFollowonMessageLocal(): transactionId=" + transactionId + ", clientId="
+ clientId + ", sessionId=" + sessionId + ", peerId=" + peerId
+ ", messageId=" + messageId);
}
WifiAwareClientState client = mClients.get(clientId);
if (client == null) {
Log.e(TAG, "sendFollowonMessageLocal: no client exists for clientId=" + clientId);
return false;
}
WifiAwareDiscoverySessionState session = client.getSession(sessionId);
if (session == null) {
Log.e(TAG, "sendFollowonMessageLocal: no session exists for clientId=" + clientId
+ ", sessionId=" + sessionId);
return false;
}
return session.sendMessage(transactionId, peerId, message, messageId);
}
private void enableUsageLocal() {
if (mDbg) Log.v(TAG, "enableUsageLocal: mUsageEnabled=" + mUsageEnabled);
if (mUsageEnabled) {
return;
}
mUsageEnabled = true;
sendAwareStateChangedBroadcast(true);
mAwareMetrics.recordEnableUsage();
}
private void disableUsageLocal(short transactionId, boolean markAsAvailable) {
if (VDBG) {
Log.v(TAG, "disableUsageLocal: transactionId=" + transactionId + ", mUsageEnabled="
+ mUsageEnabled);
}
if (!mUsageEnabled) {
return;
}
onAwareDownLocal();
mUsageEnabled = markAsAvailable;
mCurrentRangingEnabled = false;
mCurrentIdentityNotification = false;
mInstantCommModeClientRequest = INSTANT_MODE_DISABLED;
deferDisableAware();
sendAwareStateChangedBroadcast(markAsAvailable);
if (!markAsAvailable) {
mAwareMetrics.recordDisableUsage();
}
}
private boolean initiateDataPathSetupLocal(short transactionId,
WifiAwareNetworkSpecifier networkSpecifier, int peerId, int channelRequestType,
int channel, byte[] peer, String interfaceName,
boolean isOutOfBand, byte[] appInfo) {
WifiAwareDataPathSecurityConfig securityConfig = networkSpecifier
.getWifiAwareDataPathSecurityConfig();
if (VDBG) {
Log.v(TAG, "initiateDataPathSetupLocal(): transactionId=" + transactionId
+ ", networkSpecifier=" + networkSpecifier + ", peerId=" + peerId
+ ", channelRequestType=" + channelRequestType + ", channel=" + channel
+ ", peer="
+ String.valueOf(HexEncoding.encode(peer)) + ", interfaceName=" + interfaceName
+ ", securityConfig=" + ((securityConfig == null) ? "" : securityConfig)
+ ", isOutOfBand="
+ isOutOfBand + ", appInfo=" + (appInfo == null ? "<null>" : "<non-null>"));
}
boolean success = mWifiAwareNativeApi.initiateDataPath(transactionId, peerId,
channelRequestType, channel, peer, interfaceName, isOutOfBand,
appInfo, mCapabilities, networkSpecifier.getWifiAwareDataPathSecurityConfig());
if (!success) {
mDataPathMgr.onDataPathInitiateFail(networkSpecifier, NanStatusType.INTERNAL_FAILURE);
}
return success;
}
private boolean respondToDataPathRequestLocal(short transactionId, boolean accept,
int ndpId, String interfaceName, byte[] appInfo, boolean isOutOfBand,
WifiAwareDataPathSecurityConfig securityConfig) {
if (VDBG) {
Log.v(TAG,
"respondToDataPathRequestLocal(): transactionId=" + transactionId + ", accept="
+ accept + ", ndpId=" + ndpId + ", interfaceName=" + interfaceName
+ ", securityConfig=" + securityConfig
+ ", isOutOfBand=" + isOutOfBand
+ ", appInfo=" + (appInfo == null ? "<null>" : "<non-null>"));
}
boolean success = mWifiAwareNativeApi.respondToDataPathRequest(transactionId, accept, ndpId,
interfaceName, appInfo, isOutOfBand, mCapabilities, securityConfig);
if (!success) {
mDataPathMgr.onRespondToDataPathRequest(ndpId, false, NanStatusType.INTERNAL_FAILURE);
} else {
sendAwareResourcesChangedBroadcast();
}
return success;
}
private boolean endDataPathLocal(short transactionId, int ndpId) {
if (VDBG) {
Log.v(TAG,
"endDataPathLocal: transactionId=" + transactionId + ", ndpId=" + ndpId);
}
sendAwareResourcesChangedBroadcast();
return mWifiAwareNativeApi.endDataPath(transactionId, ndpId);
}
/*
* RESPONSES
*/
private void onConfigCompletedLocal(Message completedCommand) {
if (VDBG) {
Log.v(TAG, "onConfigCompleted: completedCommand=" + completedCommand);
}
if (completedCommand.arg1 == COMMAND_TYPE_CONNECT) {
if (mCurrentAwareConfiguration == null) { // enabled (as opposed to re-configured)
queryCapabilities();
mDataPathMgr.createAllInterfaces();
}
Bundle data = completedCommand.getData();
int clientId = completedCommand.arg2;
Pair<IWifiAwareEventCallback, Object> callbackAndAttributionSource =
(Pair<IWifiAwareEventCallback, Object>) completedCommand.obj;
IWifiAwareEventCallback callback = callbackAndAttributionSource.first;
ConfigRequest configRequest = (ConfigRequest) data
.getParcelable(MESSAGE_BUNDLE_KEY_CONFIG);
int uid = data.getInt(MESSAGE_BUNDLE_KEY_UID);
int pid = data.getInt(MESSAGE_BUNDLE_KEY_PID);
boolean notifyIdentityChange = data.getBoolean(
MESSAGE_BUNDLE_KEY_NOTIFY_IDENTITY_CHANGE);
String callingPackage = data.getString(MESSAGE_BUNDLE_KEY_CALLING_PACKAGE);
String callingFeatureId = data.getString(MESSAGE_BUNDLE_KEY_CALLING_FEATURE_ID);
WifiAwareClientState client = new WifiAwareClientState(mContext, clientId, uid, pid,
callingPackage, callingFeatureId, callback, configRequest, notifyIdentityChange,
SystemClock.elapsedRealtime(), mWifiPermissionsUtil,
callbackAndAttributionSource.second);
client.enableVerboseLogging(mDbg);
mClients.put(clientId, client);
mAwareMetrics.recordAttachSession(uid, notifyIdentityChange, mClients);
try {
callback.onConnectSuccess(clientId);
} catch (RemoteException e) {
Log.w(TAG,
"onConfigCompletedLocal onConnectSuccess(): RemoteException (FYI): " + e);
}
client.onInterfaceAddressChange(mCurrentDiscoveryInterfaceMac);
} else if (completedCommand.arg1 == COMMAND_TYPE_DISCONNECT) {
/*
* NOP (i.e. updated configuration after disconnecting a client)
*/
} else if (completedCommand.arg1 == COMMAND_TYPE_RECONFIGURE) {
/*
* NOP (i.e. updated configuration at power saving event)
*/
} else {
Log.wtf(TAG, "onConfigCompletedLocal: unexpected completedCommand=" + completedCommand);
return;
}
mCurrentAwareConfiguration = mergeConfigRequests(null);
if (mCurrentAwareConfiguration == null) {
Log.wtf(TAG, "onConfigCompletedLocal: got a null merged configuration after config!?");
}
mCurrentIdentityNotification = doesAnyClientNeedIdentityChangeNotifications();
mCurrentRangingEnabled = doesAnyClientNeedRanging();
mInstantCommModeClientRequest = getInstantModeFromAllClients();
if (mInstantCommModeClientRequest != INSTANT_MODE_DISABLED
&& !mInstantCommModeGlobalEnable) {
// Change the instant communication mode when timeout
mHandler.postDelayed(() -> reconfigure(), (long) mContext.getResources()
.getInteger(R.integer.config_wifiAwareInstantCommunicationModeDurationMillis));
}
}
private void onConfigFailedLocal(Message failedCommand, int reason) {
if (VDBG) {
Log.v(TAG,
"onConfigFailedLocal: failedCommand=" + failedCommand + ", reason=" + reason);
}
if (failedCommand.arg1 == COMMAND_TYPE_CONNECT) {
Pair<IWifiAwareEventCallback, Object> callbackAndAttributionSource =
(Pair<IWifiAwareEventCallback, Object>) failedCommand.obj;
IWifiAwareEventCallback callback = callbackAndAttributionSource.first;
try {
callback.onConnectFail(reason);
mAwareMetrics.recordAttachStatus(reason);
} catch (RemoteException e) {
Log.w(TAG, "onConfigFailedLocal onConnectFail(): RemoteException (FYI): " + e);
}
} else if (failedCommand.arg1 == COMMAND_TYPE_DISCONNECT) {
/*
* NOP (tried updating configuration after disconnecting a client -
* shouldn't fail but there's nothing to do - the old configuration
* is still up-and-running).
*
* OR: timed-out getting a response to a disable. Either way a NOP.
*/
} else if (failedCommand.arg1 == COMMAND_TYPE_RECONFIGURE) {
/*
* NOP (configuration change as part of possibly power saving event - should not
* fail but there's nothing to do).
*/
} else {
Log.wtf(TAG, "onConfigFailedLocal: unexpected failedCommand=" + failedCommand);
return;
}
}
private void onDisableResponseLocal(Message command, int reason) {
if (VDBG) {
Log.v(TAG, "onDisableResponseLocal: command=" + command + ", reason=" + reason);
}
/*
* do nothing:
* - success: was waiting so that don't enable while disabling
* - fail: shouldn't happen (though can if already disabled for instance)
*/
if (reason != NanStatusType.SUCCESS) {
Log.e(TAG, "onDisableResponseLocal: FAILED!? command=" + command + ", reason="
+ reason);
}
mAwareMetrics.recordDisableAware();
}
private void onSessionConfigSuccessLocal(Message completedCommand, byte pubSubId,
boolean isPublish) {
if (VDBG) {
Log.v(TAG, "onSessionConfigSuccessLocal: completedCommand=" + completedCommand
+ ", pubSubId=" + pubSubId + ", isPublish=" + isPublish);
}
boolean isRangingEnabled = false;
boolean enableInstantMode = false;
int instantModeBand;
int minRange = -1;
int maxRange = -1;
if (isPublish) {
PublishConfig publishConfig = completedCommand.getData().getParcelable(
MESSAGE_BUNDLE_KEY_CONFIG);
isRangingEnabled = publishConfig.mEnableRanging;
enableInstantMode = publishConfig.isInstantCommunicationModeEnabled();
instantModeBand = publishConfig.getInstantCommunicationBand();
} else {
SubscribeConfig subscribeConfig = completedCommand.getData().getParcelable(
MESSAGE_BUNDLE_KEY_CONFIG);
isRangingEnabled =
subscribeConfig.mMinDistanceMmSet || subscribeConfig.mMaxDistanceMmSet;
if (subscribeConfig.mMinDistanceMmSet) {
minRange = subscribeConfig.mMinDistanceMm;
}
if (subscribeConfig.mMaxDistanceMmSet) {
maxRange = subscribeConfig.mMaxDistanceMm;
}
enableInstantMode = subscribeConfig.isInstantCommunicationModeEnabled();
instantModeBand = subscribeConfig.getInstantCommunicationBand();
}
if (completedCommand.arg1 == COMMAND_TYPE_PUBLISH
|| completedCommand.arg1 == COMMAND_TYPE_SUBSCRIBE) {
int clientId = completedCommand.arg2;
IWifiAwareDiscoverySessionCallback callback =
(IWifiAwareDiscoverySessionCallback) completedCommand.obj;
WifiAwareClientState client = mClients.get(clientId);
if (client == null) {
Log.e(TAG,
"onSessionConfigSuccessLocal: no client exists for clientId=" + clientId);
return;
}
int sessionId = mSm.mNextSessionId++;
try {
callback.onSessionStarted(sessionId);
} catch (RemoteException e) {
Log.e(TAG, "onSessionConfigSuccessLocal: onSessionStarted() RemoteException=" + e);
return;
}
WifiAwareDiscoverySessionState session = new WifiAwareDiscoverySessionState(
mWifiAwareNativeApi, sessionId, pubSubId, callback, isPublish, isRangingEnabled,
SystemClock.elapsedRealtime(), enableInstantMode, instantModeBand);
session.enableVerboseLogging(mDbg);
client.addSession(session);
if (isRangingEnabled) {
mAwareMetrics.recordDiscoverySessionWithRanging(client.getUid(),
completedCommand.arg1 != COMMAND_TYPE_PUBLISH, minRange, maxRange,
mClients);
} else {
mAwareMetrics.recordDiscoverySession(client.getUid(), mClients);
}
mAwareMetrics.recordDiscoveryStatus(client.getUid(), NanStatusType.SUCCESS,
completedCommand.arg1 == COMMAND_TYPE_PUBLISH);
sendAwareResourcesChangedBroadcast();
} else if (completedCommand.arg1 == COMMAND_TYPE_UPDATE_PUBLISH
|| completedCommand.arg1 == COMMAND_TYPE_UPDATE_SUBSCRIBE) {
int clientId = completedCommand.arg2;
int sessionId = completedCommand.getData().getInt(MESSAGE_BUNDLE_KEY_SESSION_ID);
WifiAwareClientState client = mClients.get(clientId);
if (client == null) {
Log.e(TAG,
"onSessionConfigSuccessLocal: no client exists for clientId=" + clientId);
return;
}
WifiAwareDiscoverySessionState session = client.getSession(sessionId);
if (session == null) {
Log.e(TAG, "onSessionConfigSuccessLocal: no session exists for clientId=" + clientId
+ ", sessionId=" + sessionId);
return;
}
try {
session.getCallback().onSessionConfigSuccess();
} catch (RemoteException e) {
Log.e(TAG, "onSessionConfigSuccessLocal: onSessionConfigSuccess() RemoteException="
+ e);
}
session.setRangingEnabled(isRangingEnabled);
session.setInstantModeEnabled(enableInstantMode);
session.setInstantModeBand(instantModeBand);
mAwareMetrics.recordDiscoveryStatus(client.getUid(), NanStatusType.SUCCESS,
completedCommand.arg1 == COMMAND_TYPE_UPDATE_PUBLISH);
} else {
Log.wtf(TAG,
"onSessionConfigSuccessLocal: unexpected completedCommand=" + completedCommand);
return;
}
// If Ranging enabled or instant mode require changes, reconfigure.
if (mCurrentRangingEnabled != doesAnyClientNeedRanging()
|| mInstantCommModeClientRequest != getInstantModeFromAllClients()) {
reconfigure();
}
}
private void onSessionConfigFailLocal(Message failedCommand, boolean isPublish, int reason) {
if (VDBG) {
Log.v(TAG, "onSessionConfigFailLocal: failedCommand=" + failedCommand + ", isPublish="
+ isPublish + ", reason=" + reason);
}
if (failedCommand.arg1 == COMMAND_TYPE_PUBLISH
|| failedCommand.arg1 == COMMAND_TYPE_SUBSCRIBE) {
int clientId = failedCommand.arg2;
IWifiAwareDiscoverySessionCallback callback =
(IWifiAwareDiscoverySessionCallback) failedCommand.obj;
WifiAwareClientState client = mClients.get(clientId);
if (client == null) {
Log.e(TAG, "onSessionConfigFailLocal: no client exists for clientId=" + clientId);
return;
}
try {
callback.onSessionConfigFail(reason);
} catch (RemoteException e) {
Log.w(TAG, "onSessionConfigFailLocal onSessionConfigFail(): RemoteException (FYI): "
+ e);
}
mAwareMetrics.recordDiscoveryStatus(client.getUid(), reason,
failedCommand.arg1 == COMMAND_TYPE_PUBLISH);
} else if (failedCommand.arg1 == COMMAND_TYPE_UPDATE_PUBLISH
|| failedCommand.arg1 == COMMAND_TYPE_UPDATE_SUBSCRIBE) {
int clientId = failedCommand.arg2;
int sessionId = failedCommand.getData().getInt(MESSAGE_BUNDLE_KEY_SESSION_ID);
WifiAwareClientState client = mClients.get(clientId);
if (client == null) {
Log.e(TAG, "onSessionConfigFailLocal: no client exists for clientId=" + clientId);
return;
}
WifiAwareDiscoverySessionState session = client.getSession(sessionId);
if (session == null) {
Log.e(TAG, "onSessionConfigFailLocal: no session exists for clientId=" + clientId
+ ", sessionId=" + sessionId);
return;
}
try {
session.getCallback().onSessionConfigFail(reason);
} catch (RemoteException e) {
Log.e(TAG, "onSessionConfigFailLocal: onSessionConfigFail() RemoteException=" + e);
}
mAwareMetrics.recordDiscoveryStatus(client.getUid(), reason,
failedCommand.arg1 == COMMAND_TYPE_UPDATE_PUBLISH);
if (reason == NanStatusType.INVALID_SESSION_ID) {
client.removeSession(sessionId);
// If Ranging enabled or instant mode require changes, reconfigure.
if (mCurrentRangingEnabled != doesAnyClientNeedRanging()
|| mInstantCommModeClientRequest != getInstantModeFromAllClients()) {
reconfigure();
}
sendAwareResourcesChangedBroadcast();
}
} else {
Log.wtf(TAG, "onSessionConfigFailLocal: unexpected failedCommand=" + failedCommand);
}
}
private void onMessageSendSuccessLocal(Message completedCommand) {
if (VDBG) {
Log.v(TAG, "onMessageSendSuccess: completedCommand=" + completedCommand);
}
int clientId = completedCommand.arg2;
int sessionId = completedCommand.getData().getInt(MESSAGE_BUNDLE_KEY_SESSION_ID);
int messageId = completedCommand.getData().getInt(MESSAGE_BUNDLE_KEY_MESSAGE_ID);
WifiAwareClientState client = mClients.get(clientId);
if (client == null) {
Log.e(TAG, "onMessageSendSuccessLocal: no client exists for clientId=" + clientId);
return;
}
WifiAwareDiscoverySessionState session = client.getSession(sessionId);
if (session == null) {
Log.e(TAG, "onMessageSendSuccessLocal: no session exists for clientId=" + clientId
+ ", sessionId=" + sessionId);
return;
}
try {
session.getCallback().onMessageSendSuccess(messageId);
} catch (RemoteException e) {
Log.w(TAG, "onMessageSendSuccessLocal: RemoteException (FYI): " + e);
}
}
private void onMessageSendFailLocal(Message failedCommand, int reason) {
if (VDBG) {
Log.v(TAG, "onMessageSendFail: failedCommand=" + failedCommand + ", reason=" + reason);
}
int clientId = failedCommand.arg2;
int sessionId = failedCommand.getData().getInt(MESSAGE_BUNDLE_KEY_SESSION_ID);
int messageId = failedCommand.getData().getInt(MESSAGE_BUNDLE_KEY_MESSAGE_ID);
WifiAwareClientState client = mClients.get(clientId);
if (client == null) {
Log.e(TAG, "onMessageSendFailLocal: no client exists for clientId=" + clientId);
return;
}
WifiAwareDiscoverySessionState session = client.getSession(sessionId);
if (session == null) {
Log.e(TAG, "onMessageSendFailLocal: no session exists for clientId=" + clientId
+ ", sessionId=" + sessionId);
return;
}
try {
session.getCallback().onMessageSendFail(messageId, reason);
} catch (RemoteException e) {
Log.e(TAG, "onMessageSendFailLocal: onMessageSendFail RemoteException=" + e);
}
}
private void onCapabilitiesUpdatedResponseLocal(Capabilities capabilities) {
if (VDBG) {
Log.v(TAG, "onCapabilitiesUpdatedResponseLocal: capabilites=" + capabilities);
}
mCapabilities = capabilities;
mCharacteristics = null;
}
private void onCreateDataPathInterfaceResponseLocal(Message command, boolean success,
int reasonOnFailure) {
if (VDBG) {
Log.v(TAG, "onCreateDataPathInterfaceResponseLocal: command=" + command + ", success="
+ success + ", reasonOnFailure=" + reasonOnFailure);
}
if (success) {
if (VDBG) {
Log.v(TAG, "onCreateDataPathInterfaceResponseLocal: successfully created interface "
+ command.obj);
}
mDataPathMgr.onInterfaceCreated((String) command.obj);
} else {
Log.e(TAG,
"onCreateDataPathInterfaceResponseLocal: failed when trying to create "
+ "interface "
+ command.obj + ". Reason code=" + reasonOnFailure);
}
}
private void onDeleteDataPathInterfaceResponseLocal(Message command, boolean success,
int reasonOnFailure) {
if (VDBG) {
Log.v(TAG, "onDeleteDataPathInterfaceResponseLocal: command=" + command + ", success="
+ success + ", reasonOnFailure=" + reasonOnFailure);
}
if (success) {
if (VDBG) {
Log.v(TAG, "onDeleteDataPathInterfaceResponseLocal: successfully deleted interface "
+ command.obj);
}
mDataPathMgr.onInterfaceDeleted((String) command.obj);
} else {
Log.e(TAG,
"onDeleteDataPathInterfaceResponseLocal: failed when trying to delete "
+ "interface "
+ command.obj + ". Reason code=" + reasonOnFailure);
}
}
private boolean onInitiateDataPathResponseSuccessLocal(Message command, int ndpId) {
if (VDBG) {
Log.v(TAG, "onInitiateDataPathResponseSuccessLocal: command=" + command + ", ndpId="
+ ndpId);
}
return mDataPathMgr
.onDataPathInitiateSuccess((WifiAwareNetworkSpecifier) command.obj, ndpId);
}
private void onInitiateDataPathResponseFailLocal(Message command, int reason) {
if (VDBG) {
Log.v(TAG, "onInitiateDataPathResponseFailLocal: command=" + command + ", reason="
+ reason);
}
mDataPathMgr.onDataPathInitiateFail((WifiAwareNetworkSpecifier) command.obj, reason);
}
private void onRespondToDataPathSetupRequestResponseLocal(Message command, boolean success,
int reasonOnFailure) {
if (VDBG) {
Log.v(TAG, "onRespondToDataPathSetupRequestResponseLocal: command=" + command
+ ", success=" + success + ", reasonOnFailure=" + reasonOnFailure);
}
mDataPathMgr.onRespondToDataPathRequest(command.arg2, success, reasonOnFailure);
}
private void onEndPathEndResponseLocal(Message command, boolean success, int reasonOnFailure) {
if (VDBG) {
Log.v(TAG, "onEndPathEndResponseLocal: command=" + command
+ ", success=" + success + ", reasonOnFailure=" + reasonOnFailure);
}
// TODO: do something with this
}
/*
* NOTIFICATIONS
*/
private void onInterfaceAddressChangeLocal(byte[] mac) {
if (VDBG) {
Log.v(TAG, "onInterfaceAddressChange: mac=" + String.valueOf(HexEncoding.encode(mac)));
}
mCurrentDiscoveryInterfaceMac = mac;
for (int i = 0; i < mClients.size(); ++i) {
WifiAwareClientState client = mClients.valueAt(i);
client.onInterfaceAddressChange(mac);
}
mAwareMetrics.recordEnableAware();
}
private void onClusterChangeLocal(int flag, byte[] clusterId) {
if (VDBG) {
Log.v(TAG, "onClusterChange: flag=" + flag + ", clusterId="
+ String.valueOf(HexEncoding.encode(clusterId)));
}
for (int i = 0; i < mClients.size(); ++i) {
WifiAwareClientState client = mClients.valueAt(i);
client.onClusterChange(flag, clusterId, mCurrentDiscoveryInterfaceMac);
}
mAwareMetrics.recordEnableAware();
}
private void onMatchLocal(int pubSubId, int requestorInstanceId, byte[] peerMac,
byte[] serviceSpecificInfo, byte[] matchFilter, int rangingIndication, int rangeMm,
int cipherSuite, byte[] scid) {
if (VDBG) {
Log.v(TAG,
"onMatch: pubSubId=" + pubSubId + ", requestorInstanceId=" + requestorInstanceId
+ ", peerDiscoveryMac=" + String.valueOf(HexEncoding.encode(peerMac))
+ ", serviceSpecificInfo=" + Arrays.toString(serviceSpecificInfo)
+ ", matchFilter=" + Arrays.toString(matchFilter)
+ ", rangingIndication=" + rangingIndication + ", rangeMm=" + rangeMm);
}
Pair<WifiAwareClientState, WifiAwareDiscoverySessionState> data =
getClientSessionForPubSubId(pubSubId);
if (data == null) {
Log.e(TAG, "onMatch: no session found for pubSubId=" + pubSubId);
return;
}
if (data.second.isRangingEnabled()) {
mAwareMetrics.recordMatchIndicationForRangeEnabledSubscribe(rangingIndication != 0);
}
data.second.onMatch(requestorInstanceId, peerMac, serviceSpecificInfo, matchFilter,
rangingIndication, rangeMm, cipherSuite, scid);
}
private void onMatchExpiredLocal(int pubSubId, int requestorInstanceId) {
if (VDBG) {
Log.v(TAG,
"onMatchExpiredNotification: pubSubId=" + pubSubId
+ ", requestorInstanceId=" + requestorInstanceId);
}
Pair<WifiAwareClientState, WifiAwareDiscoverySessionState> data =
getClientSessionForPubSubId(pubSubId);
if (data == null) {
Log.e(TAG, "onMatch: no session found for pubSubId=" + pubSubId);
return;
}
data.second.onMatchExpired(requestorInstanceId);
}
private void onSessionTerminatedLocal(int pubSubId, boolean isPublish, int reason) {
if (VDBG) {
Log.v(TAG, "onSessionTerminatedLocal: pubSubId=" + pubSubId + ", isPublish=" + isPublish
+ ", reason=" + reason);
}
Pair<WifiAwareClientState, WifiAwareDiscoverySessionState> data =
getClientSessionForPubSubId(pubSubId);
if (data == null) {
Log.e(TAG, "onSessionTerminatedLocal: no session found for pubSubId=" + pubSubId);
return;
}
try {
data.second.getCallback().onSessionTerminated(reason);
} catch (RemoteException e) {
Log.w(TAG,
"onSessionTerminatedLocal onSessionTerminated(): RemoteException (FYI): " + e);
}
data.first.removeSession(data.second.getSessionId());
// If Ranging enabled or instant mode require changes, reconfigure.
if (mCurrentRangingEnabled != doesAnyClientNeedRanging()
|| mInstantCommModeClientRequest != getInstantModeFromAllClients()) {
reconfigure();
}
mAwareMetrics.recordDiscoverySessionDuration(data.second.getCreationTime(),
data.second.isPublishSession());
sendAwareResourcesChangedBroadcast();
}
private void onMessageReceivedLocal(int pubSubId, int requestorInstanceId, byte[] peerMac,
byte[] message) {
if (VDBG) {
Log.v(TAG,
"onMessageReceivedLocal: pubSubId=" + pubSubId + ", requestorInstanceId="
+ requestorInstanceId + ", peerDiscoveryMac="
+ String.valueOf(HexEncoding.encode(peerMac)));
}
Pair<WifiAwareClientState, WifiAwareDiscoverySessionState> data =
getClientSessionForPubSubId(pubSubId);
if (data == null) {
Log.e(TAG, "onMessageReceivedLocal: no session found for pubSubId=" + pubSubId);
return;
}
data.second.onMessageReceived(requestorInstanceId, peerMac, message);
}
private void onAwareDownLocal() {
if (VDBG) {
Log.v(TAG, "onAwareDown: mCurrentAwareConfiguration=" + mCurrentAwareConfiguration);
}
if (mCurrentAwareConfiguration == null) {
return;
}
for (int i = 0; i < mClients.size(); ++i) {
WifiAwareClientState client = mClients.valueAt(i);
mAwareMetrics.recordAttachSessionDuration(client.getCreationTime());
SparseArray<WifiAwareDiscoverySessionState> sessions = client.getSessions();
for (int j = 0; j < sessions.size(); ++j) {
mAwareMetrics.recordDiscoverySessionDuration(sessions.valueAt(j).getCreationTime(),
sessions.valueAt(j).isPublishSession());
}
client.destroy();
}
mAwareMetrics.recordDisableAware();
mClients.clear();
mCurrentAwareConfiguration = null;
mSm.onAwareDownCleanupSendQueueState();
mDataPathMgr.onAwareDownCleanupDataPaths();
mCurrentDiscoveryInterfaceMac = ALL_ZERO_MAC;
mDataPathMgr.deleteAllInterfaces();
sendAwareResourcesChangedBroadcast();
}
/*
* Utilities
*/
private Pair<WifiAwareClientState, WifiAwareDiscoverySessionState> getClientSessionForPubSubId(
int pubSubId) {
for (int i = 0; i < mClients.size(); ++i) {
WifiAwareClientState client = mClients.valueAt(i);
WifiAwareDiscoverySessionState session = client.getAwareSessionStateForPubSubId(
pubSubId);
if (session != null) {
return new Pair<>(client, session);
}
}
return null;
}
/**
* Merge all the existing client configurations with the (optional) input configuration request.
* If the configurations are "incompatible" (rules in comment below) return a null.
*/
private ConfigRequest mergeConfigRequests(ConfigRequest configRequest) {
if (mDbg) {
Log.v(TAG, "mergeConfigRequests(): mClients=[" + mClients + "], configRequest="
+ configRequest);
}
if (mClients.size() == 0 && configRequest == null) {
Log.e(TAG, "mergeConfigRequests: invalid state - called with 0 clients registered!");
return null;
}
// TODO: continue working on merge algorithm:
// - if any request 5g: enable
// - maximal master preference
// - cluster range: must be identical
// - if any request identity change: enable
// - discovery window: minimum value if specified, 0 (disable) is considered an infinity
boolean support5gBand = false;
boolean support6gBand = false;
int masterPreference = 0;
boolean clusterIdValid = false;
int clusterLow = 0;
int clusterHigh = ConfigRequest.CLUSTER_ID_MAX;
int[] discoveryWindowInterval =
{ConfigRequest.DW_INTERVAL_NOT_INIT, ConfigRequest.DW_INTERVAL_NOT_INIT};
if (configRequest != null) {
support5gBand = configRequest.mSupport5gBand;
support6gBand = configRequest.mSupport6gBand;
masterPreference = configRequest.mMasterPreference;
clusterIdValid = true;
clusterLow = configRequest.mClusterLow;
clusterHigh = configRequest.mClusterHigh;
discoveryWindowInterval = configRequest.mDiscoveryWindowInterval;
}
for (int i = 0; i < mClients.size(); ++i) {
ConfigRequest cr = mClients.valueAt(i).getConfigRequest();
// any request turns on 5G
if (cr.mSupport5gBand) {
support5gBand = true;
}
// any request turns on 5G
if (cr.mSupport6gBand) {
support6gBand = true;
}
// maximal master preference
masterPreference = Math.max(masterPreference, cr.mMasterPreference);
// cluster range must be the same across all config requests
if (!clusterIdValid) {
clusterIdValid = true;
clusterLow = cr.mClusterLow;
clusterHigh = cr.mClusterHigh;
} else {
if (clusterLow != cr.mClusterLow) return null;
if (clusterHigh != cr.mClusterHigh) return null;
}
for (int band = ConfigRequest.NAN_BAND_24GHZ; band <= ConfigRequest.NAN_BAND_5GHZ;
++band) {
if (discoveryWindowInterval[band] == ConfigRequest.DW_INTERVAL_NOT_INIT) {
discoveryWindowInterval[band] = cr.mDiscoveryWindowInterval[band];
} else if (cr.mDiscoveryWindowInterval[band]
== ConfigRequest.DW_INTERVAL_NOT_INIT) {
// do nothing: keep my values
} else if (discoveryWindowInterval[band] == ConfigRequest.DW_DISABLE) {
discoveryWindowInterval[band] = cr.mDiscoveryWindowInterval[band];
} else if (cr.mDiscoveryWindowInterval[band] == ConfigRequest.DW_DISABLE) {
// do nothing: keep my values
} else {
discoveryWindowInterval[band] = Math.min(discoveryWindowInterval[band],
cr.mDiscoveryWindowInterval[band]);
}
}
}
ConfigRequest.Builder builder = new ConfigRequest.Builder().setSupport5gBand(support5gBand)
.setMasterPreference(masterPreference).setClusterLow(clusterLow)
.setClusterHigh(clusterHigh);
for (int band = ConfigRequest.NAN_BAND_24GHZ; band <= ConfigRequest.NAN_BAND_5GHZ; ++band) {
if (discoveryWindowInterval[band] != ConfigRequest.DW_INTERVAL_NOT_INIT) {
builder.setDiscoveryWindowInterval(band, discoveryWindowInterval[band]);
}
}
return builder.build();
}
private WorkSource createMergedRequestorWs() {
if (mDbg) {
Log.v(TAG, "createMergedRequestorWs(): mClients=[" + mClients + "]");
}
WorkSource requestorWs = new WorkSource();
for (int i = 0; i < mClients.size(); ++i) {
WifiAwareClientState clientState = mClients.valueAt(i);
requestorWs.add(new WorkSource(clientState.getUid(), clientState.getCallingPackage()));
}
return requestorWs;
}
private boolean doesAnyClientNeedIdentityChangeNotifications() {
for (int i = 0; i < mClients.size(); ++i) {
if (mClients.valueAt(i).getNotifyIdentityChange()) {
return true;
}
}
return false;
}
private boolean doesAnyClientNeedRanging() {
for (int i = 0; i < mClients.size(); ++i) {
if (mClients.valueAt(i).isRangingEnabled()) {
return true;
}
}
return false;
}
private int getInstantModeFromAllClients() {
int instantMode = INSTANT_MODE_DISABLED;
for (int i = 0; i < mClients.size(); ++i) {
int currentClient = mClients.valueAt(i).getInstantMode((long) mContext.getResources()
.getInteger(R.integer.config_wifiAwareInstantCommunicationModeDurationMillis));
if (currentClient == INSTANT_MODE_5GHZ) {
return currentClient;
}
if (currentClient == INSTANT_MODE_24GHZ) {
instantMode = currentClient;
}
}
return instantMode;
}
private static String messageToString(Message msg) {
StringBuilder sb = new StringBuilder();
String s = sSmToString.get(msg.what);
if (s == null) {
s = "<unknown>";
}
sb.append(s).append("/");
if (msg.what == MESSAGE_TYPE_NOTIFICATION || msg.what == MESSAGE_TYPE_COMMAND
|| msg.what == MESSAGE_TYPE_RESPONSE) {
s = sSmToString.get(msg.arg1);
if (s == null) {
s = "<unknown>";
}
sb.append(s);
}
if (msg.what == MESSAGE_TYPE_RESPONSE || msg.what == MESSAGE_TYPE_RESPONSE_TIMEOUT) {
sb.append(" (Transaction ID=").append(msg.arg2).append(")");
}
return sb.toString();
}
/**
* Just a proxy to call {@link WifiAwareDataPathStateManager#createAllInterfaces()} for test.
*/
@VisibleForTesting
public void createAllDataPathInterfaces() {
mDataPathMgr.createAllInterfaces();
}
/**
* Dump the internal state of the class.
*/
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("AwareStateManager:");
pw.println(" mClients: [" + mClients + "]");
pw.println(" mUsageEnabled: " + mUsageEnabled);
pw.println(" mCapabilities: [" + mCapabilities + "]");
pw.println(" mCurrentAwareConfiguration: " + mCurrentAwareConfiguration);
pw.println(" mCurrentIdentityNotification: " + mCurrentIdentityNotification);
for (int i = 0; i < mClients.size(); ++i) {
mClients.valueAt(i).dump(fd, pw, args);
}
pw.println(" mSettableParameters: " + mSettableParameters);
mSm.dump(fd, pw, args);
mDataPathMgr.dump(fd, pw, args);
mWifiAwareNativeApi.dump(fd, pw, args);
pw.println("mAwareMetrics:");
mAwareMetrics.dump(fd, pw, args);
mInterfaceConflictMgr.dump(fd, pw, args);
}
private void handleLocationModeDisabled() {
for (int i = 0; i < mClients.size(); i++) {
WifiAwareClientState clientState = mClients.valueAt(i);
if (SdkLevel.isAtLeastT()) {
try {
// As location mode is disabled, only app disavowal the location can pass the
// check.
mWifiPermissionsUtil.enforceNearbyDevicesPermission(
(AttributionSource) clientState.getAttributionSource(), true,
"Wifi Aware location mode change.");
} catch (SecurityException e) {
disconnect(clientState.getClientId());
}
} else {
disconnect(clientState.getClientId());
}
}
}
private int getAwareInstantCommunicationChannel(int instantMode) {
if (instantMode != INSTANT_MODE_5GHZ) {
return AWARE_BAND_2_INSTANT_COMMUNICATION_CHANNEL_FREQ;
}
if (mAwareBand5InstantCommunicationChannelFreq == 0) {
// If 5G instant communication doesn't have a valid channel, fallback to 2G.
return AWARE_BAND_2_INSTANT_COMMUNICATION_CHANNEL_FREQ;
}
if (mAwareBand5InstantCommunicationChannelFreq > 0) {
return mAwareBand5InstantCommunicationChannelFreq;
}
List<WifiAvailableChannel> channels = mWifiInjector.getWifiThreadRunner().call(
() -> mWifiInjector.getWifiNative().getUsableChannels(WifiScanner.WIFI_BAND_5_GHZ,
OP_MODE_WIFI_AWARE, WifiAvailableChannel.FILTER_NAN_INSTANT_MODE), null);
if (channels == null || channels.isEmpty()) {
if (mDbg) {
Log.v(TAG, "No available instant communication mode channel");
}
mAwareBand5InstantCommunicationChannelFreq = 0;
} else {
if (channels.size() > 1) {
if (mDbg) {
Log.v(TAG, "should have only one 5G instant communication channel,"
+ "but size=" + channels.size());
}
}
// TODO(b/232138258): When the filter issue fixed, just check if only return channel is
// correct
for (WifiAvailableChannel channel : channels) {
int freq = channel.getFrequencyMhz();
if (freq == AWARE_BAND_5_INSTANT_COMMUNICATION_CHANNEL_FREQ_CHANNEL_149) {
mAwareBand5InstantCommunicationChannelFreq =
AWARE_BAND_5_INSTANT_COMMUNICATION_CHANNEL_FREQ_CHANNEL_149;
break;
} else if (freq == AWARE_BAND_5_INSTANT_COMMUNICATION_CHANNEL_FREQ_CHANNEL_44) {
mAwareBand5InstantCommunicationChannelFreq =
AWARE_BAND_5_INSTANT_COMMUNICATION_CHANNEL_FREQ_CHANNEL_44;
}
}
if (mAwareBand5InstantCommunicationChannelFreq == -1) {
Log.e(TAG, "Both channel 149 and 44 are not available when the 5G WI-FI is "
+ "supported");
mAwareBand5InstantCommunicationChannelFreq = 0;
}
}
return mAwareBand5InstantCommunicationChannelFreq == 0
? AWARE_BAND_2_INSTANT_COMMUNICATION_CHANNEL_FREQ
: mAwareBand5InstantCommunicationChannelFreq;
}
}