191 lines
7.4 KiB
Java
191 lines
7.4 KiB
Java
/*
|
|
* Copyright (C) 2020 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.android.server.wifi;
|
|
|
|
import android.content.Context;
|
|
import android.content.pm.PackageManager;
|
|
import android.net.wifi.p2p.WifiP2pManager;
|
|
import android.os.Handler;
|
|
import android.os.Looper;
|
|
import android.os.Message;
|
|
import android.util.Log;
|
|
|
|
import com.android.internal.util.AsyncChannel;
|
|
import com.android.server.wifi.p2p.WifiP2pServiceImpl;
|
|
|
|
/**
|
|
* Used by {@link ClientModeImpl} to communicate with
|
|
* {@link com.android.server.wifi.p2p.WifiP2pService}.
|
|
*
|
|
* TODO(b/159060934): need to think about how multiple STAs interact with P2P
|
|
*/
|
|
public class WifiP2pConnection {
|
|
private static final String TAG = "WifiP2pConnection";
|
|
|
|
private final Context mContext;
|
|
private final Handler mHandler;
|
|
private final ActiveModeWarden mActiveModeWarden;
|
|
/** Channel for sending replies. */
|
|
private final AsyncChannel mReplyChannel = new AsyncChannel();
|
|
/** Used to initiate a connection with WifiP2pService */
|
|
private AsyncChannel mWifiP2pChannel;
|
|
private boolean mTemporarilyDisconnectWifi = false;
|
|
|
|
public WifiP2pConnection(Context context, Looper looper, ActiveModeWarden activeModeWarden) {
|
|
mContext = context;
|
|
mHandler = new P2pHandler(looper);
|
|
mActiveModeWarden = activeModeWarden;
|
|
}
|
|
|
|
private void sendMessageToAllClientModeImpls(Message msg) {
|
|
for (ClientModeManager clientModeManager : mActiveModeWarden.getClientModeManagers()) {
|
|
// Need to make a copy of the message, or else MessageQueue will complain that the
|
|
// original message is already in use.
|
|
clientModeManager.sendMessageToClientModeImpl(Message.obtain(msg));
|
|
}
|
|
}
|
|
|
|
private class P2pHandler extends Handler {
|
|
P2pHandler(Looper looper) {
|
|
super(looper);
|
|
}
|
|
|
|
@Override
|
|
public void handleMessage(Message msg) {
|
|
switch (msg.what) {
|
|
case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
|
|
AsyncChannel ac = (AsyncChannel) msg.obj;
|
|
if (ac == mWifiP2pChannel) {
|
|
if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
|
|
// Handler also has a sendMessage() method, but we want to call the
|
|
// sendMessage() method on WifiP2pConnection.
|
|
WifiP2pConnection.this
|
|
.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
|
|
} else {
|
|
// TODO(b/34283611): We should probably do some cleanup or attempt a
|
|
// retry
|
|
Log.e(TAG, "WifiP2pService connection failure, error=" + msg.arg1);
|
|
}
|
|
} else {
|
|
Log.e(TAG, "got HALF_CONNECTED for unknown channel");
|
|
}
|
|
} break;
|
|
case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
|
|
AsyncChannel ac = (AsyncChannel) msg.obj;
|
|
if (ac == mWifiP2pChannel) {
|
|
Log.e(TAG, "WifiP2pService channel lost, message.arg1 =" + msg.arg1);
|
|
// TODO(b/34283611): Re-establish connection to state machine after a delay
|
|
// mWifiP2pChannel.connect(mContext, getHandler(),
|
|
// mWifiP2pManager.getMessenger());
|
|
}
|
|
} break;
|
|
case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST: {
|
|
mTemporarilyDisconnectWifi = (msg.arg1 == 1);
|
|
if (mActiveModeWarden.getClientModeManagers().isEmpty()) {
|
|
// no active client mode managers, so request is trivially satisfied
|
|
replyToMessage(msg, WifiP2pServiceImpl.DISCONNECT_WIFI_RESPONSE);
|
|
} else {
|
|
// need to tell all client mode managers to disconnect
|
|
sendMessageToAllClientModeImpls(msg);
|
|
}
|
|
} break;
|
|
default: {
|
|
// Assume P2P message, forward to all ClientModeImpl instances
|
|
sendMessageToAllClientModeImpls(msg);
|
|
} break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Setup the connection to P2P Service after boot is complete. */
|
|
public void handleBootCompleted() {
|
|
if (!isP2pSupported()) return;
|
|
|
|
WifiP2pManager p2pManager = mContext.getSystemService(WifiP2pManager.class);
|
|
if (p2pManager == null) return;
|
|
|
|
mWifiP2pChannel = new AsyncChannel();
|
|
mWifiP2pChannel.connect(mContext, mHandler, p2pManager.getP2pStateMachineMessenger());
|
|
}
|
|
|
|
private boolean isP2pSupported() {
|
|
return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT);
|
|
}
|
|
|
|
/** Return true if the connection to P2P service is established, false otherwise. */
|
|
public boolean isConnected() {
|
|
return mWifiP2pChannel != null;
|
|
}
|
|
|
|
/** Send a message to P2P service. */
|
|
public boolean sendMessage(int what) {
|
|
if (mWifiP2pChannel == null) {
|
|
Log.e(TAG, "Tried to sendMessage to uninitialized mWifiP2pChannel!", new Throwable());
|
|
return false;
|
|
}
|
|
mWifiP2pChannel.sendMessage(what);
|
|
return true;
|
|
}
|
|
|
|
/** Send a message to P2P service. */
|
|
public boolean sendMessage(int what, int arg1) {
|
|
if (mWifiP2pChannel == null) {
|
|
Log.e(TAG, "Tried to sendMessage to uninitialized mWifiP2pChannel!", new Throwable());
|
|
return false;
|
|
}
|
|
mWifiP2pChannel.sendMessage(what, arg1);
|
|
return true;
|
|
}
|
|
|
|
/** Send a message to P2P service. */
|
|
public boolean sendMessage(int what, int arg1, int arg2) {
|
|
if (mWifiP2pChannel == null) {
|
|
Log.e(TAG, "Tried to sendMessage to uninitialized mWifiP2pChannel!", new Throwable());
|
|
return false;
|
|
}
|
|
mWifiP2pChannel.sendMessage(what, arg1, arg2);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* State machine initiated requests can have replyTo set to null, indicating
|
|
* there are no recipients, we ignore those reply actions.
|
|
*/
|
|
public void replyToMessage(Message msg, int what) {
|
|
if (msg.replyTo == null) return;
|
|
Message dstMsg = obtainMessageWithWhatAndArg2(msg, what);
|
|
mReplyChannel.replyToMessage(msg, dstMsg);
|
|
}
|
|
|
|
/**
|
|
* arg2 on the source message has a unique id that needs to be retained in replies
|
|
* to match the request
|
|
* <p>see WifiManager for details
|
|
*/
|
|
private Message obtainMessageWithWhatAndArg2(Message srcMsg, int what) {
|
|
Message msg = Message.obtain();
|
|
msg.what = what;
|
|
msg.arg2 = srcMsg.arg2;
|
|
return msg;
|
|
}
|
|
|
|
/** Whether P2P service requested that we temporarily disconnect from Wifi */
|
|
public boolean shouldTemporarilyDisconnectWifi() {
|
|
return mTemporarilyDisconnectWifi;
|
|
}
|
|
}
|