| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.server.wifi.hotspot2.anqp; |
| |
| import android.util.Log; |
| |
| import com.android.internal.annotations.VisibleForTesting; |
| |
| import java.net.ProtocolException; |
| import java.nio.BufferUnderflowException; |
| import java.nio.ByteBuffer; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| |
| /** |
| * The IEI (Information Element Identity) contained in the Generic Container for the |
| * 3GPP Cellular Network ANQP element. |
| * |
| * Refer to Annex A of 3GPP TS 24.234 version 11.3.0 for information on the data format: |
| * (http://www.etsi.org/deliver/etsi_ts/124200_124299/124234/11.03.00_60/ts_124234v110300p.pdf) |
| */ |
| public class CellularNetwork { |
| private static final String TAG = "CellularNetwork"; |
| |
| /** |
| * IEI type for PLMN (Public Land Mobile Network) list. |
| */ |
| @VisibleForTesting |
| public static final int IEI_TYPE_PLMN_LIST = 0; |
| |
| @VisibleForTesting |
| public static final int IEI_CONTENT_LENGTH_MASK = 0x7F; |
| |
| /** |
| * Number of bytes for each PLMN (Public Land Mobile Network). |
| */ |
| @VisibleForTesting |
| public static final int PLMN_DATA_BYTES = 3; |
| |
| /** |
| * The value for comparing the third digit of MNC data with to determine if the MNC is |
| * two or three digits. |
| */ |
| private static final int MNC_2DIGIT_VALUE = 0xF; |
| |
| /** |
| * List of PLMN (Public Land Mobile Network) information. |
| */ |
| private final List<String> mPlmnList; |
| |
| @VisibleForTesting |
| public CellularNetwork(List<String> plmnList) { |
| mPlmnList = plmnList; |
| } |
| |
| /** |
| * Parse a CellularNetwork from the given buffer. |
| * |
| * @param payload The byte buffer to read from |
| * @return {@link CellularNetwork} |
| * @throws ProtocolException |
| * @throws BufferUnderflowException |
| */ |
| public static CellularNetwork parse(ByteBuffer payload) throws ProtocolException { |
| int ieiType = payload.get() & 0xFF; |
| int ieiSize = payload.get() & IEI_CONTENT_LENGTH_MASK; |
| |
| // Skip this IEI if it is an unsupported type. |
| if (ieiType != IEI_TYPE_PLMN_LIST) { |
| Log.e(TAG, "Ignore unsupported IEI Type: " + ieiType); |
| // Advance the buffer position to the next IEI. |
| payload.position(payload.position() + ieiSize); |
| return null; |
| } |
| |
| // Get PLMN count. |
| int plmnCount = payload.get() & 0xFF; |
| |
| // Verify IEI size with PLMN count. The IEI size contained the PLMN count field plus |
| // the bytes for the PLMNs. |
| if (ieiSize != (plmnCount * PLMN_DATA_BYTES + 1)) { |
| throw new ProtocolException("IEI size and PLMN count mismatched: IEI Size=" + ieiSize |
| + " PLMN Count=" + plmnCount); |
| } |
| |
| // Process each PLMN. |
| List<String> plmnList = new ArrayList<>(); |
| while (plmnCount > 0) { |
| plmnList.add(parsePlmn(payload)); |
| plmnCount--; |
| } |
| return new CellularNetwork(plmnList); |
| } |
| |
| public List<String> getPlmns() { |
| return Collections.unmodifiableList(mPlmnList); |
| } |
| |
| @Override |
| public boolean equals(Object thatObject) { |
| if (this == thatObject) { |
| return true; |
| } |
| if (!(thatObject instanceof CellularNetwork)) { |
| return false; |
| } |
| CellularNetwork that = (CellularNetwork) thatObject; |
| return mPlmnList.equals(that.mPlmnList); |
| } |
| |
| @Override |
| public int hashCode() { |
| return mPlmnList.hashCode(); |
| } |
| |
| @Override |
| public String toString() { |
| return "CellularNetwork{mPlmnList=" + mPlmnList + "}"; |
| } |
| |
| /** |
| * Parse the PLMN information from the given buffer. A string representing a hex value |
| * of |MCC|MNC| will be returned. |
| * |
| * PLMN Coding Format: |
| * b7 b0 |
| * | MCC Digit 2 | MCC Digit 1 | |
| * | MNC Digit 3 | MCC Digit 3 | |
| * | MNC Digit 2 | MNC Digit 1 | |
| * |
| * @param payload The buffer to read from. |
| * @return {@Link String} |
| * @throws BufferUnderflowException |
| */ |
| private static String parsePlmn(ByteBuffer payload) { |
| byte[] plmn = new byte[PLMN_DATA_BYTES]; |
| payload.get(plmn); |
| |
| // Formatted as | MCC Digit 1 | MCC Digit 2 | MCC Digit 3 | |
| int mcc = ((plmn[0] << 8) & 0xF00) | (plmn[0] & 0x0F0) | (plmn[1] & 0x00F); |
| |
| // Formated as |MNC Digit 1 | MNC Digit 2 | |
| int mnc = ((plmn[2] << 4) & 0xF0) | ((plmn[2] >> 4) & 0x0F); |
| |
| // The digit 3 of MNC decides if the MNC is 2 or 3 digits number. When it is equal to |
| // 0xF, MNC is a 2 digit value. Otherwise, it is a 3 digit number. |
| int mncDigit3 = (plmn[1] >> 4) & 0x0F; |
| return (mncDigit3 != MNC_2DIGIT_VALUE) |
| ? String.format("%03x%03x", mcc, (mnc << 4) | mncDigit3) |
| : String.format("%03x%02x", mcc, mnc); |
| } |
| } |