1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.net.wifi.passpoint;
18
19import android.net.wifi.WifiConfiguration;
20import android.os.Parcelable;
21import android.os.Parcel;
22import android.security.Credentials;
23import android.util.Log;
24
25import java.lang.reflect.Field;
26import java.lang.reflect.Method;
27
28
29/** @hide */
30public class WifiPasspointPolicy implements Parcelable {
31
32    private final static String TAG = "PasspointPolicy";
33
34    /** @hide */
35    public static final int HOME_SP = 0;
36
37    /** @hide */
38    public static final int ROAMING_PARTNER = 1;
39
40    /** @hide */
41    public static final int UNRESTRICTED = 2;
42
43    private String mName;
44    private int mCredentialPriority;
45    private int mRoamingPriority;
46    private String mBssid;
47    private String mSsid;
48    private WifiPasspointCredential mCredential;
49    private int mRestriction;// Permitted values are "HomeSP", "RoamingPartner", or "Unrestricted"
50    private boolean mIsHomeSp;
51
52    private final String INT_PRIVATE_KEY = "private_key";
53    private final String INT_PHASE2 = "phase2";
54    private final String INT_PASSWORD = "password";
55    private final String INT_IDENTITY = "identity";
56    private final String INT_EAP = "eap";
57    private final String INT_CLIENT_CERT = "client_cert";
58    private final String INT_CA_CERT = "ca_cert";
59    private final String INT_ANONYMOUS_IDENTITY = "anonymous_identity";
60    private final String INT_SIM_SLOT = "sim_slot";
61    private final String INT_ENTERPRISEFIELD_NAME ="android.net.wifi.WifiConfiguration$EnterpriseField";
62    private final String ISO8601DATEFORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
63    private final String ENTERPRISE_PHASE2_MSCHAPV2 = "auth=MSCHAPV2";
64    private final String ENTERPRISE_PHASE2_MSCHAP = "auth=MSCHAP";
65
66    /** @hide */
67    public WifiPasspointPolicy(String name, String ssid,
68            String bssid, WifiPasspointCredential pc,
69            int restriction, boolean ishomesp) {
70        mName = name;
71        if (pc != null) {
72            mCredentialPriority = pc.getPriority();
73        }
74        //PerProviderSubscription/<X+>/Policy/PreferredRoamingPartnerList/<X+>/Priority
75        mRoamingPriority = 128; //default priority value of 128
76        mSsid = ssid;
77        mCredential = pc;
78        mBssid = bssid;
79        mRestriction = restriction;
80        mIsHomeSp = ishomesp;
81    }
82
83    public String getSsid() {
84        return mSsid;
85    }
86
87    /** @hide */
88    public void setBssid(String bssid) {
89        mBssid = bssid;
90    }
91
92    public String getBssid() {
93        return mBssid;
94    }
95
96    /** @hide */
97    public void setRestriction(int r) {
98        mRestriction = r;
99    }
100
101    /** @hide */
102    public int getRestriction() {
103        return mRestriction;
104    }
105
106    /** @hide */
107    public void setHomeSp(boolean b) {
108        mIsHomeSp = b;
109    }
110
111    /** @hide */
112    public boolean isHomeSp() {
113        return mIsHomeSp;
114    }
115
116    /** @hide */
117    public void setCredential(WifiPasspointCredential newCredential) {
118        mCredential = newCredential;
119    }
120
121    public WifiPasspointCredential getCredential() {
122        // TODO: return a copy
123        return mCredential;
124    }
125
126    /** @hide */
127    public void setCredentialPriority(int priority) {
128        mCredentialPriority = priority;
129    }
130
131    /** @hide */
132    public void setRoamingPriority(int priority) {
133        mRoamingPriority = priority;
134    }
135
136    public int getCredentialPriority() {
137        return mCredentialPriority;
138    }
139
140    public int getRoamingPriority() {
141        return mRoamingPriority;
142    }
143
144    public WifiConfiguration createWifiConfiguration() {
145        WifiConfiguration wfg = new WifiConfiguration();
146        if (mBssid != null) {
147            Log.d(TAG, "create bssid:" + mBssid);
148            wfg.BSSID = mBssid;
149        }
150
151        if (mSsid != null) {
152            Log.d(TAG, "create ssid:" + mSsid);
153            wfg.SSID = mSsid;
154        }
155        //TODO: 1. add pmf configuration
156        //      2. add ocsp configuration
157        //      3. add eap-sim configuration
158        /*Key management*/
159        wfg.status = WifiConfiguration.Status.ENABLED;
160        wfg.allowedKeyManagement.clear();
161        wfg.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
162        wfg.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X);
163
164        /*Group Ciphers*/
165        wfg.allowedGroupCiphers.clear();
166        wfg.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
167        wfg.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
168
169        /*Protocols*/
170        wfg.allowedProtocols.clear();
171        wfg.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
172        wfg.allowedProtocols.set(WifiConfiguration.Protocol.WPA);
173
174        Class[] enterpriseFieldArray  = WifiConfiguration.class.getClasses();
175        Class<?> enterpriseFieldClass = null;
176
177
178        for(Class<?> myClass : enterpriseFieldArray) {
179            if(myClass.getName().equals(INT_ENTERPRISEFIELD_NAME)) {
180                enterpriseFieldClass = myClass;
181                break;
182            }
183        }
184        Log.d(TAG, "class chosen " + enterpriseFieldClass.getName() );
185
186
187        Field anonymousId = null, caCert = null, clientCert = null,
188              eap = null, identity = null, password = null,
189              phase2 = null, privateKey =  null;
190
191        Field[] fields = WifiConfiguration.class.getFields();
192
193
194        for (Field tempField : fields) {
195            if (tempField.getName().trim().equals(INT_ANONYMOUS_IDENTITY)) {
196                anonymousId = tempField;
197                Log.d(TAG, "field " + anonymousId.getName() );
198            } else if (tempField.getName().trim().equals(INT_CA_CERT)) {
199                caCert = tempField;
200            } else if (tempField.getName().trim().equals(INT_CLIENT_CERT)) {
201                clientCert = tempField;
202                Log.d(TAG, "field " + clientCert.getName() );
203            } else if (tempField.getName().trim().equals(INT_EAP)) {
204                eap = tempField;
205                Log.d(TAG, "field " + eap.getName() );
206            } else if (tempField.getName().trim().equals(INT_IDENTITY)) {
207                identity = tempField;
208                Log.d(TAG, "field " + identity.getName() );
209            } else if (tempField.getName().trim().equals(INT_PASSWORD)) {
210                password = tempField;
211                Log.d(TAG, "field " + password.getName() );
212            } else if (tempField.getName().trim().equals(INT_PHASE2)) {
213                phase2 = tempField;
214                Log.d(TAG, "field " + phase2.getName() );
215
216            } else if (tempField.getName().trim().equals(INT_PRIVATE_KEY)) {
217                privateKey = tempField;
218            }
219        }
220
221
222        Method setValue = null;
223
224        for(Method m: enterpriseFieldClass.getMethods()) {
225            if(m.getName().trim().equals("setValue")) {
226                Log.d(TAG, "method " + m.getName() );
227                setValue = m;
228                break;
229            }
230        }
231
232        try {
233            // EAP
234            String eapmethod = mCredential.getType();
235            Log.d(TAG, "eapmethod:" + eapmethod);
236            setValue.invoke(eap.get(wfg), eapmethod);
237
238            // Username, password, EAP Phase 2
239            if ("TTLS".equals(eapmethod)) {
240                setValue.invoke(phase2.get(wfg), ENTERPRISE_PHASE2_MSCHAPV2);
241                setValue.invoke(identity.get(wfg), mCredential.getUserName());
242                setValue.invoke(password.get(wfg), mCredential.getPassword());
243                setValue.invoke(anonymousId.get(wfg), "anonymous@" + mCredential.getRealm());
244            }
245
246            // EAP CA Certificate
247            String cacertificate = null;
248            String rootCA = mCredential.getCaRootCertPath();
249            if (rootCA == null){
250                cacertificate = null;
251            } else {
252                cacertificate = "keystore://" + Credentials.WIFI + "HS20" + Credentials.CA_CERTIFICATE + rootCA;
253            }
254            Log.d(TAG, "cacertificate:" + cacertificate);
255            setValue.invoke(caCert.get(wfg), cacertificate);
256
257            //User certificate
258            if ("TLS".equals(eapmethod)) {
259                String usercertificate = null;
260                String privatekey = null;
261                String clientCertPath = mCredential.getClientCertPath();
262                if (clientCertPath != null){
263                    privatekey = "keystore://" + Credentials.WIFI + "HS20" + Credentials.USER_PRIVATE_KEY + clientCertPath;
264                    usercertificate = "keystore://" + Credentials.WIFI + "HS20" + Credentials.USER_CERTIFICATE + clientCertPath;
265                }
266                Log.d(TAG, "privatekey:" + privatekey);
267                Log.d(TAG, "usercertificate:" + usercertificate);
268                if (privatekey != null && usercertificate != null) {
269                    setValue.invoke(privateKey.get(wfg), privatekey);
270                    setValue.invoke(clientCert.get(wfg), usercertificate);
271                }
272            }
273        } catch (Exception e) {
274            Log.d(TAG, "createWifiConfiguration err:" + e);
275        }
276
277        return wfg;
278    }
279
280    /** {@inheritDoc} @hide */
281    public int compareTo(WifiPasspointPolicy another) {
282        Log.d(TAG, "this:" + this);
283        Log.d(TAG, "another:" + another);
284
285        if (another == null) {
286            return -1;
287        } else if (this.mIsHomeSp == true && another.isHomeSp() == false) {
288            //home sp priority is higher then roaming
289            Log.d(TAG, "compare HomeSP  first, this is HomeSP, another isn't");
290            return -1;
291        } else if ((this.mIsHomeSp == true && another.isHomeSp() == true)) {
292            Log.d(TAG, "both HomeSP");
293            //if both home sp, compare credential priority
294            if (this.mCredentialPriority < another.getCredentialPriority()) {
295                Log.d(TAG, "this priority is higher");
296                return -1;
297            } else if (this.mCredentialPriority == another.getCredentialPriority()) {
298                Log.d(TAG, "both priorities equal");
299                //if priority still the same, compare name(ssid)
300                if (this.mName.compareTo(another.mName) != 0) {
301                    Log.d(TAG, "compare mName return:" + this.mName.compareTo(another.mName));
302                    return this.mName.compareTo(another.mName);
303                }
304                /**
305                 *if name still the same, compare credential
306                 *the device may has two more credentials(TLS,SIM..etc)
307                 *it can associate to one AP(same ssid). so we should compare by credential
308                 */
309                if (this.mCredential != null && another.mCredential != null) {
310                    if (this.mCredential.compareTo(another.mCredential) != 0) {
311                        Log.d(TAG,
312                                "compare mCredential return:" + this.mName.compareTo(another.mName));
313                        return this.mCredential.compareTo(another.mCredential);
314                    }
315                }
316            } else {
317                return 1;
318            }
319        } else if ((this.mIsHomeSp == false && another.isHomeSp() == false)) {
320            Log.d(TAG, "both RoamingSp");
321            //if both roaming sp, compare roaming priority(preferredRoamingPartnerList/<X+>/priority)
322            if (this.mRoamingPriority < another.getRoamingPriority()) {
323                Log.d(TAG, "this priority is higher");
324                return -1;
325            } else if (this.mRoamingPriority == another.getRoamingPriority()) {//priority equals, compare name
326                Log.d(TAG, "both priorities equal");
327                //if priority still the same, compare name(ssid)
328                if (this.mName.compareTo(another.mName) != 0) {
329                    Log.d(TAG, "compare mName return:" + this.mName.compareTo(another.mName));
330                    return this.mName.compareTo(another.mName);
331                }
332                //if name still the same, compare credential
333                if (this.mCredential != null && another.mCredential != null) {
334                    if (this.mCredential.compareTo(another.mCredential) != 0) {
335                        Log.d(TAG,
336                                "compare mCredential return:"
337                                        + this.mCredential.compareTo(another.mCredential));
338                        return this.mCredential.compareTo(another.mCredential);
339                    }
340                }
341            } else {
342                return 1;
343            }
344        }
345
346        Log.d(TAG, "both policies equal");
347        return 0;
348    }
349
350    @Override
351    /** @hide */
352    public String toString() {
353        return "PasspointPolicy: name=" + mName + " CredentialPriority=" + mCredentialPriority +
354                " mRoamingPriority" + mRoamingPriority +
355                " ssid=" + mSsid + " restriction=" + mRestriction +
356                " ishomesp=" + mIsHomeSp + " Credential=" + mCredential;
357    }
358
359    /** Implement the Parcelable interface {@hide} */
360    @Override
361    public int describeContents() {
362        return 0;
363    }
364
365    /** Implement the Parcelable interface {@hide} */
366    @Override
367    public void writeToParcel(Parcel dest, int flags) {
368        // TODO
369    }
370
371    /** Implement the Parcelable interface {@hide} */
372    public static final Creator<WifiPasspointPolicy> CREATOR =
373            new Creator<WifiPasspointPolicy>() {
374                @Override
375                public WifiPasspointPolicy createFromParcel(Parcel in) {
376                    return null;
377                }
378
379                @Override
380                public WifiPasspointPolicy[] newArray(int size) {
381                    return new WifiPasspointPolicy[size];
382                }
383            };
384}
385