diff options
29 files changed, 2862 insertions, 332 deletions
diff --git a/Android.mk b/Android.mk index 9192e966837e..716477806bd1 100644 --- a/Android.mk +++ b/Android.mk @@ -493,7 +493,7 @@ aidl_files := \ frameworks/base/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceInfo.aidl \ frameworks/base/wifi/java/android/net/wifi/WpsInfo.aidl \ frameworks/base/wifi/java/android/net/wifi/ScanResult.aidl \ - frameworks/base/wifi/java/android/net/wifi/ScanInfo.aidl \ + frameworks/base/wifi/java/android/net/wifi/PasspointManagementObjectDefinition.aidl \ frameworks/base/wifi/java/android/net/wifi/WifiEnterpriseConfig.aidl \ frameworks/base/wifi/java/android/net/wifi/WifiConfiguration.aidl \ frameworks/base/wifi/java/android/net/wifi/WifiInfo.aidl \ diff --git a/api/current.txt b/api/current.txt index 369127083d2f..f7608b00651e 100644 --- a/api/current.txt +++ b/api/current.txt @@ -19078,22 +19078,6 @@ package android.net.sip { package android.net.wifi { - public class ScanInfo implements android.os.Parcelable { - ctor public ScanInfo(android.net.wifi.ScanResult); - ctor public ScanInfo(long, int, java.lang.String, java.lang.String, java.lang.String, java.lang.String, byte[], int); - method public int describeContents(); - method public long getBssid(); - method public byte[] getIconData(); - method public java.lang.String getIconType(); - method public java.lang.String getName(); - method public int getOsuIdentity(); - method public int getRssi(); - method public android.net.wifi.ScanResult getScanResult(); - method public java.lang.String getServiceDescription(); - method public java.lang.String getSsid(); - method public void writeToParcel(android.os.Parcel, int); - } - public class ScanResult implements android.os.Parcelable { method public int describeContents(); method public boolean is80211mcResponder(); @@ -19254,6 +19238,7 @@ package android.net.wifi { field public static final int SIM = 4; // 0x4 field public static final int TLS = 1; // 0x1 field public static final int TTLS = 2; // 0x2 + field public static final int UNAUTH_TLS = 7; // 0x7 } public static final class WifiEnterpriseConfig.Phase2 { @@ -19296,7 +19281,6 @@ package android.net.wifi { method public java.util.List<android.net.wifi.WifiConfiguration> getConfiguredNetworks(); method public android.net.wifi.WifiInfo getConnectionInfo(); method public android.net.DhcpInfo getDhcpInfo(); - method public java.util.List<android.net.wifi.ScanInfo> getScanInfos(); method public java.util.List<android.net.wifi.ScanResult> getScanResults(); method public int getWifiState(); method public boolean is5GHzBandSupported(); @@ -19312,7 +19296,6 @@ package android.net.wifi { method public boolean reconnect(); method public boolean removeNetwork(int); method public boolean saveConfiguration(); - method public void setOsuSelection(int); method public void setTdlsEnabled(java.net.InetAddress, boolean); method public void setTdlsEnabledWithMacAddress(java.lang.String, boolean); method public boolean setWifiEnabled(boolean); diff --git a/api/system-current.txt b/api/system-current.txt index b09a4a2afcfd..ca92c45ebd9e 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -20666,12 +20666,18 @@ package android.net.wifi { } public class RttManager { + method public void disableResponder(android.net.wifi.RttManager.ResponderCallback); + method public void enableResponder(android.net.wifi.RttManager.ResponderCallback); method public deprecated android.net.wifi.RttManager.Capabilities getCapabilities(); method public android.net.wifi.RttManager.RttCapabilities getRttCapabilities(); method public void startRanging(android.net.wifi.RttManager.RttParams[], android.net.wifi.RttManager.RttListener); method public void stopRanging(android.net.wifi.RttManager.RttListener); field public static final int BASE = 160256; // 0x27200 field public static final int CMD_OP_ABORTED = 160260; // 0x27204 + field public static final int CMD_OP_DISABLE_RESPONDER = 160262; // 0x27206 + field public static final int CMD_OP_ENABLE_RESPONDER = 160261; // 0x27205 + field public static final int CMD_OP_ENALBE_RESPONDER_FAILED = 160264; // 0x27208 + field public static final int CMD_OP_ENALBE_RESPONDER_SUCCEEDED = 160263; // 0x27207 field public static final int CMD_OP_FAILED = 160258; // 0x27202 field public static final int CMD_OP_START_RANGING = 160256; // 0x27200 field public static final int CMD_OP_STOP_RANGING = 160257; // 0x27201 @@ -20680,6 +20686,7 @@ package android.net.wifi { field public static final int PREAMBLE_HT = 2; // 0x2 field public static final int PREAMBLE_LEGACY = 1; // 0x1 field public static final int PREAMBLE_VHT = 4; // 0x4 + field public static final int REASON_INITIATOR_NOT_ALLOWED_WHEN_RESPONDER_ON = -6; // 0xfffffffa field public static final int REASON_INVALID_LISTENER = -3; // 0xfffffffd field public static final int REASON_INVALID_REQUEST = -4; // 0xfffffffc field public static final int REASON_NOT_AVAILABLE = -2; // 0xfffffffe @@ -20747,6 +20754,25 @@ package android.net.wifi { field public android.net.wifi.RttManager.RttResult[] mResults; } + public static abstract class RttManager.ResponderCallback { + ctor public RttManager.ResponderCallback(); + method public abstract void onResponderEnableFailure(int); + method public abstract void onResponderEnabled(android.net.wifi.RttManager.ResponderConfig); + } + + public static class RttManager.ResponderConfig implements android.os.Parcelable { + ctor public RttManager.ResponderConfig(); + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.net.wifi.RttManager.ResponderConfig> CREATOR; + field public int centerFreq0; + field public int centerFreq1; + field public int channelWidth; + field public int frequency; + field public java.lang.String macAddress; + field public int preamble; + } + public static class RttManager.RttCapabilities implements android.os.Parcelable { ctor public RttManager.RttCapabilities(); method public int describeContents(); @@ -20756,6 +20782,7 @@ package android.net.wifi { field public boolean lcrSupported; field public boolean oneSidedRttSupported; field public int preambleSupported; + field public boolean responderSupported; field public deprecated boolean supportedPeerType; field public deprecated boolean supportedType; field public boolean twoSided11McRttSupported; @@ -20832,22 +20859,6 @@ package android.net.wifi { field public byte id; } - public class ScanInfo implements android.os.Parcelable { - ctor public ScanInfo(android.net.wifi.ScanResult); - ctor public ScanInfo(long, int, java.lang.String, java.lang.String, java.lang.String, java.lang.String, byte[], int); - method public int describeContents(); - method public long getBssid(); - method public byte[] getIconData(); - method public java.lang.String getIconType(); - method public java.lang.String getName(); - method public int getOsuIdentity(); - method public int getRssi(); - method public android.net.wifi.ScanResult getScanResult(); - method public java.lang.String getServiceDescription(); - method public java.lang.String getSsid(); - method public void writeToParcel(android.os.Parcel, int); - } - public class ScanResult implements android.os.Parcelable { method public int describeContents(); method public boolean is80211mcResponder(); @@ -21030,6 +21041,7 @@ package android.net.wifi { field public static final int SIM = 4; // 0x4 field public static final int TLS = 1; // 0x1 field public static final int TTLS = 2; // 0x2 + field public static final int UNAUTH_TLS = 7; // 0x7 } public static final class WifiEnterpriseConfig.Phase2 { @@ -21075,7 +21087,6 @@ package android.net.wifi { method public android.net.wifi.WifiConnectionStatistics getConnectionStatistics(); method public android.net.DhcpInfo getDhcpInfo(); method public java.util.List<android.net.wifi.WifiConfiguration> getPrivilegedConfiguredNetworks(); - method public java.util.List<android.net.wifi.ScanInfo> getScanInfos(); method public java.util.List<android.net.wifi.ScanResult> getScanResults(); method public int getWifiState(); method public boolean is5GHzBandSupported(); @@ -21095,7 +21106,6 @@ package android.net.wifi { method public boolean reconnect(); method public boolean removeNetwork(int); method public boolean saveConfiguration(); - method public void setOsuSelection(int); method public void setTdlsEnabled(java.net.InetAddress, boolean); method public void setTdlsEnabledWithMacAddress(java.lang.String, boolean); method public boolean setWifiEnabled(boolean); diff --git a/services/net/java/android/net/apf/ApfGenerator.java b/services/net/java/android/net/apf/ApfGenerator.java new file mode 100644 index 000000000000..96c2ba535dd1 --- /dev/null +++ b/services/net/java/android/net/apf/ApfGenerator.java @@ -0,0 +1,883 @@ +/* + * 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 android.net.apf; + +import java.util.ArrayList; +import java.util.HashMap; + +/** + * APF assembler/generator. A tool for generating an APF program. + * + * Call add*() functions to add instructions to the program, then call + * {@link generate} to get the APF bytecode for the program. + * + * @hide + */ +public class ApfGenerator { + /** + * This exception is thrown when an attempt is made to generate an illegal instruction. + */ + public static class IllegalInstructionException extends Exception { + IllegalInstructionException(String msg) { + super(msg); + } + } + private enum Opcodes { + LABEL(-1), + LDB(1), // Load 1 byte from immediate offset, e.g. "ldb R0, [5]" + LDH(2), // Load 2 bytes from immediate offset, e.g. "ldh R0, [5]" + LDW(3), // Load 4 bytes from immediate offset, e.g. "ldw R0, [5]" + LDBX(4), // Load 1 byte from immediate offset plus register, e.g. "ldbx R0, [5]R0" + LDHX(5), // Load 2 byte from immediate offset plus register, e.g. "ldhx R0, [5]R0" + LDWX(6), // Load 4 byte from immediate offset plus register, e.g. "ldwx R0, [5]R0" + ADD(7), // Add, e.g. "add R0,5" + MUL(8), // Multiply, e.g. "mul R0,5" + DIV(9), // Divide, e.g. "div R0,5" + AND(10), // And, e.g. "and R0,5" + OR(11), // Or, e.g. "or R0,5" + SH(12), // Left shift, e.g, "sh R0, 5" or "sh R0, -5" (shifts right) + LI(13), // Load immediate, e.g. "li R0,5" (immediate encoded as signed value) + JMP(14), // Jump, e.g. "jmp label" + JEQ(15), // Compare equal and branch, e.g. "jeq R0,5,label" + JNE(16), // Compare not equal and branch, e.g. "jne R0,5,label" + JGT(17), // Compare greater than and branch, e.g. "jgt R0,5,label" + JLT(18), // Compare less than and branch, e.g. "jlt R0,5,label" + JSET(19), // Compare any bits set and branch, e.g. "jset R0,5,label" + JNEBS(20), // Compare not equal byte sequence, e.g. "jnebs R0,5,label,0x1122334455" + EXT(21); // Followed by immediate indicating ExtendedOpcodes. + + final int value; + + private Opcodes(int value) { + this.value = value; + } + } + // Extended opcodes. Primary opcode is Opcodes.EXT. ExtendedOpcodes are encoded in the immediate + // field. + private enum ExtendedOpcodes { + LDM(0), // Load from memory, e.g. "ldm R0,5" + STM(16), // Store to memory, e.g. "stm R0,5" + NOT(32), // Not, e.g. "not R0" + NEG(33), // Negate, e.g. "neg R0" + SWAP(34), // Swap, e.g. "swap R0,R1" + MOVE(35); // Move, e.g. "move R0,R1" + + final int value; + + private ExtendedOpcodes(int value) { + this.value = value; + } + } + public enum Register { + R0(0), + R1(1); + + final int value; + + private Register(int value) { + this.value = value; + } + } + private class Instruction { + private final byte mOpcode; // A "Opcode" value. + private final byte mRegister; // A "Register" value. + private boolean mHasImm; + private byte mImmSize; + private boolean mImmSigned; + private int mImm; + // When mOpcode is a jump: + private byte mTargetLabelSize; + private String mTargetLabel; + // When mOpcode == Opcodes.LABEL: + private String mLabel; + // When mOpcode == Opcodes.JNEBS: + private byte[] mCompareBytes; + // Offset in bytes from the begining of this program. Set by {@link ApfGenerator#generate}. + int offset; + + Instruction(Opcodes opcode, Register register) { + mOpcode = (byte)opcode.value; + mRegister = (byte)register.value; + } + + Instruction(Opcodes opcode) { + this(opcode, Register.R0); + } + + void setImm(int imm, boolean signed) { + mHasImm = true; + mImm = imm; + mImmSigned = signed; + mImmSize = calculateImmSize(imm, signed); + } + + void setUnsignedImm(int imm) { + setImm(imm, false); + } + + void setSignedImm(int imm) { + setImm(imm, true); + } + + void setLabel(String label) throws IllegalInstructionException { + if (mLabels.containsKey(label)) { + throw new IllegalInstructionException("duplicate label " + label); + } + if (mOpcode != Opcodes.LABEL.value) { + throw new IllegalStateException("adding label to non-label instruction"); + } + mLabel = label; + mLabels.put(label, this); + } + + void setTargetLabel(String label) { + mTargetLabel = label; + mTargetLabelSize = 4; // May shrink later on in generate(). + } + + void setCompareBytes(byte[] bytes) { + if (mOpcode != Opcodes.JNEBS.value) { + throw new IllegalStateException("adding compare bytes to non-JNEBS instruction"); + } + mCompareBytes = bytes; + } + + /** + * @return size of instruction in bytes. + */ + int size() { + if (mOpcode == Opcodes.LABEL.value) { + return 0; + } + int size = 1; + if (mHasImm) { + size += generatedImmSize(); + } + if (mTargetLabel != null) { + size += generatedImmSize(); + } + if (mCompareBytes != null) { + size += mCompareBytes.length; + } + return size; + } + + /** + * Resize immediate value field so that it's only as big as required to + * contain the offset of the jump destination. + * @return {@code true} if shrunk. + */ + boolean shrink() throws IllegalInstructionException { + if (mTargetLabel == null) { + return false; + } + int oldSize = size(); + int oldTargetLabelSize = mTargetLabelSize; + mTargetLabelSize = calculateImmSize(calculateTargetLabelOffset(), false); + if (mTargetLabelSize > oldTargetLabelSize) { + throw new IllegalStateException("instruction grew"); + } + return size() < oldSize; + } + + /** + * Assemble value for instruction size field. + */ + private byte generateImmSizeField() { + byte immSize = generatedImmSize(); + // Encode size field to fit in 2 bits: 0->0, 1->1, 2->2, 3->4. + return immSize == 4 ? 3 : immSize; + } + + /** + * Assemble first byte of generated instruction. + */ + private byte generateInstructionByte() { + byte sizeField = generateImmSizeField(); + return (byte)((mOpcode << 3) | (sizeField << 1) | mRegister); + } + + /** + * Write {@code value} at offset {@code writingOffset} into {@code bytecode}. + * {@link generatedImmSize} bytes are written. {@code value} is truncated to + * {@code generatedImmSize} bytes. {@code value} is treated simply as a + * 32-bit value, so unsigned values should be zero extended and the truncation + * should simply throw away their zero-ed upper bits, and signed values should + * be sign extended and the truncation should simply throw away their signed + * upper bits. + */ + private int writeValue(int value, byte[] bytecode, int writingOffset) { + for (int i = generatedImmSize() - 1; i >= 0; i--) { + bytecode[writingOffset++] = (byte)((value >> (i * 8)) & 255); + } + return writingOffset; + } + + /** + * Generate bytecode for this instruction at offset {@link offset}. + */ + void generate(byte[] bytecode) throws IllegalInstructionException { + if (mOpcode == Opcodes.LABEL.value) { + return; + } + int writingOffset = offset; + bytecode[writingOffset++] = generateInstructionByte(); + if (mTargetLabel != null) { + writingOffset = writeValue(calculateTargetLabelOffset(), bytecode, writingOffset); + } + if (mHasImm) { + writingOffset = writeValue(mImm, bytecode, writingOffset); + } + if (mCompareBytes != null) { + System.arraycopy(mCompareBytes, 0, bytecode, writingOffset, mCompareBytes.length); + writingOffset += mCompareBytes.length; + } + if ((writingOffset - offset) != size()) { + throw new IllegalStateException("wrote " + (writingOffset - offset) + + " but should have written " + size()); + } + } + + /** + * Calculate the size of either the immediate field or the target label field, if either is + * present. Most instructions have either an immediate or a target label field, but for the + * instructions that have both, the size of the target label field must be the same as the + * size of the immediate field, because there is only one length field in the instruction + * byte, hence why this function simply takes the maximum of the two sizes, so neither is + * truncated. + */ + private byte generatedImmSize() { + return mImmSize > mTargetLabelSize ? mImmSize : mTargetLabelSize; + } + + private int calculateTargetLabelOffset() throws IllegalInstructionException { + Instruction targetLabelInstruction; + if (mTargetLabel == DROP_LABEL) { + targetLabelInstruction = mDropLabel; + } else if (mTargetLabel == PASS_LABEL) { + targetLabelInstruction = mPassLabel; + } else { + targetLabelInstruction = mLabels.get(mTargetLabel); + } + if (targetLabelInstruction == null) { + throw new IllegalInstructionException("label not found: " + mTargetLabel); + } + // Calculate distance from end of this instruction to instruction.offset. + final int targetLabelOffset = targetLabelInstruction.offset - (offset + size()); + if (targetLabelOffset < 0) { + throw new IllegalInstructionException("backward branches disallowed; label: " + + mTargetLabel); + } + return targetLabelOffset; + } + + private byte calculateImmSize(int imm, boolean signed) { + if (imm == 0) { + return 0; + } + if (signed && (imm >= -128 && imm <= 127) || + !signed && (imm >= 0 && imm <= 255)) { + return 1; + } + if (signed && (imm >= -32768 && imm <= 32767) || + !signed && (imm >= 0 && imm <= 65535)) { + return 2; + } + return 4; + } + } + + /** + * Jump to this label to terminate the program and indicate the packet + * should be dropped. + */ + public static final String DROP_LABEL = "__DROP__"; + + /** + * Jump to this label to terminate the program and indicate the packet + * should be passed to the AP. + */ + public static final String PASS_LABEL = "__PASS__"; + + /** + * Number of memory slots available for access via APF stores to memory and loads from memory. + * The memory slots are numbered 0 to {@code MEMORY_SLOTS} - 1. This must be kept in sync with + * the APF interpreter. + */ + public static final int MEMORY_SLOTS = 16; + + /** + * Memory slot number that is prefilled with the IPv4 header length. + * Note that this memory slot may be overwritten by a program that + * executes stores to this memory slot. This must be kept in sync with + * the APF interpreter. + */ + public static final int IPV4_HEADER_SIZE_MEMORY_SLOT = 13; + + /** + * Memory slot number that is prefilled with the size of the packet being filtered in bytes. + * Note that this memory slot may be overwritten by a program that + * executes stores to this memory slot. This must be kept in sync with the APF interpreter. + */ + public static final int PACKET_SIZE_MEMORY_SLOT = 14; + + /** + * Memory slot number that is prefilled with the age of the filter in seconds. The age of the + * filter is the time since the filter was installed until now. + * Note that this memory slot may be overwritten by a program that + * executes stores to this memory slot. This must be kept in sync with the APF interpreter. + */ + public static final int FILTER_AGE_MEMORY_SLOT = 15; + + /** + * First memory slot containing prefilled values. Can be used in range comparisons to determine + * if memory slot index is within prefilled slots. + */ + public static final int FIRST_PREFILLED_MEMORY_SLOT = IPV4_HEADER_SIZE_MEMORY_SLOT; + + /** + * Last memory slot containing prefilled values. Can be used in range comparisons to determine + * if memory slot index is within prefilled slots. + */ + public static final int LAST_PREFILLED_MEMORY_SLOT = FILTER_AGE_MEMORY_SLOT; + + private final ArrayList<Instruction> mInstructions = new ArrayList<Instruction>(); + private final HashMap<String, Instruction> mLabels = new HashMap<String, Instruction>(); + private final Instruction mDropLabel = new Instruction(Opcodes.LABEL); + private final Instruction mPassLabel = new Instruction(Opcodes.LABEL); + private boolean mGenerated; + + /** + * Set version of APF instruction set to generate instructions for. Returns {@code true} + * if generating for this version is supported, {@code false} otherwise. + */ + public boolean setApfVersion(int version) { + // This version number syncs up with APF_VERSION in hardware/google/apf/apf_interpreter.h + return version == 2; + } + + private void addInstruction(Instruction instruction) { + if (mGenerated) { + throw new IllegalStateException("Program already generated"); + } + mInstructions.add(instruction); + } + + /** + * Define a label at the current end of the program. Jumps can jump to this label. Labels are + * their own separate instructions, though with size 0. This facilitates having labels with + * no corresponding code to execute, for example a label at the end of a program. For example + * an {@link ApfGenerator} might be passed to a function that adds a filter like so: + * <pre> + * load from packet + * compare loaded data, jump if not equal to "next_filter" + * load from packet + * compare loaded data, jump if not equal to "next_filter" + * jump to drop label + * define "next_filter" here + * </pre> + * In this case "next_filter" may not have any generated code associated with it. + */ + public ApfGenerator defineLabel(String name) throws IllegalInstructionException { + Instruction instruction = new Instruction(Opcodes.LABEL); + instruction.setLabel(name); + addInstruction(instruction); + return this; + } + + /** + * Add an unconditional jump instruction to the end of the program. + */ + public ApfGenerator addJump(String target) { + Instruction instruction = new Instruction(Opcodes.JMP); + instruction.setTargetLabel(target); + addInstruction(instruction); + return this; + } + + /** + * Add an instruction to the end of the program to load the byte at offset {@code offset} + * bytes from the begining of the packet into {@code register}. + */ + public ApfGenerator addLoad8(Register register, int offset) { + Instruction instruction = new Instruction(Opcodes.LDB, register); + instruction.setUnsignedImm(offset); + addInstruction(instruction); + return this; + } + + /** + * Add an instruction to the end of the program to load 16-bits at offset {@code offset} + * bytes from the begining of the packet into {@code register}. + */ + public ApfGenerator addLoad16(Register register, int offset) { + Instruction instruction = new Instruction(Opcodes.LDH, register); + instruction.setUnsignedImm(offset); + addInstruction(instruction); + return this; + } + + /** + * Add an instruction to the end of the program to load 32-bits at offset {@code offset} + * bytes from the begining of the packet into {@code register}. + */ + public ApfGenerator addLoad32(Register register, int offset) { + Instruction instruction = new Instruction(Opcodes.LDW, register); + instruction.setUnsignedImm(offset); + addInstruction(instruction); + return this; + } + + /** + * Add an instruction to the end of the program to load a byte from the packet into + * {@code register}. The offset of the loaded byte from the begining of the packet is + * the sum of {@code offset} and the value in register R1. + */ + public ApfGenerator addLoad8Indexed(Register register, int offset) { + Instruction instruction = new Instruction(Opcodes.LDBX, register); + instruction.setUnsignedImm(offset); + addInstruction(instruction); + return this; + } + + /** + * Add an instruction to the end of the program to load 16-bits from the packet into + * {@code register}. The offset of the loaded 16-bits from the begining of the packet is + * the sum of {@code offset} and the value in register R1. + */ + public ApfGenerator addLoad16Indexed(Register register, int offset) { + Instruction instruction = new Instruction(Opcodes.LDHX, register); + instruction.setUnsignedImm(offset); + addInstruction(instruction); + return this; + } + + /** + * Add an instruction to the end of the program to load 32-bits from the packet into + * {@code register}. The offset of the loaded 32-bits from the begining of the packet is + * the sum of {@code offset} and the value in register R1. + */ + public ApfGenerator addLoad32Indexed(Register register, int offset) { + Instruction instruction = new Instruction(Opcodes.LDWX, register); + instruction.setUnsignedImm(offset); + addInstruction(instruction); + return this; + } + + /** + * Add an instruction to the end of the program to add {@code value} to register R0. + */ + public ApfGenerator addAdd(int value) { + Instruction instruction = new Instruction(Opcodes.ADD); + instruction.setSignedImm(value); + addInstruction(instruction); + return this; + } + + /** + * Add an instruction to the end of the program to multiply register R0 by {@code value}. + */ + public ApfGenerator addMul(int value) { + Instruction instruction = new Instruction(Opcodes.MUL); + instruction.setSignedImm(value); + addInstruction(instruction); + return this; + } + + /** + * Add an instruction to the end of the program to divide register R0 by {@code value}. + */ + public ApfGenerator addDiv(int value) { + Instruction instruction = new Instruction(Opcodes.DIV); + instruction.setSignedImm(value); + addInstruction(instruction); + return this; + } + + /** + * Add an instruction to the end of the program to logically and register R0 with {@code value}. + */ + public ApfGenerator addAnd(int value) { + Instruction instruction = new Instruction(Opcodes.AND); + instruction.setUnsignedImm(value); + addInstruction(instruction); + return this; + } + + /** + * Add an instruction to the end of the program to logically or register R0 with {@code value}. + */ + public ApfGenerator addOr(int value) { + Instruction instruction = new Instruction(Opcodes.OR); + instruction.setUnsignedImm(value); + addInstruction(instruction); + return this; + } + + /** + * Add an instruction to the end of the program to shift left register R0 by {@code value} bits. + */ + public ApfGenerator addLeftShift(int value) { + Instruction instruction = new Instruction(Opcodes.SH); + instruction.setSignedImm(value); + addInstruction(instruction); + return this; + } + + /** + * Add an instruction to the end of the program to shift right register R0 by {@code value} + * bits. + */ + public ApfGenerator addRightShift(int value) { + Instruction instruction = new Instruction(Opcodes.SH); + instruction.setSignedImm(-value); + addInstruction(instruction); + return this; + } + + /** + * Add an instruction to the end of the program to add register R1 to register R0. + */ + public ApfGenerator addAddR1() { + Instruction instruction = new Instruction(Opcodes.ADD, Register.R1); + addInstruction(instruction); + return this; + } + + /** + * Add an instruction to the end of the program to multiply register R0 by register R1. + */ + public ApfGenerator addMulR1() { + Instruction instruction = new Instruction(Opcodes.MUL, Register.R1); + addInstruction(instruction); + return this; + } + + /** + * Add an instruction to the end of the program to divide register R0 by register R1. + */ + public ApfGenerator addDivR1() { + Instruction instruction = new Instruction(Opcodes.DIV, Register.R1); + addInstruction(instruction); + return this; + } + + /** + * Add an instruction to the end of the program to logically and register R0 with register R1 + * and store the result back into register R0. + */ + public ApfGenerator addAndR1() { + Instruction instruction = new Instruction(Opcodes.AND, Register.R1); + addInstruction(instruction); + return this; + } + + /** + * Add an instruction to the end of the program to logically or register R0 with register R1 + * and store the result back into register R0. + */ + public ApfGenerator addOrR1() { + Instruction instruction = new Instruction(Opcodes.OR, Register.R1); + addInstruction(instruction); + return this; + } + + /** + * Add an instruction to the end of the program to shift register R0 left by the value in + * register R1. + */ + public ApfGenerator addLeftShiftR1() { + Instruction instruction = new Instruction(Opcodes.SH, Register.R1); + addInstruction(instruction); + return this; + } + + /** + * Add an instruction to the end of the program to move {@code value} into {@code register}. + */ + public ApfGenerator addLoadImmediate(Register register, int value) { + Instruction instruction = new Instruction(Opcodes.LI, register); + instruction.setSignedImm(value); + addInstruction(instruction); + return this; + } + + /** + * Add an instruction to the end of the program to jump to {@code target} if register R0's + * value equals {@code value}. + */ + public ApfGenerator addJumpIfR0Equals(int value, String target) { + Instruction instruction = new Instruction(Opcodes.JEQ); + instruction.setUnsignedImm(value); + instruction.setTargetLabel(target); + addInstruction(instruction); + return this; + } + + /** + * Add an instruction to the end of the program to jump to {@code target} if register R0's + * value does not equal {@code value}. + */ + public ApfGenerator addJumpIfR0NotEquals(int value, String target) { + Instruction instruction = new Instruction(Opcodes.JNE); + instruction.setUnsignedImm(value); + instruction.setTargetLabel(target); + addInstruction(instruction); + return this; + } + + /** + * Add an instruction to the end of the program to jump to {@code target} if register R0's + * value is greater than {@code value}. + */ + public ApfGenerator addJumpIfR0GreaterThan(int value, String target) { + Instruction instruction = new Instruction(Opcodes.JGT); + instruction.setUnsignedImm(value); + instruction.setTargetLabel(target); + addInstruction(instruction); + return this; + } + + /** + * Add an instruction to the end of the program to jump to {@code target} if register R0's + * value is less than {@code value}. + */ + public ApfGenerator addJumpIfR0LessThan(int value, String target) { + Instruction instruction = new Instruction(Opcodes.JLT); + instruction.setUnsignedImm(value); + instruction.setTargetLabel(target); + addInstruction(instruction); + return this; + } + + /** + * Add an instruction to the end of the program to jump to {@code target} if register R0's + * value has any bits set that are also set in {@code value}. + */ + public ApfGenerator addJumpIfR0AnyBitsSet(int value, String target) { + Instruction instruction = new Instruction(Opcodes.JSET); + instruction.setUnsignedImm(value); + instruction.setTargetLabel(target); + addInstruction(instruction); + return this; + } + /** + * Add an instruction to the end of the program to jump to {@code target} if register R0's + * value equals register R1's value. + */ + public ApfGenerator addJumpIfR0EqualsR1(String target) { + Instruction instruction = new Instruction(Opcodes.JEQ, Register.R1); + instruction.setTargetLabel(target); + addInstruction(instruction); + return this; + } + + /** + * Add an instruction to the end of the program to jump to {@code target} if register R0's + * value does not equal register R1's value. + */ + public ApfGenerator addJumpIfR0NotEqualsR1(String target) { + Instruction instruction = new Instruction(Opcodes.JNE, Register.R1); + instruction.setTargetLabel(target); + addInstruction(instruction); + return this; + } + + /** + * Add an instruction to the end of the program to jump to {@code target} if register R0's + * value is greater than register R1's value. + */ + public ApfGenerator addJumpIfR0GreaterThanR1(String target) { + Instruction instruction = new Instruction(Opcodes.JGT, Register.R1); + instruction.setTargetLabel(target); + addInstruction(instruction); + return this; + } + + /** + * Add an instruction to the end of the program to jump to {@code target} if register R0's + * value is less than register R1's value. + */ + public ApfGenerator addJumpIfR0LessThanR1(String target) { + Instruction instruction = new Instruction(Opcodes.JLT, Register.R1); + instruction.setTargetLabel(target); + addInstruction(instruction); + return this; + } + + /** + * Add an instruction to the end of the program to jump to {@code target} if register R0's + * value has any bits set that are also set in R1's value. + */ + public ApfGenerator addJumpIfR0AnyBitsSetR1(String target) { + Instruction instruction = new Instruction(Opcodes.JSET, Register.R1); + instruction.setTargetLabel(target); + addInstruction(instruction); + return this; + } + + /** + * Add an instruction to the end of the program to jump to {@code target} if the bytes of the + * packet at, an offset specified by {@code register}, match {@code bytes}. + */ + public ApfGenerator addJumpIfBytesNotEqual(Register register, byte[] bytes, String target) { + Instruction instruction = new Instruction(Opcodes.JNEBS, register); + instruction.setUnsignedImm(bytes.length); + instruction.setTargetLabel(target); + instruction.setCompareBytes(bytes); + addInstruction(instruction); + return this; + } + + /** + * Add an instruction to the end of the program to load memory slot {@code slot} into + * {@code register}. + */ + public ApfGenerator addLoadFromMemory(Register register, int slot) + throws IllegalInstructionException { + if (slot < 0 || slot > (MEMORY_SLOTS - 1)) { + throw new IllegalInstructionException("illegal memory slot number: " + slot); + } + Instruction instruction = new Instruction(Opcodes.EXT, register); + instruction.setUnsignedImm(ExtendedOpcodes.LDM.value + slot); + addInstruction(instruction); + return this; + } + + /** + * Add an instruction to the end of the program to store {@code register} into memory slot + * {@code slot}. + */ + public ApfGenerator addStoreToMemory(Register register, int slot) + throws IllegalInstructionException { + if (slot < 0 || slot > (MEMORY_SLOTS - 1)) { + throw new IllegalInstructionException("illegal memory slot number: " + slot); + } + Instruction instruction = new Instruction(Opcodes.EXT, register); + instruction.setUnsignedImm(ExtendedOpcodes.STM.value + slot); + addInstruction(instruction); + return this; + } + + /** + * Add an instruction to the end of the program to logically not {@code register}. + */ + public ApfGenerator addNot(Register register) { + Instruction instruction = new Instruction(Opcodes.EXT, register); + instruction.setUnsignedImm(ExtendedOpcodes.NOT.value); + addInstruction(instruction); + return this; + } + + /** + * Add an instruction to the end of the program to negate {@code register}. + */ + public ApfGenerator addNeg(Register register) { + Instruction instruction = new Instruction(Opcodes.EXT, register); + instruction.setUnsignedImm(ExtendedOpcodes.NEG.value); + addInstruction(instruction); + return this; + } + + /** + * Add an instruction to swap the values in register R0 and register R1. + */ + public ApfGenerator addSwap() { + Instruction instruction = new Instruction(Opcodes.EXT); + instruction.setUnsignedImm(ExtendedOpcodes.SWAP.value); + addInstruction(instruction); + return this; + } + + /** + * Add an instruction to the end of the program to move the value into + * {@code register} from the other register. + */ + public ApfGenerator addMove(Register register) { + Instruction instruction = new Instruction(Opcodes.EXT, register); + instruction.setUnsignedImm(ExtendedOpcodes.MOVE.value); + addInstruction(instruction); + return this; + } + + /** + * Updates instruction offset fields using latest instruction sizes. + * @return current program length in bytes. + */ + private int updateInstructionOffsets() { + int offset = 0; + for (Instruction instruction : mInstructions) { + instruction.offset = offset; + offset += instruction.size(); + } + return offset; + } + + /** + * Returns an overestimate of the size of the generated program. {@link #generate} may return + * a program that is smaller. + */ + public int programLengthOverEstimate() { + return updateInstructionOffsets(); + } + + /** + * Generate the bytecode for the APF program. + * @return the bytecode. + * @throws IllegalStateException if a label is referenced but not defined. + */ + public byte[] generate() throws IllegalInstructionException { + // Enforce that we can only generate once because we cannot unshrink instructions and + // PASS/DROP labels may move further away requiring unshrinking if we add further + // instructions. + if (mGenerated) { + throw new IllegalStateException("Can only generate() once!"); + } + mGenerated = true; + int total_size; + boolean shrunk; + // Shrink the immediate value fields of instructions. + // As we shrink the instructions some branch offset + // fields may shrink also, thereby shrinking the + // instructions further. Loop until we've reached the + // minimum size. Rarely will this loop more than a few times. + // Limit iterations to avoid O(n^2) behavior. + int iterations_remaining = 10; + do { + total_size = updateInstructionOffsets(); + // Update drop and pass label offsets. + mDropLabel.offset = total_size + 1; + mPassLabel.offset = total_size; + // Limit run-time in aberant circumstances. + if (iterations_remaining-- == 0) break; + // Attempt to shrink instructions. + shrunk = false; + for (Instruction instruction : mInstructions) { + if (instruction.shrink()) { + shrunk = true; + } + } + } while (shrunk); + // Generate bytecode for instructions. + byte[] bytecode = new byte[total_size]; + for (Instruction instruction : mInstructions) { + instruction.generate(bytecode); + } + return bytecode; + } +} + diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java index 62c635db8578..06b6ee75da7a 100644 --- a/services/net/java/android/net/ip/IpManager.java +++ b/services/net/java/android/net/ip/IpManager.java @@ -17,16 +17,21 @@ package android.net.ip; import android.content.Context; +import android.net.BaseDhcpStateMachine; import android.net.DhcpResults; +import android.net.DhcpStateMachine; import android.net.InterfaceConfiguration; +import android.net.LinkAddress; import android.net.LinkProperties; import android.net.LinkProperties.ProvisioningChange; import android.net.RouteInfo; import android.net.StaticIpConfiguration; +import android.net.dhcp.DhcpClient; import android.os.INetworkManagementService; import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; +import android.provider.Settings; import android.util.Log; import com.android.internal.annotations.GuardedBy; @@ -38,6 +43,7 @@ import com.android.server.net.NetlinkTracker; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; +import java.util.Objects; /** @@ -85,6 +91,10 @@ public class IpManager extends StateMachine { * as IpManager invokes them. */ + // Implementations must call IpManager#completedPreDhcpAction(). + public void onPreDhcpAction() {} + public void onPostDhcpAction() {} + // TODO: Kill with fire once DHCP and static configuration are moved // out of WifiStateMachine. public void onIPv4ProvisioningSuccess(DhcpResults dhcpResults) {} @@ -104,7 +114,7 @@ public class IpManager extends StateMachine { private static final int CMD_STOP = 1; private static final int CMD_START = 2; private static final int CMD_CONFIRM = 3; - private static final int CMD_UPDATE_DHCPV4_RESULTS = 4; + private static final int EVENT_PRE_DHCP_ACTION_COMPLETE = 4; // Sent by NetlinkTracker to communicate netlink events. private static final int EVENT_NETLINK_LINKPROPERTIES_CHANGED = 5; @@ -127,6 +137,7 @@ public class IpManager extends StateMachine { * Non-final member variables accessed only from within our StateMachine. */ private IpReachabilityMonitor mIpReachabilityMonitor; + private BaseDhcpStateMachine mDhcpStateMachine; private DhcpResults mDhcpResults; private StaticIpConfiguration mStaticIpConfig; @@ -210,17 +221,16 @@ public class IpManager extends StateMachine { sendMessage(CMD_CONFIRM); } + public void completedPreDhcpAction() { + sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE); + } + public LinkProperties getLinkProperties() { synchronized (mLock) { return new LinkProperties(mLinkProperties); } } - // TODO: Kill with fire once DHCPv4/static config is moved into IpManager. - public void updateWithDhcpResults(DhcpResults dhcpResults) { - sendMessage(CMD_UPDATE_DHCPV4_RESULTS, dhcpResults); - } - /** * Internals. @@ -275,6 +285,12 @@ public class IpManager extends StateMachine { return delta; } + private boolean linkPropertiesUnchanged(LinkProperties newLp) { + synchronized (mLock) { + return Objects.equals(newLp, mLinkProperties); + } + } + private LinkProperties assembleLinkProperties() { // [1] Create a new LinkProperties object to populate. LinkProperties newLp = new LinkProperties(); @@ -323,6 +339,29 @@ public class IpManager extends StateMachine { return newLp; } + private void clearIPv4Address() { + try { + final InterfaceConfiguration ifcg = new InterfaceConfiguration(); + ifcg.setLinkAddress(new LinkAddress("0.0.0.0/0")); + mNwService.setInterfaceConfig(mInterfaceName, ifcg); + } catch (RemoteException e) { + Log.e(TAG, "ALERT: Failed to clear IPv4 address on interface " + mInterfaceName, e); + } + } + + private void handleIPv4Success(DhcpResults dhcpResults) { + mDhcpResults = new DhcpResults(dhcpResults); + setLinkProperties(assembleLinkProperties()); + mCallback.onIPv4ProvisioningSuccess(dhcpResults); + } + + private void handleIPv4Failure() { + clearIPv4Address(); + mDhcpResults = null; + setLinkProperties(assembleLinkProperties()); + mCallback.onIPv4ProvisioningFailure(); + } + class StoppedState extends State { @Override public void enter() { @@ -351,6 +390,16 @@ public class IpManager extends StateMachine { setLinkProperties(assembleLinkProperties()); break; + case DhcpStateMachine.CMD_ON_QUIT: + // CMD_ON_QUIT is really more like "EVENT_ON_QUIT". + // Shutting down DHCPv4 progresses simultaneously with + // transitioning to StoppedState, so we can receive this + // message after we've already transitioned here. + // + // TODO: Figure out if this is actually useful and if not + // expunge it. + break; + default: return NOT_HANDLED; } @@ -365,6 +414,7 @@ public class IpManager extends StateMachine { try { mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true); mNwService.enableIpv6(mInterfaceName); + // TODO: Perhaps clearIPv4Address() as well. } catch (RemoteException re) { Log.e(TAG, "Unable to change interface settings: " + re); } catch (IllegalStateException ie) { @@ -387,10 +437,15 @@ public class IpManager extends StateMachine { // handle the result accordingly. if (mStaticIpConfig != null) { if (applyStaticIpConfig()) { - sendMessage(CMD_UPDATE_DHCPV4_RESULTS, new DhcpResults(mStaticIpConfig)); + handleIPv4Success(new DhcpResults(mStaticIpConfig)); } else { - sendMessage(CMD_UPDATE_DHCPV4_RESULTS); + handleIPv4Failure(); } + } else { + // Start DHCPv4. + makeDhcpStateMachine(); + mDhcpStateMachine.registerForPreDhcpNotification(); + mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP); } } @@ -399,6 +454,12 @@ public class IpManager extends StateMachine { mIpReachabilityMonitor.stop(); mIpReachabilityMonitor = null; + if (mDhcpStateMachine != null) { + mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP); + mDhcpStateMachine.doQuit(); + mDhcpStateMachine = null; + } + resetLinkProperties(); } @@ -410,33 +471,34 @@ public class IpManager extends StateMachine { break; case CMD_START: - // TODO: Defer this message to be delivered after a state transition - // to StoppedState. That way, receiving CMD_START in StartedState - // effects a restart. - Log.e(TAG, "ALERT: START received in StartedState."); + Log.e(TAG, "ALERT: START received in StartedState. Please fix caller."); break; case CMD_CONFIRM: + // TODO: Possibly introduce a second type of confirmation + // that both probes (a) on-link neighbors and (b) does + // a DHCPv4 RENEW. We used to do this on Wi-Fi framework + // roams. if (mCallback.usingIpReachabilityMonitor()) { mIpReachabilityMonitor.probeAll(); } break; - case CMD_UPDATE_DHCPV4_RESULTS: - final DhcpResults dhcpResults = (DhcpResults) msg.obj; - if (dhcpResults != null) { - mDhcpResults = new DhcpResults(dhcpResults); - setLinkProperties(assembleLinkProperties()); - mCallback.onIPv4ProvisioningSuccess(dhcpResults); - } else { - mDhcpResults = null; - setLinkProperties(assembleLinkProperties()); - mCallback.onIPv4ProvisioningFailure(); + case EVENT_PRE_DHCP_ACTION_COMPLETE: + // It's possible to reach here if, for example, someone + // calls completedPreDhcpAction() after provisioning with + // a static IP configuration. + if (mDhcpStateMachine != null) { + mDhcpStateMachine.sendMessage( + DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE); } break; - case EVENT_NETLINK_LINKPROPERTIES_CHANGED: + case EVENT_NETLINK_LINKPROPERTIES_CHANGED: { final LinkProperties newLp = assembleLinkProperties(); + if (linkPropertiesUnchanged(newLp)) { + break; + } final ProvisioningChange delta = setLinkProperties(newLp); // NOTE: The only receiver of these callbacks currently @@ -456,7 +518,39 @@ public class IpManager extends StateMachine { mCallback.onLinkPropertiesChange(newLp); break; } + break; + } + case DhcpStateMachine.CMD_PRE_DHCP_ACTION: + mCallback.onPreDhcpAction(); + break; + + case DhcpStateMachine.CMD_POST_DHCP_ACTION: { + // Note that onPostDhcpAction() is likely to be + // asynchronous, and thus there is no guarantee that we + // will be able to observe any of its effects here. + mCallback.onPostDhcpAction(); + + final DhcpResults dhcpResults = (DhcpResults) msg.obj; + switch (msg.arg1) { + case DhcpStateMachine.DHCP_SUCCESS: + handleIPv4Success(dhcpResults); + break; + case DhcpStateMachine.DHCP_FAILURE: + handleIPv4Failure(); + break; + default: + Log.e(TAG, "Unknown CMD_POST_DHCP_ACTION status:" + msg.arg1); + } + break; + } + + case DhcpStateMachine.CMD_ON_QUIT: + // CMD_ON_QUIT is really more like "EVENT_ON_QUIT". + // Regardless, we ignore it. + // + // TODO: Figure out if this is actually useful and if not + // expunge it. break; default: @@ -479,5 +573,23 @@ public class IpManager extends StateMachine { return true; } + + private void makeDhcpStateMachine() { + final boolean usingLegacyDhcp = (Settings.Global.getInt( + mContext.getContentResolver(), + Settings.Global.LEGACY_DHCP_CLIENT, 0) == 1); + + if (usingLegacyDhcp) { + mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine( + mContext, + IpManager.this, + mInterfaceName); + } else { + mDhcpStateMachine = DhcpClient.makeDhcpStateMachine( + mContext, + IpManager.this, + mInterfaceName); + } + } } } diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk index 33979b11d7a7..1ef7656f4f6d 100644 --- a/services/tests/servicestests/Android.mk +++ b/services/tests/servicestests/Android.mk @@ -1,3 +1,7 @@ +######################################################################### +# Build FrameworksServicesTests package +######################################################################### + LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) @@ -21,5 +25,39 @@ LOCAL_PACKAGE_NAME := FrameworksServicesTests LOCAL_CERTIFICATE := platform +LOCAL_JNI_SHARED_LIBRARIES := \ + libapfjni \ + libc++ \ + libnativehelper + include $(BUILD_PACKAGE) +######################################################################### +# Build JNI Shared Library +######################################################################### + +LOCAL_PATH:= $(LOCAL_PATH)/jni + +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests + +LOCAL_CFLAGS := -Wall -Werror + +LOCAL_C_INCLUDES := \ + libpcap \ + hardware/google/apf + +LOCAL_SRC_FILES := apf_jni.cpp + +LOCAL_SHARED_LIBRARIES := \ + libnativehelper \ + liblog + +LOCAL_STATIC_LIBRARIES := \ + libpcap \ + libapf + +LOCAL_MODULE := libapfjni + +include $(BUILD_SHARED_LIBRARY) diff --git a/services/tests/servicestests/jni/apf_jni.cpp b/services/tests/servicestests/jni/apf_jni.cpp new file mode 100644 index 000000000000..7d142eb76b95 --- /dev/null +++ b/services/tests/servicestests/jni/apf_jni.cpp @@ -0,0 +1,182 @@ +/* + * Copyright 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. + */ + +#include <JNIHelp.h> +#include <ScopedUtfChars.h> +#include <jni.h> +#include <pcap.h> +#include <stdlib.h> +#include <string> +#include <utils/Log.h> + +#include "apf_interpreter.h" + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +// JNI function acting as simply call-through to native APF interpreter. +static jint com_android_server_ApfTest_apfSimulate( + JNIEnv* env, jclass, jbyteArray program, jbyteArray packet, jint filter_age) { + return accept_packet( + (uint8_t*)env->GetByteArrayElements(program, NULL), + env->GetArrayLength(program), + (uint8_t*)env->GetByteArrayElements(packet, NULL), + env->GetArrayLength(packet), + filter_age); +} + +class ScopedPcap { + public: + ScopedPcap(pcap_t* pcap) : pcap_ptr(pcap) {} + ~ScopedPcap() { + pcap_close(pcap_ptr); + } + + pcap_t* get() const { return pcap_ptr; }; + private: + pcap_t* const pcap_ptr; +}; + +class ScopedFILE { + public: + ScopedFILE(FILE* fp) : file(fp) {} + ~ScopedFILE() { + fclose(file); + } + + FILE* get() const { return file; }; + private: + FILE* const file; +}; + +static void throwException(JNIEnv* env, const std::string& error) { + jclass newExcCls = env->FindClass("java/lang/IllegalStateException"); + if (newExcCls == 0) { + abort(); + return; + } + env->ThrowNew(newExcCls, error.c_str()); +} + +static jstring com_android_server_ApfTest_compileToBpf(JNIEnv* env, jclass, jstring jfilter) { + ScopedUtfChars filter(env, jfilter); + std::string bpf_string; + ScopedPcap pcap(pcap_open_dead(DLT_EN10MB, 65535)); + if (pcap.get() == NULL) { + throwException(env, "pcap_open_dead failed"); + return NULL; + } + + // Compile "filter" to a BPF program + bpf_program bpf; + if (pcap_compile(pcap.get(), &bpf, filter.c_str(), 0, PCAP_NETMASK_UNKNOWN)) { + throwException(env, "pcap_compile failed"); + return NULL; + } + + // Translate BPF program to human-readable format + const struct bpf_insn* insn = bpf.bf_insns; + for (uint32_t i = 0; i < bpf.bf_len; i++) { + bpf_string += bpf_image(insn++, i); + bpf_string += "\n"; + } + + return env->NewStringUTF(bpf_string.c_str()); +} + +static jboolean com_android_server_ApfTest_compareBpfApf(JNIEnv* env, jclass, jstring jfilter, + jstring jpcap_filename, jbyteArray japf_program) { + ScopedUtfChars filter(env, jfilter); + ScopedUtfChars pcap_filename(env, jpcap_filename); + const uint8_t* apf_program = (uint8_t*)env->GetByteArrayElements(japf_program, NULL); + const uint32_t apf_program_len = env->GetArrayLength(japf_program); + + // Open pcap file for BPF filtering + ScopedFILE bpf_fp(fopen(pcap_filename.c_str(), "rb")); + char pcap_error[PCAP_ERRBUF_SIZE]; + ScopedPcap bpf_pcap(pcap_fopen_offline(bpf_fp.get(), pcap_error)); + if (bpf_pcap.get() == NULL) { + throwException(env, "pcap_fopen_offline failed: " + std::string(pcap_error)); + return false; + } + + // Open pcap file for APF filtering + ScopedFILE apf_fp(fopen(pcap_filename.c_str(), "rb")); + ScopedPcap apf_pcap(pcap_fopen_offline(apf_fp.get(), pcap_error)); + if (apf_pcap.get() == NULL) { + throwException(env, "pcap_fopen_offline failed: " + std::string(pcap_error)); + return false; + } + + // Compile "filter" to a BPF program + bpf_program bpf; + if (pcap_compile(bpf_pcap.get(), &bpf, filter.c_str(), 0, PCAP_NETMASK_UNKNOWN)) { + throwException(env, "pcap_compile failed"); + return false; + } + + // Install BPF filter on bpf_pcap + if (pcap_setfilter(bpf_pcap.get(), &bpf)) { + throwException(env, "pcap_setfilter failed"); + return false; + } + + while (1) { + pcap_pkthdr bpf_header, apf_header; + // Run BPF filter to the next matching packet. + const uint8_t* bpf_packet = pcap_next(bpf_pcap.get(), &bpf_header); + + // Run APF filter to the next matching packet. + const uint8_t* apf_packet; + do { + apf_packet = pcap_next(apf_pcap.get(), &apf_header); + } while (apf_packet != NULL && !accept_packet( + apf_program, apf_program_len, apf_packet, apf_header.len, 0)); + + // Make sure both filters matched the same packet. + if (apf_packet == NULL && bpf_packet == NULL) + break; + if (apf_packet == NULL || bpf_packet == NULL) + return false; + if (apf_header.len != bpf_header.len || + apf_header.ts.tv_sec != bpf_header.ts.tv_sec || + apf_header.ts.tv_usec != bpf_header.ts.tv_usec || + memcmp(apf_packet, bpf_packet, apf_header.len)) + return false; + } + return true; +} + +extern "C" jint JNI_OnLoad(JavaVM* vm, void*) { + JNIEnv *env; + if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { + ALOGE("ERROR: GetEnv failed"); + return -1; + } + + static JNINativeMethod gMethods[] = { + { "apfSimulate", "([B[BI)I", + (void*)com_android_server_ApfTest_apfSimulate }, + { "compileToBpf", "(Ljava/lang/String;)Ljava/lang/String;", + (void*)com_android_server_ApfTest_compileToBpf }, + { "compareBpfApf", "(Ljava/lang/String;Ljava/lang/String;[B)Z", + (void*)com_android_server_ApfTest_compareBpfApf }, + }; + + jniRegisterNativeMethods(env, "com/android/server/ApfTest", + gMethods, ARRAY_SIZE(gMethods)); + + return JNI_VERSION_1_6; +} diff --git a/services/tests/servicestests/res/raw/apf.pcap b/services/tests/servicestests/res/raw/apf.pcap Binary files differnew file mode 100644 index 000000000000..963165f19f73 --- /dev/null +++ b/services/tests/servicestests/res/raw/apf.pcap diff --git a/services/tests/servicestests/src/com/android/server/ApfTest.java b/services/tests/servicestests/src/com/android/server/ApfTest.java new file mode 100644 index 000000000000..640a6c927ed3 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/ApfTest.java @@ -0,0 +1,560 @@ +/* + * Copyright (C) 2012 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; + +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.LargeTest; + +import com.android.frameworks.servicestests.R; +import android.net.apf.ApfGenerator; +import android.net.apf.ApfGenerator.IllegalInstructionException; +import android.net.apf.ApfGenerator.Register; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; + +import libcore.io.IoUtils; +import libcore.io.Streams; + +/** + * Tests for APF program generator and interpreter. + * + * Build, install and run with: + * runtest frameworks-services -c com.android.server.ApfTest + */ +public class ApfTest extends AndroidTestCase { + @Override + public void setUp() throws Exception { + super.setUp(); + // Load up native shared library containing APF interpreter exposed via JNI. + System.loadLibrary("apfjni"); + } + + // Expected return codes from APF interpreter. + private final static int PASS = 1; + private final static int DROP = 0; + // Interpreter will just accept packets without link layer headers, so pad fake packet to at + // least the minimum packet size. + private final static int MIN_PKT_SIZE = 15; + + private void assertVerdict(int expected, byte[] program, byte[] packet, int filterAge) { + assertEquals(expected, apfSimulate(program, packet, filterAge)); + } + + private void assertPass(byte[] program, byte[] packet, int filterAge) { + assertVerdict(PASS, program, packet, filterAge); + } + + private void assertDrop(byte[] program, byte[] packet, int filterAge) { + assertVerdict(DROP, program, packet, filterAge); + } + + private void assertVerdict(int expected, ApfGenerator gen, byte[] packet, int filterAge) + throws IllegalInstructionException { + assertEquals(expected, apfSimulate(gen.generate(), packet, filterAge)); + } + + private void assertPass(ApfGenerator gen, byte[] packet, int filterAge) + throws IllegalInstructionException { + assertVerdict(PASS, gen, packet, filterAge); + } + + private void assertDrop(ApfGenerator gen, byte[] packet, int filterAge) + throws IllegalInstructionException { + assertVerdict(DROP, gen, packet, filterAge); + } + + private void assertPass(ApfGenerator gen) + throws IllegalInstructionException { + assertVerdict(PASS, gen, new byte[MIN_PKT_SIZE], 0); + } + + private void assertDrop(ApfGenerator gen) + throws IllegalInstructionException { + assertVerdict(DROP, gen, new byte[MIN_PKT_SIZE], 0); + } + + /** + * Test each instruction by generating a program containing the instruction, + * generating bytecode for that program and running it through the + * interpreter to verify it functions correctly. + */ + @LargeTest + public void testApfInstructions() throws IllegalInstructionException { + // Empty program should pass because having the program counter reach the + // location immediately after the program indicates the packet should be + // passed to the AP. + ApfGenerator gen = new ApfGenerator(); + assertPass(gen); + + // Test jumping to pass label. + gen = new ApfGenerator(); + gen.addJump(gen.PASS_LABEL); + byte[] program = gen.generate(); + assertEquals(1, program.length); + assertEquals((14 << 3) | (0 << 1) | 0, program[0]); + assertPass(program, new byte[MIN_PKT_SIZE], 0); + + // Test jumping to drop label. + gen = new ApfGenerator(); + gen.addJump(gen.DROP_LABEL); + program = gen.generate(); + assertEquals(2, program.length); + assertEquals((14 << 3) | (1 << 1) | 0, program[0]); + assertEquals(1, program[1]); + assertDrop(program, new byte[15], 15); + + // Test jumping if equal to 0. + gen = new ApfGenerator(); + gen.addJumpIfR0Equals(0, gen.DROP_LABEL); + assertDrop(gen); + + // Test jumping if not equal to 0. + gen = new ApfGenerator(); + gen.addJumpIfR0NotEquals(0, gen.DROP_LABEL); + assertPass(gen); + gen = new ApfGenerator(); + gen.addLoadImmediate(Register.R0, 1); + gen.addJumpIfR0NotEquals(0, gen.DROP_LABEL); + assertDrop(gen); + + // Test jumping if registers equal. + gen = new ApfGenerator(); + gen.addJumpIfR0EqualsR1(gen.DROP_LABEL); + assertDrop(gen); + + // Test jumping if registers not equal. + gen = new ApfGenerator(); + gen.addJumpIfR0NotEqualsR1(gen.DROP_LABEL); + assertPass(gen); + gen = new ApfGenerator(); + gen.addLoadImmediate(Register.R0, 1); + gen.addJumpIfR0NotEqualsR1(gen.DROP_LABEL); + assertDrop(gen); + + // Test load immediate. + gen = new ApfGenerator(); + gen.addLoadImmediate(Register.R0, 1234567890); + gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL); + assertDrop(gen); + + // Test add. + gen = new ApfGenerator(); + gen.addAdd(1234567890); + gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL); + assertDrop(gen); + + // Test subtract. + gen = new ApfGenerator(); + gen.addAdd(-1234567890); + gen.addJumpIfR0Equals(-1234567890, gen.DROP_LABEL); + assertDrop(gen); + + // Test or. + gen = new ApfGenerator(); + gen.addOr(1234567890); + gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL); + assertDrop(gen); + + // Test and. + gen = new ApfGenerator(); + gen.addLoadImmediate(Register.R0, 1234567890); + gen.addAnd(123456789); + gen.addJumpIfR0Equals(1234567890 & 123456789, gen.DROP_LABEL); + assertDrop(gen); + + // Test left shift. + gen = new ApfGenerator(); + gen.addLoadImmediate(Register.R0, 1234567890); + gen.addLeftShift(1); + gen.addJumpIfR0Equals(1234567890 << 1, gen.DROP_LABEL); + assertDrop(gen); + + // Test right shift. + gen = new ApfGenerator(); + gen.addLoadImmediate(Register.R0, 1234567890); + gen.addRightShift(1); + gen.addJumpIfR0Equals(1234567890 >> 1, gen.DROP_LABEL); + assertDrop(gen); + + // Test multiply. + gen = new ApfGenerator(); + gen.addLoadImmediate(Register.R0, 1234567890); + gen.addMul(2); + gen.addJumpIfR0Equals(1234567890 * 2, gen.DROP_LABEL); + assertDrop(gen); + + // Test divide. + gen = new ApfGenerator(); + gen.addLoadImmediate(Register.R0, 1234567890); + gen.addDiv(2); + gen.addJumpIfR0Equals(1234567890 / 2, gen.DROP_LABEL); + assertDrop(gen); + + // Test divide by zero. + gen = new ApfGenerator(); + gen.addDiv(0); + gen.addJump(gen.DROP_LABEL); + assertPass(gen); + + // Test add. + gen = new ApfGenerator(); + gen.addLoadImmediate(Register.R1, 1234567890); + gen.addAddR1(); + gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL); + assertDrop(gen); + + // Test subtract. + gen = new ApfGenerator(); + gen.addLoadImmediate(Register.R1, -1234567890); + gen.addAddR1(); + gen.addJumpIfR0Equals(-1234567890, gen.DROP_LABEL); + assertDrop(gen); + + // Test or. + gen = new ApfGenerator(); + gen.addLoadImmediate(Register.R1, 1234567890); + gen.addOrR1(); + gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL); + assertDrop(gen); + + // Test and. + gen = new ApfGenerator(); + gen.addLoadImmediate(Register.R0, 1234567890); + gen.addLoadImmediate(Register.R1, 123456789); + gen.addAndR1(); + gen.addJumpIfR0Equals(1234567890 & 123456789, gen.DROP_LABEL); + assertDrop(gen); + + // Test left shift. + gen = new ApfGenerator(); + gen.addLoadImmediate(Register.R0, 1234567890); + gen.addLoadImmediate(Register.R1, 1); + gen.addLeftShiftR1(); + gen.addJumpIfR0Equals(1234567890 << 1, gen.DROP_LABEL); + assertDrop(gen); + + // Test right shift. + gen = new ApfGenerator(); + gen.addLoadImmediate(Register.R0, 1234567890); + gen.addLoadImmediate(Register.R1, -1); + gen.addLeftShiftR1(); + gen.addJumpIfR0Equals(1234567890 >> 1, gen.DROP_LABEL); + assertDrop(gen); + + // Test multiply. + gen = new ApfGenerator(); + gen.addLoadImmediate(Register.R0, 1234567890); + gen.addLoadImmediate(Register.R1, 2); + gen.addMulR1(); + gen.addJumpIfR0Equals(1234567890 * 2, gen.DROP_LABEL); + assertDrop(gen); + + // Test divide. + gen = new ApfGenerator(); + gen.addLoadImmediate(Register.R0, 1234567890); + gen.addLoadImmediate(Register.R1, 2); + gen.addDivR1(); + gen.addJumpIfR0Equals(1234567890 / 2, gen.DROP_LABEL); + assertDrop(gen); + + // Test divide by zero. + gen = new ApfGenerator(); + gen.addDivR1(); + gen.addJump(gen.DROP_LABEL); + assertPass(gen); + + // Test byte load. + gen = new ApfGenerator(); + gen.addLoad8(Register.R0, 1); + gen.addJumpIfR0Equals(45, gen.DROP_LABEL); + assertDrop(gen, new byte[]{123,45,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0); + + // Test out of bounds load. + gen = new ApfGenerator(); + gen.addLoad8(Register.R0, 16); + gen.addJumpIfR0Equals(0, gen.DROP_LABEL); + assertPass(gen, new byte[]{123,45,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0); + + // Test half-word load. + gen = new ApfGenerator(); + gen.addLoad16(Register.R0, 1); + gen.addJumpIfR0Equals((45 << 8) | 67, gen.DROP_LABEL); + assertDrop(gen, new byte[]{123,45,67,0,0,0,0,0,0,0,0,0,0,0,0}, 0); + + // Test word load. + gen = new ApfGenerator(); + gen.addLoad32(Register.R0, 1); + gen.addJumpIfR0Equals((45 << 24) | (67 << 16) | (89 << 8) | 12, gen.DROP_LABEL); + assertDrop(gen, new byte[]{123,45,67,89,12,0,0,0,0,0,0,0,0,0,0}, 0); + + // Test byte indexed load. + gen = new ApfGenerator(); + gen.addLoadImmediate(Register.R1, 1); + gen.addLoad8Indexed(Register.R0, 0); + gen.addJumpIfR0Equals(45, gen.DROP_LABEL); + assertDrop(gen, new byte[]{123,45,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0); + + // Test out of bounds indexed load. + gen = new ApfGenerator(); + gen.addLoadImmediate(Register.R1, 8); + gen.addLoad8Indexed(Register.R0, 8); + gen.addJumpIfR0Equals(0, gen.DROP_LABEL); + assertPass(gen, new byte[]{123,45,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0); + + // Test half-word indexed load. + gen = new ApfGenerator(); + gen.addLoadImmediate(Register.R1, 1); + gen.addLoad16Indexed(Register.R0, 0); + gen.addJumpIfR0Equals((45 << 8) | 67, gen.DROP_LABEL); + assertDrop(gen, new byte[]{123,45,67,0,0,0,0,0,0,0,0,0,0,0,0}, 0); + + // Test word indexed load. + gen = new ApfGenerator(); + gen.addLoadImmediate(Register.R1, 1); + gen.addLoad32Indexed(Register.R0, 0); + gen.addJumpIfR0Equals((45 << 24) | (67 << 16) | (89 << 8) | 12, gen.DROP_LABEL); + assertDrop(gen, new byte[]{123,45,67,89,12,0,0,0,0,0,0,0,0,0,0}, 0); + + // Test jumping if greater than. + gen = new ApfGenerator(); + gen.addJumpIfR0GreaterThan(0, gen.DROP_LABEL); + assertPass(gen); + gen = new ApfGenerator(); + gen.addLoadImmediate(Register.R0, 1); + gen.addJumpIfR0GreaterThan(0, gen.DROP_LABEL); + assertDrop(gen); + + // Test jumping if less than. + gen = new ApfGenerator(); + gen.addJumpIfR0LessThan(0, gen.DROP_LABEL); + assertPass(gen); + gen = new ApfGenerator(); + gen.addJumpIfR0LessThan(1, gen.DROP_LABEL); + assertDrop(gen); + + // Test jumping if any bits set. + gen = new ApfGenerator(); + gen.addJumpIfR0AnyBitsSet(3, gen.DROP_LABEL); + assertPass(gen); + gen = new ApfGenerator(); + gen.addLoadImmediate(Register.R0, 1); + gen.addJumpIfR0AnyBitsSet(3, gen.DROP_LABEL); + assertDrop(gen); + gen = new ApfGenerator(); + gen.addLoadImmediate(Register.R0, 3); + gen.addJumpIfR0AnyBitsSet(3, gen.DROP_LABEL); + assertDrop(gen); + + // Test jumping if register greater than. + gen = new ApfGenerator(); + gen.addJumpIfR0GreaterThanR1(gen.DROP_LABEL); + assertPass(gen); + gen = new ApfGenerator(); + gen.addLoadImmediate(Register.R0, 2); + gen.addLoadImmediate(Register.R1, 1); + gen.addJumpIfR0GreaterThanR1(gen.DROP_LABEL); + assertDrop(gen); + + // Test jumping if register less than. + gen = new ApfGenerator(); + gen.addJumpIfR0LessThanR1(gen.DROP_LABEL); + assertPass(gen); + gen = new ApfGenerator(); + gen.addLoadImmediate(Register.R1, 1); + gen.addJumpIfR0LessThanR1(gen.DROP_LABEL); + assertDrop(gen); + + // Test jumping if any bits set in register. + gen = new ApfGenerator(); + gen.addLoadImmediate(Register.R1, 3); + gen.addJumpIfR0AnyBitsSetR1(gen.DROP_LABEL); + assertPass(gen); + gen = new ApfGenerator(); + gen.addLoadImmediate(Register.R1, 3); + gen.addLoadImmediate(Register.R0, 1); + gen.addJumpIfR0AnyBitsSetR1(gen.DROP_LABEL); + assertDrop(gen); + gen = new ApfGenerator(); + gen.addLoadImmediate(Register.R1, 3); + gen.addLoadImmediate(Register.R0, 3); + gen.addJumpIfR0AnyBitsSetR1(gen.DROP_LABEL); + assertDrop(gen); + + // Test load from memory. + gen = new ApfGenerator(); + gen.addLoadFromMemory(Register.R0, 0); + gen.addJumpIfR0Equals(0, gen.DROP_LABEL); + assertDrop(gen); + + // Test store to memory. + gen = new ApfGenerator(); + gen.addLoadImmediate(Register.R1, 1234567890); + gen.addStoreToMemory(Register.R1, 12); + gen.addLoadFromMemory(Register.R0, 12); + gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL); + assertDrop(gen); + + // Test filter age pre-filled memory. + gen = new ApfGenerator(); + gen.addLoadFromMemory(Register.R0, gen.FILTER_AGE_MEMORY_SLOT); + gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL); + assertDrop(gen, new byte[MIN_PKT_SIZE], 1234567890); + + // Test packet size pre-filled memory. + gen = new ApfGenerator(); + gen.addLoadFromMemory(Register.R0, gen.PACKET_SIZE_MEMORY_SLOT); + gen.addJumpIfR0Equals(MIN_PKT_SIZE, gen.DROP_LABEL); + assertDrop(gen); + + // Test IPv4 header size pre-filled memory. + gen = new ApfGenerator(); + gen.addLoadFromMemory(Register.R0, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); + gen.addJumpIfR0Equals(20, gen.DROP_LABEL); + assertDrop(gen, new byte[]{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0x45}, 0); + + // Test not. + gen = new ApfGenerator(); + gen.addLoadImmediate(Register.R0, 1234567890); + gen.addNot(Register.R0); + gen.addJumpIfR0Equals(~1234567890, gen.DROP_LABEL); + assertDrop(gen); + + // Test negate. + gen = new ApfGenerator(); + gen.addLoadImmediate(Register.R0, 1234567890); + gen.addNeg(Register.R0); + gen.addJumpIfR0Equals(-1234567890, gen.DROP_LABEL); + assertDrop(gen); + + // Test move. + gen = new ApfGenerator(); + gen.addLoadImmediate(Register.R1, 1234567890); + gen.addMove(Register.R0); + gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL); + assertDrop(gen); + gen = new ApfGenerator(); + gen.addLoadImmediate(Register.R0, 1234567890); + gen.addMove(Register.R1); + gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL); + assertDrop(gen); + + // Test swap. + gen = new ApfGenerator(); + gen.addLoadImmediate(Register.R1, 1234567890); + gen.addSwap(); + gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL); + assertDrop(gen); + gen = new ApfGenerator(); + gen.addLoadImmediate(Register.R0, 1234567890); + gen.addSwap(); + gen.addJumpIfR0Equals(0, gen.DROP_LABEL); + assertDrop(gen); + + // Test jump if bytes not equal. + gen = new ApfGenerator(); + gen.addLoadImmediate(Register.R0, 1); + gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{123}, gen.DROP_LABEL); + program = gen.generate(); + assertEquals(6, program.length); + assertEquals((13 << 3) | (1 << 1) | 0, program[0]); + assertEquals(1, program[1]); + assertEquals(((20 << 3) | (1 << 1) | 0) - 256, program[2]); + assertEquals(1, program[3]); + assertEquals(1, program[4]); + assertEquals(123, program[5]); + assertDrop(program, new byte[MIN_PKT_SIZE], 0); + gen = new ApfGenerator(); + gen.addLoadImmediate(Register.R0, 1); + gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{123}, gen.DROP_LABEL); + byte[] packet123 = new byte[]{0,123,0,0,0,0,0,0,0,0,0,0,0,0,0}; + assertPass(gen, packet123, 0); + gen = new ApfGenerator(); + gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{123}, gen.DROP_LABEL); + assertDrop(gen, packet123, 0); + gen = new ApfGenerator(); + gen.addLoadImmediate(Register.R0, 1); + gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{1,2,30,4,5}, gen.DROP_LABEL); + byte[] packet12345 = new byte[]{0,1,2,3,4,5,0,0,0,0,0,0,0,0,0}; + assertDrop(gen, packet12345, 0); + gen = new ApfGenerator(); + gen.addLoadImmediate(Register.R0, 1); + gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{1,2,3,4,5}, gen.DROP_LABEL); + assertPass(gen, packet12345, 0); + } + + /** + * Generate some BPF programs, translate them to APF, then run APF and BPF programs + * over packet traces and verify both programs filter out the same packets. + */ + @LargeTest + public void testApfAgainstBpf() throws Exception { + String[] tcpdump_filters = new String[]{ "udp", "tcp", "icmp", "icmp6", "udp port 53", + "arp", "dst 239.255.255.250", "arp or tcp or udp port 53", "net 192.168.1.0/24", + "arp or icmp6 or portrange 53-54", "portrange 53-54 or portrange 100-50000", + "tcp[tcpflags] & (tcp-ack|tcp-fin) != 0 and (ip[2:2] > 57 or icmp)" }; + String pcap_filename = stageFile(R.raw.apf); + for (String tcpdump_filter : tcpdump_filters) { + byte[] apf_program = Bpf2Apf.convert(compileToBpf(tcpdump_filter)); + assertTrue("Failed to match for filter: " + tcpdump_filter, + compareBpfApf(tcpdump_filter, pcap_filename, apf_program)); + } + } + + /** + * Stage a file for testing, i.e. make it native accessible. Given a resource ID, + * copy that resource into the app's data directory and return the path to it. + */ + private String stageFile(int rawId) throws Exception { + File file = new File(getContext().getFilesDir(), "staged_file"); + new File(file.getParent()).mkdirs(); + InputStream in = null; + OutputStream out = null; + try { + in = getContext().getResources().openRawResource(rawId); + out = new FileOutputStream(file); + Streams.copy(in, out); + } finally { + if (in != null) in.close(); + if (out != null) out.close(); + } + return file.getAbsolutePath(); + } + + /** + * Call the APF interpreter the run {@code program} on {@code packet} pretending the + * filter was installed {@code filter_age} seconds ago. + */ + private native static int apfSimulate(byte[] program, byte[] packet, int filter_age); + + /** + * Compile a tcpdump human-readable filter (e.g. "icmp" or "tcp port 54") into a BPF + * prorgam and return a human-readable dump of the BPF program identical to "tcpdump -d". + */ + private native static String compileToBpf(String filter); + + /** + * Open packet capture file {@code pcap_filename} and filter the packets using tcpdump + * human-readable filter (e.g. "icmp" or "tcp port 54") compiled to a BPF program and + * at the same time using APF program {@code apf_program}. Return {@code true} if + * both APF and BPF programs filter out exactly the same packets. + */ + private native static boolean compareBpfApf(String filter, String pcap_filename, + byte[] apf_program); +} diff --git a/services/tests/servicestests/src/com/android/server/Bpf2Apf.java b/services/tests/servicestests/src/com/android/server/Bpf2Apf.java new file mode 100644 index 000000000000..29594a84fc48 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/Bpf2Apf.java @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2015 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; + +import android.net.apf.ApfGenerator; +import android.net.apf.ApfGenerator.IllegalInstructionException; +import android.net.apf.ApfGenerator.Register; + +import java.io.BufferedReader; +import java.io.InputStreamReader; + +/** + * BPF to APF translator. + * + * Note: This is for testing purposes only and is not guaranteed to support + * translation of all BPF programs. + * + * Example usage: + * javac net/java/android/net/apf/ApfGenerator.java \ + * tests/servicestests/src/com/android/server/Bpf2Apf.java + * sudo tcpdump -i em1 -d icmp | java -classpath tests/servicestests/src:net/java \ + * com.android.server.Bpf2Apf + */ +public class Bpf2Apf { + private static int parseImm(String line, String arg) { + if (!arg.startsWith("#0x")) { + throw new IllegalArgumentException("Unhandled instruction: " + line); + } + final long val_long = Long.parseLong(arg.substring(3), 16); + if (val_long < 0 || val_long > Long.parseLong("ffffffff", 16)) { + throw new IllegalArgumentException("Unhandled instruction: " + line); + } + return new Long((val_long << 32) >> 32).intValue(); + } + + /** + * Convert a single line of "tcpdump -d" (human readable BPF program dump) {@code line} into + * APF instruction(s) and append them to {@code gen}. Here's an example line: + * (001) jeq #0x86dd jt 2 jf 7 + */ + private static void convertLine(String line, ApfGenerator gen) + throws IllegalInstructionException { + if (line.indexOf("(") != 0 || line.indexOf(")") != 4 || line.indexOf(" ") != 5) { + throw new IllegalArgumentException("Unhandled instruction: " + line); + } + int label = Integer.parseInt(line.substring(1, 4)); + gen.defineLabel(Integer.toString(label)); + String opcode = line.substring(6, 10).trim(); + String arg = line.substring(15, Math.min(32, line.length())).trim(); + switch (opcode) { + case "ld": + case "ldh": + case "ldb": + case "ldx": + case "ldxb": + case "ldxh": + Register dest = opcode.contains("x") ? Register.R1 : Register.R0; + if (arg.equals("4*([14]&0xf)")) { + if (!opcode.equals("ldxb")) { + throw new IllegalArgumentException("Unhandled instruction: " + line); + } + gen.addLoadFromMemory(dest, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); + break; + } + if (arg.equals("#pktlen")) { + if (!opcode.equals("ld")) { + throw new IllegalArgumentException("Unhandled instruction: " + line); + } + gen.addLoadFromMemory(dest, gen.PACKET_SIZE_MEMORY_SLOT); + break; + } + if (arg.startsWith("#0x")) { + if (!opcode.equals("ld")) { + throw new IllegalArgumentException("Unhandled instruction: " + line); + } + gen.addLoadImmediate(dest, parseImm(line, arg)); + break; + } + if (arg.startsWith("M[")) { + if (!opcode.startsWith("ld")) { + throw new IllegalArgumentException("Unhandled instruction: " + line); + } + int memory_slot = Integer.parseInt(arg.substring(2, arg.length() - 1)); + if (memory_slot < 0 || memory_slot >= gen.MEMORY_SLOTS || + // Disallow use of pre-filled slots as BPF programs might + // wrongfully assume they're initialized to 0. + (memory_slot >= gen.FIRST_PREFILLED_MEMORY_SLOT && + memory_slot <= gen.LAST_PREFILLED_MEMORY_SLOT)) { + throw new IllegalArgumentException("Unhandled instruction: " + line); + } + gen.addLoadFromMemory(dest, memory_slot); + break; + } + if (arg.startsWith("[x + ")) { + int offset = Integer.parseInt(arg.substring(5, arg.length() - 1)); + switch (opcode) { + case "ld": + case "ldx": + gen.addLoad32Indexed(dest, offset); + break; + case "ldh": + case "ldxh": + gen.addLoad16Indexed(dest, offset); + break; + case "ldb": + case "ldxb": + gen.addLoad8Indexed(dest, offset); + break; + } + } else { + int offset = Integer.parseInt(arg.substring(1, arg.length() - 1)); + switch (opcode) { + case "ld": + case "ldx": + gen.addLoad32(dest, offset); + break; + case "ldh": + case "ldxh": + gen.addLoad16(dest, offset); + break; + case "ldb": + case "ldxb": + gen.addLoad8(dest, offset); + break; + } + } + break; + case "st": + case "stx": + Register src = opcode.contains("x") ? Register.R1 : Register.R0; + if (!arg.startsWith("M[")) { + throw new IllegalArgumentException("Unhandled instruction: " + line); + } + int memory_slot = Integer.parseInt(arg.substring(2, arg.length() - 1)); + if (memory_slot < 0 || memory_slot >= gen.MEMORY_SLOTS || + // Disallow overwriting pre-filled slots + (memory_slot >= gen.FIRST_PREFILLED_MEMORY_SLOT && + memory_slot <= gen.LAST_PREFILLED_MEMORY_SLOT)) { + throw new IllegalArgumentException("Unhandled instruction: " + line); + } + gen.addStoreToMemory(src, memory_slot); + break; + case "add": + case "and": + case "or": + case "sub": + if (arg.equals("x")) { + switch(opcode) { + case "add": + gen.addAddR1(); + break; + case "and": + gen.addAndR1(); + break; + case "or": + gen.addOrR1(); + break; + case "sub": + gen.addNeg(Register.R1); + gen.addAddR1(); + gen.addNeg(Register.R1); + break; + } + } else { + int imm = parseImm(line, arg); + switch(opcode) { + case "add": + gen.addAdd(imm); + break; + case "and": + gen.addAnd(imm); + break; + case "or": + gen.addOr(imm); + break; + case "sub": + gen.addAdd(-imm); + break; + } + } + break; + case "jeq": + case "jset": + case "jgt": + case "jge": + int val = 0; + boolean reg_compare; + if (arg.startsWith("x")) { + reg_compare = true; + } else { + reg_compare = false; + val = parseImm(line, arg); + } + int jt_offset = line.indexOf("jt"); + int jf_offset = line.indexOf("jf"); + String true_label = line.substring(jt_offset + 2, jf_offset).trim(); + String false_label = line.substring(jf_offset + 2).trim(); + boolean true_label_is_fallthrough = Integer.parseInt(true_label) == label + 1; + boolean false_label_is_fallthrough = Integer.parseInt(false_label) == label + 1; + if (true_label_is_fallthrough && false_label_is_fallthrough) + break; + switch (opcode) { + case "jeq": + if (!true_label_is_fallthrough) { + if (reg_compare) { + gen.addJumpIfR0EqualsR1(true_label); + } else { + gen.addJumpIfR0Equals(val, true_label); + } + } + if (!false_label_is_fallthrough) { + if (!true_label_is_fallthrough) { + gen.addJump(false_label); + } else if (reg_compare) { + gen.addJumpIfR0NotEqualsR1(false_label); + } else { + gen.addJumpIfR0NotEquals(val, false_label); + } + } + break; + case "jset": + if (reg_compare) { + gen.addJumpIfR0AnyBitsSetR1(true_label); + } else { + gen.addJumpIfR0AnyBitsSet(val, true_label); + } + if (!false_label_is_fallthrough) { + gen.addJump(false_label); + } + break; + case "jgt": + if (!true_label_is_fallthrough || + // We have no less-than-or-equal-to register to register + // comparison instruction, so in this case we'll jump + // around an unconditional jump. + (!false_label_is_fallthrough && reg_compare)) { + if (reg_compare) { + gen.addJumpIfR0GreaterThanR1(true_label); + } else { + gen.addJumpIfR0GreaterThan(val, true_label); + } + } + if (!false_label_is_fallthrough) { + if (!true_label_is_fallthrough || reg_compare) { + gen.addJump(false_label); + } else { + gen.addJumpIfR0LessThan(val + 1, false_label); + } + } + break; + case "jge": + if (!false_label_is_fallthrough || + // We have no greater-than-or-equal-to register to register + // comparison instruction, so in this case we'll jump + // around an unconditional jump. + (!true_label_is_fallthrough && reg_compare)) { + if (reg_compare) { + gen.addJumpIfR0LessThanR1(false_label); + } else { + gen.addJumpIfR0LessThan(val, false_label); + } + } + if (!true_label_is_fallthrough) { + if (!false_label_is_fallthrough || reg_compare) { + gen.addJump(true_label); + } else { + gen.addJumpIfR0GreaterThan(val - 1, true_label); + } + } + break; + } + break; + case "ret": + if (arg.equals("#0")) { + gen.addJump(gen.DROP_LABEL); + } else { + gen.addJump(gen.PASS_LABEL); + } + break; + case "tax": + gen.addMove(Register.R1); + break; + case "txa": + gen.addMove(Register.R0); + break; + default: + throw new IllegalArgumentException("Unhandled instruction: " + line); + } + } + + /** + * Convert the output of "tcpdump -d" (human readable BPF program dump) {@code bpf} into an APF + * program and return it. + */ + public static byte[] convert(String bpf) throws IllegalInstructionException { + ApfGenerator gen = new ApfGenerator(); + for (String line : bpf.split("\\n")) convertLine(line, gen); + return gen.generate(); + } + + /** + * Convert the output of "tcpdump -d" (human readable BPF program dump) piped in stdin into an + * APF program and output it via stdout. + */ + public static void main(String[] args) throws Exception { + BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); + String line = null; + StringBuilder responseData = new StringBuilder(); + ApfGenerator gen = new ApfGenerator(); + while ((line = in.readLine()) != null) convertLine(line, gen); + System.out.write(gen.generate()); + } +} diff --git a/telephony/java/com/android/ims/ImsCallForwardInfo.java b/telephony/java/com/android/ims/ImsCallForwardInfo.java index 3f8fd19a16ce..eeee0fc938bc 100644 --- a/telephony/java/com/android/ims/ImsCallForwardInfo.java +++ b/telephony/java/com/android/ims/ImsCallForwardInfo.java @@ -31,6 +31,8 @@ public class ImsCallForwardInfo implements Parcelable { public int mStatus; // 0x91: International, 0x81: Unknown public int mToA; + // Service class + public int mServiceClass; // Number (it will not include the "sip" or "tel" URI scheme) public String mNumber; // No reply timer for CF @@ -55,13 +57,16 @@ public class ImsCallForwardInfo implements Parcelable { out.writeInt(mToA); out.writeString(mNumber); out.writeInt(mTimeSeconds); + out.writeInt(mServiceClass); } @Override public String toString() { return super.toString() + ", Condition: " + mCondition + ", Status: " + ((mStatus == 0) ? "disabled" : "enabled") - + ", ToA: " + mToA + ", Number=" + mNumber + + ", ToA: " + mToA + + ", Service Class: " + mServiceClass + + ", Number=" + mNumber + ", Time (seconds): " + mTimeSeconds; } @@ -71,6 +76,7 @@ public class ImsCallForwardInfo implements Parcelable { mToA = in.readInt(); mNumber = in.readString(); mTimeSeconds = in.readInt(); + mServiceClass = in.readInt(); } public static final Creator<ImsCallForwardInfo> CREATOR = diff --git a/telephony/java/com/android/ims/ImsReasonInfo.java b/telephony/java/com/android/ims/ImsReasonInfo.java index 2769a2b27968..c909c6dc518e 100644 --- a/telephony/java/com/android/ims/ImsReasonInfo.java +++ b/telephony/java/com/android/ims/ImsReasonInfo.java @@ -84,6 +84,8 @@ public class ImsReasonInfo implements Parcelable { public static final int CODE_LOCAL_CALL_VOLTE_RETRY_REQUIRED = 147; // IMS call is already terminated (in TERMINATED state) public static final int CODE_LOCAL_CALL_TERMINATED = 148; + // Handover not feasible + public static final int CODE_LOCAL_HO_NOT_FEASIBLE = 149; /** * TIMEOUT (IMS -> Telephony) @@ -153,6 +155,9 @@ public class ImsReasonInfo implements Parcelable { public static final int CODE_SIP_USER_REJECTED = 361; // Others public static final int CODE_SIP_GLOBAL_ERROR = 362; + // Emergency failure + public static final int CODE_EMERGENCY_TEMP_FAILURE = 363; + public static final int CODE_EMERGENCY_PERM_FAILURE = 364; /** * MEDIA (IMS -> Telephony) @@ -236,6 +241,14 @@ public class ImsReasonInfo implements Parcelable { public static final int CODE_ANSWERED_ELSEWHERE = 1014; /** + * Supplementary services (HOLD/RESUME) failure error codes. + * Values for Supplemetary services failure - Failed, Cancelled and Re-Invite collision. + */ + public static final int CODE_SUPP_SVC_FAILED = 1201; + public static final int CODE_SUPP_SVC_CANCELLED = 1202; + public static final int CODE_SUPP_SVC_REINVITE_COLLISION = 1203; + + /** * Network string error messages. * mExtraMessage may have these values. */ diff --git a/telephony/java/com/android/ims/ImsStreamMediaProfile.java b/telephony/java/com/android/ims/ImsStreamMediaProfile.java index 5a99212ae6f4..216cef59f301 100644 --- a/telephony/java/com/android/ims/ImsStreamMediaProfile.java +++ b/telephony/java/com/android/ims/ImsStreamMediaProfile.java @@ -51,6 +51,16 @@ public class ImsStreamMediaProfile implements Parcelable { public static final int AUDIO_QUALITY_GSM_EFR = 8; public static final int AUDIO_QUALITY_GSM_FR = 9; public static final int AUDIO_QUALITY_GSM_HR = 10; + public static final int AUDIO_QUALITY_G711U = 11; + public static final int AUDIO_QUALITY_G723 = 12; + public static final int AUDIO_QUALITY_G711A = 13; + public static final int AUDIO_QUALITY_G722 = 14; + public static final int AUDIO_QUALITY_G711AB = 15; + public static final int AUDIO_QUALITY_G729 = 16; + public static final int AUDIO_QUALITY_EVS_NB = 17; + public static final int AUDIO_QUALITY_EVS_WB = 18; + public static final int AUDIO_QUALITY_EVS_SWB = 19; + public static final int AUDIO_QUALITY_EVS_FB = 20; /** * Video information diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java index 7d5645ed7337..429839f6f6f9 100644 --- a/telephony/java/com/android/internal/telephony/RILConstants.java +++ b/telephony/java/com/android/internal/telephony/RILConstants.java @@ -77,6 +77,21 @@ public interface RILConstants { int SIM_SAP_MSG_SIZE_TOO_SMALL = 34; int SIM_SAP_CONNECT_OK_CALL_ONGOING = 35; int LCE_NOT_SUPPORTED = 36; /* Link Capacity Estimation (LCE) not supported */ + int NO_MEMORY = 37; /* Not sufficient memory to process the request */ + int INTERNAL_ERR = 38; /* Hit unexpected vendor internal error scenario */ + int SYSTEM_ERR = 39; /* Hit platform or system error */ + int MODEM_ERR = 40; /* Hit unexpected modem error */ + int INVALID_STATE = 41; /* Can not process the request as vendor RIL is in + invalid state. */ + int NO_RESOURCES = 42; /* Not sufficient resource to process the request */ + int SIM_ERR = 43; /* Received error from SIM card */ + int INVALID_ARGUMENTS = 44; /* Received invalid arguments in request */ + int INVALID_SIM_STATE = 45; /* Can not process the request in current SIM state */ + int INVALID_MODEM_STATE = 46; /* Can not process the request in current Modem state */ + int INVALID_CALL_ID = 47; /* Received invalid call id in request */ + int NO_SMS_TO_ACK = 48; /* ACK received when there is no SMS to ack */ + int NETWORK_ERR = 49; /* Received error from network */ + int REQUEST_RATE_LIMITED = 50; /* Operation denied due to overly-frequent requests */ /* NETWORK_MODE_* See ril.h RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE */ diff --git a/wifi/java/android/net/wifi/AnqpInformationElement.java b/wifi/java/android/net/wifi/AnqpInformationElement.java new file mode 100644 index 000000000000..47b712991c49 --- /dev/null +++ b/wifi/java/android/net/wifi/AnqpInformationElement.java @@ -0,0 +1,80 @@ +/* + * 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 android.net.wifi; + +/** + * This object contains the payload of an ANQP element. + * Vendor id is the vendor ID for the element, or 0 if it is an 802.11(u) element. + * Hotspot 2.0 uses the WFA Vendor ID which is 0x506f9a + * The payload contains the bytes of the payload, starting after the length octet(s). + * @hide + */ +public class AnqpInformationElement { + public static final int HOTSPOT20_VENDOR_ID = 0x506f9a; + + public static final int ANQP_QUERY_LIST = 256; + public static final int ANQP_CAPABILITY_LIST = 257; + public static final int ANQP_VENUE_NAME = 258; + public static final int ANQP_EMERGENCY_NUMBER = 259; + public static final int ANQP_NWK_AUTH_TYPE = 260; + public static final int ANQP_ROAMING_CONSORTIUM = 261; + public static final int ANQP_IP_ADDR_AVAILABILITY = 262; + public static final int ANQP_NAI_REALM = 263; + public static final int ANQP_3GPP_NETWORK = 264; + public static final int ANQP_GEO_LOC = 265; + public static final int ANQP_CIVIC_LOC = 266; + public static final int ANQP_LOC_URI = 267; + public static final int ANQP_DOM_NAME = 268; + public static final int ANQP_EMERGENCY_ALERT = 269; + public static final int ANQP_TDLS_CAP = 270; + public static final int ANQP_EMERGENCY_NAI = 271; + public static final int ANQP_NEIGHBOR_REPORT = 272; + public static final int ANQP_VENDOR_SPEC = 56797; + + public static final int HS_QUERY_LIST = 1; + public static final int HS_CAPABILITY_LIST = 2; + public static final int HS_FRIENDLY_NAME = 3; + public static final int HS_WAN_METRICS = 4; + public static final int HS_CONN_CAPABILITY = 5; + public static final int HS_NAI_HOME_REALM_QUERY = 6; + public static final int HS_OPERATING_CLASS = 7; + public static final int HS_OSU_PROVIDERS = 8; + public static final int HS_ICON_REQUEST = 10; + public static final int HS_ICON_FILE = 11; + + private final int mVendorId; + private final int mElementId; + private final byte[] mPayload; + + public AnqpInformationElement(int vendorId, int elementId, byte[] payload) { + mVendorId = vendorId; + mElementId = elementId; + mPayload = payload; + } + + public int getVendorId() { + return mVendorId; + } + + public int getElementId() { + return mElementId; + } + + public byte[] getPayload() { + return mPayload; + } +} diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl index bad4e9cf0397..1c7a308746b0 100644 --- a/wifi/java/android/net/wifi/IWifiManager.aidl +++ b/wifi/java/android/net/wifi/IWifiManager.aidl @@ -20,7 +20,7 @@ import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; import android.net.wifi.ScanSettings; import android.net.wifi.ScanResult; -import android.net.wifi.ScanInfo; +import android.net.wifi.PasspointManagementObjectDefinition; import android.net.wifi.WifiConnectionStatistics; import android.net.wifi.WifiActivityEnergyInfo; import android.net.Network; @@ -50,6 +50,17 @@ interface IWifiManager int addOrUpdateNetwork(in WifiConfiguration config); + int addPasspointManagementObject(String mo); + + int modifyPasspointManagementObject(String fqdn, + in List<PasspointManagementObjectDefinition> mos); + + void queryPasspointIcon(long bssid, String fileName); + + int matchProviderWithCurrentNetwork(String fqdn); + + void deauthenticateNetwork(long holdoff, boolean ess); + boolean removeNetwork(int netId); boolean enableNetwork(int netId, boolean disableOthers); @@ -64,10 +75,6 @@ interface IWifiManager void disconnect(); - List<ScanInfo> getScanInfos(String callingPackage); - - void setOsuSelection(int osuID); - void reconnect(); void reassociate(); diff --git a/wifi/java/android/net/wifi/ScanInfo.aidl b/wifi/java/android/net/wifi/PasspointManagementObjectDefinition.aidl index 18ae5088146f..eb7cc39d5e33 100644 --- a/wifi/java/android/net/wifi/ScanInfo.aidl +++ b/wifi/java/android/net/wifi/PasspointManagementObjectDefinition.aidl @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015, The Android Open Source Project + * Copyright (c) 2008, 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. @@ -16,4 +16,4 @@ package android.net.wifi; -parcelable ScanInfo; +parcelable PasspointManagementObjectDefinition; diff --git a/wifi/java/android/net/wifi/PasspointManagementObjectDefinition.java b/wifi/java/android/net/wifi/PasspointManagementObjectDefinition.java new file mode 100644 index 000000000000..9fc1706ceb03 --- /dev/null +++ b/wifi/java/android/net/wifi/PasspointManagementObjectDefinition.java @@ -0,0 +1,81 @@ +/* + * 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 android.net.wifi; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * This object describes a partial tree structure in the Hotspot 2.0 release 2 management object. + * The object is used during subscription remediation to modify parts of an existing PPS MO + * tree (Hotspot 2.0 specification section 9.1). + * @hide + */ +public class PasspointManagementObjectDefinition implements Parcelable { + private final String mBaseUri; + private final String mUrn; + private final String mMoTree; + + public PasspointManagementObjectDefinition(String baseUri, String urn, String moTree) { + mBaseUri = baseUri; + mUrn = urn; + mMoTree = moTree; + } + + public String getBaseUri() { + return mBaseUri; + } + + public String getUrn() { + return mUrn; + } + + public String getMoTree() { + return mMoTree; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mBaseUri); + dest.writeString(mUrn); + dest.writeString(mMoTree); + } + + /** + * Implement the Parcelable interface {@hide} + */ + public static final Creator<PasspointManagementObjectDefinition> CREATOR = + new Creator<PasspointManagementObjectDefinition>() { + public PasspointManagementObjectDefinition createFromParcel(Parcel in) { + return new PasspointManagementObjectDefinition( + in.readString(), /* base URI */ + in.readString(), /* URN */ + in.readString() /* Tree XML */ + ); + } + + public PasspointManagementObjectDefinition[] newArray(int size) { + return new PasspointManagementObjectDefinition[size]; + } + }; +} + diff --git a/wifi/java/android/net/wifi/RttManager.java b/wifi/java/android/net/wifi/RttManager.java index 503e4a25e9fb..9137d9d90439 100644 --- a/wifi/java/android/net/wifi/RttManager.java +++ b/wifi/java/android/net/wifi/RttManager.java @@ -136,6 +136,9 @@ public class RttManager { public static final int REASON_INVALID_REQUEST = -4; /** Do not have required permission */ public static final int REASON_PERMISSION_DENIED = -5; + /** Ranging failed because responder role is enabled in STA mode.*/ + public static final int + REASON_INITIATOR_NOT_ALLOWED_WHEN_RESPONDER_ON = -6; public static final String DESCRIPTION_KEY = "android.net.wifi.RttManager.Description"; @@ -191,6 +194,8 @@ public class RttManager { public int preambleSupported; //RTT bandwidth supported public int bwSupported; + // Whether STA responder role is supported. + public boolean responderSupported; @Override public String toString() { @@ -244,6 +249,9 @@ public class RttManager { sb.append("is supported."); + sb.append(" STA responder role is ") + .append(responderSupported ? "supported" : "not supported."); + return sb.toString(); } /** Implement the Parcelable interface {@hide} */ @@ -261,7 +269,7 @@ public class RttManager { dest.writeInt(lcrSupported ? 1 : 0); dest.writeInt(preambleSupported); dest.writeInt(bwSupported); - + dest.writeInt(responderSupported ? 1 : 0); } /** Implement the Parcelable interface {@hide} */ @@ -275,6 +283,7 @@ public class RttManager { capabilities.lcrSupported = in.readInt() == 1 ? true : false; capabilities.preambleSupported = in.readInt(); capabilities.bwSupported = in.readInt(); + capabilities.responderSupported = (in.readInt() == 1); return capabilities; } /** Implement the Parcelable interface {@hide} */ @@ -898,6 +907,160 @@ public class RttManager { sAsyncChannel.sendMessage(CMD_OP_STOP_RANGING, 0, removeListener(listener)); } + /** + * Callbacks for responder operations. + * <p> + * A {@link ResponderCallback} is the handle to the calling client. {@link RttManager} will keep + * a reference to the callback for the entire period when responder is enabled. The same + * callback as used in enabling responder needs to be passed for disabling responder. + * The client can freely destroy or reuse the callback after {@link RttManager#disableResponder} + * is called. + */ + public abstract static class ResponderCallback { + /** Callback when responder is enabled. */ + public abstract void onResponderEnabled(ResponderConfig config); + /** Callback when enabling responder failed. */ + public abstract void onResponderEnableFailure(int reason); + // TODO: consider adding onResponderAborted once it's supported. + } + + /** + * Enable Wi-Fi RTT responder mode on the device. The enabling result will be delivered via + * {@code callback}. + * <p> + * Note calling this method with the same callback when the responder is already enabled won't + * change the responder state, a cached {@link ResponderConfig} from the last enabling will be + * returned through the callback. + * + * @param callback Callback for responder enabling/disabling result. + * @throws IllegalArgumentException If {@code callback} is null. + */ + public void enableResponder(ResponderCallback callback) { + if (callback == null) { + throw new IllegalArgumentException("callback cannot be null"); + } + validateChannel(); + int key = putListenerIfAbsent(callback); + sAsyncChannel.sendMessage(CMD_OP_ENABLE_RESPONDER, 0, key); + } + + /** + * Disable Wi-Fi RTT responder mode on the device. The {@code callback} needs to be the + * same one used in {@link #enableResponder(ResponderCallback)}. + * <p> + * Calling this method when responder isn't enabled won't have any effect. The callback can be + * reused for enabling responder after this method is called. + * + * @param callback The same callback used for enabling responder. + * @throws IllegalArgumentException If {@code callback} is null. + */ + public void disableResponder(ResponderCallback callback) { + if (callback == null) { + throw new IllegalArgumentException("callback cannot be null"); + } + validateChannel(); + int key = removeListener(callback); + if (key == INVALID_KEY) { + Log.e(TAG, "responder not enabled yet"); + return; + } + sAsyncChannel.sendMessage(CMD_OP_DISABLE_RESPONDER, 0, key); + } + + /** + * Configuration used for RTT responder mode. The configuration information can be used by a + * peer device to range the responder. + * + * @see ScanResult + */ + public static class ResponderConfig implements Parcelable { + + // TODO: make all fields final once we can get mac address from responder HAL APIs. + /** + * Wi-Fi mac address used for responder mode. + */ + public String macAddress = ""; + + /** + * The primary 20 MHz frequency (in MHz) of the channel where responder is enabled. + * @see ScanResult#frequency + */ + public int frequency; + + /** + * Center frequency of the channel where responder is enabled on. Only in use when channel + * width is at least 40MHz. + * @see ScanResult#centerFreq0 + */ + public int centerFreq0; + + /** + * Center frequency of the second segment when channel width is 80 + 80 MHz. + * @see ScanResult#centerFreq1 + */ + public int centerFreq1; + + /** + * Width of the channel where responder is enabled on. + * @see ScanResult#channelWidth + */ + public int channelWidth; + + /** + * Preamble supported by responder. + */ + public int preamble; + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("macAddress = ").append(macAddress) + .append(" frequency = ").append(frequency) + .append(" centerFreq0 = ").append(centerFreq0) + .append(" centerFreq1 = ").append(centerFreq1) + .append(" channelWidth = ").append(channelWidth) + .append(" preamble = ").append(preamble); + return builder.toString(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(macAddress); + dest.writeInt(frequency); + dest.writeInt(centerFreq0); + dest.writeInt(centerFreq1); + dest.writeInt(channelWidth); + dest.writeInt(preamble); + } + + /** Implement {@link Parcelable} interface */ + public static final Parcelable.Creator<ResponderConfig> CREATOR = + new Parcelable.Creator<ResponderConfig>() { + @Override + public ResponderConfig createFromParcel(Parcel in) { + ResponderConfig config = new ResponderConfig(); + config.macAddress = in.readString(); + config.frequency = in.readInt(); + config.centerFreq0 = in.readInt(); + config.centerFreq1 = in.readInt(); + config.channelWidth = in.readInt(); + config.preamble = in.readInt(); + return config; + } + + @Override + public ResponderConfig[] newArray(int size) { + return new ResponderConfig[size]; + } + }; + + } + /* private methods */ public static final int BASE = Protocol.BASE_WIFI_RTT_MANAGER; @@ -906,6 +1069,12 @@ public class RttManager { public static final int CMD_OP_FAILED = BASE + 2; public static final int CMD_OP_SUCCEEDED = BASE + 3; public static final int CMD_OP_ABORTED = BASE + 4; + public static final int CMD_OP_ENABLE_RESPONDER = BASE + 5; + public static final int CMD_OP_DISABLE_RESPONDER = BASE + 6; + public static final int + CMD_OP_ENALBE_RESPONDER_SUCCEEDED = BASE + 7; + public static final int + CMD_OP_ENALBE_RESPONDER_FAILED = BASE + 8; private Context mContext; private IRttManager mService; @@ -992,6 +1161,23 @@ public class RttManager { return key; } + // Insert a listener if it doesn't exist in sListenerMap. Returns the key of the listener. + private static int putListenerIfAbsent(Object listener) { + if (listener == null) return INVALID_KEY; + synchronized (sListenerMapLock) { + int key = getListenerKey(listener); + if (key != INVALID_KEY) { + return key; + } + do { + key = sListenerKey++; + } while (key == INVALID_KEY); + sListenerMap.put(key, listener); + return key; + } + + } + private static Object getListener(int key) { if (key == INVALID_KEY) return null; synchronized (sListenerMapLock) { @@ -1047,9 +1233,9 @@ public class RttManager { // to fail and throw an exception sAsyncChannel = null; } - sConnected.countDown(); return; case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED: + sConnected.countDown(); return; case AsyncChannel.CMD_CHANNEL_DISCONNECTED: Log.e(TAG, "Channel connection lost"); @@ -1082,6 +1268,14 @@ public class RttManager { ((RttListener) listener).onAborted(); removeListener(msg.arg2); break; + case CMD_OP_ENALBE_RESPONDER_SUCCEEDED: + ResponderConfig config = (ResponderConfig) msg.obj; + ((ResponderCallback) (listener)).onResponderEnabled(config); + break; + case CMD_OP_ENALBE_RESPONDER_FAILED: + ((ResponderCallback) (listener)).onResponderEnableFailure(msg.arg1); + removeListener(msg.arg2); + break; default: if (DBG) Log.d(TAG, "Ignoring message " + msg.what); return; diff --git a/wifi/java/android/net/wifi/ScanInfo.java b/wifi/java/android/net/wifi/ScanInfo.java deleted file mode 100644 index 39186fa7a38a..000000000000 --- a/wifi/java/android/net/wifi/ScanInfo.java +++ /dev/null @@ -1,189 +0,0 @@ -package android.net.wifi; - -import android.os.Parcel; -import android.os.Parcelable; - -public class ScanInfo implements Parcelable { - private final ScanResult mScanResult; - - private final long mBSSID; // The BSSID of the best AP with an SSID matching the OSU - private final int mRSSI; // RSSI of the AP with BSSID - private final String mSSID; // The SSID to connect to for an OSU connection. - private final String mName; - private final String mServiceDescription; - private final String mIconType; - private final byte[] mIconData; - private final int mOSUIdentity; - - public ScanInfo(ScanResult scanResult) { - mScanResult = scanResult; - - mBSSID = -1; - mRSSI = -1; - mSSID = null; - mName = null; - mServiceDescription = null; - mIconType = null; - mIconData = null; - mOSUIdentity = -1; - } - - public ScanInfo(long BSSID, int rssi, String SSID, String name, String serviceDescription, - String iconType, byte[] iconData, int OSUIdentity) { - mBSSID = BSSID; - mRSSI = rssi; - mSSID = SSID; - mName = name; - mServiceDescription = serviceDescription; - mIconType = iconType; - mIconData = iconData; - mOSUIdentity = OSUIdentity; - - mScanResult = null; - } - - /** - * Get the scan result of this ScanInfo. - * @return The ScanResult, if this ScanInfo contains a one. If the ScanInfo contains - * OSU information getScanResult will return null. - */ - public ScanResult getScanResult() { - return mScanResult; - } - - /** - * OSU only: The BSSID of the AP who advertises the OSU SSID. This value is not guaranteed to - * be correct; In the somewhat unlikely case that multiple APs advertise OSU SSIDs that matches - * an OSU information element returned through ANQP and one of those is not related to an OSU - * there is a (slight) risk that the BSSID is for a "spoof" OSU. - * The matching algorithm that produces the ScanInfo objects makes a best effort to get the - * matching right though and since it is (a) fair to assume that the OSU SSID resides on the - * same AP as the one advertising the OSU information, and (b) BSSIDs for multi-SSID APs are - * typically adjacent to each other, matching will prefer the BSSID closest to the advertising - * APs BSSID if multiple SSIDs match. - * @return The BSSID. - */ - public long getBssid() { - return mBSSID; - } - - /** - * OSU only. - * @return The signal level of the AP associated with the BSSID from getBSSID. - */ - public int getRssi() { - return mRSSI; - } - - /** - * OSU only. - * @return The SSID of the AP to which to associate to establish an OSU connection. - */ - public String getSsid() { - return mSSID; - } - - /** - * OSU only. - * @return The name of the Service Provider of the OSU. - */ - public String getName() { - return mName; - } - - /** - * OSU only. - * @return The service description of the OSU. - */ - public String getServiceDescription() { - return mServiceDescription; - } - - /** - * OSU only. - * Get the type of icon that icon data represents, e.g. JPG, PNG etc. This field is formatted - * using standard MIME encodings per RFC-4288 and IANA MIME media types. - * @return The icon type in icon data. - */ - public String getIconType() { - return mIconType; - } - - /** - * OSU only. - * @return The binary data of the icon. - */ - public byte[] getIconData() { - return mIconData; - } - - /** - * OSU only. - * @return a unique identity for the OSU. This value is generated by the framework and should - * be used to uniquely identify a specific OSU. Please note that values may be reused after - * a very long time-span (in any normal scenario, likely years) and implementations should make - * sure to not rely on any long term persisted values. - */ - public int getOsuIdentity() { - return mOSUIdentity; - } - - private static final int ScanResultMarker = 0; - private static final int OSUMarker = 1; - - @Override - public int describeContents() { - return 0; - } - - /** Implement the Parcelable interface {@hide} */ - public static final Creator<ScanInfo> CREATOR = - new Creator<ScanInfo>() { - @Override - public ScanInfo createFromParcel(Parcel source) { - int marker = source.readInt(); - if (marker == ScanResultMarker) { - return new ScanInfo(ScanResult.CREATOR.createFromParcel(source)); - } - else if (marker == OSUMarker) { - return new ScanInfo( - source.readLong(), - source.readInt(), - source.readString(), - source.readString(), - source.readString(), - source.readString(), - source.createByteArray(), - source.readInt() - ); - } - else { - throw new RuntimeException("Bad ScanInfo data"); - } - } - - @Override - public ScanInfo[] newArray(int size) { - return new ScanInfo[0]; - } - }; - - @Override - public void writeToParcel(Parcel dest, int flags) { - if (mScanResult != null) { - dest.writeInt(ScanResultMarker); - mScanResult.writeToParcel(dest, flags); - return; - } - - dest.writeInt(OSUMarker); - dest.writeLong(mBSSID); - dest.writeInt(mRSSI); - dest.writeString(mSSID); - dest.writeString(mName); - dest.writeString(mServiceDescription); - dest.writeString(mIconType); - dest.writeByteArray(mIconData); - dest.writeInt(mOSUIdentity); - } -} diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java index 6179ed42d2d1..31da6701e338 100644 --- a/wifi/java/android/net/wifi/ScanResult.java +++ b/wifi/java/android/net/wifi/ScanResult.java @@ -43,6 +43,19 @@ public class ScanResult implements Parcelable { * The address of the access point. */ public String BSSID; + + /** + * The HESSID from the beacon. + * @hide + */ + public long hessid; + + /** + * The ANQP Domain ID from the Hotspot 2.0 Indication element, if present. + * @hide + */ + public int anqpDomainId; + /** * Describes the authentication, key management, and encryption schemes * supported by the access point. @@ -342,14 +355,27 @@ public class ScanResult implements Parcelable { /** information elements found in the beacon * @hide */ - public InformationElement informationElements[]; + public InformationElement[] informationElements; + + /** ANQP response elements. + * @hide + */ + public AnqpInformationElement[] anqpElements; /** {@hide} */ - public ScanResult(WifiSsid wifiSsid, String BSSID, String caps, int level, int frequency, - long tsf) { + public ScanResult(WifiSsid wifiSsid, String BSSID, long hessid, int anqpDomainId, + byte[] osuProviders, String caps, int level, int frequency, long tsf) { this.wifiSsid = wifiSsid; this.SSID = (wifiSsid != null) ? wifiSsid.toString() : WifiSsid.NONE; this.BSSID = BSSID; + this.hessid = hessid; + this.anqpDomainId = anqpDomainId; + if (osuProviders != null) { + this.anqpElements = new AnqpInformationElement[1]; + this.anqpElements[0] = + new AnqpInformationElement(AnqpInformationElement.HOTSPOT20_VENDOR_ID, + AnqpInformationElement.HS_OSU_PROVIDERS, osuProviders); + } this.capabilities = caps; this.level = level; this.frequency = frequency; @@ -381,11 +407,14 @@ public class ScanResult implements Parcelable { } /** {@hide} */ - public ScanResult(String Ssid, String BSSID, String caps, int level, int frequency, + public ScanResult(String Ssid, String BSSID, long hessid, int anqpDomainId, String caps, + int level, int frequency, long tsf, int distCm, int distSdCm, int channelWidth, int centerFreq0, int centerFreq1, boolean is80211McRTTResponder) { this.SSID = Ssid; this.BSSID = BSSID; + this.hessid = hessid; + this.anqpDomainId = anqpDomainId; this.capabilities = caps; this.level = level; this.frequency = frequency; @@ -403,11 +432,12 @@ public class ScanResult implements Parcelable { } /** {@hide} */ - public ScanResult(WifiSsid wifiSsid, String Ssid, String BSSID, String caps, int level, + public ScanResult(WifiSsid wifiSsid, String Ssid, String BSSID, long hessid, int anqpDomainId, + String caps, int level, int frequency, long tsf, int distCm, int distSdCm, int channelWidth, int centerFreq0, int centerFreq1, boolean is80211McRTTResponder) { - this(Ssid, BSSID, caps,level, frequency, tsf, distCm, distSdCm, channelWidth, centerFreq0, - centerFreq1, is80211McRTTResponder); + this(Ssid, BSSID, hessid, anqpDomainId, caps, level, frequency, tsf, distCm, + distSdCm, channelWidth, centerFreq0, centerFreq1, is80211McRTTResponder); this.wifiSsid = wifiSsid; } @@ -417,6 +447,10 @@ public class ScanResult implements Parcelable { wifiSsid = source.wifiSsid; SSID = source.SSID; BSSID = source.BSSID; + hessid = source.hessid; + anqpDomainId = source.anqpDomainId; + informationElements = source.informationElements; + anqpElements = source.anqpElements; capabilities = source.capabilities; level = source.level; frequency = source.frequency; @@ -497,6 +531,8 @@ public class ScanResult implements Parcelable { } dest.writeString(SSID); dest.writeString(BSSID); + dest.writeLong(hessid); + dest.writeInt(anqpDomainId); dest.writeString(capabilities); dest.writeInt(level); dest.writeInt(frequency); @@ -533,6 +569,18 @@ public class ScanResult implements Parcelable { for (int i = 0; i < anqpLines.size(); i++) { dest.writeString(anqpLines.get(i)); } + } + else { + dest.writeInt(0); + } + if (anqpElements != null) { + dest.writeInt(anqpElements.length); + for (AnqpInformationElement element : anqpElements) { + dest.writeInt(element.getVendorId()); + dest.writeInt(element.getElementId()); + dest.writeInt(element.getPayload().length); + dest.writeByteArray(element.getPayload()); + } } else { dest.writeInt(0); } @@ -547,19 +595,22 @@ public class ScanResult implements Parcelable { wifiSsid = WifiSsid.CREATOR.createFromParcel(in); } ScanResult sr = new ScanResult( - wifiSsid, - in.readString(), /* SSID */ - in.readString(), /* BSSID */ - in.readString(), /* capabilities */ - in.readInt(), /* level */ - in.readInt(), /* frequency */ - in.readLong(), /* timestamp */ - in.readInt(), /* distanceCm */ - in.readInt(), /* distanceSdCm */ - in.readInt(), /* channelWidth */ - in.readInt(), /* centerFreq0 */ - in.readInt(), /* centerFreq1 */ - false /* rtt responder, fixed with flags below */ + wifiSsid, + in.readString(), /* SSID */ + in.readString(), /* BSSID */ + in.readLong(), /* HESSID */ + in.readInt(), /* ANQP Domain ID */ + in.readString(), /* capabilities */ + in.readInt(), /* level */ + in.readInt(), /* frequency */ + in.readLong(), /* timestamp */ + in.readInt(), /* distanceCm */ + in.readInt(), /* distanceSdCm */ + in.readInt(), /* channelWidth */ + in.readInt(), /* centerFreq0 */ + in.readInt(), /* centerFreq1 */ + false /* rtt responder, + fixed with flags below */ ); sr.seen = in.readLong(); @@ -591,6 +642,19 @@ public class ScanResult implements Parcelable { sr.anqpLines.add(in.readString()); } } + n = in.readInt(); + if (n != 0) { + sr.anqpElements = new AnqpInformationElement[n]; + for (int i = 0; i < n; i++) { + int vendorId = in.readInt(); + int elementId = in.readInt(); + int len = in.readInt(); + byte[] payload = new byte[len]; + in.readByteArray(payload); + sr.anqpElements[i] = + new AnqpInformationElement(vendorId, elementId, payload); + } + } return sr; } diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index c6354bd6a5a7..c8065f90ed98 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -58,6 +58,9 @@ public class WifiConfiguration implements Parcelable { /** {@hide} */ public static final int INVALID_NETWORK_ID = -1; + /** {@hide} */ + private String mPasspointManagementObjectTree; + /** * Recognized key management schemes. */ @@ -79,11 +82,16 @@ public class WifiConfiguration implements Parcelable { * @hide */ public static final int WPA2_PSK = 4; + /** + * Hotspot 2.0 r2 OSEN: + * @hide + */ + public static final int OSEN = 5; public static final String varName = "key_mgmt"; public static final String[] strings = { "NONE", "WPA_PSK", "WPA_EAP", "IEEE8021X", - "WPA2_PSK" }; + "WPA2_PSK", "OSEN" }; } /** @@ -96,10 +104,14 @@ public class WifiConfiguration implements Parcelable { public static final int WPA = 0; /** WPA2/IEEE 802.11i */ public static final int RSN = 1; + /** HS2.0 r2 OSEN + * @hide + */ + public static final int OSEN = 2; public static final String varName = "proto"; - public static final String[] strings = { "WPA", "RSN" }; + public static final String[] strings = { "WPA", "RSN", "OSEN" }; } /** @@ -158,10 +170,15 @@ public class WifiConfiguration implements Parcelable { public static final int TKIP = 2; /** AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0] */ public static final int CCMP = 3; + /** Hotspot 2.0 r2 OSEN + * @hide + */ + public static final int GTK_NOT_USED = 4; public static final String varName = "group"; - public static final String[] strings = { "WEP40", "WEP104", "TKIP", "CCMP" }; + public static final String[] strings = + { "WEP40", "WEP104", "TKIP", "CCMP", "GTK_NOT_USED" }; } /** Possible status of a network configuration. */ @@ -1734,6 +1751,16 @@ public class WifiConfiguration implements Parcelable { return shared || (UserHandle.getUserId(creatorUid) == userId); } + /** @hide */ + public void setPasspointManagementObjectTree(String passpointManagementObjectTree) { + mPasspointManagementObjectTree = passpointManagementObjectTree; + } + + /** @hide */ + public String getMoTree() { + return mPasspointManagementObjectTree; + } + /** copy constructor {@hide} */ public WifiConfiguration(WifiConfiguration source) { if (source != null) { @@ -1885,6 +1912,7 @@ public class WifiConfiguration implements Parcelable { dest.writeInt(numNoInternetAccessReports); dest.writeInt(noInternetAccessExpected ? 1 : 0); dest.writeInt(shared ? 1 : 0); + dest.writeString(mPasspointManagementObjectTree); } /** Implement the Parcelable interface {@hide} */ @@ -1953,6 +1981,7 @@ public class WifiConfiguration implements Parcelable { config.numNoInternetAccessReports = in.readInt(); config.noInternetAccessExpected = in.readInt() != 0; config.shared = in.readInt() != 0; + config.mPasspointManagementObjectTree = in.readString(); return config; } diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java index 53efe6c19015..a406fd75cfbd 100644 --- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java +++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java @@ -101,6 +101,8 @@ public class WifiEnterpriseConfig implements Parcelable { /** @hide */ public static final String CA_CERT_KEY = "ca_cert"; /** @hide */ + public static final String CA_PATH_KEY = "ca_path"; + /** @hide */ public static final String ENGINE_KEY = "engine"; /** @hide */ public static final String ENGINE_ID_KEY = "engine_id"; @@ -284,8 +286,11 @@ public class WifiEnterpriseConfig implements Parcelable { public static final int AKA = 5; /** EAP-Authentication and Key Agreement Prime */ public static final int AKA_PRIME = 6; + /** Hotspot 2.0 r2 OSEN */ + public static final int UNAUTH_TLS = 7; /** @hide */ - public static final String[] strings = { "PEAP", "TLS", "TTLS", "PWD", "SIM", "AKA", "AKA'" }; + public static final String[] strings = + { "PEAP", "TLS", "TTLS", "PWD", "SIM", "AKA", "AKA'", "WFA-UNAUTH-TLS" }; /** Prevent initialization */ private Eap() {} @@ -653,6 +658,33 @@ public class WifiEnterpriseConfig implements Parcelable { mCaCerts = null; } + /** + * Set the ca_path directive on wpa_supplicant. + * + * From wpa_supplicant documentation: + * + * Directory path for CA certificate files (PEM). This path may contain + * multiple CA certificates in OpenSSL format. Common use for this is to + * point to system trusted CA list which is often installed into directory + * like /etc/ssl/certs. If configured, these certificates are added to the + * list of trusted CAs. ca_cert may also be included in that case, but it is + * not required. + * @param domain The path for CA certificate files + * @hide + */ + public void setCaPath(String path) { + setFieldValue(CA_PATH_KEY, path); + } + + /** + * Get the domain_suffix_match value. See setDomSuffixMatch. + * @return The path for CA certificate files. + * @hide + */ + public String getCaPath() { + return getFieldValue(CA_PATH_KEY, ""); + } + /** Set Client certificate alias. * * <p> See the {@link android.security.KeyChain} for details on installing or choosing diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 50f2cf0a48f7..4133cda9fe01 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -26,28 +26,26 @@ import android.net.DhcpInfo; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkRequest; -import android.net.wifi.ScanSettings; import android.os.Binder; import android.os.Build; -import android.os.IBinder; import android.os.Handler; import android.os.HandlerThread; +import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.Messenger; import android.os.RemoteException; import android.os.WorkSource; -import android.os.Messenger; import android.util.Log; import android.util.SparseArray; -import java.net.InetAddress; -import java.util.concurrent.CountDownLatch; - import com.android.internal.annotations.GuardedBy; import com.android.internal.util.AsyncChannel; import com.android.internal.util.Protocol; +import java.net.InetAddress; import java.util.List; +import java.util.concurrent.CountDownLatch; /** * This class provides the primary API for managing all aspects of Wi-Fi @@ -114,6 +112,51 @@ public class WifiManager { public static final int WIFI_CREDENTIAL_FORGOT = 1; /** + * Broadcast intent action indicating that the a Passpoint release 2 icon has been received. + * @hide + */ + public static final String PASSPOINT_ICON_RECEIVED_ACTION = + "android.net.wifi.PASSPOINT_ICON_RECEIVED"; + /** @hide */ + public static final String EXTRA_PASSPOINT_ICON_BSSID = "bssid"; + /** @hide */ + public static final String EXTRA_PASSPOINT_ICON_FILE = "file"; + /** @hide */ + public static final String EXTRA_PASSPOINT_ICON_DATA = "icon"; + + /** + * Broadcast intent action indicating that the a Passpoint release + * 2 WNM frame has been received. + * @hide + */ + public static final String PASSPOINT_WNM_FRAME_RECEIVED_ACTION = + "android.net.wifi.PASSPOINT_WNM_FRAME_RECEIVED"; + /** + * Originating BSS + * @hide */ + public static final String EXTRA_PASSPOINT_WNM_BSSID = "bssid"; + /** + * SOAP-XML or OMA-DM + * @hide */ + public static final String EXTRA_PASSPOINT_WNM_METHOD = "method"; + /** + * Type of Passpoint match + * @hide */ + public static final String EXTRA_PASSPOINT_WNM_PPOINT_MATCH = "match"; + /** + * String + * @hide */ + public static final String EXTRA_PASSPOINT_WNM_URL = "url"; + /** + * Boolean true=ess, false=bss + * @hide */ + public static final String EXTRA_PASSPOINT_WNM_ESS = "ess"; + /** + * Delay in seconds + * @hide */ + public static final String EXTRA_PASSPOINT_WNM_DELAY = "delay"; + + /** * Broadcast intent action indicating that Wi-Fi has been enabled, disabled, * enabling, disabling, or unknown. One extra provides this state as an int. * Another extra provides the previous state, if available. @@ -771,6 +814,76 @@ public class WifiManager { } /** + * Add a Hotspot 2.0 release 2 Management Object + * @param mo The MO in XML form + * @return -1 for failure + * @hide + */ + public int addPasspointManagementObject(String mo) { + try { + return mService.addPasspointManagementObject(mo); + } catch (RemoteException e) { + return -1; + } + } + + /** + * Modify a Hotspot 2.0 release 2 Management Object + * @param fqdn The FQDN of the service provider + * @param mos A List of MO definitions to be updated + * @return the number of nodes updated, or -1 for failure + * @hide + */ + public int modifyPasspointManagementObject(String fqdn, + List<PasspointManagementObjectDefinition> mos) { + try { + return mService.modifyPasspointManagementObject(fqdn, mos); + } catch (RemoteException e) { + return -1; + } + } + + /** + * Query for a Hotspot 2.0 release 2 OSU icon + * @param bssid The BSSID of the AP + * @param fileName Icon file name + * @hide + */ + public void queryPasspointIcon(long bssid, String fileName) { + try { + mService.queryPasspointIcon(bssid, fileName); + } catch (RemoteException e) { + } + } + + /** + * Match the currently associated network against the SP matching the given FQDN + * @param fqdn FQDN of the SP + * @return ordinal [HomeProvider, RoamingProvider, Incomplete, None, Declined] + * @hide + */ + public int matchProviderWithCurrentNetwork(String fqdn) { + try { + return mService.matchProviderWithCurrentNetwork(fqdn); + } catch (RemoteException e) { + return -1; + } + } + + /** + * Deauthenticate and set the re-authentication hold off time for the current network + * @param holdoff hold off time in milliseconds + * @param ess set if the hold off pertains to an ESS rather than a BSS + * @hide + */ + public void deauthenticateNetwork(long holdoff, boolean ess) { + try { + mService.deauthenticateNetwork(holdoff, ess); + } catch (RemoteException e) { + } + } + + /** * Remove the specified network from the list of configured networks. * This may result in the asynchronous delivery of state change * events. @@ -1191,30 +1304,6 @@ public class WifiManager { } /** - * An augmented version of getScanResults that returns ScanResults as well as OSU information - * wrapped in ScanInfo objects. - * @return - */ - public List<ScanInfo> getScanInfos() { - try { - return mService.getScanInfos(mContext.getOpPackageName()); - } catch (RemoteException e) { - return null; - } - } - - /** - * Notify the OSU framework about the currently selected OSU. - * @param osuID The OSU ID from ScanInfo.getOsuIdentity() - */ - public void setOsuSelection(int osuID) { - try { - mService.setOsuSelection(osuID); - } catch (RemoteException e) { - } - } - - /** * Check if scanning is always available. * * If this return {@code true}, apps can issue {@link #startScan} and fetch scan results diff --git a/wifi/java/android/net/wifi/nan/IWifiNanEventListener.aidl b/wifi/java/android/net/wifi/nan/IWifiNanEventListener.aidl index 13efc361fbb3..fa666afd1c27 100644 --- a/wifi/java/android/net/wifi/nan/IWifiNanEventListener.aidl +++ b/wifi/java/android/net/wifi/nan/IWifiNanEventListener.aidl @@ -1,11 +1,11 @@ -/** - * Copyright (c) 2016, The Android Open Source Project +/* + * 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 + * 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, @@ -26,7 +26,7 @@ import android.net.wifi.nan.ConfigRequest; oneway interface IWifiNanEventListener { void onConfigCompleted(in ConfigRequest completedConfig); - void onConfigFailed(int reason); + void onConfigFailed(in ConfigRequest failedConfig, int reason); void onNanDown(int reason); void onIdentityChanged(); } diff --git a/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl b/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl index ec9e4628d609..f382d97762d3 100644 --- a/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl +++ b/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl @@ -1,11 +1,11 @@ -/** - * Copyright (c) 2016, The Android Open Source Project +/* + * 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 + * 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, diff --git a/wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl b/wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl index 50c34d946918..d60d8caae70e 100644 --- a/wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl +++ b/wifi/java/android/net/wifi/nan/IWifiNanSessionListener.aidl @@ -1,11 +1,11 @@ -/** - * Copyright (c) 2016, The Android Open Source Project +/* + * 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 + * 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, diff --git a/wifi/java/android/net/wifi/nan/WifiNanEventListener.java b/wifi/java/android/net/wifi/nan/WifiNanEventListener.java index eae0a55f7af1..5c18bd7e0f07 100644 --- a/wifi/java/android/net/wifi/nan/WifiNanEventListener.java +++ b/wifi/java/android/net/wifi/nan/WifiNanEventListener.java @@ -47,7 +47,8 @@ public class WifiNanEventListener { /** * Configuration failed callback event registration flag. Corresponding - * callback is {@link WifiNanEventListener#onConfigFailed(int)}. + * callback is + * {@link WifiNanEventListener#onConfigFailed(ConfigRequest, int)}. */ public static final int LISTEN_CONFIG_FAILED = 0x1 << 1; @@ -93,7 +94,7 @@ public class WifiNanEventListener { WifiNanEventListener.this.onConfigCompleted((ConfigRequest) msg.obj); break; case LISTEN_CONFIG_FAILED: - WifiNanEventListener.this.onConfigFailed(msg.arg1); + WifiNanEventListener.this.onConfigFailed((ConfigRequest) msg.obj, msg.arg1); break; case LISTEN_NAN_DOWN: WifiNanEventListener.this.onNanDown(msg.arg1); @@ -129,7 +130,7 @@ public class WifiNanEventListener { * * @param reason Failure reason code, see {@code NanSessionListener.FAIL_*}. */ - public void onConfigFailed(int reason) { + public void onConfigFailed(ConfigRequest failedConfig, int reason) { Log.w(TAG, "onConfigFailed: called in stub - override if interested or disable"); } @@ -173,11 +174,14 @@ public class WifiNanEventListener { } @Override - public void onConfigFailed(int reason) { - if (VDBG) Log.v(TAG, "onConfigFailed: reason=" + reason); + public void onConfigFailed(ConfigRequest failedConfig, int reason) { + if (VDBG) { + Log.v(TAG, "onConfigFailed: failedConfig=" + failedConfig + ", reason=" + reason); + } Message msg = mHandler.obtainMessage(LISTEN_CONFIG_FAILED); msg.arg1 = reason; + msg.obj = failedConfig; mHandler.sendMessage(msg); } diff --git a/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java b/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java index d5e59f0edaf5..092508766570 100644 --- a/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java +++ b/wifi/java/android/net/wifi/nan/WifiNanSessionListener.java @@ -303,8 +303,8 @@ public class WifiNanSessionListener { * message). Override to implement your custom response. * <p> * Note that either this callback or - * {@link WifiNanSessionListener#onMessageSendFail(int)} will be received - - * never both. + * {@link WifiNanSessionListener#onMessageSendFail(int, int)} will be + * received - never both. */ public void onMessageSendSuccess(int messageId) { if (VDBG) Log.v(TAG, "onMessageSendSuccess: called in stub - override if interested"); @@ -319,8 +319,8 @@ public class WifiNanSessionListener { * message). Override to implement your custom response. * <p> * Note that either this callback or - * {@link WifiNanSessionListener#onMessageSendSuccess()} will be received - - * never both + * {@link WifiNanSessionListener#onMessageSendSuccess(int)} will be received + * - never both * * @param reason The failure reason using {@code NanSessionListener.FAIL_*} * codes. |