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}