package com.android; import android.app.Activity; import android.app.AlertDialog; import android.app.IntentService; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.TaskStackBuilder; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.graphics.BitmapFactory; import android.graphics.drawable.BitmapDrawable; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; import com.android.anqp.OSUProvider; import com.android.hotspot2.AppBridge; import com.android.hotspot2.PasspointMatch; import com.android.hotspot2.osu.OSUInfo; import com.android.hotspot2.osu.OSUManager; import org.xml.sax.SAXException; import java.io.IOException; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.concurrent.TimeUnit; //import com.android.Osu.R; /** * Main activity. */ public class MainActivity extends Activity { private static final int NOTIFICATION_ID = 0; // Used for OSU count private static final int NOTIFICATION_MESSAGE_ID = 1; // Used for other messages private static final Locale LOCALE = java.util.Locale.getDefault(); private static volatile OSUService sOsuService; private ListView osuListView; private OsuListAdapter2 osuListAdapter; private String message; public MainActivity() { } @Override protected void onResume() { super.onResume(); if (message != null) { showDialog(message); message = null; } } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Intent intent = getIntent(); Bundle bundle = intent.getExtras(); if (bundle == null) { // User interaction if (sOsuService == null) { Intent serviceIntent = new Intent(this, OSUService.class); serviceIntent.putExtra(ACTION_KEY, "dummy-key"); startService(serviceIntent); return; } List<OSUInfo> osuInfos = sOsuService.getOsuInfos(); setContentView(R.layout.activity_main); Log.d("osu", "osu count:" + osuInfos.size()); View noOsuView = findViewById(R.id.no_osu); if (osuInfos.size() > 0) { noOsuView.setVisibility(View.GONE); osuListAdapter = new OsuListAdapter2(this, osuInfos); osuListView = (ListView) findViewById(R.id.profile_list); osuListView.setAdapter(osuListAdapter); osuListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) { OSUInfo osuData = (OSUInfo) adapterView.getAdapter().getItem(position); Log.d("osu", "launch osu:" + osuData.getName(LOCALE) + " id:" + osuData.getOsuID()); sOsuService.selectOsu(osuData.getOsuID()); finish(); } }); } else { noOsuView.setVisibility(View.VISIBLE); } } else if (intent.getAction().equals(AppBridge.ACTION_OSU_NOTIFICATION)) { if (bundle.containsKey(AppBridge.OSU_COUNT)) { showOsuCount(bundle.getInt("osu-count", 0), Collections.<OSUInfo>emptyList()); } else if (bundle.containsKey(AppBridge.PROV_SUCCESS)) { showStatus(bundle.getBoolean(AppBridge.PROV_SUCCESS), bundle.getString(AppBridge.SP_NAME), bundle.getString(AppBridge.PROV_MESSAGE), null); } else if (bundle.containsKey(AppBridge.DEAUTH)) { showDeauth(bundle.getString(AppBridge.SP_NAME), bundle.getBoolean(AppBridge.DEAUTH), bundle.getInt(AppBridge.DEAUTH_DELAY), bundle.getString(AppBridge.DEAUTH_URL)); } /* else if (bundle.containsKey(AppBridge.OSU_INFO)) { List<OsuData> osus = printOsuDataList(bundle.getParcelableArray(AppBridge.OSU_INFO)); showOsuList(osus); } */ } } private void showOsuCount(int osuCount, List<OSUInfo> osus) { if (osuCount > 0) { printOsuDataList(osus); sendNotification(osuCount); } else { cancelNotification(); } finish(); } private void showStatus(boolean provSuccess, String spName, String provMessage, String remoteStatus) { if (provSuccess) { sendDialogMessage( String.format("Credentials for %s was successfully installed", spName)); } else { if (spName != null) { if (remoteStatus != null) { sendDialogMessage( String.format("Failed to install credentials for %s: %s: %s", spName, provMessage, remoteStatus)); } else { sendDialogMessage( String.format("Failed to install credentials for %s: %s", spName, provMessage)); } } else { sendDialogMessage( String.format("Failed to contact OSU: %s", provMessage)); } } } private void showDeauth(String spName, boolean ess, int delay, String url) { String delayReadable = getReadableTimeInSeconds(delay); if (ess) { if (delay > 60) { sendDialogMessage( String.format("There is an issue connecting to %s [for the next %s]. " + "Please visit %s for details", spName, delayReadable, url)); } else { sendDialogMessage( String.format("There is an issue connecting to %s. " + "Please visit %s for details", spName, url)); } } else { sendDialogMessage( String.format("There is an issue with the closest Access Point for %s. " + "You may wait %s or move to another Access Point to " + "regain access. Please visit %s for details.", spName, delayReadable, url)); } } private static final String ACTION_KEY = "action"; public static class WifiReceiver extends BroadcastReceiver { @Override public void onReceive(Context c, Intent intent) { Log.d(OSUManager.TAG, "OSU App got intent: " + intent.getAction()); Intent serviceIntent; serviceIntent = new Intent(c, OSUService.class); serviceIntent.putExtra(ACTION_KEY, intent.getAction()); serviceIntent.putExtras(intent); c.startService(serviceIntent); } } public static class OSUService extends IntentService { private OSUManager mOsuManager; private final IBinder mBinder = new Binder(); public OSUService() { super("OSUService"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { onHandleIntent(intent); return START_STICKY; } @Override public void onCreate() { super.onCreate(); Log.d("YYY", String.format("Service %x running, OSU %x", System.identityHashCode(this), System.identityHashCode(mOsuManager))); if (mOsuManager == null) { mOsuManager = new OSUManager(this); } sOsuService = this; } @Override public void onDestroy() { super.onDestroy(); Log.d("YYY", String.format("Service %x killed", System.identityHashCode(this))); } @Override public IBinder onBind(Intent intent) { return mBinder; } @Override protected void onHandleIntent(Intent intent) { Bundle bundle = intent.getExtras(); WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE); Log.d(OSUManager.TAG, "OSU Service got intent: " + intent.getStringExtra(ACTION_KEY)); switch (intent.getStringExtra(ACTION_KEY)) { case WifiManager.SCAN_RESULTS_AVAILABLE_ACTION: mOsuManager.pushScanResults(wifiManager.getScanResults()); break; // TODO(b/32883320): use updated intent. /* case WifiManager.PASSPOINT_WNM_FRAME_RECEIVED_ACTION: long bssid = bundle.getLong(WifiManager.EXTRA_PASSPOINT_WNM_BSSID); String url = bundle.getString(WifiManager.EXTRA_PASSPOINT_WNM_URL); try { if (bundle.containsKey(WifiManager.EXTRA_PASSPOINT_WNM_METHOD)) { int method = bundle.getInt(WifiManager.EXTRA_PASSPOINT_WNM_METHOD); if (method != OSUProvider.OSUMethod.SoapXml.ordinal()) { Log.w(OSUManager.TAG, "Unsupported remediation method: " + method); } PasspointMatch match = null; if (bundle.containsKey(WifiManager.EXTRA_PASSPOINT_WNM_PPOINT_MATCH)) { int ordinal = bundle.getInt(WifiManager.EXTRA_PASSPOINT_WNM_PPOINT_MATCH); if (ordinal >= 0 && ordinal < PasspointMatch.values().length) { match = PasspointMatch.values()[ordinal]; } } mOsuManager.wnmRemediate(bssid, url, match); } else if (bundle.containsKey(WifiManager.EXTRA_PASSPOINT_WNM_ESS)) { boolean ess = bundle.getBoolean(WifiManager.EXTRA_PASSPOINT_WNM_ESS); int delay = bundle.getInt(WifiManager.EXTRA_PASSPOINT_WNM_DELAY); mOsuManager.deauth(bssid, ess, delay, url); } else { Log.w(OSUManager.TAG, "Unknown WNM event"); } } catch (IOException | SAXException e) { Log.w(OSUManager.TAG, "Remediation event failed to parse: " + e); } break; case WifiManager.PASSPOINT_ICON_RECEIVED_ACTION: mOsuManager.notifyIconReceived( bundle.getLong(WifiManager.EXTRA_PASSPOINT_ICON_BSSID), bundle.getString(WifiManager.EXTRA_PASSPOINT_ICON_FILE), bundle.getByteArray(WifiManager.EXTRA_PASSPOINT_ICON_DATA)); break; */ case WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION: mOsuManager.networkConfigChange((WifiConfiguration) intent.getParcelableExtra(WifiManager.EXTRA_WIFI_CONFIGURATION)); break; case WifiManager.WIFI_STATE_CHANGED_ACTION: int state = bundle.getInt(WifiManager.EXTRA_WIFI_STATE); if (state == WifiManager.WIFI_STATE_DISABLED) { mOsuManager.wifiStateChange(false); } else if (state == WifiManager.WIFI_STATE_ENABLED) { mOsuManager.wifiStateChange(true); } break; case WifiManager.NETWORK_STATE_CHANGED_ACTION: mOsuManager.networkConnectEvent((WifiInfo) intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO)); break; } } public List<OSUInfo> getOsuInfos() { return mOsuManager.getAvailableOSUs(); } public void selectOsu(int id) { mOsuManager.setOSUSelection(id); } } private String getReadableTimeInSeconds(int timeSeconds) { long hours = TimeUnit.SECONDS.toHours(timeSeconds); long minutes = TimeUnit.SECONDS.toMinutes(timeSeconds) - TimeUnit.HOURS.toMinutes(hours); long seconds = timeSeconds - TimeUnit.HOURS.toSeconds(hours) - TimeUnit.MINUTES.toSeconds(minutes); if (hours > 0) { return String.format("%02d:%02d:%02d", hours, minutes, seconds); } else { return String.format("%ds", seconds); } } private void sendNotification(int count) { Notification.Builder builder = new Notification.Builder(this) .setContentTitle(String.format("%s OSU available", count)) .setContentText("Choose one to connect") .setSmallIcon(android.R.drawable.ic_dialog_info) .setAutoCancel(false); Intent resultIntent = new Intent(this, MainActivity.class); TaskStackBuilder stackBuilder = TaskStackBuilder.create(this); stackBuilder.addParentStack(MainActivity.class); stackBuilder.addNextIntent(resultIntent); PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); builder.setContentIntent(resultPendingIntent); NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); notificationManager.notify(NOTIFICATION_ID, builder.build()); } private void cancelNotification() { NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); notificationManager.cancel(NOTIFICATION_ID); } private void sendDialogMessage(String message) { // sendNotificationMessage(message); this.message = message; } private void showDialog(String message) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setMessage(message) .setTitle("OSU"); builder.setOnCancelListener(new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialogInterface) { dialogInterface.cancel(); finish(); } }); AlertDialog dialog = builder.create(); dialog.show(); } private void sendNotificationMessage(String title) { Notification.Builder builder = new Notification.Builder(this) .setContentTitle(title) .setContentText("Click to dismiss.") .setSmallIcon(android.R.drawable.ic_dialog_info) .setAutoCancel(true); NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); notificationManager.notify(NOTIFICATION_MESSAGE_ID, builder.build()); } private static class OsuListAdapter2 extends ArrayAdapter<OSUInfo> { private Activity activity; public OsuListAdapter2(Activity activity, List<OSUInfo> osuDataList) { super(activity, R.layout.list_item, osuDataList); this.activity = activity; } @Override public View getView(int position, View convertView, ViewGroup parent) { View view = convertView; if (view == null) { view = LayoutInflater.from(getContext()).inflate(R.layout.list_item, parent, false); } OSUInfo osuData = getItem(position); TextView osuName = (TextView) view.findViewById(R.id.profile_name); osuName.setText(osuData.getName(LOCALE)); TextView osuDetail = (TextView) view.findViewById(R.id.profile_detail); osuDetail.setText(osuData.getServiceDescription(LOCALE)); ImageView osuIcon = (ImageView) view.findViewById(R.id.profile_logo); byte[] iconData = osuData.getIconFileElement().getIconData(); osuIcon.setImageDrawable( new BitmapDrawable(activity.getResources(), BitmapFactory.decodeByteArray(iconData, 0, iconData.length))); return view; } } private void printOsuDataList(List<OSUInfo> osuDataList) { for (OSUInfo osuData : osuDataList) { Log.d("osu", String.format("OSUData:[%s][%s][%d]", osuData.getName(LOCALE), osuData.getServiceDescription(LOCALE), osuData.getOsuID())); } } }