upload android base code part8
This commit is contained in:
parent
841ae54672
commit
5425409085
57075 changed files with 9846578 additions and 0 deletions
33
android/packages/services/Car/obd2-lib/Android.mk
Normal file
33
android/packages/services/Car/obd2-lib/Android.mk
Normal file
|
@ -0,0 +1,33 @@
|
|||
# Copyright (C) 2017 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.
|
||||
#
|
||||
#
|
||||
|
||||
#disble build in PDK, missing aidl import breaks build
|
||||
ifneq ($(TARGET_BUILD_PDK),true)
|
||||
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := $(call all-java-files-under, src)
|
||||
|
||||
LOCAL_MODULE := com.android.car.obd2
|
||||
LOCAL_JAVA_LANGUAGE_VERSION := 1.8
|
||||
|
||||
LOCAL_STATIC_JAVA_LIBRARIES += vehicle-hal-support-lib \
|
||||
|
||||
include $(BUILD_STATIC_JAVA_LIBRARY)
|
||||
|
||||
endif #TARGET_BUILD_PDK
|
21
android/packages/services/Car/obd2-lib/AndroidManifest.xml
Normal file
21
android/packages/services/Car/obd2-lib/AndroidManifest.xml
Normal file
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2017 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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
|
||||
package="com.android.car.obd2" >
|
||||
<uses-sdk android:minSdkVersion="23" android:targetSdkVersion="23" />
|
||||
</manifest>
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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.car.obd2;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* A wrapper over an int[] that offers a moving offset into the array, allowing for sequential
|
||||
* consumption of the array data
|
||||
*/
|
||||
public class IntegerArrayStream {
|
||||
private final int[] mData;
|
||||
private int mIndex;
|
||||
|
||||
public IntegerArrayStream(int[] data) {
|
||||
mData = data;
|
||||
mIndex = 0;
|
||||
}
|
||||
|
||||
public int peek() {
|
||||
return mData[mIndex];
|
||||
}
|
||||
|
||||
public int consume() {
|
||||
return mData[mIndex++];
|
||||
}
|
||||
|
||||
public int residualLength() {
|
||||
return mData.length - mIndex;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return residualLength() == 0;
|
||||
}
|
||||
|
||||
public boolean hasAtLeast(int n) {
|
||||
return residualLength() >= n;
|
||||
}
|
||||
|
||||
public <T> T hasAtLeast(int n, Function<IntegerArrayStream, T> ifTrue) {
|
||||
return hasAtLeast(n, ifTrue, null);
|
||||
}
|
||||
|
||||
public <T> T hasAtLeast(
|
||||
int n,
|
||||
Function<IntegerArrayStream, T> ifTrue,
|
||||
Function<IntegerArrayStream, T> ifFalse) {
|
||||
if (hasAtLeast(n)) {
|
||||
return ifTrue.apply(this);
|
||||
} else {
|
||||
if (ifFalse != null) {
|
||||
return ifFalse.apply(this);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the content of this stream against an expected data-set.
|
||||
*
|
||||
* <p>If any element of values causes a mismatch, that element will not be consumed and this
|
||||
* method will return false. All elements that do match are consumed from the stream.
|
||||
*
|
||||
* <p>For instance, given a stream with {1,2,3,4}, a call of expect(1,2,5) will consume 1 and 2,
|
||||
* will return false, and stream.peek() will return 3 since it is the first element that did not
|
||||
* match and was not consumed.
|
||||
*
|
||||
* @param values The values to compare this stream's elements against.
|
||||
* @return true if all elements of values match this stream, false otherwise.
|
||||
*/
|
||||
public boolean expect(int... values) {
|
||||
if (!hasAtLeast(values.length)) {
|
||||
return false;
|
||||
}
|
||||
for (int value : values) {
|
||||
if (value != peek()) {
|
||||
return false;
|
||||
} else {
|
||||
consume();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,196 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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.car.obd2;
|
||||
|
||||
import com.android.car.obd2.commands.AmbientAirTemperature;
|
||||
import com.android.car.obd2.commands.CalculatedEngineLoad;
|
||||
import com.android.car.obd2.commands.EngineCoolantTemperature;
|
||||
import com.android.car.obd2.commands.EngineOilTemperature;
|
||||
import com.android.car.obd2.commands.EngineRuntime;
|
||||
import com.android.car.obd2.commands.FuelGaugePressure;
|
||||
import com.android.car.obd2.commands.FuelSystemStatus;
|
||||
import com.android.car.obd2.commands.FuelTankLevel;
|
||||
import com.android.car.obd2.commands.FuelTrimCommand.Bank1LongTermFuelTrimCommand;
|
||||
import com.android.car.obd2.commands.FuelTrimCommand.Bank1ShortTermFuelTrimCommand;
|
||||
import com.android.car.obd2.commands.FuelTrimCommand.Bank2LongTermFuelTrimCommand;
|
||||
import com.android.car.obd2.commands.FuelTrimCommand.Bank2ShortTermFuelTrimCommand;
|
||||
import com.android.car.obd2.commands.RPM;
|
||||
import com.android.car.obd2.commands.Speed;
|
||||
import com.android.car.obd2.commands.ThrottlePosition;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Base class of OBD2 command objects that query a "vehicle" and return an individual data point
|
||||
* represented as a Java type.
|
||||
*
|
||||
* @param <ValueType> The Java type that represents the value of this command's output.
|
||||
*/
|
||||
public abstract class Obd2Command<ValueType> {
|
||||
|
||||
/**
|
||||
* Abstract representation of an object whose job it is to receive the bytes read from the OBD2
|
||||
* connection and return a Java representation of a command's value.
|
||||
*
|
||||
* @param <ValueType>
|
||||
*/
|
||||
public interface OutputSemanticHandler<ValueType> {
|
||||
int getPid();
|
||||
|
||||
Optional<ValueType> consume(IntegerArrayStream data);
|
||||
}
|
||||
|
||||
public static final int LIVE_FRAME = 1;
|
||||
public static final int FREEZE_FRAME = 2;
|
||||
|
||||
private static final HashMap<Integer, OutputSemanticHandler<Integer>>
|
||||
SUPPORTED_INTEGER_COMMANDS = new HashMap<>();
|
||||
private static final HashMap<Integer, OutputSemanticHandler<Float>> SUPPORTED_FLOAT_COMMANDS =
|
||||
new HashMap<>();
|
||||
|
||||
private static void addSupportedIntegerCommands(
|
||||
OutputSemanticHandler<Integer>... integerOutputSemanticHandlers) {
|
||||
for (OutputSemanticHandler<Integer> integerOutputSemanticHandler :
|
||||
integerOutputSemanticHandlers) {
|
||||
SUPPORTED_INTEGER_COMMANDS.put(
|
||||
integerOutputSemanticHandler.getPid(), integerOutputSemanticHandler);
|
||||
}
|
||||
}
|
||||
|
||||
private static void addSupportedFloatCommands(
|
||||
OutputSemanticHandler<Float>... floatOutputSemanticHandlers) {
|
||||
for (OutputSemanticHandler<Float> floatOutputSemanticHandler :
|
||||
floatOutputSemanticHandlers) {
|
||||
SUPPORTED_FLOAT_COMMANDS.put(
|
||||
floatOutputSemanticHandler.getPid(), floatOutputSemanticHandler);
|
||||
}
|
||||
}
|
||||
|
||||
public static Set<Integer> getSupportedIntegerCommands() {
|
||||
return SUPPORTED_INTEGER_COMMANDS.keySet();
|
||||
}
|
||||
|
||||
public static Set<Integer> getSupportedFloatCommands() {
|
||||
return SUPPORTED_FLOAT_COMMANDS.keySet();
|
||||
}
|
||||
|
||||
public static OutputSemanticHandler<Integer> getIntegerCommand(int pid) {
|
||||
return SUPPORTED_INTEGER_COMMANDS.get(pid);
|
||||
}
|
||||
|
||||
public static OutputSemanticHandler<Float> getFloatCommand(int pid) {
|
||||
return SUPPORTED_FLOAT_COMMANDS.get(pid);
|
||||
}
|
||||
|
||||
static {
|
||||
addSupportedFloatCommands(
|
||||
new AmbientAirTemperature(),
|
||||
new CalculatedEngineLoad(),
|
||||
new FuelTankLevel(),
|
||||
new Bank2ShortTermFuelTrimCommand(),
|
||||
new Bank2LongTermFuelTrimCommand(),
|
||||
new Bank1LongTermFuelTrimCommand(),
|
||||
new Bank1ShortTermFuelTrimCommand(),
|
||||
new ThrottlePosition());
|
||||
addSupportedIntegerCommands(
|
||||
new EngineOilTemperature(),
|
||||
new EngineCoolantTemperature(),
|
||||
new FuelGaugePressure(),
|
||||
new FuelSystemStatus(),
|
||||
new RPM(),
|
||||
new EngineRuntime(),
|
||||
new Speed());
|
||||
}
|
||||
|
||||
protected final int mMode;
|
||||
protected final OutputSemanticHandler<ValueType> mSemanticHandler;
|
||||
|
||||
Obd2Command(int mode, OutputSemanticHandler<ValueType> semanticHandler) {
|
||||
mMode = mode;
|
||||
mSemanticHandler = Objects.requireNonNull(semanticHandler);
|
||||
}
|
||||
|
||||
public abstract Optional<ValueType> run(Obd2Connection connection) throws Exception;
|
||||
|
||||
public int getPid() {
|
||||
return mSemanticHandler.getPid();
|
||||
}
|
||||
|
||||
public static final <T> LiveFrameCommand<T> getLiveFrameCommand(OutputSemanticHandler handler) {
|
||||
return new LiveFrameCommand<>(handler);
|
||||
}
|
||||
|
||||
public static final <T> FreezeFrameCommand<T> getFreezeFrameCommand(
|
||||
OutputSemanticHandler handler, int frameId) {
|
||||
return new FreezeFrameCommand<>(handler, frameId);
|
||||
}
|
||||
|
||||
/**
|
||||
* An OBD2 command that returns live frame data.
|
||||
*
|
||||
* @param <ValueType> The Java type that represents the command's result type.
|
||||
*/
|
||||
public static class LiveFrameCommand<ValueType> extends Obd2Command<ValueType> {
|
||||
private static final int RESPONSE_MARKER = 0x41;
|
||||
|
||||
LiveFrameCommand(OutputSemanticHandler<ValueType> semanticHandler) {
|
||||
super(LIVE_FRAME, semanticHandler);
|
||||
}
|
||||
|
||||
public Optional<ValueType> run(Obd2Connection connection)
|
||||
throws IOException, InterruptedException {
|
||||
String command = String.format("%02X%02X", mMode, mSemanticHandler.getPid());
|
||||
int[] data = connection.run(command);
|
||||
IntegerArrayStream stream = new IntegerArrayStream(data);
|
||||
if (stream.expect(RESPONSE_MARKER, mSemanticHandler.getPid())) {
|
||||
return mSemanticHandler.consume(stream);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An OBD2 command that returns freeze frame data.
|
||||
*
|
||||
* @param <ValueType> The Java type that represents the command's result type.
|
||||
*/
|
||||
public static class FreezeFrameCommand<ValueType> extends Obd2Command<ValueType> {
|
||||
private static final int RESPONSE_MARKER = 0x42;
|
||||
|
||||
private int mFrameId;
|
||||
|
||||
FreezeFrameCommand(OutputSemanticHandler<ValueType> semanticHandler, int frameId) {
|
||||
super(FREEZE_FRAME, semanticHandler);
|
||||
mFrameId = frameId;
|
||||
}
|
||||
|
||||
public Optional<ValueType> run(Obd2Connection connection)
|
||||
throws IOException, InterruptedException {
|
||||
String command =
|
||||
String.format("%02X%02X %02X", mMode, mSemanticHandler.getPid(), mFrameId);
|
||||
int[] data = connection.run(command);
|
||||
IntegerArrayStream stream = new IntegerArrayStream(data);
|
||||
if (stream.expect(RESPONSE_MARKER, mSemanticHandler.getPid(), mFrameId)) {
|
||||
return mSemanticHandler.consume(stream);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,336 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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.car.obd2;
|
||||
|
||||
import android.util.Log;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/** This class represents a connection between Java code and a "vehicle" that talks OBD2. */
|
||||
public class Obd2Connection {
|
||||
private static final String TAG = Obd2Connection.class.getSimpleName();
|
||||
private static final boolean DBG = false;
|
||||
|
||||
/**
|
||||
* The transport layer that moves OBD2 requests from us to the remote entity and viceversa. It
|
||||
* is possible for this to be USB, Bluetooth, or just as simple as a pty for a simulator.
|
||||
*/
|
||||
public interface UnderlyingTransport {
|
||||
String getAddress();
|
||||
|
||||
boolean reconnect();
|
||||
|
||||
boolean isConnected();
|
||||
|
||||
InputStream getInputStream();
|
||||
|
||||
OutputStream getOutputStream();
|
||||
}
|
||||
|
||||
private final UnderlyingTransport mConnection;
|
||||
|
||||
private static final String[] initCommands =
|
||||
new String[] {"ATD", "ATZ", "AT E0", "AT L0", "AT S0", "AT H0", "AT SP 0"};
|
||||
|
||||
public Obd2Connection(UnderlyingTransport connection) {
|
||||
mConnection = Objects.requireNonNull(connection);
|
||||
runInitCommands();
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return mConnection.getAddress();
|
||||
}
|
||||
|
||||
private void runInitCommands() {
|
||||
for (final String initCommand : initCommands) {
|
||||
try {
|
||||
runImpl(initCommand);
|
||||
} catch (IOException | InterruptedException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean reconnect() {
|
||||
if (!mConnection.reconnect()) return false;
|
||||
runInitCommands();
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isConnected() {
|
||||
return mConnection.isConnected();
|
||||
}
|
||||
|
||||
static int toDigitValue(char c) {
|
||||
if ((c >= '0') && (c <= '9')) return c - '0';
|
||||
switch (c) {
|
||||
case 'a':
|
||||
case 'A':
|
||||
return 10;
|
||||
case 'b':
|
||||
case 'B':
|
||||
return 11;
|
||||
case 'c':
|
||||
case 'C':
|
||||
return 12;
|
||||
case 'd':
|
||||
case 'D':
|
||||
return 13;
|
||||
case 'e':
|
||||
case 'E':
|
||||
return 14;
|
||||
case 'f':
|
||||
case 'F':
|
||||
return 15;
|
||||
default:
|
||||
throw new IllegalArgumentException(c + " is not a valid hex digit");
|
||||
}
|
||||
}
|
||||
|
||||
int[] toHexValues(String buffer) {
|
||||
int[] values = new int[buffer.length() / 2];
|
||||
for (int i = 0; i < values.length; ++i) {
|
||||
values[i] =
|
||||
16 * toDigitValue(buffer.charAt(2 * i))
|
||||
+ toDigitValue(buffer.charAt(2 * i + 1));
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
private String runImpl(String command) throws IOException, InterruptedException {
|
||||
InputStream in = Objects.requireNonNull(mConnection.getInputStream());
|
||||
OutputStream out = Objects.requireNonNull(mConnection.getOutputStream());
|
||||
|
||||
if (DBG) {
|
||||
Log.i(TAG, "runImpl(" + command + ")");
|
||||
}
|
||||
|
||||
out.write((command + "\r").getBytes());
|
||||
out.flush();
|
||||
|
||||
StringBuilder response = new StringBuilder();
|
||||
while (true) {
|
||||
int value = in.read();
|
||||
if (value < 0) continue;
|
||||
char c = (char) value;
|
||||
// this is the prompt, stop here
|
||||
if (c == '>') break;
|
||||
if (c == '\r' || c == '\n' || c == ' ' || c == '\t' || c == '.') continue;
|
||||
response.append(c);
|
||||
}
|
||||
|
||||
String responseValue = response.toString();
|
||||
|
||||
if (DBG) {
|
||||
Log.i(TAG, "runImpl() returned " + responseValue);
|
||||
}
|
||||
|
||||
return responseValue;
|
||||
}
|
||||
|
||||
String removeSideData(String response, String... patterns) {
|
||||
for (String pattern : patterns) {
|
||||
if (response.contains(pattern)) response = response.replaceAll(pattern, "");
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
String unpackLongFrame(String response) {
|
||||
// long frames come back to us containing colon separated portions
|
||||
if (response.indexOf(':') < 0) return response;
|
||||
|
||||
// remove everything until the first colon
|
||||
response = response.substring(response.indexOf(':') + 1);
|
||||
|
||||
// then remove the <digit>: portions (sequential frame parts)
|
||||
//TODO(egranata): maybe validate the sequence of digits is progressive
|
||||
return response.replaceAll("[0-9]:", "");
|
||||
}
|
||||
|
||||
public int[] run(String command) throws IOException, InterruptedException {
|
||||
String responseValue = runImpl(command);
|
||||
String originalResponseValue = responseValue;
|
||||
String unspacedCommand = command.replaceAll(" ", "");
|
||||
if (responseValue.startsWith(unspacedCommand))
|
||||
responseValue = responseValue.substring(unspacedCommand.length());
|
||||
responseValue = unpackLongFrame(responseValue);
|
||||
|
||||
if (DBG) {
|
||||
Log.i(TAG, "post-processed response " + responseValue);
|
||||
}
|
||||
|
||||
//TODO(egranata): should probably handle these intelligently
|
||||
responseValue =
|
||||
removeSideData(
|
||||
responseValue,
|
||||
"SEARCHING",
|
||||
"ERROR",
|
||||
"BUS INIT",
|
||||
"BUSINIT",
|
||||
"BUS ERROR",
|
||||
"BUSERROR",
|
||||
"STOPPED");
|
||||
if (responseValue.equals("OK")) return new int[] {1};
|
||||
if (responseValue.equals("?")) return new int[] {0};
|
||||
if (responseValue.equals("NODATA")) return new int[] {};
|
||||
if (responseValue.equals("UNABLETOCONNECT")) throw new IOException("connection failure");
|
||||
if (responseValue.equals("CANERROR")) throw new IOException("CAN bus error");
|
||||
try {
|
||||
return toHexValues(responseValue);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.e(
|
||||
TAG,
|
||||
String.format(
|
||||
"conversion error: command: '%s', original response: '%s'"
|
||||
+ ", processed response: '%s'",
|
||||
command, originalResponseValue, responseValue));
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
static class FourByteBitSet {
|
||||
private static final int[] masks =
|
||||
new int[] {
|
||||
0b0000_0001,
|
||||
0b0000_0010,
|
||||
0b0000_0100,
|
||||
0b0000_1000,
|
||||
0b0001_0000,
|
||||
0b0010_0000,
|
||||
0b0100_0000,
|
||||
0b1000_0000
|
||||
};
|
||||
|
||||
private final byte mByte0;
|
||||
private final byte mByte1;
|
||||
private final byte mByte2;
|
||||
private final byte mByte3;
|
||||
|
||||
FourByteBitSet(byte b0, byte b1, byte b2, byte b3) {
|
||||
mByte0 = b0;
|
||||
mByte1 = b1;
|
||||
mByte2 = b2;
|
||||
mByte3 = b3;
|
||||
}
|
||||
|
||||
private byte getByte(int index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return mByte0;
|
||||
case 1:
|
||||
return mByte1;
|
||||
case 2:
|
||||
return mByte2;
|
||||
case 3:
|
||||
return mByte3;
|
||||
default:
|
||||
throw new IllegalArgumentException(index + " is not a valid byte index");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean getBit(byte b, int index) {
|
||||
if (index < 0 || index >= masks.length)
|
||||
throw new IllegalArgumentException(index + " is not a valid bit index");
|
||||
return 0 != (b & masks[index]);
|
||||
}
|
||||
|
||||
public boolean getBit(int b, int index) {
|
||||
return getBit(getByte(b), index);
|
||||
}
|
||||
}
|
||||
|
||||
public Set<Integer> getSupportedPIDs() throws IOException, InterruptedException {
|
||||
Set<Integer> result = new HashSet<>();
|
||||
String[] pids = new String[] {"0100", "0120", "0140", "0160"};
|
||||
int basePid = 1;
|
||||
for (String pid : pids) {
|
||||
int[] responseData = run(pid);
|
||||
if (responseData.length >= 6) {
|
||||
byte byte0 = (byte) (responseData[2] & 0xFF);
|
||||
byte byte1 = (byte) (responseData[3] & 0xFF);
|
||||
byte byte2 = (byte) (responseData[4] & 0xFF);
|
||||
byte byte3 = (byte) (responseData[5] & 0xFF);
|
||||
if (DBG) {
|
||||
Log.i(TAG, String.format("supported PID at base %d payload %02X%02X%02X%02X",
|
||||
basePid, byte0, byte1, byte2, byte3));
|
||||
}
|
||||
FourByteBitSet fourByteBitSet = new FourByteBitSet(byte0, byte1, byte2, byte3);
|
||||
for (int byteIndex = 0; byteIndex < 4; ++byteIndex) {
|
||||
for (int bitIndex = 7; bitIndex >= 0; --bitIndex) {
|
||||
if (fourByteBitSet.getBit(byteIndex, bitIndex)) {
|
||||
int command = basePid + 8 * byteIndex + 7 - bitIndex;
|
||||
if (DBG) {
|
||||
Log.i(TAG, "command " + command + " found supported");
|
||||
}
|
||||
result.add(command);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
basePid += 0x20;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
String getDiagnosticTroubleCode(IntegerArrayStream source) {
|
||||
final char[] components = new char[] {'P', 'C', 'B', 'U'};
|
||||
final char[] firstDigits = new char[] {'0', '1', '2', '3'};
|
||||
final char[] otherDigits =
|
||||
new char[] {
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
|
||||
};
|
||||
|
||||
StringBuilder builder = new StringBuilder(5);
|
||||
|
||||
int byte0 = source.consume();
|
||||
int byte1 = source.consume();
|
||||
|
||||
int componentMask = (byte0 & 0xC0) >> 6;
|
||||
int firstDigitMask = (byte0 & 0x30) >> 4;
|
||||
int secondDigitMask = (byte0 & 0x0F);
|
||||
int thirdDigitMask = (byte1 & 0xF0) >> 4;
|
||||
int fourthDigitMask = (byte1 & 0x0F);
|
||||
|
||||
builder.append(components[componentMask]);
|
||||
builder.append(firstDigits[firstDigitMask]);
|
||||
builder.append(otherDigits[secondDigitMask]);
|
||||
builder.append(otherDigits[thirdDigitMask]);
|
||||
builder.append(otherDigits[fourthDigitMask]);
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public List<String> getDiagnosticTroubleCodes() throws IOException, InterruptedException {
|
||||
List<String> result = new ArrayList<>();
|
||||
int[] response = run("03");
|
||||
IntegerArrayStream stream = new IntegerArrayStream(response);
|
||||
if (stream.isEmpty()) return result;
|
||||
if (!stream.expect(0x43))
|
||||
throw new IllegalArgumentException("data from remote end not a mode 3 response");
|
||||
int count = stream.consume();
|
||||
for (int i = 0; i < count; ++i) {
|
||||
result.add(getDiagnosticTroubleCode(stream));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,177 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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.car.obd2;
|
||||
|
||||
import android.os.SystemClock;
|
||||
import android.util.JsonWriter;
|
||||
import android.util.Log;
|
||||
import com.android.car.obd2.Obd2Command.FreezeFrameCommand;
|
||||
import com.android.car.obd2.Obd2Command.OutputSemanticHandler;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
public class Obd2FreezeFrameGenerator {
|
||||
public static final String FRAME_TYPE_FREEZE = "freeze";
|
||||
public static final String TAG = Obd2FreezeFrameGenerator.class.getSimpleName();
|
||||
|
||||
private final Obd2Connection mConnection;
|
||||
private final List<OutputSemanticHandler<Integer>> mIntegerCommands = new ArrayList<>();
|
||||
private final List<OutputSemanticHandler<Float>> mFloatCommands = new ArrayList<>();
|
||||
|
||||
private List<String> mPreviousDtcs = new ArrayList<>();
|
||||
|
||||
public Obd2FreezeFrameGenerator(Obd2Connection connection)
|
||||
throws IOException, InterruptedException {
|
||||
mConnection = connection;
|
||||
Set<Integer> connectionPids = connection.getSupportedPIDs();
|
||||
Set<Integer> apiIntegerPids = Obd2Command.getSupportedIntegerCommands();
|
||||
Set<Integer> apiFloatPids = Obd2Command.getSupportedFloatCommands();
|
||||
apiIntegerPids
|
||||
.stream()
|
||||
.filter(connectionPids::contains)
|
||||
.forEach((Integer pid) -> mIntegerCommands.add(Obd2Command.getIntegerCommand(pid)));
|
||||
apiFloatPids
|
||||
.stream()
|
||||
.filter(connectionPids::contains)
|
||||
.forEach((Integer pid) -> mFloatCommands.add(Obd2Command.getFloatCommand(pid)));
|
||||
Log.i(
|
||||
TAG,
|
||||
String.format(
|
||||
"connectionPids = %s\napiIntegerPids=%s\napiFloatPids = %s\n"
|
||||
+ "mIntegerCommands = %s\nmFloatCommands = %s\n",
|
||||
connectionPids,
|
||||
apiIntegerPids,
|
||||
apiFloatPids,
|
||||
mIntegerCommands,
|
||||
mFloatCommands));
|
||||
}
|
||||
|
||||
public JsonWriter generate(JsonWriter jsonWriter) throws IOException, InterruptedException {
|
||||
return generate(jsonWriter, SystemClock.elapsedRealtimeNanos());
|
||||
}
|
||||
|
||||
// OBD2 does not have a notion of timestamping the fault codes
|
||||
// As such, we need to perform additional magic in order to figure out
|
||||
// whether a fault code we retrieved is the same as a fault code we already
|
||||
// saw in a past iteration. The logic goes as follows:
|
||||
// for every position i in currentDtcs, if mPreviousDtcs[i] is the same
|
||||
// fault code, then assume they are identical. If they are not the same fault code,
|
||||
// then everything in currentDtcs[i...size()) is assumed to be a new fault code as
|
||||
// something in the list must have moved around; if currentDtcs is shorter than
|
||||
// mPreviousDtcs then obviously exit at the end of currentDtcs; if currentDtcs
|
||||
// is longer, however, anything in currentDtcs past the end of mPreviousDtcs is a new
|
||||
// fault code and will be included
|
||||
private final class FreezeFrameIdentity {
|
||||
public final String dtc;
|
||||
public final int id;
|
||||
|
||||
FreezeFrameIdentity(String dtc, int id) {
|
||||
this.dtc = dtc;
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
private List<FreezeFrameIdentity> discoverNewDtcs(List<String> currentDtcs) {
|
||||
List<FreezeFrameIdentity> newDtcs = new ArrayList<>();
|
||||
int currentIndex = 0;
|
||||
boolean inCopyAllMode = false;
|
||||
|
||||
for (; currentIndex < currentDtcs.size(); ++currentIndex) {
|
||||
if (currentIndex == mPreviousDtcs.size()) {
|
||||
// we have more current DTCs than previous DTCs, copy everything
|
||||
inCopyAllMode = true;
|
||||
break;
|
||||
}
|
||||
if (!currentDtcs.get(currentIndex).equals(mPreviousDtcs.get(currentIndex))) {
|
||||
// we found a different DTC, copy everything
|
||||
inCopyAllMode = true;
|
||||
break;
|
||||
}
|
||||
// same DTC, not at end of either list yet, keep looping
|
||||
}
|
||||
|
||||
if (inCopyAllMode) {
|
||||
for (; currentIndex < currentDtcs.size(); ++currentIndex) {
|
||||
newDtcs.add(new FreezeFrameIdentity(currentDtcs.get(currentIndex), currentIndex));
|
||||
}
|
||||
}
|
||||
|
||||
return newDtcs;
|
||||
}
|
||||
|
||||
public JsonWriter generate(JsonWriter jsonWriter, long timestamp)
|
||||
throws IOException, InterruptedException {
|
||||
List<String> currentDtcs = mConnection.getDiagnosticTroubleCodes();
|
||||
List<FreezeFrameIdentity> newDtcs = discoverNewDtcs(currentDtcs);
|
||||
mPreviousDtcs = currentDtcs;
|
||||
for (FreezeFrameIdentity freezeFrame : newDtcs) {
|
||||
jsonWriter.beginObject();
|
||||
jsonWriter.name("type").value(FRAME_TYPE_FREEZE);
|
||||
jsonWriter.name("timestamp").value(timestamp);
|
||||
jsonWriter.name("stringValue").value(freezeFrame.dtc);
|
||||
jsonWriter.name("intValues").beginArray();
|
||||
for (OutputSemanticHandler<Integer> handler : mIntegerCommands) {
|
||||
FreezeFrameCommand<Integer> command =
|
||||
Obd2Command.getFreezeFrameCommand(handler, freezeFrame.id);
|
||||
try {
|
||||
Optional<Integer> result = command.run(mConnection);
|
||||
if (result.isPresent()) {
|
||||
jsonWriter.beginObject();
|
||||
jsonWriter.name("id").value(command.getPid());
|
||||
jsonWriter.name("value").value(result.get());
|
||||
jsonWriter.endObject();
|
||||
}
|
||||
} catch (IOException | InterruptedException e) {
|
||||
Log.w(
|
||||
TAG,
|
||||
String.format(
|
||||
"unable to retrieve OBD2 pid %d due to exception: %s",
|
||||
command.getPid(), e));
|
||||
// skip this entry
|
||||
}
|
||||
}
|
||||
jsonWriter.endArray();
|
||||
jsonWriter.name("floatValues").beginArray();
|
||||
for (OutputSemanticHandler<Float> handler : mFloatCommands) {
|
||||
FreezeFrameCommand<Float> command =
|
||||
Obd2Command.getFreezeFrameCommand(handler, freezeFrame.id);
|
||||
try {
|
||||
Optional<Float> result = command.run(mConnection);
|
||||
if (result.isPresent()) {
|
||||
jsonWriter.beginObject();
|
||||
jsonWriter.name("id").value(command.getPid());
|
||||
jsonWriter.name("value").value(result.get());
|
||||
jsonWriter.endObject();
|
||||
}
|
||||
} catch (IOException | InterruptedException e) {
|
||||
Log.w(
|
||||
TAG,
|
||||
String.format(
|
||||
"unable to retrieve OBD2 pid %d due to exception: %s",
|
||||
command.getPid(), e));
|
||||
// skip this entry
|
||||
}
|
||||
}
|
||||
jsonWriter.endArray();
|
||||
jsonWriter.endObject();
|
||||
}
|
||||
return jsonWriter;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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.car.obd2;
|
||||
|
||||
import android.os.SystemClock;
|
||||
import android.util.JsonWriter;
|
||||
import android.util.Log;
|
||||
import com.android.car.obd2.Obd2Command.LiveFrameCommand;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
public class Obd2LiveFrameGenerator {
|
||||
public static final String FRAME_TYPE_LIVE = "live";
|
||||
public static final String TAG = Obd2LiveFrameGenerator.class.getSimpleName();
|
||||
|
||||
private final Obd2Connection mConnection;
|
||||
private final List<LiveFrameCommand<Integer>> mIntegerCommands = new ArrayList<>();
|
||||
private final List<LiveFrameCommand<Float>> mFloatCommands = new ArrayList<>();
|
||||
|
||||
public Obd2LiveFrameGenerator(Obd2Connection connection)
|
||||
throws IOException, InterruptedException {
|
||||
mConnection = connection;
|
||||
Set<Integer> connectionPids = connection.getSupportedPIDs();
|
||||
Set<Integer> apiIntegerPids = Obd2Command.getSupportedIntegerCommands();
|
||||
Set<Integer> apiFloatPids = Obd2Command.getSupportedFloatCommands();
|
||||
apiIntegerPids
|
||||
.stream()
|
||||
.filter(connectionPids::contains)
|
||||
.forEach(
|
||||
(Integer pid) ->
|
||||
mIntegerCommands.add(
|
||||
Obd2Command.getLiveFrameCommand(
|
||||
Obd2Command.getIntegerCommand(pid))));
|
||||
apiFloatPids
|
||||
.stream()
|
||||
.filter(connectionPids::contains)
|
||||
.forEach(
|
||||
(Integer pid) ->
|
||||
mFloatCommands.add(
|
||||
Obd2Command.getLiveFrameCommand(
|
||||
Obd2Command.getFloatCommand(pid))));
|
||||
Log.i(
|
||||
TAG,
|
||||
String.format(
|
||||
"connectionPids = %s\napiIntegerPids=%s\napiFloatPids = %s\n"
|
||||
+ "mIntegerCommands = %s\nmFloatCommands = %s\n",
|
||||
connectionPids,
|
||||
apiIntegerPids,
|
||||
apiFloatPids,
|
||||
mIntegerCommands,
|
||||
mFloatCommands));
|
||||
}
|
||||
|
||||
public JsonWriter generate(JsonWriter jsonWriter) throws IOException {
|
||||
return generate(jsonWriter, SystemClock.elapsedRealtimeNanos());
|
||||
}
|
||||
|
||||
public JsonWriter generate(JsonWriter jsonWriter, long timestamp) throws IOException {
|
||||
jsonWriter.beginObject();
|
||||
jsonWriter.name("type").value(FRAME_TYPE_LIVE);
|
||||
jsonWriter.name("timestamp").value(timestamp);
|
||||
jsonWriter.name("intValues").beginArray();
|
||||
for (LiveFrameCommand<Integer> command : mIntegerCommands) {
|
||||
try {
|
||||
Optional<Integer> result = command.run(mConnection);
|
||||
if (result.isPresent()) {
|
||||
jsonWriter.beginObject();
|
||||
jsonWriter.name("id").value(command.getPid());
|
||||
jsonWriter.name("value").value(result.get());
|
||||
jsonWriter.endObject();
|
||||
}
|
||||
} catch (IOException | InterruptedException e) {
|
||||
Log.w(
|
||||
TAG,
|
||||
String.format(
|
||||
"unable to retrieve OBD2 pid %d due to exception: %s",
|
||||
command.getPid(), e));
|
||||
// skip this entry
|
||||
}
|
||||
}
|
||||
jsonWriter.endArray();
|
||||
|
||||
jsonWriter.name("floatValues").beginArray();
|
||||
for (LiveFrameCommand<Float> command : mFloatCommands) {
|
||||
try {
|
||||
Optional<Float> result = command.run(mConnection);
|
||||
if (result.isPresent()) {
|
||||
jsonWriter.beginObject();
|
||||
jsonWriter.name("id").value(command.getPid());
|
||||
jsonWriter.name("value").value(result.get());
|
||||
jsonWriter.endObject();
|
||||
}
|
||||
} catch (IOException | InterruptedException e) {
|
||||
Log.w(
|
||||
TAG,
|
||||
String.format(
|
||||
"unable to retrieve OBD2 pid %d due to exception: %s",
|
||||
command.getPid(), e));
|
||||
// skip this entry
|
||||
}
|
||||
}
|
||||
jsonWriter.endArray();
|
||||
|
||||
return jsonWriter.endObject();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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.car.obd2.commands;
|
||||
|
||||
import com.android.car.obd2.IntegerArrayStream;
|
||||
import com.android.car.obd2.Obd2Command;
|
||||
import java.util.Optional;
|
||||
|
||||
public class AmbientAirTemperature implements Obd2Command.OutputSemanticHandler<Float> {
|
||||
@Override
|
||||
public int getPid() {
|
||||
return 0x46;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Float> consume(IntegerArrayStream data) {
|
||||
return data.hasAtLeast(
|
||||
1,
|
||||
theData -> Optional.of(theData.consume() * (100.0f / 255.0f)),
|
||||
theData -> Optional.<Float>empty());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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.car.obd2.commands;
|
||||
|
||||
import com.android.car.obd2.IntegerArrayStream;
|
||||
import com.android.car.obd2.Obd2Command;
|
||||
import java.util.Optional;
|
||||
|
||||
public class CalculatedEngineLoad implements Obd2Command.OutputSemanticHandler<Float> {
|
||||
@Override
|
||||
public int getPid() {
|
||||
return 0x04;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Float> consume(IntegerArrayStream data) {
|
||||
return data.hasAtLeast(
|
||||
1,
|
||||
theData -> Optional.of(theData.consume() / 2.25f),
|
||||
theData -> Optional.<Float>empty());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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.car.obd2.commands;
|
||||
|
||||
import com.android.car.obd2.IntegerArrayStream;
|
||||
import com.android.car.obd2.Obd2Command;
|
||||
import java.util.Optional;
|
||||
|
||||
public class EngineCoolantTemperature implements Obd2Command.OutputSemanticHandler<Integer> {
|
||||
@Override
|
||||
public int getPid() {
|
||||
return 0x05;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Integer> consume(IntegerArrayStream data) {
|
||||
return data.hasAtLeast(
|
||||
1,
|
||||
theData -> Optional.of(theData.consume() - 40),
|
||||
theData -> Optional.<Integer>empty());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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.car.obd2.commands;
|
||||
|
||||
import com.android.car.obd2.IntegerArrayStream;
|
||||
import com.android.car.obd2.Obd2Command;
|
||||
import java.util.Optional;
|
||||
|
||||
public class EngineOilTemperature implements Obd2Command.OutputSemanticHandler<Integer> {
|
||||
@Override
|
||||
public int getPid() {
|
||||
return 0x5C;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Integer> consume(IntegerArrayStream data) {
|
||||
return data.hasAtLeast(
|
||||
1,
|
||||
theData -> Optional.of(theData.consume() - 40),
|
||||
theData -> Optional.<Integer>empty());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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.car.obd2.commands;
|
||||
|
||||
import com.android.car.obd2.IntegerArrayStream;
|
||||
import com.android.car.obd2.Obd2Command;
|
||||
import java.util.Optional;
|
||||
|
||||
public class EngineRuntime implements Obd2Command.OutputSemanticHandler<Integer> {
|
||||
@Override
|
||||
public int getPid() {
|
||||
return 0x1F;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Integer> consume(IntegerArrayStream data) {
|
||||
return data.hasAtLeast(
|
||||
2,
|
||||
theData -> Optional.of(theData.consume() * 256 + theData.consume()),
|
||||
theData -> Optional.<Integer>empty());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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.car.obd2.commands;
|
||||
|
||||
import com.android.car.obd2.IntegerArrayStream;
|
||||
import com.android.car.obd2.Obd2Command;
|
||||
import java.util.Optional;
|
||||
|
||||
public class FuelGaugePressure implements Obd2Command.OutputSemanticHandler<Integer> {
|
||||
@Override
|
||||
public int getPid() {
|
||||
return 0x0A;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Integer> consume(IntegerArrayStream data) {
|
||||
return data.hasAtLeast(
|
||||
1,
|
||||
theData -> Optional.of(theData.consume() * 3),
|
||||
theData -> Optional.<Integer>empty());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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.car.obd2.commands;
|
||||
|
||||
import com.android.car.obd2.IntegerArrayStream;
|
||||
import com.android.car.obd2.Obd2Command;
|
||||
import java.util.Optional;
|
||||
|
||||
public class FuelSystemStatus implements Obd2Command.OutputSemanticHandler<Integer> {
|
||||
@Override
|
||||
public int getPid() {
|
||||
return 0x03;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Integer> consume(IntegerArrayStream data) {
|
||||
return data.hasAtLeast(
|
||||
1,
|
||||
theData -> Optional.of(theData.consume()),
|
||||
theData -> Optional.<Integer>empty());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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.car.obd2.commands;
|
||||
|
||||
import com.android.car.obd2.IntegerArrayStream;
|
||||
import com.android.car.obd2.Obd2Command;
|
||||
import java.util.Optional;
|
||||
|
||||
public class FuelTankLevel implements Obd2Command.OutputSemanticHandler<Float> {
|
||||
@Override
|
||||
public int getPid() {
|
||||
return 0x2F;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Float> consume(IntegerArrayStream data) {
|
||||
return data.hasAtLeast(
|
||||
1,
|
||||
theData -> Optional.of(theData.consume() * (100.0f / 255.0f)),
|
||||
theData -> Optional.<Float>empty());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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.car.obd2.commands;
|
||||
|
||||
import com.android.car.obd2.IntegerArrayStream;
|
||||
import com.android.car.obd2.Obd2Command;
|
||||
import java.util.Optional;
|
||||
|
||||
public abstract class FuelTrimCommand implements Obd2Command.OutputSemanticHandler<Float> {
|
||||
@Override
|
||||
public Optional<Float> consume(IntegerArrayStream data) {
|
||||
return data.hasAtLeast(
|
||||
1,
|
||||
theData -> Optional.of(3 * theData.consume() / 1.28f - 100),
|
||||
theData -> Optional.<Float>empty());
|
||||
}
|
||||
|
||||
public static class Bank1ShortTermFuelTrimCommand extends FuelTrimCommand {
|
||||
@Override
|
||||
public int getPid() {
|
||||
return 0x06;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Bank1LongTermFuelTrimCommand extends FuelTrimCommand {
|
||||
@Override
|
||||
public int getPid() {
|
||||
return 0x07;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Bank2ShortTermFuelTrimCommand extends FuelTrimCommand {
|
||||
@Override
|
||||
public int getPid() {
|
||||
return 0x08;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Bank2LongTermFuelTrimCommand extends FuelTrimCommand {
|
||||
@Override
|
||||
public int getPid() {
|
||||
return 0x09;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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.car.obd2.commands;
|
||||
|
||||
import com.android.car.obd2.IntegerArrayStream;
|
||||
import com.android.car.obd2.Obd2Command;
|
||||
import java.util.Optional;
|
||||
|
||||
public class RPM implements Obd2Command.OutputSemanticHandler<Integer> {
|
||||
@Override
|
||||
public int getPid() {
|
||||
return 0x0C;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Integer> consume(IntegerArrayStream data) {
|
||||
return data.hasAtLeast(
|
||||
2,
|
||||
theData -> Optional.of((theData.consume() * 256 + theData.consume()) / 4),
|
||||
theData -> Optional.<Integer>empty());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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.car.obd2.commands;
|
||||
|
||||
import com.android.car.obd2.IntegerArrayStream;
|
||||
import com.android.car.obd2.Obd2Command;
|
||||
import java.util.Optional;
|
||||
|
||||
public class Speed implements Obd2Command.OutputSemanticHandler<Integer> {
|
||||
@Override
|
||||
public int getPid() {
|
||||
return 0x0D;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Integer> consume(IntegerArrayStream data) {
|
||||
return data.hasAtLeast(
|
||||
1,
|
||||
theData -> Optional.of(theData.consume()),
|
||||
theData -> Optional.<Integer>empty());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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.car.obd2.commands;
|
||||
|
||||
import com.android.car.obd2.IntegerArrayStream;
|
||||
import com.android.car.obd2.Obd2Command;
|
||||
import java.util.Optional;
|
||||
|
||||
public class ThrottlePosition implements Obd2Command.OutputSemanticHandler<Float> {
|
||||
@Override
|
||||
public int getPid() {
|
||||
return 0x11;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Float> consume(IntegerArrayStream data) {
|
||||
return data.hasAtLeast(
|
||||
1,
|
||||
theData -> Optional.of(theData.consume() * 0.392157f),
|
||||
theData -> Optional.<Float>empty());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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.car.obd2.connections;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothSocket;
|
||||
import android.util.Log;
|
||||
import com.android.car.obd2.Obd2Connection;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
public class BluetoothConnection implements Obd2Connection.UnderlyingTransport {
|
||||
|
||||
/**
|
||||
* This is the well-known UUID for the Bluetooth SPP (Serial Port Profile)
|
||||
*/
|
||||
private static final UUID SERIAL_PORT_PROFILE = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
|
||||
|
||||
private final BluetoothDevice mDevice;
|
||||
private BluetoothSocket mSocket = null;
|
||||
|
||||
public static final String TAG = BluetoothConnection.class.getSimpleName();
|
||||
|
||||
public BluetoothConnection(String bluetoothAddress) {
|
||||
this(BluetoothAdapter.getDefaultAdapter().getRemoteDevice(bluetoothAddress));
|
||||
}
|
||||
|
||||
public BluetoothConnection(BluetoothDevice device) {
|
||||
mDevice = Objects.requireNonNull(device);
|
||||
connect();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAddress() {
|
||||
return mDevice.getAddress();
|
||||
}
|
||||
|
||||
/**
|
||||
* Establish an RFCOMM connection to the remote device.
|
||||
*
|
||||
* Assumes there is no existing connection.
|
||||
*
|
||||
* This method may take time to return (or even not return in pathological cases).
|
||||
* It is a good idea to wrap it in some kind of Promise-like object.
|
||||
*
|
||||
* @return true if it could connect, false otherwise
|
||||
*/
|
||||
private boolean connect() {
|
||||
try {
|
||||
mSocket = mDevice.createRfcommSocketToServiceRecord(SERIAL_PORT_PROFILE);
|
||||
mSocket.connect();
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "BluetoothConnection couldn't be established due to an exception: " + e);
|
||||
mSocket = null;
|
||||
return false;
|
||||
}
|
||||
return mSocket.isConnected();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
return mSocket != null && mSocket.isConnected();
|
||||
}
|
||||
|
||||
private void close() {
|
||||
if (isConnected()) {
|
||||
try {
|
||||
mSocket.close();
|
||||
} catch (IOException e) {
|
||||
// we are letting go of the connection anyway, so log and continue
|
||||
Log.w(TAG, "IOException during BluetoothSocket close(): " + e);
|
||||
} finally {
|
||||
mSocket = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean reconnect() {
|
||||
close();
|
||||
return connect();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() {
|
||||
if (isConnected()) {
|
||||
try {
|
||||
return mSocket.getInputStream();
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "failed to get Bluetooth input stream: " + e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream getOutputStream() {
|
||||
if (isConnected()) {
|
||||
try {
|
||||
return mSocket.getOutputStream();
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "failed to get Bluetooth output stream: " + e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue