1package com.android.hotspot2.app;
2
3import android.app.IntentService;
4import android.content.BroadcastReceiver;
5import android.content.Context;
6import android.content.Intent;
7import android.content.IntentFilter;
8import android.net.wifi.WifiConfiguration;
9import android.net.wifi.WifiInfo;
10import android.net.wifi.WifiManager;
11import android.os.Bundle;
12import android.os.IBinder;
13import android.util.Log;
14
15import com.android.anqp.OSUProvider;
16import com.android.hotspot2.PasspointMatch;
17import com.android.hotspot2.osu.OSUManager;
18
19import java.io.IOException;
20import java.util.List;
21
22/**
23 * This is the Hotspot 2.0 release 2 OSU background service that is continuously running and caches
24 * OSU information.
25 *
26 * The OSU App is made up of two services; FlowService and OSUService.
27 *
28 * OSUService is a long running light weight service, kept alive throughout the lifetime of the
29 * operating system by being bound from the framework (in WifiManager in stage
30 * PHASE_THIRD_PARTY_APPS_CAN_START), and is responsible for continuously caching OSU information
31 * and notifying the UI when OSUs are available.
32 *
33 * FlowService is only started on demand from OSUService and is responsible for handling actual
34 * provisioning and remediation flows, and requires a fairly significant memory footprint.
35 *
36 * FlowService is defined to run in its own process through the definition
37 *      <service android:name=".flow.FlowService" android:process=":osuflow">
38 * in the AndroidManifest.
39 * This is done as a means to keep total app memory footprint low (pss < 10M) and only start the
40 * FlowService on demand and make it available for "garbage collection" by the OS when not in use.
41 */
42public class OSUService extends IntentService {
43    public static final String REMEDIATION_DONE_ACTION = "com.android.hotspot2.REMEDIATION_DONE";
44    public static final String REMEDIATION_FQDN_EXTRA = "com.android.hotspot2.REMEDIATION_FQDN";
45    public static final String REMEDIATION_POLICY_EXTRA = "com.android.hotspot2.REMEDIATION_POLICY";
46
47    private static final String[] INTENTS = {
48            WifiManager.SCAN_RESULTS_AVAILABLE_ACTION,
49            // TODO(b/32883320): use updated intent definitions.
50            //WifiManager.PASSPOINT_WNM_FRAME_RECEIVED_ACTION,
51            //WifiManager.PASSPOINT_ICON_RECEIVED_ACTION,
52            WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION,
53            WifiManager.WIFI_STATE_CHANGED_ACTION,
54            WifiManager.NETWORK_STATE_CHANGED_ACTION,
55            REMEDIATION_DONE_ACTION
56    };
57
58    private OSUManager mOsuManager;
59    private final LocalServiceBinder mLocalServiceBinder;
60
61    public OSUService() {
62        super("OSUService");
63        mLocalServiceBinder = new LocalServiceBinder(this);
64    }
65
66    /*
67    public final class OSUAccessorImpl extends IOSUAccessor.Stub {
68        public List<OSUData> getOsuData() {
69            List<OSUInfo> infos = getOsuInfos();
70            List<OSUData> data = new ArrayList<>(infos.size());
71            for (OSUInfo osuInfo : infos) {
72                data.add(new OSUData(osuInfo));
73            }
74            return data;
75        }
76
77        public void selectOsu(int id) {
78            OSUService.this.selectOsu(id);
79        }
80    }
81    */
82
83    @Override
84    public int onStartCommand(Intent intent, int flags, int startId) {
85        onHandleIntent(intent);
86        return START_STICKY;
87    }
88
89    @Override
90    public IBinder onBind(Intent intent) {
91        BroadcastReceiver receiver = new BroadcastReceiver() {
92            @Override
93            public void onReceive(Context context, Intent intent) {
94                handleIntent(intent.getAction(), intent);
95            }
96        };
97        for (String intentString : INTENTS) {
98            registerReceiver(receiver, new IntentFilter(intentString));
99        }
100        return mLocalServiceBinder;
101    }
102
103    @Override
104    protected void onHandleIntent(Intent intent) {
105        if (intent == null) {
106            Log.d(OSUManager.TAG, "Null intent!");
107            return;
108        }
109        //handleIntent(intent.getStringExtra(MainActivity.ACTION_KEY), intent);
110    }
111
112    private void handleIntent(String action, Intent intent) {
113        WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
114        Bundle bundle = intent.getExtras();
115        if (mOsuManager == null) {
116            mOsuManager = new OSUManager(this);
117        }
118        Log.d(OSUManager.TAG, "Got intent " + intent.getAction());
119
120        switch (action) {
121            case WifiManager.SCAN_RESULTS_AVAILABLE_ACTION:
122                mOsuManager.pushScanResults(wifiManager.getScanResults());
123                break;
124            // TODO(b/32883320): use updated intent definitions.
125            /*
126            case WifiManager.PASSPOINT_WNM_FRAME_RECEIVED_ACTION:
127                long bssid = bundle.getLong(WifiManager.EXTRA_PASSPOINT_WNM_BSSID);
128                String url = bundle.getString(WifiManager.EXTRA_PASSPOINT_WNM_URL);
129
130                try {
131                    if (bundle.containsKey(WifiManager.EXTRA_PASSPOINT_WNM_METHOD)) {
132                        int method = bundle.getInt(WifiManager.EXTRA_PASSPOINT_WNM_METHOD);
133                        if (method != OSUProvider.OSUMethod.SoapXml.ordinal()) {
134                            Log.w(OSUManager.TAG, "Unsupported remediation method: " + method);
135                            return;
136                        }
137                        PasspointMatch match = null;
138                        if (bundle.containsKey(WifiManager.EXTRA_PASSPOINT_WNM_PPOINT_MATCH)) {
139                            int ordinal =
140                                    bundle.getInt(WifiManager.EXTRA_PASSPOINT_WNM_PPOINT_MATCH);
141                            if (ordinal >= 0 && ordinal < PasspointMatch.values().length) {
142                                match = PasspointMatch.values()[ordinal];
143                            }
144                        }
145                        mOsuManager.wnmRemediate(bssid, url, match);
146                    } else if (bundle.containsKey(WifiManager.EXTRA_PASSPOINT_WNM_ESS)) {
147                        boolean ess = bundle.getBoolean(WifiManager.EXTRA_PASSPOINT_WNM_ESS);
148                        int delay = bundle.getInt(WifiManager.EXTRA_PASSPOINT_WNM_DELAY);
149                        mOsuManager.deauth(bssid, ess, delay, url);
150                    } else {
151                        Log.w(OSUManager.TAG, "Unknown WNM event");
152                    }
153                } catch (IOException e) {
154                    Log.w(OSUManager.TAG, "Remediation event failed to parse: " + e);
155                }
156                break;
157            case WifiManager.PASSPOINT_ICON_RECEIVED_ACTION:
158                mOsuManager.notifyIconReceived(
159                        bundle.getLong(WifiManager.EXTRA_PASSPOINT_ICON_BSSID),
160                        bundle.getString(WifiManager.EXTRA_PASSPOINT_ICON_FILE),
161                        bundle.getByteArray(WifiManager.EXTRA_PASSPOINT_ICON_DATA));
162                break;
163            */
164            case WifiManager.NETWORK_STATE_CHANGED_ACTION:
165                mOsuManager.networkConnectChange(
166                        (WifiInfo) intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO));
167                break;
168            case WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION:
169                boolean multiNetwork =
170                        bundle.getBoolean(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, false);
171                if (multiNetwork) {
172                    mOsuManager.networkConfigChanged();
173                } else if (bundle.getInt(WifiManager.EXTRA_CHANGE_REASON,
174                        WifiManager.CHANGE_REASON_CONFIG_CHANGE)
175                        == WifiManager.CHANGE_REASON_REMOVED) {
176                    WifiConfiguration configuration =
177                            intent.getParcelableExtra(WifiManager.EXTRA_WIFI_CONFIGURATION);
178                    mOsuManager.networkDeleted(configuration);
179                } else {
180                    mOsuManager.networkConfigChanged();
181                }
182                break;
183            case WifiManager.WIFI_STATE_CHANGED_ACTION:
184                int state = bundle.getInt(WifiManager.EXTRA_WIFI_STATE);
185                if (state == WifiManager.WIFI_STATE_DISABLED) {
186                    mOsuManager.wifiStateChange(false);
187                } else if (state == WifiManager.WIFI_STATE_ENABLED) {
188                    mOsuManager.wifiStateChange(true);
189                }
190                break;
191            case REMEDIATION_DONE_ACTION:
192                String fqdn = bundle.getString(REMEDIATION_FQDN_EXTRA);
193                boolean policy = bundle.getBoolean(REMEDIATION_POLICY_EXTRA);
194                mOsuManager.remediationDone(fqdn, policy);
195                break;
196            }
197    }
198
199    public List<OSUData> getOsuData() {
200        return mOsuManager.getAvailableOSUs();
201    }
202
203    public void selectOsu(int id) {
204        mOsuManager.setOSUSelection(id);
205    }
206}
207