NAIRealmData.java revision fa04a81daf829e6e5c099c9a249b8dd8dd112102
1package com.android.server.wifi.hotspot2.anqp;
2
3import com.android.server.wifi.hotspot2.anqp.eap.EAPMethod;
4import com.android.server.wifi.ByteBufferReader;
5import com.android.server.wifi.hotspot2.AuthMatch;
6import com.android.server.wifi.hotspot2.DomainMatcher;
7import com.android.server.wifi.hotspot2.Utils;
8
9import java.net.ProtocolException;
10import java.nio.ByteBuffer;
11import java.nio.charset.StandardCharsets;
12import java.util.ArrayList;
13import java.util.Collections;
14import java.util.List;
15
16/**
17 * The NAI Realm Data ANQP sub-element, IEEE802.11-2012 section 8.4.4.10 figure 8-418
18 */
19public class NAIRealmData {
20    private final List<String> mRealms;
21    private final List<EAPMethod> mEAPMethods;
22
23    public NAIRealmData(ByteBuffer payload) throws ProtocolException {
24        if (payload.remaining() < 5) {
25            throw new ProtocolException("Runt payload: " + payload.remaining());
26        }
27
28        int length = payload.getShort() & Constants.SHORT_MASK;
29        if (length > payload.remaining()) {
30            throw new ProtocolException("Invalid data length: " + length);
31        }
32        boolean utf8 = (payload.get() & 1) == Constants.UTF8_INDICATOR;
33
34        String realm = ByteBufferReader.readStringWithByteLength(
35                payload, utf8 ? StandardCharsets.UTF_8 : StandardCharsets.US_ASCII);
36        String[] realms = realm.split(";");
37        mRealms = new ArrayList<>();
38        for (String realmElement : realms) {
39            if (realmElement.length() > 0) {
40                mRealms.add(realmElement);
41            }
42        }
43
44        int methodCount = payload.get() & Constants.BYTE_MASK;
45        mEAPMethods = new ArrayList<>(methodCount);
46        while (methodCount > 0) {
47            mEAPMethods.add(new EAPMethod(payload));
48            methodCount--;
49        }
50    }
51
52    public List<String> getRealms() {
53        return Collections.unmodifiableList(mRealms);
54    }
55
56    public List<EAPMethod> getEAPMethods() {
57        return Collections.unmodifiableList(mEAPMethods);
58    }
59
60    // TODO(b/32714185): revisit this when integrating the new Passpoint implementation and add
61    // unit tests for this.
62    public int match(String realmToMatch, EAPMethod credMethod) {
63        int realmMatch = AuthMatch.None;
64        if (!mRealms.isEmpty()) {
65            for (String realm : mRealms) {
66                if (DomainMatcher.arg2SubdomainOfArg1(realmToMatch, realm)) {
67                    realmMatch = AuthMatch.Realm;
68                    break;
69                }
70            }
71            if (realmMatch == AuthMatch.None || mEAPMethods.isEmpty()) {
72                return realmMatch;
73            }
74            // else there is a realm match and one or more EAP methods - check them.
75        }
76        else if (mEAPMethods.isEmpty()) {
77            return AuthMatch.Indeterminate;
78        }
79
80        int best = AuthMatch.None;
81        for (EAPMethod eapMethod : mEAPMethods) {
82            int match = eapMethod.match(credMethod) | realmMatch;
83            if (match > best) {
84                best = match;
85                if (best == AuthMatch.Exact) {
86                    return best;
87                }
88            }
89        }
90        return best;
91    }
92
93    @Override
94    public String toString() {
95        StringBuilder sb = new StringBuilder();
96
97        sb.append("  NAI Realm(s)");
98        for (String realm : mRealms) {
99            sb.append(' ').append(realm);
100        }
101        sb.append('\n');
102
103        for (EAPMethod eapMethod : mEAPMethods) {
104            sb.append( "    " ).append(eapMethod.toString());
105        }
106        return sb.toString();
107    }
108}
109