Skip to content

Commit ddbf089

Browse files
authored
Merge pull request #24 from PureSwift/feature/hardware-sensor
Add `AndroidHardware` target
2 parents 83e1b48 + 0d2bb34 commit ddbf089

11 files changed

Lines changed: 810 additions & 26 deletions

File tree

Package.swift

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ var package = Package(
7575
),
7676
.library(
7777
name: "AndroidNDK", targets: ["AndroidNDK"]
78+
),
79+
.library(
80+
name: "AndroidHardware", targets: ["AndroidHardware"]
7881
)
7982
],
8083
dependencies: [
@@ -145,7 +148,8 @@ var package = Package(
145148
"AndroidWidget",
146149
"AndroidWebKit",
147150
"AndroidLogging",
148-
"AndroidLooper"
151+
"AndroidLooper",
152+
"AndroidHardware"
149153
],
150154
swiftSettings: [
151155
.swiftLanguageMode(.v5),
@@ -448,6 +452,21 @@ var package = Package(
448452
linkerSettings: [
449453
.linkedLibrary("android", .when(platforms: [.android]))
450454
]
455+
),
456+
.target(
457+
name: "AndroidHardware",
458+
dependencies: [
459+
"AndroidNDK",
460+
"AndroidLooper"
461+
],
462+
swiftSettings: [
463+
.swiftLanguageMode(.v6),
464+
ndkVersionDefine,
465+
sdkVersionDefine
466+
],
467+
linkerSettings: [
468+
.linkedLibrary("android", .when(platforms: [.android]))
469+
]
451470
)
452471
],
453472
swiftLanguageModes: [.v5, .v6]

Sources/AndroidBinder/Error.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,4 +125,17 @@ internal extension binder_status_t {
125125
let error = AndroidBinderError(errorCode, file: file, function: function)
126126
return .failure(error)
127127
}
128+
129+
func mapError<T>(
130+
as _: T.Type,
131+
file: StaticString = #file,
132+
function: StaticString = #function
133+
) -> Result<T, AndroidBinderError> {
134+
guard self != STATUS_OK else {
135+
fatalError("mapError(as:) must only be used for non-STATUS_OK results.")
136+
}
137+
let errorCode = AndroidBinderError.ErrorCode(rawValue: self)
138+
let error = AndroidBinderError(errorCode, file: file, function: function)
139+
return .failure(error)
140+
}
128141
}

Sources/AndroidBinder/Parcel.swift

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,8 @@ public extension Parcel {
135135
func marshal(start: Int = 0, length: Int? = nil) throws(AndroidBinderError) -> [UInt8] {
136136
let len = length ?? (dataSize - start)
137137
var buffer = [UInt8](repeating: 0, count: len)
138-
try buffer.withUnsafeMutableBufferPointer { buf in
139-
if let base = buf.baseAddress {
140-
try handle.marshal(into: base, start: start, length: len).get()
141-
}
138+
if let base = buffer.withUnsafeMutableBufferPointer({ $0.baseAddress }) {
139+
try handle.marshal(into: base, start: start, length: len).get()
142140
}
143141
return buffer
144142
}
@@ -151,10 +149,8 @@ public extension Parcel {
151149
* \param data the bytes to unmarshal.
152150
*/
153151
func unmarshal(_ data: [UInt8]) throws(AndroidBinderError) {
154-
try data.withUnsafeBufferPointer { buf in
155-
if let base = buf.baseAddress {
156-
try handle.unmarshal(base, length: data.count).get()
157-
}
152+
if let base = data.withUnsafeBufferPointer({ $0.baseAddress }) {
153+
try handle.unmarshal(base, length: data.count).get()
158154
}
159155
}
160156
}
@@ -800,24 +796,25 @@ internal extension Parcel.Handle {
800796
func readString() -> Result<String, AndroidBinderError> {
801797
var ctx = ParcelStringReadContext(buffer: nil)
802798
let status = withUnsafeMutablePointer(to: &ctx) { ctxPtr -> binder_status_t in
803-
AParcel_readString(pointer, ctxPtr) { userData, length -> UnsafeMutablePointer<CChar>? in
804-
guard let userData = userData else { return nil }
799+
AParcel_readString(pointer, ctxPtr) { userData, length, outBuffer -> Bool in
800+
guard let userData = userData else { return false }
805801
let buf = UnsafeMutablePointer<CChar>.allocate(capacity: Int(length) + 1)
806802
buf[Int(length)] = 0
807803
userData.assumingMemoryBound(to: ParcelStringReadContext.self).pointee.buffer = buf
808-
return buf
804+
outBuffer?.pointee = buf
805+
return true
809806
}
810807
}
811808
defer { ctx.buffer?.deallocate() }
812-
guard status == STATUS_OK else { return status.mapError() }
809+
guard status == STATUS_OK else { return status.mapError(as: String.self) }
813810
guard let buf = ctx.buffer else { return .success("") }
814811
return .success(String(cString: buf))
815812
}
816813

817814
func readStrongBinder() -> Result<AndroidBinder, AndroidBinderError> {
818815
var binderPtr: OpaquePointer? = nil
819816
let status = AParcel_readStrongBinder(pointer, &binderPtr)
820-
guard status == STATUS_OK else { return status.mapError() }
817+
guard status == STATUS_OK else { return status.mapError(as: AndroidBinder.self) }
821818
guard let ptr = binderPtr else {
822819
return .failure(AndroidBinderError(AndroidBinderError.ErrorCode.unexpectedNull))
823820
}
@@ -832,7 +829,7 @@ internal extension Parcel.Handle {
832829
func readStatusHeader() -> Result<Status, AndroidBinderError> {
833830
var statusPtr: OpaquePointer? = nil
834831
let statusCode = AParcel_readStatusHeader(pointer, &statusPtr)
835-
guard statusCode == STATUS_OK else { return statusCode.mapError() }
832+
guard statusCode == STATUS_OK else { return statusCode.mapError(as: Status.self) }
836833
guard let ptr = statusPtr else {
837834
return .failure(AndroidBinderError(AndroidBinderError.ErrorCode.unexpectedNull))
838835
}
@@ -979,7 +976,7 @@ internal extension Parcel.Handle {
979976
}
980977
}
981978
defer { ctx.buffer?.deallocate() }
982-
guard status == STATUS_OK else { return status.mapError() }
979+
guard status == STATUS_OK else { return status.mapError(as: [Int8]?.self) }
983980
if ctx.isNull { return .success(nil) }
984981
if let buf = ctx.buffer {
985982
return .success(Array(UnsafeBufferPointer(start: buf, count: Int(ctx.count))))
@@ -1004,7 +1001,7 @@ internal extension Parcel.Handle {
10041001
}
10051002
}
10061003
defer { ctx.buffer?.deallocate() }
1007-
guard status == STATUS_OK else { return status.mapError() }
1004+
guard status == STATUS_OK else { return status.mapError(as: [Int32]?.self) }
10081005
if ctx.isNull { return .success(nil) }
10091006
if let buf = ctx.buffer {
10101007
return .success(Array(UnsafeBufferPointer(start: buf, count: Int(ctx.count))))
@@ -1029,7 +1026,7 @@ internal extension Parcel.Handle {
10291026
}
10301027
}
10311028
defer { ctx.buffer?.deallocate() }
1032-
guard status == STATUS_OK else { return status.mapError() }
1029+
guard status == STATUS_OK else { return status.mapError(as: [UInt32]?.self) }
10331030
if ctx.isNull { return .success(nil) }
10341031
if let buf = ctx.buffer {
10351032
return .success(Array(UnsafeBufferPointer(start: buf, count: Int(ctx.count))))
@@ -1054,7 +1051,7 @@ internal extension Parcel.Handle {
10541051
}
10551052
}
10561053
defer { ctx.buffer?.deallocate() }
1057-
guard status == STATUS_OK else { return status.mapError() }
1054+
guard status == STATUS_OK else { return status.mapError(as: [Int64]?.self) }
10581055
if ctx.isNull { return .success(nil) }
10591056
if let buf = ctx.buffer {
10601057
return .success(Array(UnsafeBufferPointer(start: buf, count: Int(ctx.count))))
@@ -1079,7 +1076,7 @@ internal extension Parcel.Handle {
10791076
}
10801077
}
10811078
defer { ctx.buffer?.deallocate() }
1082-
guard status == STATUS_OK else { return status.mapError() }
1079+
guard status == STATUS_OK else { return status.mapError(as: [UInt64]?.self) }
10831080
if ctx.isNull { return .success(nil) }
10841081
if let buf = ctx.buffer {
10851082
return .success(Array(UnsafeBufferPointer(start: buf, count: Int(ctx.count))))
@@ -1104,7 +1101,7 @@ internal extension Parcel.Handle {
11041101
}
11051102
}
11061103
defer { ctx.buffer?.deallocate() }
1107-
guard status == STATUS_OK else { return status.mapError() }
1104+
guard status == STATUS_OK else { return status.mapError(as: [Float]?.self) }
11081105
if ctx.isNull { return .success(nil) }
11091106
if let buf = ctx.buffer {
11101107
return .success(Array(UnsafeBufferPointer(start: buf, count: Int(ctx.count))))
@@ -1129,7 +1126,7 @@ internal extension Parcel.Handle {
11291126
}
11301127
}
11311128
defer { ctx.buffer?.deallocate() }
1132-
guard status == STATUS_OK else { return status.mapError() }
1129+
guard status == STATUS_OK else { return status.mapError(as: [Double]?.self) }
11331130
if ctx.isNull { return .success(nil) }
11341131
if let buf = ctx.buffer {
11351132
return .success(Array(UnsafeBufferPointer(start: buf, count: Int(ctx.count))))
@@ -1154,7 +1151,7 @@ internal extension Parcel.Handle {
11541151
}
11551152
}
11561153
defer { ctx.buffer?.deallocate() }
1157-
guard status == STATUS_OK else { return status.mapError() }
1154+
guard status == STATUS_OK else { return status.mapError(as: [UInt16]?.self) }
11581155
if ctx.isNull { return .success(nil) }
11591156
if let buf = ctx.buffer {
11601157
return .success(Array(UnsafeBufferPointer(start: buf, count: Int(ctx.count))))
@@ -1184,7 +1181,7 @@ internal extension Parcel.Handle {
11841181
)
11851182
}
11861183
defer { ctx.elements?.deallocate() }
1187-
guard status == STATUS_OK else { return status.mapError() }
1184+
guard status == STATUS_OK else { return status.mapError(as: [Bool]?.self) }
11881185
if ctx.isNull { return .success(nil) }
11891186
if let elements = ctx.elements {
11901187
return .success(Array(UnsafeBufferPointer(start: elements, count: Int(ctx.count))))
@@ -1255,4 +1252,4 @@ private struct ParcelUInt16ArrayReadContext {
12551252
var buffer: UnsafeMutablePointer<UInt16>?
12561253
var count: Int32
12571254
var isNull: Bool
1258-
}
1255+
}

Sources/AndroidBinder/Syscalls.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ func AParcel_readDouble(_ parcel: OpaquePointer, _ outValue: UnsafeMutablePointe
8181
func AParcel_readBool(_ parcel: OpaquePointer, _ outValue: UnsafeMutablePointer<Bool>) -> binder_status_t { stub() }
8282
func AParcel_readChar(_ parcel: OpaquePointer, _ outValue: UnsafeMutablePointer<UInt16>) -> binder_status_t { stub() }
8383
func AParcel_readByte(_ parcel: OpaquePointer, _ outValue: UnsafeMutablePointer<Int8>) -> binder_status_t { stub() }
84-
func AParcel_readString(_ parcel: OpaquePointer, _ stringData: UnsafeMutableRawPointer?, _ allocator: (@convention(c) (UnsafeMutableRawPointer?, Int32) -> UnsafeMutablePointer<CChar>?)?) -> binder_status_t { stub() }
84+
func AParcel_readString(_ parcel: OpaquePointer, _ stringData: UnsafeMutableRawPointer?, _ allocator: (@convention(c) (UnsafeMutableRawPointer?, Int32, UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?) -> Bool)?) -> binder_status_t { stub() }
8585
func AParcel_readStrongBinder(_ parcel: OpaquePointer, _ outBinder: UnsafeMutablePointer<OpaquePointer?>) -> binder_status_t { stub() }
8686
func AParcel_readParcelFileDescriptor(_ parcel: OpaquePointer, _ outFd: UnsafeMutablePointer<Int32>) -> binder_status_t { stub() }
8787
func AParcel_readStatusHeader(_ parcel: OpaquePointer, _ outStatus: UnsafeMutablePointer<OpaquePointer?>) -> binder_status_t { stub() }
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//
2+
// Error.swift
3+
// SwiftAndroid
4+
//
5+
// Created by Alsey Coleman Miller on 7/6/25.
6+
//
7+
8+
/// Android Sensor Error
9+
public enum AndroidSensorError: Swift.Error {
10+
11+
/// Unable to get sensor manager instance.
12+
case invalidManager
13+
14+
/// Unable to create event queue.
15+
case createEventQueue
16+
17+
/// Unable to enable sensor (result code).
18+
case enableSensor(Int32)
19+
20+
/// Unable to disable sensor (result code).
21+
case disableSensor(Int32)
22+
23+
/// Unable to register sensor (result code).
24+
case registerSensor(Int32)
25+
26+
/// Unable to set event rate (result code).
27+
case setEventRate(Int32)
28+
29+
/// Error reading sensor events (result code).
30+
case getEvents(Int32)
31+
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
//
2+
// Sensor.swift
3+
// SwiftAndroid
4+
//
5+
// Created by Alsey Coleman Miller on 7/6/25.
6+
//
7+
8+
#if os(Android)
9+
import Android
10+
import AndroidNDK
11+
#endif
12+
13+
/// A reference to a hardware sensor.
14+
///
15+
/// Sensor instances are vended by `SensorManager` and do not have independent
16+
/// ownership — the underlying pointer is valid for the lifetime of the
17+
/// `SensorManager` that created it.
18+
public struct Sensor: @unchecked Sendable {
19+
20+
internal let pointer: OpaquePointer
21+
22+
internal init(_ pointer: OpaquePointer) {
23+
self.pointer = pointer
24+
}
25+
}
26+
27+
// MARK: - Properties
28+
29+
public extension Sensor {
30+
31+
/// The name string of the sensor.
32+
var name: String {
33+
guard let cStr = ASensor_getName(pointer) else { return "" }
34+
return String(cString: cStr)
35+
}
36+
37+
/// The vendor string of the sensor.
38+
var vendor: String {
39+
guard let cStr = ASensor_getVendor(pointer) else { return "" }
40+
return String(cString: cStr)
41+
}
42+
43+
/// The type of the sensor.
44+
var type: SensorType {
45+
SensorType(rawValue: ASensor_getType(pointer))
46+
}
47+
48+
/// The resolution of the sensor in the sensor's unit.
49+
var resolution: Float {
50+
ASensor_getResolution(pointer)
51+
}
52+
53+
/// The minimum delay in microseconds between two events, or 0 if the sensor
54+
/// reports only when values change.
55+
var minDelay: Int32 {
56+
ASensor_getMinDelay(pointer)
57+
}
58+
59+
/// The maximum number of events that the hardware FIFO can hold.
60+
var fifoMaxEventCount: Int32 {
61+
ASensor_getFifoMaxEventCount(pointer)
62+
}
63+
64+
/// The number of events reserved for this sensor in the hardware FIFO.
65+
var fifoReservedEventCount: Int32 {
66+
ASensor_getFifoReservedEventCount(pointer)
67+
}
68+
69+
/// The type string of the sensor (e.g. `"android.sensor.accelerometer"`).
70+
var stringType: String? {
71+
ASensor_getStringType(pointer).map { String(cString: $0) }
72+
}
73+
74+
/// The reporting mode of the sensor.
75+
var reportingMode: SensorReportingMode? {
76+
SensorReportingMode(rawValue: ASensor_getReportingMode(pointer))
77+
}
78+
79+
/// Whether this sensor is a wake-up sensor.
80+
var isWakeUpSensor: Bool {
81+
ASensor_isWakeUpSensor(pointer)
82+
}
83+
84+
/// The hardware sensor handle, unique within a device.
85+
var handle: Int32 {
86+
ASensor_getHandle(pointer)
87+
}
88+
}
89+
90+
// MARK: - Equatable
91+
92+
extension Sensor: Equatable {
93+
94+
public static func == (lhs: Sensor, rhs: Sensor) -> Bool {
95+
lhs.pointer == rhs.pointer
96+
}
97+
}
98+
99+
// MARK: - Hashable
100+
101+
extension Sensor: Hashable {
102+
103+
public func hash(into hasher: inout Hasher) {
104+
hasher.combine(pointer)
105+
}
106+
}
107+
108+
// MARK: - CustomStringConvertible
109+
110+
extension Sensor: CustomStringConvertible {
111+
112+
public var description: String {
113+
"\(name) (\(vendor))"
114+
}
115+
}

0 commit comments

Comments
 (0)