1dda570201ac851dd85af3861f7e575721d3345daRomain Guy/* 2dda570201ac851dd85af3861f7e575721d3345daRomain Guy * Copyright (C) 2017 The Android Open Source Project 3dda570201ac851dd85af3861f7e575721d3345daRomain Guy * 4dda570201ac851dd85af3861f7e575721d3345daRomain Guy * Licensed under the Apache License, Version 2.0 (the "License"); 5dda570201ac851dd85af3861f7e575721d3345daRomain Guy * you may not use this file except in compliance with the License. 6dda570201ac851dd85af3861f7e575721d3345daRomain Guy * You may obtain a copy of the License at 7dda570201ac851dd85af3861f7e575721d3345daRomain Guy * 8dda570201ac851dd85af3861f7e575721d3345daRomain Guy * http://www.apache.org/licenses/LICENSE-2.0 9dda570201ac851dd85af3861f7e575721d3345daRomain Guy * 10dda570201ac851dd85af3861f7e575721d3345daRomain Guy * Unless required by applicable law or agreed to in writing, software 11dda570201ac851dd85af3861f7e575721d3345daRomain Guy * distributed under the License is distributed on an "AS IS" BASIS, 12dda570201ac851dd85af3861f7e575721d3345daRomain Guy * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13dda570201ac851dd85af3861f7e575721d3345daRomain Guy * See the License for the specific language governing permissions and 14dda570201ac851dd85af3861f7e575721d3345daRomain Guy * limitations under the License. 15dda570201ac851dd85af3861f7e575721d3345daRomain Guy */ 16dda570201ac851dd85af3861f7e575721d3345daRomain Guy 175e00c7ce063116c11315639f0035aca8ad73e8ccChris Craikpackage com.android.servicestests.apps.conntestapp; 18dda570201ac851dd85af3861f7e575721d3345daRomain Guy 19bfed827028eae460a40464be7dbe146f4d748e26Nick Kralevichimport android.app.Activity; 2038e0c32852e3b9d8ca4a9d3791577f52536419cbJohn Reckimport android.content.BroadcastReceiver; 21dda570201ac851dd85af3861f7e575721d3345daRomain Guyimport android.content.Context; 22ca79cf69d09efa0c327e9b1237d86a119aea5da7Derek Sollenbergerimport android.content.Intent; 23260ab726486317496bc12a57d599ea96dcde3284Mike Reedimport android.content.IntentFilter; 24dda570201ac851dd85af3861f7e575721d3345daRomain Guyimport android.content.pm.PackageManager; 2576d3a1b8d035d27bc80b0f2fc480a903bd001514Derek Sollenbergerimport android.net.ConnectivityManager; 26dda570201ac851dd85af3861f7e575721d3345daRomain Guyimport android.net.NetworkInfo; 27dda570201ac851dd85af3861f7e575721d3345daRomain Guyimport android.os.AsyncTask; 28dda570201ac851dd85af3861f7e575721d3345daRomain Guyimport android.os.Bundle; 29dda570201ac851dd85af3861f7e575721d3345daRomain Guyimport android.os.RemoteException; 308550c4c7b5952b7a4e1e0ede95c9492d03099a13Romain Guyimport android.util.Log; 318550c4c7b5952b7a4e1e0ede95c9492d03099a13Romain Guy 328550c4c7b5952b7a4e1e0ede95c9492d03099a13Romain Guyimport com.android.servicestests.aidl.INetworkStateObserver; 33dda570201ac851dd85af3861f7e575721d3345daRomain Guy 343b20251a355c88193c439f928a84ae69483fb488John Reckimport java.net.HttpURLConnection; 352bf68f063b0077ddef6ebfe54f2ae5e063c2c229Romain Guyimport java.net.URL; 36dda570201ac851dd85af3861f7e575721d3345daRomain Guy 378cd3edfa15cc9cdbffa935d19ab894426b08d174Greg Danielpublic class ConnTestActivity extends Activity { 38dda570201ac851dd85af3861f7e575721d3345daRomain Guy private static final String TAG = ConnTestActivity.class.getSimpleName(); 3938e0c32852e3b9d8ca4a9d3791577f52536419cbJohn Reck 40564acf7c9bff822f608cda0d5df0a64a9f9aaefdChris Craik private static final String TEST_PKG = ConnTestActivity.class.getPackage().getName(); 418cd3edfa15cc9cdbffa935d19ab894426b08d174Greg Daniel private static final String ACTION_FINISH_ACTIVITY = TEST_PKG + ".FINISH"; 428cd3edfa15cc9cdbffa935d19ab894426b08d174Greg Daniel private static final String EXTRA_NETWORK_STATE_OBSERVER = TEST_PKG + ".observer"; 438cd3edfa15cc9cdbffa935d19ab894426b08d174Greg Daniel 44bfd1cd620991ac2fa9202fdce6c00ec47d071935Chris Craik private static final int NETWORK_TIMEOUT_MS = 5 * 1000; 45bfd1cd620991ac2fa9202fdce6c00ec47d071935Chris Craik 468cd3edfa15cc9cdbffa935d19ab894426b08d174Greg Daniel private static final String NETWORK_STATUS_TEMPLATE = "%s|%s|%s|%s|%s"; 478cd3edfa15cc9cdbffa935d19ab894426b08d174Greg Daniel 488cd3edfa15cc9cdbffa935d19ab894426b08d174Greg Daniel private BroadcastReceiver finishCommandReceiver = null; 498cd3edfa15cc9cdbffa935d19ab894426b08d174Greg Daniel 50d15ebf25c595b855f6978d0600218e3ea5f31e92Chet Haase @Override 518550c4c7b5952b7a4e1e0ede95c9492d03099a13Romain Guy public void onCreate(Bundle savedInstanceState) { 528cd3edfa15cc9cdbffa935d19ab894426b08d174Greg Daniel super.onCreate(savedInstanceState); 539ace8f5e79e76893fe4ca9e4d10f6c4056330485Romain Guy 548cd3edfa15cc9cdbffa935d19ab894426b08d174Greg Daniel notifyNetworkStateObserver(); 559ace8f5e79e76893fe4ca9e4d10f6c4056330485Romain Guy 568cd3edfa15cc9cdbffa935d19ab894426b08d174Greg Daniel finishCommandReceiver = new BroadcastReceiver() { 579ace8f5e79e76893fe4ca9e4d10f6c4056330485Romain Guy @Override 588cd3edfa15cc9cdbffa935d19ab894426b08d174Greg Daniel public void onReceive(Context context, Intent intent) { 599ace8f5e79e76893fe4ca9e4d10f6c4056330485Romain Guy ConnTestActivity.this.finish(); 608cd3edfa15cc9cdbffa935d19ab894426b08d174Greg Daniel } 619ace8f5e79e76893fe4ca9e4d10f6c4056330485Romain Guy }; 629757ac0b9d62f6aea5e47cfb375f445c78bb7897Chris Craik registerReceiver(finishCommandReceiver, new IntentFilter(ACTION_FINISH_ACTIVITY)); 639757ac0b9d62f6aea5e47cfb375f445c78bb7897Chris Craik } 649757ac0b9d62f6aea5e47cfb375f445c78bb7897Chris Craik 659757ac0b9d62f6aea5e47cfb375f445c78bb7897Chris Craik @Override 669757ac0b9d62f6aea5e47cfb375f445c78bb7897Chris Craik public void onStop() { 679757ac0b9d62f6aea5e47cfb375f445c78bb7897Chris Craik if (finishCommandReceiver != null) { 689757ac0b9d62f6aea5e47cfb375f445c78bb7897Chris Craik unregisterReceiver(finishCommandReceiver); 699757ac0b9d62f6aea5e47cfb375f445c78bb7897Chris Craik } 709ace8f5e79e76893fe4ca9e4d10f6c4056330485Romain Guy super.onStop(); 719ace8f5e79e76893fe4ca9e4d10f6c4056330485Romain Guy } 729ace8f5e79e76893fe4ca9e4d10f6c4056330485Romain Guy 739ace8f5e79e76893fe4ca9e4d10f6c4056330485Romain Guy private void notifyNetworkStateObserver() { 74260ab726486317496bc12a57d599ea96dcde3284Mike Reed if (getIntent() == null) { 759ace8f5e79e76893fe4ca9e4d10f6c4056330485Romain Guy return; 769ace8f5e79e76893fe4ca9e4d10f6c4056330485Romain Guy } 779ace8f5e79e76893fe4ca9e4d10f6c4056330485Romain Guy 789ace8f5e79e76893fe4ca9e4d10f6c4056330485Romain Guy final Bundle extras = getIntent().getExtras(); 793bbacf27c0be1bae4e4483577fc89ae3113abe5dRomain Guy if (extras == null) { 809ace8f5e79e76893fe4ca9e4d10f6c4056330485Romain Guy return; 819ace8f5e79e76893fe4ca9e4d10f6c4056330485Romain Guy } 829ace8f5e79e76893fe4ca9e4d10f6c4056330485Romain Guy final INetworkStateObserver observer = INetworkStateObserver.Stub.asInterface( 83260ab726486317496bc12a57d599ea96dcde3284Mike Reed extras.getBinder(EXTRA_NETWORK_STATE_OBSERVER)); 849ace8f5e79e76893fe4ca9e4d10f6c4056330485Romain Guy if (observer != null) { 859ace8f5e79e76893fe4ca9e4d10f6c4056330485Romain Guy AsyncTask.execute(() -> { 869ace8f5e79e76893fe4ca9e4d10f6c4056330485Romain Guy try { 8776d3a1b8d035d27bc80b0f2fc480a903bd001514Derek Sollenberger observer.onNetworkStateChecked(checkNetworkStatus(ConnTestActivity.this)); 889ace8f5e79e76893fe4ca9e4d10f6c4056330485Romain Guy } catch (RemoteException e) { 899ace8f5e79e76893fe4ca9e4d10f6c4056330485Romain Guy Log.e(TAG, "Error occured while notifying the observer: " + e); 909ace8f5e79e76893fe4ca9e4d10f6c4056330485Romain Guy } 915e00c7ce063116c11315639f0035aca8ad73e8ccChris Craik }); 922055abaa0a590c35e27e1ae2e7d7cfccdfb98b59Romain Guy } 939ace8f5e79e76893fe4ca9e4d10f6c4056330485Romain Guy } 949ace8f5e79e76893fe4ca9e4d10f6c4056330485Romain Guy 959fc27819d75e24ad63d7b383d80f5cb66a577a0dRomain Guy /** 969fc27819d75e24ad63d7b383d80f5cb66a577a0dRomain Guy * Checks whether the network is available and return a string which can then be send as a 97302a9df1d50373c82923bb84ff665dfce584fb22Romain Guy * result data for the ordered broadcast. 98302a9df1d50373c82923bb84ff665dfce584fb22Romain Guy * 99302a9df1d50373c82923bb84ff665dfce584fb22Romain Guy * <p> 100302a9df1d50373c82923bb84ff665dfce584fb22Romain Guy * The string has the following format: 1019fc27819d75e24ad63d7b383d80f5cb66a577a0dRomain Guy * 1020e89e2b7bcb2c035e8cee77f93120e7c5617f8d2John Reck * <p><pre><code> 1030e89e2b7bcb2c035e8cee77f93120e7c5617f8d2John Reck * NetinfoState|NetinfoDetailedState|RealConnectionCheck|RealConnectionCheckDetails|Netinfo 1040e89e2b7bcb2c035e8cee77f93120e7c5617f8d2John Reck * </code></pre> 1050e89e2b7bcb2c035e8cee77f93120e7c5617f8d2John Reck * 1060e89e2b7bcb2c035e8cee77f93120e7c5617f8d2John Reck * <p>Where: 1078cd3edfa15cc9cdbffa935d19ab894426b08d174Greg Daniel * 1083e9999bd866fac71c72e6b484a9836c87c328a08sergeyv * <ul> 1093e9999bd866fac71c72e6b484a9836c87c328a08sergeyv * <li>{@code NetinfoState}: enum value of {@link NetworkInfo.State}. 1108aa195d7081b889f3a7b1f426cbd8556377aae5eRomain Guy * <li>{@code NetinfoDetailedState}: enum value of {@link NetworkInfo.DetailedState}. 1118cd3edfa15cc9cdbffa935d19ab894426b08d174Greg Daniel * <li>{@code RealConnectionCheck}: boolean value of a real connection check (i.e., an attempt 1123b20251a355c88193c439f928a84ae69483fb488John Reck * to access an external website. 1138cd3edfa15cc9cdbffa935d19ab894426b08d174Greg Daniel * <li>{@code RealConnectionCheckDetails}: if HTTP output core or exception string of the real 1148cd3edfa15cc9cdbffa935d19ab894426b08d174Greg Daniel * connection attempt 1159ace8f5e79e76893fe4ca9e4d10f6c4056330485Romain Guy * <li>{@code Netinfo}: string representation of the {@link NetworkInfo}. 1168550c4c7b5952b7a4e1e0ede95c9492d03099a13Romain Guy * </ul> 117171c592f0b7066acf279863c8a52ddabea49d3dbRomain Guy * 118171c592f0b7066acf279863c8a52ddabea49d3dbRomain Guy * For example, if the connection was established fine, the result would be something like: 1193e9999bd866fac71c72e6b484a9836c87c328a08sergeyv * <p><pre><code> 120f219da5e32e85deb442468ee9a63bb28eb198557Romain Guy * CONNECTED|CONNECTED|true|200|[type: WIFI[], state: CONNECTED/CONNECTED, reason: ...] 121f219da5e32e85deb442468ee9a63bb28eb198557Romain Guy * </code></pre> 1229757ac0b9d62f6aea5e47cfb375f445c78bb7897Chris Craik */ 1239757ac0b9d62f6aea5e47cfb375f445c78bb7897Chris Craik private String checkNetworkStatus(Context context) { 124e5c6584a402fb3b1fe0507e4e00e601bec8f1bbcChris Craik final ConnectivityManager cm = 1259757ac0b9d62f6aea5e47cfb375f445c78bb7897Chris Craik (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 1269757ac0b9d62f6aea5e47cfb375f445c78bb7897Chris Craik final String address = "http://example.com"; 1279ace8f5e79e76893fe4ca9e4d10f6c4056330485Romain Guy final NetworkInfo networkInfo = cm.getActiveNetworkInfo(); 128aa6c24c21c727a196451332448d4e3b11a80be69Romain Guy Log.d(TAG, "Running checkNetworkStatus() on thread " 1293e9999bd866fac71c72e6b484a9836c87c328a08sergeyv + Thread.currentThread().getName() + " for UID " + getUid(context) 1309757ac0b9d62f6aea5e47cfb375f445c78bb7897Chris Craik + "\n\tactiveNetworkInfo: " + networkInfo + "\n\tURL: " + address); 131aa6c24c21c727a196451332448d4e3b11a80be69Romain Guy boolean checkStatus = false; 1329ace8f5e79e76893fe4ca9e4d10f6c4056330485Romain Guy String checkDetails = "N/A"; 133aa6c24c21c727a196451332448d4e3b11a80be69Romain Guy try { 1343e9999bd866fac71c72e6b484a9836c87c328a08sergeyv final URL url = new URL(address); 135aa6c24c21c727a196451332448d4e3b11a80be69Romain Guy final HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 136aa6c24c21c727a196451332448d4e3b11a80be69Romain Guy conn.setReadTimeout(NETWORK_TIMEOUT_MS); 137aa6c24c21c727a196451332448d4e3b11a80be69Romain Guy conn.setConnectTimeout(NETWORK_TIMEOUT_MS / 2); 138aa6c24c21c727a196451332448d4e3b11a80be69Romain Guy conn.setRequestMethod("GET"); 139aa6c24c21c727a196451332448d4e3b11a80be69Romain Guy conn.setDoInput(true); 1408f0095cd33558e9cc8a440047908e53b68906f5fRomain Guy conn.connect(); 141302a9df1d50373c82923bb84ff665dfce584fb22Romain Guy final int response = conn.getResponseCode(); 142302a9df1d50373c82923bb84ff665dfce584fb22Romain Guy checkStatus = true; 143302a9df1d50373c82923bb84ff665dfce584fb22Romain Guy checkDetails = "HTTP response for " + address + ": " + response; 144302a9df1d50373c82923bb84ff665dfce584fb22Romain Guy } catch (Exception e) { 145302a9df1d50373c82923bb84ff665dfce584fb22Romain Guy checkStatus = false; 146dda570201ac851dd85af3861f7e575721d3345daRomain Guy checkDetails = "Exception getting " + address + ": " + e; 147dda570201ac851dd85af3861f7e575721d3345daRomain Guy } 148dda570201ac851dd85af3861f7e575721d3345daRomain Guy Log.d(TAG, checkDetails); 149dda570201ac851dd85af3861f7e575721d3345daRomain Guy final String state, detailedState; 150 if (networkInfo != null) { 151 state = networkInfo.getState().name(); 152 detailedState = networkInfo.getDetailedState().name(); 153 } else { 154 state = detailedState = "null"; 155 } 156 final String status = String.format(NETWORK_STATUS_TEMPLATE, state, detailedState, 157 Boolean.valueOf(checkStatus), checkDetails, networkInfo); 158 Log.d(TAG, "Offering " + status); 159 return status; 160 } 161 162 private int getUid(Context context) { 163 final String packageName = context.getPackageName(); 164 try { 165 return context.getPackageManager().getPackageUid(packageName, 0); 166 } catch (PackageManager.NameNotFoundException e) { 167 throw new IllegalStateException("Could not get UID for " + packageName, e); 168 } 169 } 170}