com.entertailion.android.dial.ServerFinder.java Source code

Java tutorial

Introduction

Here is the source code for com.entertailion.android.dial.ServerFinder.java

Source

/*
 * Copyright (C) 2009 Google Inc.  All rights reserved.
 *
 * 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.entertailion.android.dial;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;

import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.util.EntityUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.Settings;
import android.text.InputFilter;
import android.text.InputType;
import android.text.TextUtils;
import android.text.method.NumberKeyListener;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

/**
 * Device discovery with UPnP.
 */
public final class ServerFinder extends Activity {
    private static final String LOG_TAG = "ServerFinder";

    /**
     * Request code used by wifi settings activity
     */
    private static final int CODE_WIFI_SETTINGS = 1;

    public static final String MANUAL_IP_ADDRESS = "manual_ip_address";

    private static final String HEADER_APPLICATION_URL = "Application-URL";

    private ProgressDialog progressDialog;
    private AlertDialog confirmationDialog;
    private DialServer previousDialServer;
    private List<DialServer> recentlyConnectedServers;

    private InetAddress broadcastAddress;
    private WifiManager wifiManager;
    private boolean active;

    /**
     * Handles used to pass data back to calling activities.
     */
    public static final String EXTRA_DIAL_SERVER = "dial_server";
    public static final String EXTRA_RECENTLY_CONNECTED = "recently_connected";

    private ListView stbList;
    private final DeviceFinderListAdapter dataAdapter;

    private BroadcastHandler broadcastHandler;
    private BroadcastDiscoveryClient broadcastClient;
    private Thread broadcastClientThread;

    private TrackedDialServers trackedServers;

    private Handler handler = new Handler();

    /**
     * Handler message number for a service update from broadcast client.
     */
    public static final int BROADCAST_RESPONSE = 100;

    /**
     * Handler message number for all delayed messages
     */
    private static final int DELAYED_MESSAGE = 101;

    private enum DelayedMessage {
        BROADCAST_TIMEOUT, DIAL_SERVER_FOUND;

        Message obtainMessage(Handler handler) {
            Message message = handler.obtainMessage(DELAYED_MESSAGE);
            message.obj = this;
            return message;
        }
    }

    public ServerFinder() {
        dataAdapter = new DeviceFinderListAdapter();
        trackedServers = new TrackedDialServers();
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.device_finder_layout);

        previousDialServer = getIntent().getParcelableExtra(EXTRA_DIAL_SERVER);

        recentlyConnectedServers = getIntent().getParcelableArrayListExtra(EXTRA_RECENTLY_CONNECTED);

        broadcastHandler = new BroadcastHandler();
        wifiManager = (WifiManager) getSystemService(WIFI_SERVICE);

        stbList = (ListView) findViewById(R.id.stb_list);
        stbList.setOnItemClickListener(selectHandler);
        stbList.setAdapter(dataAdapter);

        ((Button) findViewById(R.id.button_manual)).setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                buildManualIpDialog().show();
            }
        });

        Analytics.createAnalytics(this);
    }

    private void showOtherDevices() {
        broadcastHandler.removeMessages(DELAYED_MESSAGE);
        if (progressDialog.isShowing()) {
            try {
                progressDialog.dismiss();
            } catch (Throwable e) {
                Log.e(LOG_TAG, "showOtherDevices", e);
            }
        }
        if (confirmationDialog != null && confirmationDialog.isShowing()) {
            try {
                confirmationDialog.dismiss();
            } catch (Throwable e) {
                Log.e(LOG_TAG, "showOtherDevices", e);
            }
        }
        findViewById(R.id.device_finder).setVisibility(View.VISIBLE);
    }

    @Override
    protected void onStart() {
        super.onStart();

        Analytics.startAnalytics(this);

        try {
            broadcastAddress = InetAddress.getByName("239.255.255.250");
        } catch (UnknownHostException e) {
            Log.e(LOG_TAG, "broadcastAddress", e);
        }

        startBroadcast();
    }

    @Override
    protected void onPause() {
        active = false;
        broadcastHandler.removeMessages(DELAYED_MESSAGE);
        super.onPause();
    }

    @Override
    protected void onResume() {
        super.onResume();
        active = true;
    }

    @Override
    protected void onStop() {
        if (null != broadcastClient) {
            broadcastClient.stop();
            broadcastClient = null;
        }

        super.onStop();
        Analytics.stopAnalytics(this);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        Log.d(LOG_TAG, "ActivityResult: " + requestCode + ", " + resultCode);
        if (requestCode == CODE_WIFI_SETTINGS) {
            if (!isWifiAvailable()) {
                buildNoWifiDialog().show();
            } else {
                startBroadcast();
            }
        }
    }

    private OnItemClickListener selectHandler = new OnItemClickListener() {
        public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
            DialServer DialServer = (DialServer) parent.getItemAtPosition(position);
            if (DialServer != null) {
                connectToEntry(DialServer);
            }
        }
    };

    /**
     * Connects to the chosen entry in the list. Finishes the activity and
     * returns the informations on the chosen box.
     * 
     * @param DialServer
     *            the listEntry representing the box you want to connect to
     */
    private void connectToEntry(DialServer DialServer) {
        Intent resultIntent = new Intent();
        resultIntent.putExtra(EXTRA_DIAL_SERVER, DialServer);
        setResult(RESULT_OK, resultIntent);
        finish();
    }

    private void startBroadcast() {
        if (!isWifiAvailable()) {
            try {
                buildNoWifiDialog().show();
            } catch (Exception e) {
                Log.e(LOG_TAG, "startBroadcast", e);
            }
            return;
        }
        try {
            broadcastClient = new BroadcastDiscoveryClient(broadcastAddress, broadcastHandler);
            broadcastClientThread = new Thread(broadcastClient);
            broadcastClientThread.start();
            Message message = DelayedMessage.BROADCAST_TIMEOUT.obtainMessage(broadcastHandler);
            broadcastHandler.sendMessageDelayed(message, getResources().getInteger(R.integer.broadcast_timeout));
            showProgressDialog(buildBroadcastProgressDialog());
        } catch (RuntimeException e) {
            Log.e(LOG_TAG, "startBroadcast", e);
        }
    }

    private void enableWifi() {
        if (null != wifiManager) {
            wifiManager.setWifiEnabled(true);
        }
    }

    /**
     * Returns an intent that starts this activity.
     */
    public static Intent createConnectIntent(Context ctx, DialServer recentlyConnected,
            ArrayList<DialServer> recentlyConnectedList) {
        Intent intent = new Intent(ctx, ServerFinder.class);
        intent.putExtra(EXTRA_DIAL_SERVER, recentlyConnected);
        intent.putParcelableArrayListExtra(EXTRA_RECENTLY_CONNECTED, recentlyConnectedList);
        return intent;
    }

    private class DeviceFinderListAdapter extends BaseAdapter {
        public int getCount() {
            return getTotalSize();
        }

        @Override
        public boolean hasStableIds() {
            return false;
        }

        @Override
        public boolean areAllItemsEnabled() {
            return false;
        }

        @Override
        public boolean isEnabled(int position) {
            return position != trackedServers.size();
        }

        public Object getItem(int position) {
            return getDialServer(position);
        }

        public long getItemId(int position) {
            return position;
        }

        public View getView(int position, View convertView, ViewGroup parent) {
            ListEntryView liv;

            if (position == trackedServers.size()) {
                return getLayoutInflater().inflate(R.layout.device_list_separator_layout, null);
            }

            if (convertView == null || !(convertView instanceof ListEntryView)) {
                liv = (ListEntryView) getLayoutInflater().inflate(R.layout.device_list_item_layout, null);
            } else {
                liv = (ListEntryView) convertView;
            }

            liv.setListEntry(getDialServer(position));
            return liv;
        }

        private int getTotalSize() {
            return trackedServers.size();
        }

        private DialServer getDialServer(int position) {
            if (position < trackedServers.size()) {
                return trackedServers.get(position);
            }
            return null;
        }
    }

    /**
     * Represents an entry in the box list.
     */
    public static class ListEntryView extends LinearLayout {

        public ListEntryView(Context context, AttributeSet attrs) {
            super(context, attrs);
            myContext = context;
        }

        public ListEntryView(Context context) {
            super(context);
            myContext = context;
        }

        @Override
        protected void onFinishInflate() {
            super.onFinishInflate();
            tvName = (TextView) findViewById(R.id.device_list_item_name);
            tvTargetAddr = (TextView) findViewById(R.id.device_list_target_addr);
        }

        private void updateContents() {
            if (null != tvName) {
                String txt = myContext.getString(R.string.unkown_tgt_name);
                if (null != listEntry) {
                    txt = formatName(listEntry);
                }
                tvName.setText(txt);
            }

            if (null != tvTargetAddr) {
                String txt = myContext.getString(R.string.unkown_tgt_addr);
                if ((null != listEntry) && (null != listEntry.getIpAddress())) {
                    txt = listEntry.getIpAddress().getHostAddress();
                }
                tvTargetAddr.setText(txt);
            }
        }

        public DialServer getListEntry() {
            return listEntry;
        }

        public void setListEntry(DialServer listEntry) {
            this.listEntry = listEntry;
            updateContents();
        }

        private Context myContext = null;
        private DialServer listEntry = null;
        private TextView tvName = null;
        private TextView tvTargetAddr = null;
    }

    private final class BroadcastHandler extends Handler {
        /** {inheritDoc} */
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == DELAYED_MESSAGE) {
                if (!active) {
                    return;
                }
                switch ((DelayedMessage) msg.obj) {
                case BROADCAST_TIMEOUT:
                    try {
                        broadcastClient.stop();
                        try {
                            if (progressDialog.isShowing()) {
                                progressDialog.dismiss();
                            }
                        } catch (Throwable e) {
                            Log.e(LOG_TAG, "handleMessage", e);
                        }
                        buildBroadcastTimeoutDialog().show();
                    } catch (Exception e1) {
                        Log.e(LOG_TAG, "handleMessage", e1);
                    }
                    break;

                case DIAL_SERVER_FOUND:
                    try {
                        // Check if there is previously connected remote and
                        // suggest it
                        // for connection:
                        DialServer toConnect = null;
                        if (previousDialServer != null) {
                            Log.d(LOG_TAG, "Previous Remote Device: " + previousDialServer);
                            toConnect = trackedServers.findDialServer(previousDialServer);
                        }
                        if (toConnect == null) {
                            Log.d(LOG_TAG, "No previous device found.");
                            // No default found - suggest any device
                            toConnect = trackedServers.get(0);
                        }

                        try {
                            progressDialog.dismiss();
                        } catch (Throwable e) {
                            Log.e(LOG_TAG, "handleMessage", e);
                        }
                        confirmationDialog = buildConfirmationDialog(toConnect);
                        confirmationDialog.show();
                    } catch (Exception e) {
                        Log.e(LOG_TAG, "handleMessage", e);
                    }
                    break;
                }
            }

            switch (msg.what) {
            case BROADCAST_RESPONSE:
                final BroadcastAdvertisement advert = (BroadcastAdvertisement) msg.obj;
                if (advert.getLocation() != null) {
                    new Thread(new Runnable() {
                        public void run() {
                            HttpResponse response = new HttpRequestHelper().sendHttpGet(advert.getLocation());
                            if (response != null) {
                                String appsUrl = null;
                                Header header = response.getLastHeader(HEADER_APPLICATION_URL);
                                if (header != null) {
                                    appsUrl = header.getValue();
                                    Log.d(LOG_TAG, "appsUrl=" + appsUrl);
                                }
                                String friendlyName = null;
                                String manufacturer = null;
                                String modelName = null;
                                String uuid = null;
                                try {
                                    InputStream inputStream = response.getEntity().getContent();
                                    BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
                                    XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
                                    factory.setNamespaceAware(true);
                                    XmlPullParser parser = factory.newPullParser();
                                    parser.setInput(reader);
                                    int eventType = parser.getEventType();
                                    String lastTagName = null;
                                    while (eventType != XmlPullParser.END_DOCUMENT) {
                                        switch (eventType) {
                                        case XmlPullParser.START_DOCUMENT:
                                            break;
                                        case XmlPullParser.START_TAG:
                                            String tagName = parser.getName();
                                            lastTagName = tagName;
                                            break;
                                        case XmlPullParser.TEXT:
                                            if (lastTagName != null) {
                                                if ("friendlyName".equals(lastTagName)) {
                                                    friendlyName = parser.getText();
                                                } else if ("UDN".equals(lastTagName)) {
                                                    uuid = parser.getText();
                                                } else if ("manufacturer".equals(lastTagName)) {
                                                    manufacturer = parser.getText();
                                                } else if ("modelName".equals(lastTagName)) {
                                                    modelName = parser.getText();
                                                }
                                            }
                                            break;
                                        case XmlPullParser.END_TAG:
                                            tagName = parser.getName();
                                            lastTagName = null;
                                            break;
                                        }
                                        eventType = parser.next();
                                    }
                                    inputStream.close();
                                } catch (Exception e) {
                                    Log.e(LOG_TAG, "parse device description", e);
                                }
                                Log.d(LOG_TAG, "friendlyName=" + friendlyName);
                                final DialServer dialServer = new DialServer(advert.getLocation(),
                                        advert.getIpAddress(), advert.getPort(), appsUrl, friendlyName, uuid,
                                        manufacturer, modelName);
                                handler.post(new Runnable() {
                                    public void run() {
                                        handleDialServerAdd(dialServer);
                                    }
                                });
                            }
                        }
                    }).start();
                }
                break;
            }
        }
    }

    private void handleDialServerAdd(final DialServer dialServer) {
        if (trackedServers.add(dialServer)) {
            Log.v(LOG_TAG, "Adding new device: " + dialServer);

            // Notify data adapter and update title.
            dataAdapter.notifyDataSetChanged();

            // Show confirmation dialog only for the first STB and only if
            // progress
            // dialog is visible.
            if ((trackedServers.size() == 1) && progressDialog.isShowing()) {
                broadcastHandler.removeMessages(DELAYED_MESSAGE);
                // delayed automatic adding
                Message message = DelayedMessage.DIAL_SERVER_FOUND.obtainMessage(broadcastHandler);
                broadcastHandler.sendMessageDelayed(message,
                        getResources().getInteger(R.integer.gtv_finder_reconnect_delay));
            }
        }
    }

    private ProgressDialog buildBroadcastProgressDialog() {
        String message;
        String networkName = getNetworkName();
        if (!TextUtils.isEmpty(networkName)) {
            message = getString(R.string.finder_searching_with_ssid, networkName);
        } else {
            message = getString(R.string.finder_searching);
        }

        return buildProgressDialog(message, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialogInterface, int which) {
                broadcastHandler.removeMessages(DELAYED_MESSAGE);
                showOtherDevices();
            }
        });
    }

    private ProgressDialog buildProgressDialog(String message, DialogInterface.OnClickListener cancelListener) {
        ProgressDialog dialog = new ProgressDialog(this);
        dialog.setMessage(message);
        dialog.setOnKeyListener(new DialogInterface.OnKeyListener() {
            public boolean onKey(DialogInterface dialogInterface, int which, KeyEvent event) {
                if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
                    finish();
                    return true;
                }
                return false;
            }
        });
        dialog.setButton(getString(R.string.finder_cancel), cancelListener);
        return dialog;
    }

    private AlertDialog buildConfirmationDialog(final DialServer dialServer) {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        View view = LayoutInflater.from(this).inflate(R.layout.device_info, null);
        final TextView ipTextView = (TextView) view.findViewById(R.id.device_info_ip_address);

        builder.setMessage(formatName(dialServer));
        ipTextView.setText(dialServer.getIpAddress().getHostAddress());

        return builder.setTitle(R.string.finder_label).setCancelable(false)
                .setPositiveButton(R.string.finder_connect, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialogInterface, int id) {
                        connectToEntry(dialServer);
                    }
                }).setNegativeButton(R.string.finder_add_other, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialogInterface, int id) {
                        showOtherDevices();
                    }
                }).create();
    }

    private static final String formatName(DialServer dialServer) {
        StringBuffer buffer = new StringBuffer();
        if (dialServer.getFriendlyName() != null) {
            buffer.append(dialServer.getFriendlyName());
            if (dialServer.getModelName() != null) {
                buffer.append("/").append(dialServer.getModelName());
            }
            if (dialServer.getManufacturer() != null) {
                buffer.append("/").append(dialServer.getManufacturer());
            }
        } else {
            if (dialServer.getModelName() != null) {
                buffer.append("/").append(dialServer.getModelName());
            }
            if (dialServer.getManufacturer() != null) {
                buffer.append("/").append(dialServer.getManufacturer());
            }
        }
        return buffer.toString();
    }

    private AlertDialog buildManualIpDialog() {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        View view = LayoutInflater.from(this).inflate(R.layout.manual_ip, null);
        final EditText ipEditText = (EditText) view.findViewById(R.id.manual_ip_entry);

        ipEditText.setFilters(new InputFilter[] { new NumberKeyListener() {
            @Override
            protected char[] getAcceptedChars() {
                return "0123456789.:".toCharArray();
            }

            public int getInputType() {
                return InputType.TYPE_CLASS_NUMBER;
            }
        } });

        SharedPreferences settings = getSharedPreferences(MainActivity.PREFS_NAME, Activity.MODE_PRIVATE);
        String ip = settings.getString(MANUAL_IP_ADDRESS, "");
        ipEditText.setText(ip);

        builder.setPositiveButton(R.string.manual_ip_connect, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                DialServer DialServer = DialServerFromString(ipEditText.getText().toString());
                if (DialServer != null) {
                    connectToEntry(DialServer);
                    try {
                        SharedPreferences settings = getSharedPreferences(MainActivity.PREFS_NAME,
                                Activity.MODE_PRIVATE);
                        SharedPreferences.Editor editor = settings.edit();
                        editor.putString(MANUAL_IP_ADDRESS, ipEditText.getText().toString());
                        editor.commit();
                    } catch (Exception e) {
                    }
                } else {
                    Toast.makeText(ServerFinder.this, getString(R.string.manual_ip_error_address),
                            Toast.LENGTH_LONG).show();
                }
            }
        }).setNegativeButton(R.string.manual_ip_cancel, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                // do nothing
            }
        }).setCancelable(true).setTitle(R.string.manual_ip_label).setMessage(R.string.manual_ip_entry_label)
                .setView(view);
        return builder.create();
    }

    private AlertDialog buildBroadcastTimeoutDialog() {
        String message;
        String networkName = getNetworkName();
        if (!TextUtils.isEmpty(networkName)) {
            message = getString(R.string.finder_no_devices_with_ssid, networkName);
        } else {
            message = getString(R.string.finder_no_devices);
        }

        return buildTimeoutDialog(message, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialogInterface, int id) {
                startBroadcast();
            }
        });
    }

    private AlertDialog buildTimeoutDialog(CharSequence message, DialogInterface.OnClickListener retryListener) {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);

        return builder.setMessage(message).setCancelable(false)
                .setPositiveButton(R.string.finder_manual, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialogInterface, int id) {
                        buildManualIpDialog().show();
                    }
                }).setNegativeButton(R.string.finder_cancel, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialogInterface, int id) {
                        setResult(RESULT_CANCELED, null);
                        finish();
                    }
                }).create();
    }

    private AlertDialog buildNoWifiDialog() {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);

        builder.setMessage(R.string.finder_wifi_not_available);
        builder.setCancelable(false);
        builder.setPositiveButton(R.string.finder_configure, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialogInterface, int id) {
                Intent intent = new Intent(Settings.ACTION_WIRELESS_SETTINGS);
                startActivityForResult(intent, CODE_WIFI_SETTINGS);
            }
        });
        builder.setNegativeButton(R.string.finder_cancel, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialogInterface, int id) {
                setResult(RESULT_CANCELED, null);
                finish();
            }
        });
        return builder.create();
    }

    private void showProgressDialog(ProgressDialog newDialog) {
        try {
            try {
                if ((progressDialog != null) && progressDialog.isShowing()) {
                    progressDialog.dismiss();
                }
            } catch (Throwable e) {
                Log.e(LOG_TAG, "showProgressDialog", e);
            }
            progressDialog = newDialog;
            newDialog.show();
        } catch (Exception e) {
            Log.e(LOG_TAG, "showProgressDialog", e);
        }
    }

    private DialServer DialServerFromString(String text) {
        String[] ipPort = text.split(":");
        int port;
        if (ipPort.length == 1) {
            port = getResources().getInteger(R.integer.manual_default_port);
        } else if (ipPort.length == 2) {
            try {
                port = Integer.parseInt(ipPort[1]);
            } catch (NumberFormatException e) {
                return null;
            }
        } else {
            return null;
        }

        try {
            InetAddress address = InetAddress.getByName(ipPort[0]);
            // TODO
            return new DialServer(null, address, port, null, getString(R.string.manual_ip_default_box_name), null,
                    null, null);
        } catch (UnknownHostException e) {
        }
        return null;
    }

    private boolean isSimulator() {
        return Build.FINGERPRINT.startsWith("generic");
    }

    private boolean isWifiAvailable() {
        try {
            if (isSimulator()) {
                return true;
            }
            if (!wifiManager.isWifiEnabled()) {
                return false;
            }
            WifiInfo info = wifiManager.getConnectionInfo();
            return info != null && info.getIpAddress() != 0;
        } catch (Exception e) {
            Log.e(LOG_TAG, "isWifiAvailable", e);
        }
        return false;
    }

    private String getNetworkName() {
        if (isSimulator()) {
            return "generic";
        }
        if (!isWifiAvailable()) {
            return null;
        }
        WifiInfo info = wifiManager.getConnectionInfo();
        return info != null ? info.getSSID() : null;
    }
}