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 WifiP2pConnection(Context context, Looper looper, ActiveModeWarden activeModeWarden)48 public WifiP2pConnection(Context context, Looper looper, ActiveModeWarden activeModeWarden) { 49 mContext = context; 50 mHandler = new P2pHandler(looper); 51 mActiveModeWarden = activeModeWarden; 52 } 53 sendMessageToAllClientModeImpls(Message msg)54 private void sendMessageToAllClientModeImpls(Message msg) { 55 for (ClientModeManager clientModeManager : mActiveModeWarden.getClientModeManagers()) { 56 // Need to make a copy of the message, or else MessageQueue will complain that the 57 // original message is already in use. 58 clientModeManager.sendMessageToClientModeImpl(Message.obtain(msg)); 59 } 60 } 61 62 private class P2pHandler extends Handler { P2pHandler(Looper looper)63 P2pHandler(Looper looper) { 64 super(looper); 65 } 66 67 @Override handleMessage(Message msg)68 public void handleMessage(Message msg) { 69 switch (msg.what) { 70 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: { 71 AsyncChannel ac = (AsyncChannel) msg.obj; 72 if (ac == mWifiP2pChannel) { 73 if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { 74 // Handler also has a sendMessage() method, but we want to call the 75 // sendMessage() method on WifiP2pConnection. 76 WifiP2pConnection.this 77 .sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION); 78 } else { 79 // TODO(b/34283611): We should probably do some cleanup or attempt a 80 // retry 81 Log.e(TAG, "WifiP2pService connection failure, error=" + msg.arg1); 82 } 83 } else { 84 Log.e(TAG, "got HALF_CONNECTED for unknown channel"); 85 } 86 } break; 87 case AsyncChannel.CMD_CHANNEL_DISCONNECTED: { 88 AsyncChannel ac = (AsyncChannel) msg.obj; 89 if (ac == mWifiP2pChannel) { 90 Log.e(TAG, "WifiP2pService channel lost, message.arg1 =" + msg.arg1); 91 // TODO(b/34283611): Re-establish connection to state machine after a delay 92 // mWifiP2pChannel.connect(mContext, getHandler(), 93 // mWifiP2pManager.getMessenger()); 94 } 95 } break; 96 case WifiP2pServiceImpl.DISCONNECT_WIFI_REQUEST: { 97 mTemporarilyDisconnectWifi = (msg.arg1 == 1); 98 if (mActiveModeWarden.getClientModeManagers().isEmpty()) { 99 // no active client mode managers, so request is trivially satisfied 100 replyToMessage(msg, WifiP2pServiceImpl.DISCONNECT_WIFI_RESPONSE); 101 } else { 102 // need to tell all client mode managers to disconnect 103 sendMessageToAllClientModeImpls(msg); 104 } 105 } break; 106 default: { 107 // Assume P2P message, forward to all ClientModeImpl instances 108 sendMessageToAllClientModeImpls(msg); 109 } break; 110 } 111 } 112 } 113 114 /** Setup the connection to P2P Service after boot is complete. */ handleBootCompleted()115 public void handleBootCompleted() { 116 if (!isP2pSupported()) return; 117 118 WifiP2pManager p2pManager = mContext.getSystemService(WifiP2pManager.class); 119 if (p2pManager == null) return; 120 121 mWifiP2pChannel = new AsyncChannel(); 122 mWifiP2pChannel.connect(mContext, mHandler, p2pManager.getP2pStateMachineMessenger()); 123 } 124 isP2pSupported()125 private boolean isP2pSupported() { 126 return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT); 127 } 128 129 /** Return true if the connection to P2P service is established, false otherwise. */ isConnected()130 public boolean isConnected() { 131 return mWifiP2pChannel != null; 132 } 133 134 /** Send a message to P2P service. */ sendMessage(int what)135 public boolean sendMessage(int what) { 136 if (mWifiP2pChannel == null) { 137 Log.e(TAG, "Tried to sendMessage to uninitialized mWifiP2pChannel!", new Throwable()); 138 return false; 139 } 140 mWifiP2pChannel.sendMessage(what); 141 return true; 142 } 143 144 /** Send a message to P2P service. */ sendMessage(int what, int arg1)145 public boolean sendMessage(int what, int arg1) { 146 if (mWifiP2pChannel == null) { 147 Log.e(TAG, "Tried to sendMessage to uninitialized mWifiP2pChannel!", new Throwable()); 148 return false; 149 } 150 mWifiP2pChannel.sendMessage(what, arg1); 151 return true; 152 } 153 154 /** Send a message to P2P service. */ sendMessage(int what, int arg1, int arg2)155 public boolean sendMessage(int what, int arg1, int arg2) { 156 if (mWifiP2pChannel == null) { 157 Log.e(TAG, "Tried to sendMessage to uninitialized mWifiP2pChannel!", new Throwable()); 158 return false; 159 } 160 mWifiP2pChannel.sendMessage(what, arg1, arg2); 161 return true; 162 } 163 164 /** 165 * State machine initiated requests can have replyTo set to null, indicating 166 * there are no recipients, we ignore those reply actions. 167 */ replyToMessage(Message msg, int what)168 public void replyToMessage(Message msg, int what) { 169 if (msg.replyTo == null) return; 170 Message dstMsg = obtainMessageWithWhatAndArg2(msg, what); 171 mReplyChannel.replyToMessage(msg, dstMsg); 172 } 173 174 /** 175 * arg2 on the source message has a unique id that needs to be retained in replies 176 * to match the request 177 * <p>see WifiManager for details 178 */ obtainMessageWithWhatAndArg2(Message srcMsg, int what)179 private Message obtainMessageWithWhatAndArg2(Message srcMsg, int what) { 180 Message msg = Message.obtain(); 181 msg.what = what; 182 msg.arg2 = srcMsg.arg2; 183 return msg; 184 } 185 186 /** Whether P2P service requested that we temporarily disconnect from Wifi */ shouldTemporarilyDisconnectWifi()187 public boolean shouldTemporarilyDisconnectWifi() { 188 return mTemporarilyDisconnectWifi; 189 } 190 } 191