1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.wifi; 18 19 import android.content.Context; 20 import android.content.pm.PackageManager; 21 import android.net.wifi.p2p.WifiP2pManager; 22 import android.os.Handler; 23 import android.os.Looper; 24 import android.os.Message; 25 import android.util.Log; 26 27 import com.android.internal.util.AsyncChannel; 28 import com.android.server.wifi.p2p.WifiP2pServiceImpl; 29 30 /** 31 * Used by {@link ClientModeImpl} to communicate with 32 * {@link com.android.server.wifi.p2p.WifiP2pService}. 33 * 34 * TODO(b/159060934): need to think about how multiple STAs interact with P2P 35 */ 36 public class WifiP2pConnection { 37 private static final String TAG = "WifiP2pConnection"; 38 39 private final Context mContext; 40 private final Handler mHandler; 41 private final ActiveModeWarden mActiveModeWarden; 42 /** Channel for sending replies. */ 43 private final AsyncChannel mReplyChannel = new AsyncChannel(); 44 /** Used to initiate a connection with WifiP2pService */ 45 private AsyncChannel mWifiP2pChannel; 46 private boolean mTemporarilyDisconnectWifi = false; 47 48 /** Used to check if P2P state machine is in waitingState */ 49 private boolean mWaitingState = false; 50 WifiP2pConnection(Context context, Looper looper, ActiveModeWarden activeModeWarden)51 public WifiP2pConnection(Context context, Looper looper, ActiveModeWarden activeModeWarden) { 52 mContext = context; 53 mHandler = new P2pHandler(looper); 54 mActiveModeWarden = activeModeWarden; 55 } 56 sendMessageToAllClientModeImpls(Message msg)57 private void sendMessageToAllClientModeImpls(Message msg) { 58 for (ClientModeManager clientModeManager : mActiveModeWarden.getClientModeManagers()) { 59 // Need to make a copy of the message, or else MessageQueue will complain that the 60 // original message is already in use. 61 clientModeManager.sendMessageToClientModeImpl(Message.obtain(msg)); 62 } 63 } 64 65 private class P2pHandler extends Handler { P2pHandler(Looper looper)66 P2pHandler(Looper looper) { 67 super(looper); 68 } 69 70 @Override handleMessage(Message msg)71 public void handleMessage(Message msg) { 72 switch (msg.what) { 73 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: { 74 AsyncChannel ac = (AsyncChannel) msg.obj; 75 if (ac == mWifiP2pChannel) { 76 if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { 77 // Handler also has a sendMessage() method, but we want to call the 78 // sendMessage() method on WifiP2pConnection. 79 WifiP2pConnection.this 80 .sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION); 81 } else { 82 // TODO(b/34283611): We should probably do some cleanup or attempt a 83 // retry 84 Log.e(TAG, "WifiP2pService connection failure, error=" + msg.arg1); 85 } 86 } else { 87 Log.e(TAG, "got HALF_CONNECTED for unknown channel"); 88 } 89 } break; 90 case AsyncChannel.CMD_CHANNEL_DISCONNECTED: { 91 AsyncChannel ac = (AsyncChannel) msg.obj; 92 if (ac == mWifiP2pChannel) { 93 Log.e(TAG, "WifiP2pService channel lost, message.arg1 =" + msg.arg1); 94 // TODO(b/34283611): Re-establish connection to state machine after a delay 95 // mWifiP2pChannel.connect(mContext, getHandler(), 96 // mWifiP2pManager.getMessenger()); 97 } 98 } break; 99 case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST: { 100 mTemporarilyDisconnectWifi = (msg.arg1 == 1); 101 if (mActiveModeWarden.getClientModeManagers().isEmpty()) { 102 // no active client mode managers, so request is trivially satisfied 103 replyToMessage(msg, WifiP2pServiceImpl.DISCONNECT_WIFI_RESPONSE); 104 } else { 105 // need to tell all client mode managers to disconnect 106 sendMessageToAllClientModeImpls(msg); 107 } 108 } break; 109 default: { 110 // Assume P2P message, forward to all ClientModeImpl instances 111 sendMessageToAllClientModeImpls(msg); 112 } break; 113 } 114 } 115 } 116 117 /** Setup the connection to P2P Service after boot is complete. */ handleBootCompleted()118 public void handleBootCompleted() { 119 if (!isP2pSupported()) return; 120 121 WifiP2pManager p2pManager = mContext.getSystemService(WifiP2pManager.class); 122 if (p2pManager == null) return; 123 124 mWifiP2pChannel = new AsyncChannel(); 125 mWifiP2pChannel.connect(mContext, mHandler, p2pManager.getP2pStateMachineMessenger()); 126 } 127 isP2pSupported()128 private boolean isP2pSupported() { 129 return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT); 130 } 131 132 /** Return true if the connection to P2P service is established, false otherwise. */ isConnected()133 public boolean isConnected() { 134 return mWifiP2pChannel != null; 135 } 136 137 /** Send a message to P2P service. */ sendMessage(int what)138 public boolean sendMessage(int what) { 139 if (mWifiP2pChannel == null) { 140 Log.e(TAG, "Tried to sendMessage to uninitialized mWifiP2pChannel!", new Throwable()); 141 return false; 142 } 143 mWifiP2pChannel.sendMessage(what); 144 return true; 145 } 146 147 /** Send a message to P2P service. */ sendMessage(int what, int arg1)148 public boolean sendMessage(int what, int arg1) { 149 if (mWifiP2pChannel == null) { 150 Log.e(TAG, "Tried to sendMessage to uninitialized mWifiP2pChannel!", new Throwable()); 151 return false; 152 } 153 mWifiP2pChannel.sendMessage(what, arg1); 154 return true; 155 } 156 157 /** Send a message to P2P service. */ sendMessage(int what, int arg1, int arg2)158 public boolean sendMessage(int what, int arg1, int arg2) { 159 if (mWifiP2pChannel == null) { 160 Log.e(TAG, "Tried to sendMessage to uninitialized mWifiP2pChannel!", new Throwable()); 161 return false; 162 } 163 mWifiP2pChannel.sendMessage(what, arg1, arg2); 164 return true; 165 } 166 167 /** 168 * State machine initiated requests can have replyTo set to null, indicating 169 * there are no recipients, we ignore those reply actions. 170 */ replyToMessage(Message msg, int what)171 public void replyToMessage(Message msg, int what) { 172 if (msg.replyTo == null) return; 173 Message dstMsg = obtainMessageWithWhatAndArg2(msg, what); 174 mReplyChannel.replyToMessage(msg, dstMsg); 175 } 176 177 /** 178 * arg2 on the source message has a unique id that needs to be retained in replies 179 * to match the request 180 * <p>see WifiManager for details 181 */ obtainMessageWithWhatAndArg2(Message srcMsg, int what)182 private Message obtainMessageWithWhatAndArg2(Message srcMsg, int what) { 183 Message msg = Message.obtain(); 184 msg.what = what; 185 msg.arg2 = srcMsg.arg2; 186 return msg; 187 } 188 189 /** Whether P2P service requested that we temporarily disconnect from Wifi */ shouldTemporarilyDisconnectWifi()190 public boolean shouldTemporarilyDisconnectWifi() { 191 return mTemporarilyDisconnectWifi; 192 } 193 setP2pInWaitingState(boolean inWaitingState)194 public void setP2pInWaitingState(boolean inWaitingState) { 195 mWaitingState = inWaitingState; 196 } 197 198 /** whether the P2P state machine is in waitingState for user response to create interface */ isP2pInWaitingState()199 public boolean isP2pInWaitingState() { 200 return mWaitingState; 201 } 202 } 203