1 package com.android; 2 3 import android.app.Activity; 4 import android.app.AlertDialog; 5 import android.app.IntentService; 6 import android.app.Notification; 7 import android.app.NotificationManager; 8 import android.app.PendingIntent; 9 import android.app.TaskStackBuilder; 10 import android.content.BroadcastReceiver; 11 import android.content.Context; 12 import android.content.DialogInterface; 13 import android.content.Intent; 14 import android.graphics.BitmapFactory; 15 import android.graphics.drawable.BitmapDrawable; 16 import android.net.wifi.WifiConfiguration; 17 import android.net.wifi.WifiInfo; 18 import android.net.wifi.WifiManager; 19 import android.os.Binder; 20 import android.os.Bundle; 21 import android.os.IBinder; 22 import android.util.Log; 23 import android.view.LayoutInflater; 24 import android.view.View; 25 import android.view.ViewGroup; 26 import android.widget.AdapterView; 27 import android.widget.ArrayAdapter; 28 import android.widget.ImageView; 29 import android.widget.ListView; 30 import android.widget.TextView; 31 32 import com.android.anqp.OSUProvider; 33 import com.android.hotspot2.AppBridge; 34 import com.android.hotspot2.PasspointMatch; 35 import com.android.hotspot2.osu.OSUInfo; 36 import com.android.hotspot2.osu.OSUManager; 37 38 import org.xml.sax.SAXException; 39 40 import java.io.IOException; 41 import java.util.Collections; 42 import java.util.List; 43 import java.util.Locale; 44 import java.util.concurrent.TimeUnit; 45 46 //import com.android.Osu.R; 47 48 /** 49 * Main activity. 50 */ 51 public class MainActivity extends Activity { 52 private static final int NOTIFICATION_ID = 0; // Used for OSU count 53 private static final int NOTIFICATION_MESSAGE_ID = 1; // Used for other messages 54 private static final Locale LOCALE = java.util.Locale.getDefault(); 55 56 private static volatile OSUService sOsuService; 57 58 private ListView osuListView; 59 private OsuListAdapter2 osuListAdapter; 60 private String message; 61 MainActivity()62 public MainActivity() { 63 64 } 65 66 @Override onResume()67 protected void onResume() { 68 super.onResume(); 69 if (message != null) { 70 showDialog(message); 71 message = null; 72 } 73 } 74 75 @Override onCreate(Bundle savedInstanceState)76 public void onCreate(Bundle savedInstanceState) { 77 super.onCreate(savedInstanceState); 78 79 Intent intent = getIntent(); 80 Bundle bundle = intent.getExtras(); 81 82 if (bundle == null) { // User interaction 83 if (sOsuService == null) { 84 Intent serviceIntent = new Intent(this, OSUService.class); 85 serviceIntent.putExtra(ACTION_KEY, "dummy-key"); 86 startService(serviceIntent); 87 return; 88 } 89 90 List<OSUInfo> osuInfos = sOsuService.getOsuInfos(); 91 92 setContentView(R.layout.activity_main); 93 Log.d("osu", "osu count:" + osuInfos.size()); 94 View noOsuView = findViewById(R.id.no_osu); 95 if (osuInfos.size() > 0) { 96 noOsuView.setVisibility(View.GONE); 97 osuListAdapter = new OsuListAdapter2(this, osuInfos); 98 osuListView = (ListView) findViewById(R.id.profile_list); 99 osuListView.setAdapter(osuListAdapter); 100 osuListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 101 @Override 102 public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) { 103 OSUInfo osuData = (OSUInfo) adapterView.getAdapter().getItem(position); 104 Log.d("osu", "launch osu:" + osuData.getName(LOCALE) 105 + " id:" + osuData.getOsuID()); 106 sOsuService.selectOsu(osuData.getOsuID()); 107 finish(); 108 } 109 }); 110 } else { 111 noOsuView.setVisibility(View.VISIBLE); 112 } 113 } else if (intent.getAction().equals(AppBridge.ACTION_OSU_NOTIFICATION)) { 114 if (bundle.containsKey(AppBridge.OSU_COUNT)) { 115 showOsuCount(bundle.getInt("osu-count", 0), Collections.<OSUInfo>emptyList()); 116 } else if (bundle.containsKey(AppBridge.PROV_SUCCESS)) { 117 showStatus(bundle.getBoolean(AppBridge.PROV_SUCCESS), 118 bundle.getString(AppBridge.SP_NAME), 119 bundle.getString(AppBridge.PROV_MESSAGE), 120 null); 121 } else if (bundle.containsKey(AppBridge.DEAUTH)) { 122 showDeauth(bundle.getString(AppBridge.SP_NAME), 123 bundle.getBoolean(AppBridge.DEAUTH), 124 bundle.getInt(AppBridge.DEAUTH_DELAY), 125 bundle.getString(AppBridge.DEAUTH_URL)); 126 } 127 /* 128 else if (bundle.containsKey(AppBridge.OSU_INFO)) { 129 List<OsuData> osus = printOsuDataList(bundle.getParcelableArray(AppBridge.OSU_INFO)); 130 showOsuList(osus); 131 } 132 */ 133 } 134 } 135 showOsuCount(int osuCount, List<OSUInfo> osus)136 private void showOsuCount(int osuCount, List<OSUInfo> osus) { 137 if (osuCount > 0) { 138 printOsuDataList(osus); 139 sendNotification(osuCount); 140 } else { 141 cancelNotification(); 142 } 143 finish(); 144 } 145 showStatus(boolean provSuccess, String spName, String provMessage, String remoteStatus)146 private void showStatus(boolean provSuccess, String spName, String provMessage, 147 String remoteStatus) { 148 if (provSuccess) { 149 sendDialogMessage( 150 String.format("Credentials for %s was successfully installed", spName)); 151 } else { 152 if (spName != null) { 153 if (remoteStatus != null) { 154 sendDialogMessage( 155 String.format("Failed to install credentials for %s: %s: %s", 156 spName, provMessage, remoteStatus)); 157 } else { 158 sendDialogMessage( 159 String.format("Failed to install credentials for %s: %s", 160 spName, provMessage)); 161 } 162 } else { 163 sendDialogMessage( 164 String.format("Failed to contact OSU: %s", provMessage)); 165 } 166 } 167 } 168 showDeauth(String spName, boolean ess, int delay, String url)169 private void showDeauth(String spName, boolean ess, int delay, String url) { 170 String delayReadable = getReadableTimeInSeconds(delay); 171 if (ess) { 172 if (delay > 60) { 173 sendDialogMessage( 174 String.format("There is an issue connecting to %s [for the next %s]. " + 175 "Please visit %s for details", spName, delayReadable, url)); 176 } else { 177 sendDialogMessage( 178 String.format("There is an issue connecting to %s. " + 179 "Please visit %s for details", spName, url)); 180 } 181 } else { 182 sendDialogMessage( 183 String.format("There is an issue with the closest Access Point for %s. " + 184 "You may wait %s or move to another Access Point to " + 185 "regain access. Please visit %s for details.", 186 spName, delayReadable, url)); 187 } 188 } 189 190 private static final String ACTION_KEY = "action"; 191 192 public static class WifiReceiver extends BroadcastReceiver { 193 @Override onReceive(Context c, Intent intent)194 public void onReceive(Context c, Intent intent) { 195 Log.d(OSUManager.TAG, "OSU App got intent: " + intent.getAction()); 196 Intent serviceIntent; 197 serviceIntent = new Intent(c, OSUService.class); 198 serviceIntent.putExtra(ACTION_KEY, intent.getAction()); 199 serviceIntent.putExtras(intent); 200 c.startService(serviceIntent); 201 } 202 } 203 204 public static class OSUService extends IntentService { 205 private OSUManager mOsuManager; 206 private final IBinder mBinder = new Binder(); 207 OSUService()208 public OSUService() { 209 super("OSUService"); 210 } 211 212 @Override onStartCommand(Intent intent, int flags, int startId)213 public int onStartCommand(Intent intent, int flags, int startId) { 214 onHandleIntent(intent); 215 return START_STICKY; 216 } 217 218 @Override onCreate()219 public void onCreate() { 220 super.onCreate(); 221 Log.d("YYY", String.format("Service %x running, OSU %x", 222 System.identityHashCode(this), System.identityHashCode(mOsuManager))); 223 if (mOsuManager == null) { 224 mOsuManager = new OSUManager(this); 225 } 226 sOsuService = this; 227 } 228 229 @Override onDestroy()230 public void onDestroy() { 231 super.onDestroy(); 232 Log.d("YYY", String.format("Service %x killed", System.identityHashCode(this))); 233 } 234 235 @Override onBind(Intent intent)236 public IBinder onBind(Intent intent) { 237 return mBinder; 238 } 239 240 @Override onHandleIntent(Intent intent)241 protected void onHandleIntent(Intent intent) { 242 Bundle bundle = intent.getExtras(); 243 WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE); 244 Log.d(OSUManager.TAG, "OSU Service got intent: " + intent.getStringExtra(ACTION_KEY)); 245 switch (intent.getStringExtra(ACTION_KEY)) { 246 case WifiManager.SCAN_RESULTS_AVAILABLE_ACTION: 247 mOsuManager.pushScanResults(wifiManager.getScanResults()); 248 break; 249 case WifiManager.PASSPOINT_WNM_FRAME_RECEIVED_ACTION: 250 long bssid = bundle.getLong(WifiManager.EXTRA_PASSPOINT_WNM_BSSID); 251 String url = bundle.getString(WifiManager.EXTRA_PASSPOINT_WNM_URL); 252 253 try { 254 if (bundle.containsKey(WifiManager.EXTRA_PASSPOINT_WNM_METHOD)) { 255 int method = bundle.getInt(WifiManager.EXTRA_PASSPOINT_WNM_METHOD); 256 if (method != OSUProvider.OSUMethod.SoapXml.ordinal()) { 257 Log.w(OSUManager.TAG, "Unsupported remediation method: " + method); 258 } 259 PasspointMatch match = null; 260 if (bundle.containsKey(WifiManager.EXTRA_PASSPOINT_WNM_PPOINT_MATCH)) { 261 int ordinal = 262 bundle.getInt(WifiManager.EXTRA_PASSPOINT_WNM_PPOINT_MATCH); 263 if (ordinal >= 0 && ordinal < PasspointMatch.values().length) { 264 match = PasspointMatch.values()[ordinal]; 265 } 266 } 267 mOsuManager.wnmRemediate(bssid, url, match); 268 } else if (bundle.containsKey(WifiManager.EXTRA_PASSPOINT_WNM_ESS)) { 269 boolean ess = bundle.getBoolean(WifiManager.EXTRA_PASSPOINT_WNM_ESS); 270 int delay = bundle.getInt(WifiManager.EXTRA_PASSPOINT_WNM_DELAY); 271 mOsuManager.deauth(bssid, ess, delay, url); 272 } else { 273 Log.w(OSUManager.TAG, "Unknown WNM event"); 274 } 275 } catch (IOException | SAXException e) { 276 Log.w(OSUManager.TAG, "Remediation event failed to parse: " + e); 277 } 278 break; 279 case WifiManager.PASSPOINT_ICON_RECEIVED_ACTION: 280 mOsuManager.notifyIconReceived( 281 bundle.getLong(WifiManager.EXTRA_PASSPOINT_ICON_BSSID), 282 bundle.getString(WifiManager.EXTRA_PASSPOINT_ICON_FILE), 283 bundle.getByteArray(WifiManager.EXTRA_PASSPOINT_ICON_DATA)); 284 break; 285 case WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION: 286 mOsuManager.networkConfigChange((WifiConfiguration) 287 intent.getParcelableExtra(WifiManager.EXTRA_WIFI_CONFIGURATION)); 288 break; 289 case WifiManager.WIFI_STATE_CHANGED_ACTION: 290 int state = bundle.getInt(WifiManager.EXTRA_WIFI_STATE); 291 if (state == WifiManager.WIFI_STATE_DISABLED) { 292 mOsuManager.wifiStateChange(false); 293 } else if (state == WifiManager.WIFI_STATE_ENABLED) { 294 mOsuManager.wifiStateChange(true); 295 } 296 break; 297 case WifiManager.NETWORK_STATE_CHANGED_ACTION: 298 mOsuManager.networkConnectEvent((WifiInfo) 299 intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO)); 300 break; 301 } 302 } 303 getOsuInfos()304 public List<OSUInfo> getOsuInfos() { 305 return mOsuManager.getAvailableOSUs(); 306 } 307 selectOsu(int id)308 public void selectOsu(int id) { 309 mOsuManager.setOSUSelection(id); 310 } 311 } 312 getReadableTimeInSeconds(int timeSeconds)313 private String getReadableTimeInSeconds(int timeSeconds) { 314 long hours = TimeUnit.SECONDS.toHours(timeSeconds); 315 long minutes = TimeUnit.SECONDS.toMinutes(timeSeconds) - TimeUnit.HOURS.toMinutes(hours); 316 long seconds = 317 timeSeconds - TimeUnit.HOURS.toSeconds(hours) - TimeUnit.MINUTES.toSeconds(minutes); 318 if (hours > 0) { 319 return String.format("%02d:%02d:%02d", hours, minutes, seconds); 320 } else { 321 return String.format("%ds", seconds); 322 } 323 } 324 sendNotification(int count)325 private void sendNotification(int count) { 326 Notification.Builder builder = 327 new Notification.Builder(this) 328 .setContentTitle(String.format("%s OSU available", count)) 329 .setContentText("Choose one to connect") 330 .setSmallIcon(android.R.drawable.ic_dialog_info) 331 .setAutoCancel(false); 332 Intent resultIntent = new Intent(this, MainActivity.class); 333 334 TaskStackBuilder stackBuilder = TaskStackBuilder.create(this); 335 stackBuilder.addParentStack(MainActivity.class); 336 stackBuilder.addNextIntent(resultIntent); 337 PendingIntent resultPendingIntent = 338 stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); 339 builder.setContentIntent(resultPendingIntent); 340 NotificationManager notificationManager = 341 (NotificationManager) getSystemService(NOTIFICATION_SERVICE); 342 notificationManager.notify(NOTIFICATION_ID, builder.build()); 343 } 344 cancelNotification()345 private void cancelNotification() { 346 NotificationManager notificationManager = 347 (NotificationManager) getSystemService(NOTIFICATION_SERVICE); 348 notificationManager.cancel(NOTIFICATION_ID); 349 } 350 sendDialogMessage(String message)351 private void sendDialogMessage(String message) { 352 // sendNotificationMessage(message); 353 this.message = message; 354 } 355 showDialog(String message)356 private void showDialog(String message) { 357 AlertDialog.Builder builder = new AlertDialog.Builder(this); 358 builder.setMessage(message) 359 .setTitle("OSU"); 360 builder.setOnCancelListener(new DialogInterface.OnCancelListener() { 361 @Override 362 public void onCancel(DialogInterface dialogInterface) { 363 dialogInterface.cancel(); 364 finish(); 365 } 366 }); 367 AlertDialog dialog = builder.create(); 368 dialog.show(); 369 } 370 sendNotificationMessage(String title)371 private void sendNotificationMessage(String title) { 372 Notification.Builder builder = 373 new Notification.Builder(this) 374 .setContentTitle(title) 375 .setContentText("Click to dismiss.") 376 .setSmallIcon(android.R.drawable.ic_dialog_info) 377 .setAutoCancel(true); 378 NotificationManager notificationManager = 379 (NotificationManager) getSystemService(NOTIFICATION_SERVICE); 380 notificationManager.notify(NOTIFICATION_MESSAGE_ID, builder.build()); 381 } 382 383 private static class OsuListAdapter2 extends ArrayAdapter<OSUInfo> { 384 private Activity activity; 385 OsuListAdapter2(Activity activity, List<OSUInfo> osuDataList)386 public OsuListAdapter2(Activity activity, List<OSUInfo> osuDataList) { 387 super(activity, R.layout.list_item, osuDataList); 388 this.activity = activity; 389 } 390 391 @Override getView(int position, View convertView, ViewGroup parent)392 public View getView(int position, View convertView, ViewGroup parent) { 393 View view = convertView; 394 if (view == null) { 395 view = LayoutInflater.from(getContext()).inflate(R.layout.list_item, parent, false); 396 } 397 OSUInfo osuData = getItem(position); 398 TextView osuName = (TextView) view.findViewById(R.id.profile_name); 399 osuName.setText(osuData.getName(LOCALE)); 400 TextView osuDetail = (TextView) view.findViewById(R.id.profile_detail); 401 osuDetail.setText(osuData.getServiceDescription(LOCALE)); 402 ImageView osuIcon = (ImageView) view.findViewById(R.id.profile_logo); 403 byte[] iconData = osuData.getIconFileElement().getIconData(); 404 osuIcon.setImageDrawable( 405 new BitmapDrawable(activity.getResources(), 406 BitmapFactory.decodeByteArray(iconData, 0, iconData.length))); 407 return view; 408 } 409 } 410 printOsuDataList(List<OSUInfo> osuDataList)411 private void printOsuDataList(List<OSUInfo> osuDataList) { 412 for (OSUInfo osuData : osuDataList) { 413 Log.d("osu", String.format("OSUData:[%s][%s][%d]", 414 osuData.getName(LOCALE), osuData.getServiceDescription(LOCALE), 415 osuData.getOsuID())); 416 } 417 } 418 419 } 420