Home | History | Annotate | Download | only in p2p
      1 /*
      2  * Copyright (C) 2011 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 android.net.wifi.p2p;
     18 
     19 import android.app.AlertDialog;
     20 import android.app.Notification;
     21 import android.app.NotificationManager;
     22 import android.app.PendingIntent;
     23 import android.content.BroadcastReceiver;
     24 import android.content.Context;
     25 import android.content.DialogInterface;
     26 import android.content.DialogInterface.OnClickListener;
     27 import android.content.Intent;
     28 import android.content.IntentFilter;
     29 import android.content.pm.PackageManager;
     30 import android.content.res.Resources;
     31 import android.net.IConnectivityManager;
     32 import android.net.ConnectivityManager;
     33 import android.net.DhcpInfoInternal;
     34 import android.net.DhcpStateMachine;
     35 import android.net.InterfaceConfiguration;
     36 import android.net.LinkAddress;
     37 import android.net.LinkProperties;
     38 import android.net.NetworkInfo;
     39 import android.net.NetworkUtils;
     40 import android.net.wifi.WifiManager;
     41 import android.net.wifi.WifiMonitor;
     42 import android.net.wifi.WifiNative;
     43 import android.net.wifi.WifiStateMachine;
     44 import android.net.wifi.WpsInfo;
     45 import android.os.Binder;
     46 import android.os.IBinder;
     47 import android.os.INetworkManagementService;
     48 import android.os.Handler;
     49 import android.os.HandlerThread;
     50 import android.os.Message;
     51 import android.os.Messenger;
     52 import android.os.ServiceManager;
     53 import android.os.SystemProperties;
     54 import android.provider.Settings;
     55 import android.util.Slog;
     56 import android.view.LayoutInflater;
     57 import android.view.View;
     58 import android.view.WindowManager;
     59 import android.widget.EditText;
     60 
     61 import com.android.internal.R;
     62 import com.android.internal.telephony.TelephonyIntents;
     63 import com.android.internal.util.AsyncChannel;
     64 import com.android.internal.util.Protocol;
     65 import com.android.internal.util.State;
     66 import com.android.internal.util.StateMachine;
     67 
     68 import java.io.FileDescriptor;
     69 import java.io.PrintWriter;
     70 import java.util.Collection;
     71 
     72 /**
     73  * WifiP2pService inclues a state machine to perform Wi-Fi p2p operations. Applications
     74  * communicate with this service to issue device discovery and connectivity requests
     75  * through the WifiP2pManager interface. The state machine communicates with the wifi
     76  * driver through wpa_supplicant and handles the event responses through WifiMonitor.
     77  *
     78  * Note that the term Wifi when used without a p2p suffix refers to the client mode
     79  * of Wifi operation
     80  * @hide
     81  */
     82 public class WifiP2pService extends IWifiP2pManager.Stub {
     83     private static final String TAG = "WifiP2pService";
     84     private static final boolean DBG = true;
     85     private static final String NETWORKTYPE = "WIFI_P2P";
     86 
     87     private Context mContext;
     88     private String mInterface;
     89     private Notification mNotification;
     90 
     91     INetworkManagementService mNwService;
     92     private DhcpStateMachine mDhcpStateMachine;
     93 
     94     //Tracked to notify the user about wifi client/hotspot being shut down
     95     //during p2p bring up
     96     private int mWifiState = WifiManager.WIFI_STATE_DISABLED;
     97     private int mWifiApState = WifiManager.WIFI_AP_STATE_DISABLED;
     98 
     99     private P2pStateMachine mP2pStateMachine;
    100     private AsyncChannel mReplyChannel = new AsyncChannel();
    101     private AsyncChannel mWifiChannel;
    102 
    103     /* Two minutes comes from the wpa_supplicant setting */
    104     private static final int GROUP_NEGOTIATION_WAIT_TIME_MS = 120 * 1000;
    105     private static int mGroupNegotiationTimeoutIndex = 0;
    106 
    107     /**
    108      * Delay between restarts upon failure to setup connection with supplicant
    109      */
    110     private static final int P2P_RESTART_INTERVAL_MSECS = 5000;
    111 
    112     /**
    113      * Number of times we attempt to restart p2p
    114      */
    115     private static final int P2P_RESTART_TRIES = 5;
    116 
    117     private int mP2pRestartCount = 0;
    118 
    119     private static final int BASE = Protocol.BASE_WIFI_P2P_SERVICE;
    120 
    121     /* Message sent to WifiStateMachine to indicate p2p enable is pending */
    122     public static final int P2P_ENABLE_PENDING              =   BASE + 1;
    123     /* Message sent to WifiStateMachine to indicate Wi-Fi client/hotspot operation can proceed */
    124     public static final int WIFI_ENABLE_PROCEED             =   BASE + 2;
    125 
    126     /* Delayed message to timeout of group negotiation */
    127     public static final int GROUP_NEGOTIATION_TIMED_OUT     =   BASE + 3;
    128 
    129     /* User accepted to disable Wi-Fi in order to enable p2p */
    130     private static final int WIFI_DISABLE_USER_ACCEPT       =   BASE + 4;
    131     /* User rejected to disable Wi-Fi in order to enable p2p */
    132     private static final int WIFI_DISABLE_USER_REJECT       =   BASE + 5;
    133 
    134     /* Airplane mode changed */
    135     private static final int AIRPLANE_MODE_CHANGED          =   BASE + 6;
    136     /* Emergency callback mode */
    137     private static final int EMERGENCY_CALLBACK_MODE        =   BASE + 7;
    138     private static final int WPS_PBC                        =   BASE + 8;
    139     private static final int WPS_PIN                        =   BASE + 9;
    140 
    141     private final boolean mP2pSupported;
    142 
    143     private WifiP2pDevice mThisDevice = new WifiP2pDevice();
    144 
    145     /* When a group has been explicitly created by an app, we persist the group
    146      * even after all clients have been disconnected until an explicit remove
    147      * is invoked */
    148     private boolean mPersistGroup;
    149 
    150     private NetworkInfo mNetworkInfo;
    151 
    152     /* Is chosen as a unique range to avoid conflict with
    153        the range defined in Tethering.java */
    154     private static final String[] DHCP_RANGE = {"192.168.49.2", "192.168.49.254"};
    155     private static final String SERVER_ADDRESS = "192.168.49.1";
    156 
    157     public WifiP2pService(Context context) {
    158         mContext = context;
    159 
    160         mInterface = SystemProperties.get("wifi.interface", "wlan0");
    161         mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI_P2P, 0, NETWORKTYPE, "");
    162 
    163         mP2pSupported = mContext.getPackageManager().hasSystemFeature(
    164                 PackageManager.FEATURE_WIFI_DIRECT);
    165 
    166         mThisDevice.primaryDeviceType = mContext.getResources().getString(
    167                 com.android.internal.R.string.config_wifi_p2p_device_type);
    168         mThisDevice.deviceName = getDefaultDeviceName();
    169 
    170         mP2pStateMachine = new P2pStateMachine(TAG, mP2pSupported);
    171         mP2pStateMachine.start();
    172 
    173         // broadcasts
    174         IntentFilter filter = new IntentFilter();
    175         filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
    176         filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
    177         filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
    178         filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
    179         mContext.registerReceiver(new WifiStateReceiver(), filter);
    180 
    181     }
    182 
    183     public void connectivityServiceReady() {
    184         IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
    185         mNwService = INetworkManagementService.Stub.asInterface(b);
    186     }
    187 
    188     private class WifiStateReceiver extends BroadcastReceiver {
    189         @Override
    190         public void onReceive(Context context, Intent intent) {
    191             String action = intent.getAction();
    192             if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
    193                 mWifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
    194                         WifiManager.WIFI_STATE_DISABLED);
    195             } else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) {
    196                 mWifiApState = intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_STATE,
    197                         WifiManager.WIFI_AP_STATE_DISABLED);
    198             } else if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
    199                 mP2pStateMachine.sendMessage(AIRPLANE_MODE_CHANGED);
    200             } else if (action.equals(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) {
    201                 if (intent.getBooleanExtra("phoneinECMState", false) == true) {
    202                     mP2pStateMachine.sendMessage(EMERGENCY_CALLBACK_MODE);
    203                 }
    204             }
    205         }
    206     }
    207 
    208     private void enforceAccessPermission() {
    209         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE,
    210                 "WifiP2pService");
    211     }
    212 
    213     private void enforceChangePermission() {
    214         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE,
    215                 "WifiP2pService");
    216     }
    217 
    218     /* We use the 4 digits of the ANDROID_ID to have a friendly
    219      * default that has low likelihood of collision with a peer */
    220     private String getDefaultDeviceName() {
    221         String id = Settings.Secure.getString(mContext.getContentResolver(),
    222                     Settings.Secure.ANDROID_ID);
    223         return "Android_" + id.substring(0,4);
    224     }
    225 
    226     /**
    227      * Get a reference to handler. This is used by a client to establish
    228      * an AsyncChannel communication with WifiP2pService
    229      */
    230     public Messenger getMessenger() {
    231         enforceAccessPermission();
    232         enforceChangePermission();
    233         return new Messenger(mP2pStateMachine.getHandler());
    234     }
    235 
    236     @Override
    237     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    238         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
    239                 != PackageManager.PERMISSION_GRANTED) {
    240             pw.println("Permission Denial: can't dump WifiP2pService from from pid="
    241                     + Binder.getCallingPid()
    242                     + ", uid=" + Binder.getCallingUid());
    243             return;
    244         }
    245     }
    246 
    247 
    248     /**
    249      * Handles interaction with WifiStateMachine
    250      */
    251     private class P2pStateMachine extends StateMachine {
    252 
    253         private DefaultState mDefaultState = new DefaultState();
    254         private P2pNotSupportedState mP2pNotSupportedState = new P2pNotSupportedState();
    255         private P2pDisablingState mP2pDisablingState = new P2pDisablingState();
    256         private P2pDisabledState mP2pDisabledState = new P2pDisabledState();
    257         private WaitForUserActionState mWaitForUserActionState = new WaitForUserActionState();
    258         private WaitForWifiDisableState mWaitForWifiDisableState = new WaitForWifiDisableState();
    259         private P2pEnablingState mP2pEnablingState = new P2pEnablingState();
    260         private P2pEnabledState mP2pEnabledState = new P2pEnabledState();
    261         // Inactive is when p2p is enabled with no connectivity
    262         private InactiveState mInactiveState = new InactiveState();
    263         private GroupNegotiationState mGroupNegotiationState = new GroupNegotiationState();
    264         private GroupCreatedState mGroupCreatedState = new GroupCreatedState();
    265 
    266         private WifiMonitor mWifiMonitor = new WifiMonitor(this);
    267 
    268         private WifiP2pDeviceList mPeers = new WifiP2pDeviceList();
    269         private WifiP2pInfo mWifiP2pInfo = new WifiP2pInfo();
    270         private WifiP2pGroup mGroup;
    271 
    272         // Saved WifiP2pConfig from GO negotiation request
    273         private WifiP2pConfig mSavedGoNegotiationConfig;
    274 
    275         // Saved WifiP2pConfig from connect request
    276         private WifiP2pConfig mSavedConnectConfig;
    277 
    278         // Saved WifiP2pGroup from invitation request
    279         private WifiP2pGroup mSavedP2pGroup;
    280 
    281         P2pStateMachine(String name, boolean p2pSupported) {
    282             super(name);
    283 
    284             addState(mDefaultState);
    285                 addState(mP2pNotSupportedState, mDefaultState);
    286                 addState(mP2pDisablingState, mDefaultState);
    287                 addState(mP2pDisabledState, mDefaultState);
    288                     addState(mWaitForUserActionState, mP2pDisabledState);
    289                     addState(mWaitForWifiDisableState, mP2pDisabledState);
    290                 addState(mP2pEnablingState, mDefaultState);
    291                 addState(mP2pEnabledState, mDefaultState);
    292                     addState(mInactiveState, mP2pEnabledState);
    293                     addState(mGroupNegotiationState, mP2pEnabledState);
    294                     addState(mGroupCreatedState, mP2pEnabledState);
    295 
    296             if (p2pSupported) {
    297                 setInitialState(mP2pDisabledState);
    298             } else {
    299                 setInitialState(mP2pNotSupportedState);
    300             }
    301         }
    302 
    303     class DefaultState extends State {
    304         @Override
    305         public boolean processMessage(Message message) {
    306             if (DBG) logd(getName() + message.toString());
    307             switch (message.what) {
    308                 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
    309                     if (message.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
    310                         if (DBG) logd("Full connection with WifiStateMachine established");
    311                         mWifiChannel = (AsyncChannel) message.obj;
    312                     } else {
    313                         loge("Full connection failure, error = " + message.arg1);
    314                         mWifiChannel = null;
    315                     }
    316                     break;
    317 
    318                 case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
    319                     if (message.arg1 == AsyncChannel.STATUS_SEND_UNSUCCESSFUL) {
    320                         loge("Send failed, client connection lost");
    321                     } else {
    322                         loge("Client connection lost with reason: " + message.arg1);
    323                     }
    324                     mWifiChannel = null;
    325                     break;
    326 
    327                 case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION:
    328                     AsyncChannel ac = new AsyncChannel();
    329                     ac.connect(mContext, getHandler(), message.replyTo);
    330                     break;
    331                 case WifiStateMachine.WIFI_ENABLE_PENDING:
    332                     // Disable p2p operation before we can respond
    333                     sendMessage(WifiP2pManager.DISABLE_P2P);
    334                     deferMessage(message);
    335                     break;
    336                 case WifiP2pManager.ENABLE_P2P:
    337                     replyToMessage(message, WifiP2pManager.ENABLE_P2P_FAILED,
    338                             WifiP2pManager.BUSY);
    339                     break;
    340                 case WifiP2pManager.DISABLE_P2P:
    341                     replyToMessage(message, WifiP2pManager.DISABLE_P2P_FAILED,
    342                             WifiP2pManager.BUSY);
    343                     break;
    344                 case WifiP2pManager.DISCOVER_PEERS:
    345                     replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
    346                             WifiP2pManager.BUSY);
    347                     break;
    348                 case WifiP2pManager.CONNECT:
    349                     replyToMessage(message, WifiP2pManager.CONNECT_FAILED,
    350                             WifiP2pManager.BUSY);
    351                     break;
    352                 case WifiP2pManager.CANCEL_CONNECT:
    353                     replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_FAILED,
    354                             WifiP2pManager.BUSY);
    355                     break;
    356                 case WifiP2pManager.CREATE_GROUP:
    357                     replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED,
    358                             WifiP2pManager.BUSY);
    359                     break;
    360                 case WifiP2pManager.REMOVE_GROUP:
    361                     replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED,
    362                             WifiP2pManager.BUSY);
    363                     break;
    364                 case WifiP2pManager.REQUEST_PEERS:
    365                     replyToMessage(message, WifiP2pManager.RESPONSE_PEERS, mPeers);
    366                     break;
    367                 case WifiP2pManager.REQUEST_CONNECTION_INFO:
    368                     replyToMessage(message, WifiP2pManager.RESPONSE_CONNECTION_INFO, mWifiP2pInfo);
    369                     break;
    370                 case WifiP2pManager.REQUEST_GROUP_INFO:
    371                     replyToMessage(message, WifiP2pManager.RESPONSE_GROUP_INFO, mGroup);
    372                     break;
    373                 case AIRPLANE_MODE_CHANGED:
    374                     if (isAirplaneModeOn()) sendMessage(WifiP2pManager.DISABLE_P2P);
    375                     break;
    376                 case EMERGENCY_CALLBACK_MODE:
    377                     sendMessage(WifiP2pManager.DISABLE_P2P);
    378                     break;
    379                     // Ignore
    380                 case WIFI_DISABLE_USER_ACCEPT:
    381                 case WIFI_DISABLE_USER_REJECT:
    382                 case GROUP_NEGOTIATION_TIMED_OUT:
    383                     break;
    384                 default:
    385                     loge("Unhandled message " + message);
    386                     return NOT_HANDLED;
    387             }
    388             return HANDLED;
    389         }
    390     }
    391 
    392     class P2pNotSupportedState extends State {
    393         @Override
    394         public boolean processMessage(Message message) {
    395             switch (message.what) {
    396                 // Allow Wi-Fi to proceed
    397                 case WifiStateMachine.WIFI_ENABLE_PENDING:
    398                     replyToMessage(message, WIFI_ENABLE_PROCEED);
    399                     break;
    400                 case WifiP2pManager.ENABLE_P2P:
    401                     replyToMessage(message, WifiP2pManager.ENABLE_P2P_FAILED,
    402                             WifiP2pManager.P2P_UNSUPPORTED);
    403                     break;
    404                 case WifiP2pManager.DISABLE_P2P:
    405                     replyToMessage(message, WifiP2pManager.DISABLE_P2P_FAILED,
    406                             WifiP2pManager.P2P_UNSUPPORTED);
    407                     break;
    408                 case WifiP2pManager.DISCOVER_PEERS:
    409                     replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
    410                             WifiP2pManager.P2P_UNSUPPORTED);
    411                     break;
    412                 case WifiP2pManager.CONNECT:
    413                     replyToMessage(message, WifiP2pManager.CONNECT_FAILED,
    414                             WifiP2pManager.P2P_UNSUPPORTED);
    415                     break;
    416                 case WifiP2pManager.CANCEL_CONNECT:
    417                     replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_FAILED,
    418                             WifiP2pManager.P2P_UNSUPPORTED);
    419                     break;
    420                 case WifiP2pManager.CREATE_GROUP:
    421                     replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED,
    422                             WifiP2pManager.P2P_UNSUPPORTED);
    423                     break;
    424                 case WifiP2pManager.REMOVE_GROUP:
    425                     replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED,
    426                             WifiP2pManager.P2P_UNSUPPORTED);
    427                     break;
    428                default:
    429                     return NOT_HANDLED;
    430             }
    431             return HANDLED;
    432         }
    433     }
    434 
    435     class P2pDisablingState extends State {
    436         @Override
    437         public void enter() {
    438             if (DBG) logd(getName());
    439             logd("stopping supplicant");
    440             if (!WifiNative.stopSupplicant()) {
    441                 loge("Failed to stop supplicant, issue kill");
    442                 WifiNative.killSupplicant();
    443             }
    444         }
    445 
    446         @Override
    447         public boolean processMessage(Message message) {
    448             if (DBG) logd(getName() + message.toString());
    449             switch (message.what) {
    450                 case WifiMonitor.SUP_DISCONNECTION_EVENT:
    451                     logd("Supplicant connection lost");
    452                     WifiNative.closeSupplicantConnection();
    453                     transitionTo(mP2pDisabledState);
    454                     break;
    455                 case WifiP2pManager.ENABLE_P2P:
    456                 case WifiP2pManager.DISABLE_P2P:
    457                     deferMessage(message);
    458                     break;
    459                 default:
    460                     return NOT_HANDLED;
    461             }
    462             return HANDLED;
    463         }
    464     }
    465 
    466 
    467     class P2pDisabledState extends State {
    468        @Override
    469         public void enter() {
    470             if (DBG) logd(getName());
    471         }
    472 
    473         @Override
    474         public boolean processMessage(Message message) {
    475             if (DBG) logd(getName() + message.toString());
    476             switch (message.what) {
    477                 case WifiP2pManager.ENABLE_P2P:
    478                     OnClickListener listener = new OnClickListener() {
    479                         @Override
    480                         public void onClick(DialogInterface dialog, int which) {
    481                             if (which == DialogInterface.BUTTON_POSITIVE) {
    482                                 sendMessage(WIFI_DISABLE_USER_ACCEPT);
    483                             } else {
    484                                 sendMessage(WIFI_DISABLE_USER_REJECT);
    485                             }
    486                         }
    487                     };
    488 
    489                     // Show a user request dialog if we know Wi-Fi client/hotspot is in operation
    490                     if (mWifiState != WifiManager.WIFI_STATE_DISABLED ||
    491                             mWifiApState != WifiManager.WIFI_AP_STATE_DISABLED) {
    492                         Resources r = Resources.getSystem();
    493                         AlertDialog dialog = new AlertDialog.Builder(mContext)
    494                             .setTitle(r.getString(R.string.wifi_p2p_dialog_title))
    495                             .setMessage(r.getString(R.string.wifi_p2p_turnon_message))
    496                             .setPositiveButton(r.getString(R.string.ok), listener)
    497                             .setNegativeButton(r.getString(R.string.cancel), listener)
    498                             .create();
    499                         dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
    500                         dialog.show();
    501                         transitionTo(mWaitForUserActionState);
    502                     } else {
    503                         mWifiChannel.sendMessage(P2P_ENABLE_PENDING);
    504                         transitionTo(mWaitForWifiDisableState);
    505                     }
    506                     replyToMessage(message, WifiP2pManager.ENABLE_P2P_SUCCEEDED);
    507                     break;
    508                 case WifiP2pManager.DISABLE_P2P:
    509                     replyToMessage(message, WifiP2pManager.DISABLE_P2P_SUCCEEDED);
    510                     break;
    511                 case WifiStateMachine.WIFI_ENABLE_PENDING:
    512                     replyToMessage(message, WIFI_ENABLE_PROCEED);
    513                     break;
    514                 default:
    515                     return NOT_HANDLED;
    516             }
    517             return HANDLED;
    518         }
    519     }
    520 
    521     class WaitForUserActionState extends State {
    522         @Override
    523         public void enter() {
    524             if (DBG) logd(getName());
    525         }
    526 
    527         @Override
    528         public boolean processMessage(Message message) {
    529             if (DBG) logd(getName() + message.toString());
    530             switch (message.what) {
    531                 case WIFI_DISABLE_USER_ACCEPT:
    532                     mWifiChannel.sendMessage(P2P_ENABLE_PENDING);
    533                     transitionTo(mWaitForWifiDisableState);
    534                     break;
    535                 case WIFI_DISABLE_USER_REJECT:
    536                     logd("User rejected enabling p2p");
    537                     sendP2pStateChangedBroadcast(false);
    538                     transitionTo(mP2pDisabledState);
    539                     break;
    540                 case WifiP2pManager.ENABLE_P2P:
    541                 case WifiP2pManager.DISABLE_P2P:
    542                     deferMessage(message);
    543                     break;
    544                 default:
    545                     return NOT_HANDLED;
    546             }
    547             return HANDLED;
    548         }
    549     }
    550 
    551     class WaitForWifiDisableState extends State {
    552         @Override
    553         public void enter() {
    554             if (DBG) logd(getName());
    555         }
    556 
    557         @Override
    558         public boolean processMessage(Message message) {
    559             if (DBG) logd(getName() + message.toString());
    560             switch (message.what) {
    561                 case WifiStateMachine.P2P_ENABLE_PROCEED:
    562                     try {
    563                         mNwService.wifiFirmwareReload(mInterface, "P2P");
    564                     } catch (Exception e) {
    565                         loge("Failed to reload p2p firmware " + e);
    566                         // continue
    567                     }
    568 
    569                     //A runtime crash can leave the interface up and
    570                     //this affects p2p when supplicant starts up.
    571                     //Ensure interface is down before a supplicant start.
    572                     try {
    573                         mNwService.setInterfaceDown(mInterface);
    574                     } catch (Exception e) {
    575                         if (DBG) Slog.w(TAG, "Unable to bring down wlan interface: " + e);
    576                     }
    577 
    578                     if (WifiNative.startP2pSupplicant()) {
    579                         mWifiMonitor.startMonitoring();
    580                         transitionTo(mP2pEnablingState);
    581                     } else {
    582                         notifyP2pEnableFailure();
    583                         transitionTo(mP2pDisabledState);
    584                     }
    585                     break;
    586                 case WifiP2pManager.ENABLE_P2P:
    587                 case WifiP2pManager.DISABLE_P2P:
    588                     deferMessage(message);
    589                     break;
    590                 default:
    591                     return NOT_HANDLED;
    592             }
    593             return HANDLED;
    594         }
    595     }
    596 
    597     class P2pEnablingState extends State {
    598         @Override
    599         public void enter() {
    600             if (DBG) logd(getName());
    601         }
    602 
    603         @Override
    604         public boolean processMessage(Message message) {
    605             if (DBG) logd(getName() + message.toString());
    606             switch (message.what) {
    607                 case WifiMonitor.SUP_CONNECTION_EVENT:
    608                     logd("P2p start successful");
    609                     transitionTo(mInactiveState);
    610                     break;
    611                 case WifiMonitor.SUP_DISCONNECTION_EVENT:
    612                     if (++mP2pRestartCount <= P2P_RESTART_TRIES) {
    613                         loge("Failed to start p2p, retry");
    614                         WifiNative.killSupplicant();
    615                         sendMessageDelayed(WifiP2pManager.ENABLE_P2P, P2P_RESTART_INTERVAL_MSECS);
    616                     } else {
    617                         loge("Failed " + mP2pRestartCount + " times to start p2p, quit ");
    618                         mP2pRestartCount = 0;
    619                     }
    620                     transitionTo(mP2pDisabledState);
    621                     break;
    622                 case WifiP2pManager.ENABLE_P2P:
    623                 case WifiP2pManager.DISABLE_P2P:
    624                     deferMessage(message);
    625                     break;
    626                 default:
    627                     return NOT_HANDLED;
    628             }
    629             return HANDLED;
    630         }
    631     }
    632 
    633     class P2pEnabledState extends State {
    634         @Override
    635         public void enter() {
    636             if (DBG) logd(getName());
    637             sendP2pStateChangedBroadcast(true);
    638             mNetworkInfo.setIsAvailable(true);
    639             initializeP2pSettings();
    640             showNotification();
    641         }
    642 
    643         @Override
    644         public boolean processMessage(Message message) {
    645             if (DBG) logd(getName() + message.toString());
    646             switch (message.what) {
    647                 case WifiP2pManager.ENABLE_P2P:
    648                     replyToMessage(message, WifiP2pManager.ENABLE_P2P_SUCCEEDED);
    649                     break;
    650                 case WifiP2pManager.DISABLE_P2P:
    651                     if (mPeers.clear()) sendP2pPeersChangedBroadcast();
    652                     replyToMessage(message, WifiP2pManager.DISABLE_P2P_SUCCEEDED);
    653                     transitionTo(mP2pDisablingState);
    654                     break;
    655                 case WifiP2pManager.DISCOVER_PEERS:
    656                     int timeout = message.arg1;
    657                     if (WifiNative.p2pFind(timeout)) {
    658                         replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_SUCCEEDED);
    659                     } else {
    660                         replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
    661                                 WifiP2pManager.ERROR);
    662                     }
    663                     break;
    664                 case WifiMonitor.P2P_DEVICE_FOUND_EVENT:
    665                     WifiP2pDevice device = (WifiP2pDevice) message.obj;
    666                     if (mThisDevice.deviceAddress.equals(device.deviceAddress)) break;
    667                     mPeers.update(device);
    668                     sendP2pPeersChangedBroadcast();
    669                     break;
    670                 case WifiMonitor.P2P_DEVICE_LOST_EVENT:
    671                     device = (WifiP2pDevice) message.obj;
    672                     if (mPeers.remove(device)) sendP2pPeersChangedBroadcast();
    673                     break;
    674                 case WifiP2pManager.CONNECT:
    675                     if (DBG) logd(getName() + " sending connect");
    676                     mSavedConnectConfig = (WifiP2pConfig) message.obj;
    677                     mPersistGroup = false;
    678                     int netId = configuredNetworkId(mSavedConnectConfig.deviceAddress);
    679                     if (netId >= 0) {
    680                         //TODO: if failure, remove config and do a regular p2pConnect()
    681                         WifiNative.p2pReinvoke(netId, mSavedConnectConfig.deviceAddress);
    682                     } else {
    683                         boolean join = false;
    684                         if (isGroupOwner(mSavedConnectConfig.deviceAddress)) join = true;
    685                         String pin = WifiNative.p2pConnect(mSavedConnectConfig, join);
    686                         try {
    687                             Integer.parseInt(pin);
    688                             notifyWpsPin(pin, mSavedConnectConfig.deviceAddress);
    689                         } catch (NumberFormatException ignore) {
    690                             // do nothing if p2pConnect did not return a pin
    691                         }
    692                     }
    693                     updateDeviceStatus(mSavedConnectConfig.deviceAddress, WifiP2pDevice.INVITED);
    694                     sendP2pPeersChangedBroadcast();
    695                     replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED);
    696                     transitionTo(mGroupNegotiationState);
    697                     break;
    698                 case WifiMonitor.SUP_DISCONNECTION_EVENT:  /* Supplicant died */
    699                     loge("Connection lost, restart p2p");
    700                     WifiNative.killSupplicant();
    701                     WifiNative.closeSupplicantConnection();
    702                     if (mPeers.clear()) sendP2pPeersChangedBroadcast();
    703                     transitionTo(mP2pDisabledState);
    704                     sendMessageDelayed(WifiP2pManager.ENABLE_P2P, P2P_RESTART_INTERVAL_MSECS);
    705                     break;
    706                 case WifiMonitor.P2P_GROUP_STARTED_EVENT:
    707                     mGroup = (WifiP2pGroup) message.obj;
    708                     if (DBG) logd(getName() + " group started");
    709                     if (mGroup.isGroupOwner()) {
    710                         startDhcpServer(mGroup.getInterface());
    711                     } else {
    712                         mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(mContext,
    713                                 P2pStateMachine.this, mGroup.getInterface());
    714                         mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);
    715                         WifiP2pDevice groupOwner = mGroup.getOwner();
    716                         updateDeviceStatus(groupOwner.deviceAddress, WifiP2pDevice.CONNECTED);
    717                         sendP2pPeersChangedBroadcast();
    718                     }
    719                     transitionTo(mGroupCreatedState);
    720                     break;
    721                 default:
    722                     return NOT_HANDLED;
    723             }
    724             return HANDLED;
    725         }
    726 
    727         @Override
    728         public void exit() {
    729             sendP2pStateChangedBroadcast(false);
    730             mNetworkInfo.setIsAvailable(false);
    731             clearNotification();
    732         }
    733     }
    734 
    735     class InactiveState extends State {
    736         @Override
    737         public void enter() {
    738             if (DBG) logd(getName());
    739             //Start listening every time we get inactive
    740             WifiNative.p2pListen();
    741         }
    742 
    743         @Override
    744         public boolean processMessage(Message message) {
    745             if (DBG) logd(getName() + message.toString());
    746             switch (message.what) {
    747                 case WifiMonitor.P2P_GO_NEGOTIATION_REQUEST_EVENT:
    748                     mSavedGoNegotiationConfig = (WifiP2pConfig) message.obj;
    749                     notifyP2pGoNegotationRequest(mSavedGoNegotiationConfig);
    750                     break;
    751                 case WifiP2pManager.CREATE_GROUP:
    752                     mPersistGroup = true;
    753                     if (WifiNative.p2pGroupAdd()) {
    754                         replyToMessage(message, WifiP2pManager.CREATE_GROUP_SUCCEEDED);
    755                     } else {
    756                         replyToMessage(message, WifiP2pManager.CREATE_GROUP_FAILED,
    757                                 WifiP2pManager.ERROR);
    758                     }
    759                     transitionTo(mGroupNegotiationState);
    760                     break;
    761                 case WifiMonitor.P2P_INVITATION_RECEIVED_EVENT:
    762                     WifiP2pGroup group = (WifiP2pGroup) message.obj;
    763                     notifyP2pInvitationReceived(group);
    764                     break;
    765                 default:
    766                     return NOT_HANDLED;
    767             }
    768             return HANDLED;
    769         }
    770     }
    771 
    772     class GroupNegotiationState extends State {
    773         @Override
    774         public void enter() {
    775             if (DBG) logd(getName());
    776             sendMessageDelayed(obtainMessage(GROUP_NEGOTIATION_TIMED_OUT,
    777                     ++mGroupNegotiationTimeoutIndex, 0), GROUP_NEGOTIATION_WAIT_TIME_MS);
    778         }
    779 
    780         @Override
    781         public boolean processMessage(Message message) {
    782             if (DBG) logd(getName() + message.toString());
    783             switch (message.what) {
    784                 // We ignore these right now, since we get a GROUP_STARTED notification
    785                 // afterwards
    786                 case WifiMonitor.P2P_GO_NEGOTIATION_SUCCESS_EVENT:
    787                 case WifiMonitor.P2P_GROUP_FORMATION_SUCCESS_EVENT:
    788                     if (DBG) logd(getName() + " go success");
    789                     break;
    790                 case WifiMonitor.P2P_GO_NEGOTIATION_FAILURE_EVENT:
    791                 case WifiMonitor.P2P_GROUP_FORMATION_FAILURE_EVENT:
    792                     if (DBG) logd(getName() + " go failure");
    793                     updateDeviceStatus(mSavedConnectConfig.deviceAddress, WifiP2pDevice.FAILED);
    794                     mSavedConnectConfig = null;
    795                     sendP2pPeersChangedBroadcast();
    796                     transitionTo(mInactiveState);
    797                     break;
    798                 case GROUP_NEGOTIATION_TIMED_OUT:
    799                     if (mGroupNegotiationTimeoutIndex == message.arg1) {
    800                         if (DBG) logd("Group negotiation timed out");
    801                         updateDeviceStatus(mSavedConnectConfig.deviceAddress, WifiP2pDevice.FAILED);
    802                         mSavedConnectConfig = null;
    803                         sendP2pPeersChangedBroadcast();
    804                         transitionTo(mInactiveState);
    805                     }
    806                     break;
    807                 case WifiP2pManager.DISCOVER_PEERS:
    808                     /* Discovery will break negotiation */
    809                     replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
    810                             WifiP2pManager.BUSY);
    811                     break;
    812                 case WifiP2pManager.CANCEL_CONNECT:
    813                     if (WifiNative.p2pCancelConnect()) {
    814                         replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_SUCCEEDED);
    815                     } else {
    816                         replyToMessage(message, WifiP2pManager.CANCEL_CONNECT_FAILED,
    817                                 WifiP2pManager.ERROR);
    818                     }
    819                     break;
    820                 default:
    821                     return NOT_HANDLED;
    822             }
    823             return HANDLED;
    824         }
    825     }
    826 
    827     class GroupCreatedState extends State {
    828         @Override
    829         public void enter() {
    830             if (DBG) logd(getName());
    831             mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
    832 
    833             updateThisDevice(WifiP2pDevice.CONNECTED);
    834 
    835             //DHCP server has already been started if I am a group owner
    836             if (mGroup.isGroupOwner()) {
    837                 setWifiP2pInfoOnGroupFormation(SERVER_ADDRESS);
    838                 sendP2pConnectionChangedBroadcast();
    839             }
    840         }
    841 
    842         @Override
    843         public boolean processMessage(Message message) {
    844             if (DBG) logd(getName() + message.toString());
    845             switch (message.what) {
    846                 case WifiMonitor.AP_STA_CONNECTED_EVENT:
    847                     //After a GO setup, STA connected event comes with interface address
    848                     String interfaceAddress = (String) message.obj;
    849                     String deviceAddress = getDeviceAddress(interfaceAddress);
    850                     if (deviceAddress != null) {
    851                         mGroup.addClient(deviceAddress);
    852                         updateDeviceStatus(deviceAddress, WifiP2pDevice.CONNECTED);
    853                         if (DBG) logd(getName() + " ap sta connected");
    854                         sendP2pPeersChangedBroadcast();
    855                     } else {
    856                         loge("Connect on unknown device address : " + interfaceAddress);
    857                     }
    858                     break;
    859                 case WifiMonitor.AP_STA_DISCONNECTED_EVENT:
    860                     interfaceAddress = (String) message.obj;
    861                     deviceAddress = getDeviceAddress(interfaceAddress);
    862                     if (deviceAddress != null) {
    863                         updateDeviceStatus(deviceAddress, WifiP2pDevice.AVAILABLE);
    864                         if (mGroup.removeClient(deviceAddress)) {
    865                             if (DBG) logd("Removed client " + deviceAddress);
    866                             if (!mPersistGroup && mGroup.isClientListEmpty()) {
    867                                 Slog.d(TAG, "Client list empty, remove non-persistent p2p group");
    868                                 WifiNative.p2pGroupRemove(mGroup.getInterface());
    869                             }
    870                         } else {
    871                             if (DBG) logd("Failed to remove client " + deviceAddress);
    872                             for (WifiP2pDevice c : mGroup.getClientList()) {
    873                                 if (DBG) logd("client " + c.deviceAddress);
    874                             }
    875                         }
    876                         sendP2pPeersChangedBroadcast();
    877                         if (DBG) loge(getName() + " ap sta disconnected");
    878                     } else {
    879                         loge("Disconnect on unknown device address : " + interfaceAddress);
    880                     }
    881                     break;
    882                 case DhcpStateMachine.CMD_POST_DHCP_ACTION:
    883                     DhcpInfoInternal dhcpInfo = (DhcpInfoInternal) message.obj;
    884                     if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS &&
    885                             dhcpInfo != null) {
    886                         if (DBG) logd("DhcpInfo: " + dhcpInfo);
    887                         setWifiP2pInfoOnGroupFormation(dhcpInfo.serverAddress);
    888                         sendP2pConnectionChangedBroadcast();
    889                     } else {
    890                         WifiNative.p2pGroupRemove(mGroup.getInterface());
    891                     }
    892                     break;
    893                 case WifiP2pManager.REMOVE_GROUP:
    894                     if (DBG) loge(getName() + " remove group");
    895                     if (WifiNative.p2pGroupRemove(mGroup.getInterface())) {
    896                         replyToMessage(message, WifiP2pManager.REMOVE_GROUP_SUCCEEDED);
    897                     } else {
    898                         replyToMessage(message, WifiP2pManager.REMOVE_GROUP_FAILED,
    899                                 WifiP2pManager.ERROR);
    900                     }
    901                     break;
    902                 case WifiMonitor.P2P_GROUP_REMOVED_EVENT:
    903                     if (DBG) loge(getName() + " group removed");
    904                     Collection <WifiP2pDevice> devices = mGroup.getClientList();
    905                     boolean changed = false;
    906                     for (WifiP2pDevice d : mPeers.getDeviceList()) {
    907                         if (devices.contains(d) || mGroup.getOwner().equals(d)) {
    908                             d.status = WifiP2pDevice.AVAILABLE;
    909                             changed = true;
    910                         }
    911                     }
    912 
    913                     if (mGroup.isGroupOwner()) {
    914                         stopDhcpServer();
    915                     } else {
    916                         if (DBG) logd("stop DHCP client");
    917                         mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP);
    918                         mDhcpStateMachine.quit();
    919                         mDhcpStateMachine = null;
    920                     }
    921 
    922                     mGroup = null;
    923                     if (changed) sendP2pPeersChangedBroadcast();
    924                     transitionTo(mInactiveState);
    925                     break;
    926                 case WifiMonitor.P2P_DEVICE_LOST_EVENT:
    927                     WifiP2pDevice device = (WifiP2pDevice) message.obj;
    928                     if (device.equals(mGroup.getOwner())) {
    929                         logd("Lost the group owner, killing p2p connection");
    930                         WifiNative.p2pGroupRemove(mGroup.getInterface());
    931                     } else if (mGroup.removeClient(device)) {
    932                         if (!mPersistGroup && mGroup.isClientListEmpty()) {
    933                             Slog.d(TAG, "Client list empty, removing a non-persistent p2p group");
    934                             WifiNative.p2pGroupRemove(mGroup.getInterface());
    935                         }
    936                     }
    937                     return NOT_HANDLED; // Do the regular device lost handling
    938                 case WifiP2pManager.DISABLE_P2P:
    939                     sendMessage(WifiP2pManager.REMOVE_GROUP);
    940                     deferMessage(message);
    941                     break;
    942                 case WifiP2pManager.CONNECT:
    943                     WifiP2pConfig config = (WifiP2pConfig) message.obj;
    944                     logd("Inviting device : " + config.deviceAddress);
    945                     if (WifiNative.p2pInvite(mGroup, config.deviceAddress)) {
    946                         updateDeviceStatus(config.deviceAddress, WifiP2pDevice.INVITED);
    947                         sendP2pPeersChangedBroadcast();
    948                         replyToMessage(message, WifiP2pManager.CONNECT_SUCCEEDED);
    949                     } else {
    950                         replyToMessage(message, WifiP2pManager.CONNECT_FAILED,
    951                                 WifiP2pManager.ERROR);
    952                     }
    953                     // TODO: figure out updating the status to declined when invitation is rejected
    954                     break;
    955                 case WifiMonitor.P2P_INVITATION_RESULT_EVENT:
    956                     logd("===> INVITATION RESULT EVENT : " + message.obj);
    957                     break;
    958                 case WifiMonitor.P2P_PROV_DISC_PBC_REQ_EVENT:
    959                     notifyP2pProvDiscPbcRequest((WifiP2pDevice) message.obj);
    960                     break;
    961                 case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT:
    962                     notifyP2pProvDiscPinRequest((WifiP2pDevice) message.obj);
    963                     break;
    964                 case WifiMonitor.P2P_GROUP_STARTED_EVENT:
    965                     Slog.e(TAG, "Duplicate group creation event notice, ignore");
    966                     break;
    967                 case WPS_PBC:
    968                     WifiNative.wpsPbc();
    969                     break;
    970                 case WPS_PIN:
    971                     WifiNative.wpsPin((String) message.obj);
    972                     break;
    973                 default:
    974                     return NOT_HANDLED;
    975             }
    976             return HANDLED;
    977         }
    978 
    979         public void exit() {
    980             updateThisDevice(WifiP2pDevice.AVAILABLE);
    981             setWifiP2pInfoOnGroupTermination();
    982             mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, null);
    983             sendP2pConnectionChangedBroadcast();
    984         }
    985     }
    986 
    987     private void sendP2pStateChangedBroadcast(boolean enabled) {
    988         final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
    989         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    990         if (enabled) {
    991             intent.putExtra(WifiP2pManager.EXTRA_WIFI_STATE,
    992                     WifiP2pManager.WIFI_P2P_STATE_ENABLED);
    993         } else {
    994             intent.putExtra(WifiP2pManager.EXTRA_WIFI_STATE,
    995                     WifiP2pManager.WIFI_P2P_STATE_DISABLED);
    996         }
    997         mContext.sendStickyBroadcast(intent);
    998     }
    999 
   1000     private void sendThisDeviceChangedBroadcast() {
   1001         final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
   1002         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
   1003         intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE, new WifiP2pDevice(mThisDevice));
   1004         mContext.sendStickyBroadcast(intent);
   1005     }
   1006 
   1007     private void sendP2pPeersChangedBroadcast() {
   1008         final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
   1009         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
   1010         mContext.sendBroadcast(intent);
   1011     }
   1012 
   1013     private void sendP2pConnectionChangedBroadcast() {
   1014         if (DBG) logd("sending p2p connection changed broadcast");
   1015         Intent intent = new Intent(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
   1016         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
   1017                 | Intent.FLAG_RECEIVER_REPLACE_PENDING);
   1018         intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO, new WifiP2pInfo(mWifiP2pInfo));
   1019         intent.putExtra(WifiP2pManager.EXTRA_NETWORK_INFO, new NetworkInfo(mNetworkInfo));
   1020         mContext.sendStickyBroadcast(intent);
   1021     }
   1022 
   1023     private void startDhcpServer(String intf) {
   1024         InterfaceConfiguration ifcg = null;
   1025         try {
   1026             ifcg = mNwService.getInterfaceConfig(intf);
   1027             ifcg.addr = new LinkAddress(NetworkUtils.numericToInetAddress(
   1028                         SERVER_ADDRESS), 24);
   1029             ifcg.interfaceFlags = "[up]";
   1030             mNwService.setInterfaceConfig(intf, ifcg);
   1031             /* This starts the dnsmasq server */
   1032             mNwService.startTethering(DHCP_RANGE);
   1033         } catch (Exception e) {
   1034             loge("Error configuring interface " + intf + ", :" + e);
   1035             return;
   1036         }
   1037 
   1038         logd("Started Dhcp server on " + intf);
   1039    }
   1040 
   1041     private void stopDhcpServer() {
   1042         try {
   1043             mNwService.stopTethering();
   1044         } catch (Exception e) {
   1045             loge("Error stopping Dhcp server" + e);
   1046             return;
   1047         }
   1048 
   1049         logd("Stopped Dhcp server");
   1050     }
   1051 
   1052     private void notifyP2pEnableFailure() {
   1053         Resources r = Resources.getSystem();
   1054         AlertDialog dialog = new AlertDialog.Builder(mContext)
   1055             .setTitle(r.getString(R.string.wifi_p2p_dialog_title))
   1056             .setMessage(r.getString(R.string.wifi_p2p_failed_message))
   1057             .setPositiveButton(r.getString(R.string.ok), null)
   1058             .create();
   1059         dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
   1060         dialog.show();
   1061     }
   1062 
   1063     private void notifyWpsPin(String pin, String peerAddress) {
   1064         Resources r = Resources.getSystem();
   1065         AlertDialog dialog = new AlertDialog.Builder(mContext)
   1066             .setTitle(r.getString(R.string.wifi_p2p_dialog_title))
   1067             .setMessage(r.getString(R.string.wifi_p2p_pin_display_message, pin, peerAddress))
   1068             .setPositiveButton(r.getString(R.string.ok), null)
   1069             .create();
   1070         dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
   1071         dialog.show();
   1072     }
   1073 
   1074     private void notifyP2pGoNegotationRequest(WifiP2pConfig config) {
   1075         Resources r = Resources.getSystem();
   1076         WpsInfo wps = config.wps;
   1077         final View textEntryView = LayoutInflater.from(mContext)
   1078                 .inflate(R.layout.wifi_p2p_go_negotiation_request_alert, null);
   1079         final EditText pin = (EditText) textEntryView .findViewById(R.id.wifi_p2p_wps_pin);
   1080 
   1081         AlertDialog dialog = new AlertDialog.Builder(mContext)
   1082             .setTitle(r.getString(R.string.wifi_p2p_dialog_title))
   1083             .setView(textEntryView)
   1084             .setPositiveButton(r.getString(R.string.ok), new OnClickListener() {
   1085                         public void onClick(DialogInterface dialog, int which) {
   1086                             if (DBG) logd(getName() + " connect " + pin.getText());
   1087 
   1088                             if (pin.getVisibility() == View.GONE) {
   1089                                 mSavedGoNegotiationConfig.wps.setup = WpsInfo.PBC;
   1090                             } else {
   1091                                 mSavedGoNegotiationConfig.wps.setup = WpsInfo.KEYPAD;
   1092                                 mSavedGoNegotiationConfig.wps.pin = pin.getText().toString();
   1093                             }
   1094                             sendMessage(WifiP2pManager.CONNECT, mSavedGoNegotiationConfig);
   1095                             mSavedGoNegotiationConfig = null;
   1096                         }
   1097                     })
   1098             .setNegativeButton(r.getString(R.string.cancel), new OnClickListener() {
   1099                         @Override
   1100                         public void onClick(DialogInterface dialog, int which) {
   1101                             if (DBG) logd(getName() + " ignore connect");
   1102                             mSavedGoNegotiationConfig = null;
   1103                         }
   1104                     })
   1105             .create();
   1106 
   1107         if (wps.setup == WpsInfo.PBC) {
   1108             pin.setVisibility(View.GONE);
   1109             dialog.setMessage(r.getString(R.string.wifi_p2p_pbc_go_negotiation_request_message,
   1110                         config.deviceAddress));
   1111         } else {
   1112             dialog.setMessage(r.getString(R.string.wifi_p2p_pin_go_negotiation_request_message,
   1113                         config.deviceAddress));
   1114         }
   1115 
   1116         dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
   1117         dialog.show();
   1118     }
   1119 
   1120     private void notifyP2pProvDiscPbcRequest(WifiP2pDevice peer) {
   1121         Resources r = Resources.getSystem();
   1122         final View textEntryView = LayoutInflater.from(mContext)
   1123                 .inflate(R.layout.wifi_p2p_go_negotiation_request_alert, null);
   1124         final EditText pin = (EditText) textEntryView .findViewById(R.id.wifi_p2p_wps_pin);
   1125 
   1126         AlertDialog dialog = new AlertDialog.Builder(mContext)
   1127             .setTitle(r.getString(R.string.wifi_p2p_dialog_title))
   1128             .setView(textEntryView)
   1129             .setPositiveButton(r.getString(R.string.ok), new OnClickListener() {
   1130                         public void onClick(DialogInterface dialog, int which) {
   1131                                 if (DBG) logd(getName() + " wps_pbc");
   1132                                 sendMessage(WPS_PBC);
   1133                         }
   1134                     })
   1135             .setNegativeButton(r.getString(R.string.cancel), null)
   1136             .create();
   1137 
   1138         pin.setVisibility(View.GONE);
   1139         dialog.setMessage(r.getString(R.string.wifi_p2p_pbc_go_negotiation_request_message,
   1140                         peer.deviceAddress));
   1141 
   1142         dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
   1143         dialog.show();
   1144     }
   1145 
   1146     private void notifyP2pProvDiscPinRequest(WifiP2pDevice peer) {
   1147         Resources r = Resources.getSystem();
   1148         final View textEntryView = LayoutInflater.from(mContext)
   1149                 .inflate(R.layout.wifi_p2p_go_negotiation_request_alert, null);
   1150         final EditText pin = (EditText) textEntryView .findViewById(R.id.wifi_p2p_wps_pin);
   1151 
   1152         AlertDialog dialog = new AlertDialog.Builder(mContext)
   1153             .setTitle(r.getString(R.string.wifi_p2p_dialog_title))
   1154             .setView(textEntryView)
   1155             .setPositiveButton(r.getString(R.string.ok), new OnClickListener() {
   1156                     public void onClick(DialogInterface dialog, int which) {
   1157                         if (DBG) logd(getName() + " wps_pin");
   1158                         sendMessage(WPS_PIN, pin.getText().toString());
   1159                     }
   1160                     })
   1161             .setNegativeButton(r.getString(R.string.cancel), null)
   1162             .create();
   1163 
   1164         dialog.setMessage(r.getString(R.string.wifi_p2p_pin_go_negotiation_request_message,
   1165                         peer.deviceAddress));
   1166 
   1167         dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
   1168         dialog.show();
   1169     }
   1170 
   1171     private void notifyP2pInvitationReceived(WifiP2pGroup group) {
   1172         mSavedP2pGroup = group;
   1173         Resources r = Resources.getSystem();
   1174         final View textEntryView = LayoutInflater.from(mContext)
   1175                 .inflate(R.layout.wifi_p2p_go_negotiation_request_alert, null);
   1176         final EditText pin = (EditText) textEntryView .findViewById(R.id.wifi_p2p_wps_pin);
   1177 
   1178         AlertDialog dialog = new AlertDialog.Builder(mContext)
   1179             .setTitle(r.getString(R.string.wifi_p2p_dialog_title))
   1180             .setView(textEntryView)
   1181             .setPositiveButton(r.getString(R.string.ok), new OnClickListener() {
   1182                         public void onClick(DialogInterface dialog, int which) {
   1183                                 WifiP2pConfig config = new WifiP2pConfig();
   1184                                 config.deviceAddress = mSavedP2pGroup.getOwner().deviceAddress;
   1185                                 if (DBG) logd(getName() + " connect to invited group");
   1186                                 sendMessage(WifiP2pManager.CONNECT, config);
   1187                                 mSavedP2pGroup = null;
   1188                         }
   1189                     })
   1190             .setNegativeButton(r.getString(R.string.cancel), null)
   1191             .create();
   1192 
   1193         pin.setVisibility(View.GONE);
   1194         dialog.setMessage(r.getString(R.string.wifi_p2p_pbc_go_negotiation_request_message,
   1195                         group.getOwner().deviceAddress));
   1196 
   1197         dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
   1198         dialog.show();
   1199     }
   1200 
   1201     private void updateDeviceStatus(String deviceAddress, int status) {
   1202         for (WifiP2pDevice d : mPeers.getDeviceList()) {
   1203             if (d.deviceAddress.equals(deviceAddress)) {
   1204                 d.status = status;
   1205             }
   1206         }
   1207     }
   1208 
   1209     private boolean isGroupOwner(String deviceAddress) {
   1210         for (WifiP2pDevice d : mPeers.getDeviceList()) {
   1211             if (d.deviceAddress.equals(deviceAddress)) {
   1212                 return d.isGroupOwner();
   1213             }
   1214         }
   1215         return false;
   1216     }
   1217 
   1218     //TODO: implement when wpa_supplicant is fixed
   1219     private int configuredNetworkId(String deviceAddress) {
   1220         return -1;
   1221     }
   1222 
   1223     private void setWifiP2pInfoOnGroupFormation(String serverAddress) {
   1224         mWifiP2pInfo.groupFormed = true;
   1225         mWifiP2pInfo.isGroupOwner = mGroup.isGroupOwner();
   1226         mWifiP2pInfo.groupOwnerAddress = NetworkUtils.numericToInetAddress(serverAddress);
   1227     }
   1228 
   1229     private void setWifiP2pInfoOnGroupTermination() {
   1230         mWifiP2pInfo.groupFormed = false;
   1231         mWifiP2pInfo.isGroupOwner = false;
   1232         mWifiP2pInfo.groupOwnerAddress = null;
   1233     }
   1234 
   1235     private String getDeviceAddress(String interfaceAddress) {
   1236         for (WifiP2pDevice d : mPeers.getDeviceList()) {
   1237             if (interfaceAddress.equals(WifiNative.p2pGetInterfaceAddress(d.deviceAddress))) {
   1238                 return d.deviceAddress;
   1239             }
   1240         }
   1241         return null;
   1242     }
   1243 
   1244     private void initializeP2pSettings() {
   1245         WifiNative.setPersistentReconnect(true);
   1246         WifiNative.setDeviceName(mThisDevice.deviceName);
   1247         WifiNative.setDeviceType(mThisDevice.primaryDeviceType);
   1248 
   1249         mThisDevice.deviceAddress = WifiNative.p2pGetDeviceAddress();
   1250         updateThisDevice(WifiP2pDevice.AVAILABLE);
   1251         if (DBG) Slog.d(TAG, "DeviceAddress: " + mThisDevice.deviceAddress);
   1252     }
   1253 
   1254     private void updateThisDevice(int status) {
   1255         mThisDevice.status = status;
   1256         sendThisDeviceChangedBroadcast();
   1257     }
   1258 
   1259     //State machine initiated requests can have replyTo set to null indicating
   1260     //there are no recepients, we ignore those reply actions
   1261     private void replyToMessage(Message msg, int what) {
   1262         if (msg.replyTo == null) return;
   1263         Message dstMsg = obtainMessage(msg);
   1264         dstMsg.what = what;
   1265         mReplyChannel.replyToMessage(msg, dstMsg);
   1266     }
   1267 
   1268     private void replyToMessage(Message msg, int what, int arg1) {
   1269         if (msg.replyTo == null) return;
   1270         Message dstMsg = obtainMessage(msg);
   1271         dstMsg.what = what;
   1272         dstMsg.arg1 = arg1;
   1273         mReplyChannel.replyToMessage(msg, dstMsg);
   1274     }
   1275 
   1276     private void replyToMessage(Message msg, int what, Object obj) {
   1277         if (msg.replyTo == null) return;
   1278         Message dstMsg = obtainMessage(msg);
   1279         dstMsg.what = what;
   1280         dstMsg.obj = obj;
   1281         mReplyChannel.replyToMessage(msg, dstMsg);
   1282     }
   1283 
   1284     /* arg2 on the source message has a hash code that needs to be retained in replies
   1285      * see WifiP2pManager for details */
   1286     private Message obtainMessage(Message srcMsg) {
   1287         Message msg = Message.obtain();
   1288         msg.arg2 = srcMsg.arg2;
   1289         return msg;
   1290     }
   1291 
   1292     private void logd(String s) {
   1293         Slog.d(TAG, s);
   1294     }
   1295 
   1296     private void loge(String s) {
   1297         Slog.e(TAG, s);
   1298     }
   1299 
   1300     private void showNotification() {
   1301         NotificationManager notificationManager =
   1302             (NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE);
   1303         if (notificationManager == null || mNotification != null) {
   1304             return;
   1305         }
   1306 
   1307         Intent intent = new Intent(android.provider.Settings.ACTION_WIRELESS_SETTINGS);
   1308         intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
   1309 
   1310         PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
   1311 
   1312         Resources r = Resources.getSystem();
   1313         CharSequence title = r.getText(R.string.wifi_p2p_enabled_notification_title);
   1314         CharSequence message = r.getText(R.string.wifi_p2p_enabled_notification_message);
   1315 
   1316         mNotification = new Notification();
   1317         mNotification.when = 0;
   1318         //TODO: might change to be a seperate icon
   1319         mNotification.icon = R.drawable.stat_sys_tether_wifi;
   1320         mNotification.defaults &= ~Notification.DEFAULT_SOUND;
   1321         mNotification.flags = Notification.FLAG_ONGOING_EVENT;
   1322         mNotification.tickerText = title;
   1323         mNotification.setLatestEventInfo(mContext, title, message, pi);
   1324 
   1325         notificationManager.notify(mNotification.icon, mNotification);
   1326     }
   1327 
   1328     private void clearNotification() {
   1329         NotificationManager notificationManager =
   1330             (NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE);
   1331         if (notificationManager != null && mNotification != null) {
   1332             notificationManager.cancel(mNotification.icon);
   1333             mNotification = null;
   1334         }
   1335     }
   1336 
   1337     private boolean isAirplaneSensitive() {
   1338         String airplaneModeRadios = Settings.System.getString(mContext.getContentResolver(),
   1339                 Settings.System.AIRPLANE_MODE_RADIOS);
   1340         return airplaneModeRadios == null
   1341             || airplaneModeRadios.contains(Settings.System.RADIO_WIFI);
   1342     }
   1343 
   1344     private boolean isAirplaneModeOn() {
   1345         return isAirplaneSensitive() && Settings.System.getInt(mContext.getContentResolver(),
   1346                 Settings.System.AIRPLANE_MODE_ON, 0) == 1;
   1347     }
   1348 
   1349     }
   1350 }
   1351