Skip to content

Commit 4dcd8f6

Browse files
xaionaro@dx.centerxaionaro@dx.center
authored andcommitted
chore: accept min_api_level in regenerated specs
The aidl2spec baseline diffing produces min_api_level annotations. The codegen no longer uses them (replaced by DEX signature matching), but they must be in the committed specs for check-generated to pass.
1 parent 0e2aec9 commit 4dcd8f6

5 files changed

Lines changed: 452 additions & 0 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,4 @@
4242
# Lean 4
4343
/.lake
4444
proofs/.lake
45+
*.out

binder/versionaware/dex/dex_file.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,12 @@ const (
1717
offStringIDsOff = 0x3C
1818
offTypeIDsSize = 0x40
1919
offTypeIDsOff = 0x44
20+
offProtoIDsSize = 0x48
21+
offProtoIDsOff = 0x4C
2022
offFieldIDsSize = 0x50
2123
offFieldIDsOff = 0x54
24+
offMethodIDsSize = 0x58
25+
offMethodIDsOff = 0x5C
2226
offClassDefsSize = 0x60
2327
offClassDefsOff = 0x64
2428
)
@@ -27,7 +31,9 @@ const (
2731
const (
2832
stringIDItemSize = 4
2933
typeIDItemSize = 4
34+
protoIDItemSize = 12
3035
fieldIDItemSize = 8
36+
methodIDItemSize = 8
3137
classDefItemSize = 32
3238
)
3339

@@ -40,8 +46,12 @@ type dexFile struct {
4046
stringIDsOff uint32
4147
typeIDsSize uint32
4248
typeIDsOff uint32
49+
protoIDsSize uint32
50+
protoIDsOff uint32
4351
fieldIDsSize uint32
4452
fieldIDsOff uint32
53+
methodIDsSize uint32
54+
methodIDsOff uint32
4555
classDefsSize uint32
4656
classDefsOff uint32
4757
}
@@ -62,8 +72,12 @@ func parseDEXFile(data []byte) (*dexFile, error) {
6272
stringIDsOff: binary.LittleEndian.Uint32(data[offStringIDsOff:]),
6373
typeIDsSize: binary.LittleEndian.Uint32(data[offTypeIDsSize:]),
6474
typeIDsOff: binary.LittleEndian.Uint32(data[offTypeIDsOff:]),
75+
protoIDsSize: binary.LittleEndian.Uint32(data[offProtoIDsSize:]),
76+
protoIDsOff: binary.LittleEndian.Uint32(data[offProtoIDsOff:]),
6577
fieldIDsSize: binary.LittleEndian.Uint32(data[offFieldIDsSize:]),
6678
fieldIDsOff: binary.LittleEndian.Uint32(data[offFieldIDsOff:]),
79+
methodIDsSize: binary.LittleEndian.Uint32(data[offMethodIDsSize:]),
80+
methodIDsOff: binary.LittleEndian.Uint32(data[offMethodIDsOff:]),
6781
classDefsSize: binary.LittleEndian.Uint32(data[offClassDefsSize:]),
6882
classDefsOff: binary.LittleEndian.Uint32(data[offClassDefsOff:]),
6983
}
@@ -79,7 +93,9 @@ func parseDEXFile(data []byte) (*dexFile, error) {
7993
}{
8094
{"string_ids", f.stringIDsOff, f.stringIDsSize, stringIDItemSize},
8195
{"type_ids", f.typeIDsOff, f.typeIDsSize, typeIDItemSize},
96+
{"proto_ids", f.protoIDsOff, f.protoIDsSize, protoIDItemSize},
8297
{"field_ids", f.fieldIDsOff, f.fieldIDsSize, fieldIDItemSize},
98+
{"method_ids", f.methodIDsOff, f.methodIDsSize, methodIDItemSize},
8399
{"class_defs", f.classDefsOff, f.classDefsSize, classDefItemSize},
84100
}
85101
for _, s := range sections {
@@ -167,6 +183,76 @@ func (f *dexFile) readTypeDescriptorBytes(idx uint32) ([]byte, error) {
167183
return f.readStringBytes(descriptorIdx)
168184
}
169185

186+
// readTypeDescriptor returns the type descriptor at the given type_ids
187+
// index as an allocated string. The returned string is safe to retain
188+
// after f.data is reused or freed.
189+
func (f *dexFile) readTypeDescriptor(idx uint32) (string, error) {
190+
b, err := f.readTypeDescriptorBytes(idx)
191+
if err != nil {
192+
return "", err
193+
}
194+
return string(b), nil
195+
}
196+
197+
// readMethodID parses the method_id_item at the given method_ids index.
198+
// Returns the class type index, proto index, and name string index.
199+
func (f *dexFile) readMethodID(
200+
idx uint32,
201+
) (classIdx uint16, protoIdx uint16, nameIdx uint32, err error) {
202+
if idx >= f.methodIDsSize {
203+
return 0, 0, 0, fmt.Errorf("method index %d out of range (size=%d)", idx, f.methodIDsSize)
204+
}
205+
206+
off := uint64(f.methodIDsOff) + uint64(idx)*methodIDItemSize
207+
if off+methodIDItemSize > uint64(len(f.data)) {
208+
return 0, 0, 0, fmt.Errorf("method_id_item at offset 0x%x out of bounds", off)
209+
}
210+
211+
classIdx = binary.LittleEndian.Uint16(f.data[off:])
212+
protoIdx = binary.LittleEndian.Uint16(f.data[off+2:])
213+
nameIdx = binary.LittleEndian.Uint32(f.data[off+4:])
214+
return classIdx, protoIdx, nameIdx, nil
215+
}
216+
217+
// readProtoParams returns the parameter type indices for the proto_id
218+
// at the given proto_ids index. Returns nil for zero-parameter protos.
219+
func (f *dexFile) readProtoParams(protoIdx uint32) ([]uint32, error) {
220+
if protoIdx >= f.protoIDsSize {
221+
return nil, fmt.Errorf("proto index %d out of range (size=%d)", protoIdx, f.protoIDsSize)
222+
}
223+
224+
off := uint64(f.protoIDsOff) + uint64(protoIdx)*protoIDItemSize
225+
if off+protoIDItemSize > uint64(len(f.data)) {
226+
return nil, fmt.Errorf("proto_id_item at offset 0x%x out of bounds", off)
227+
}
228+
229+
// proto_id_item: shorty_idx(u32), return_type_idx(u32), parameters_off(u32)
230+
paramsOff := binary.LittleEndian.Uint32(f.data[off+8:])
231+
if paramsOff == 0 {
232+
return nil, nil
233+
}
234+
235+
dataLen := uint32(len(f.data))
236+
if paramsOff+4 > dataLen {
237+
return nil, fmt.Errorf("type_list at offset 0x%x out of bounds", paramsOff)
238+
}
239+
240+
// type_list: uint32 size, then size * uint16 type_idx entries.
241+
size := binary.LittleEndian.Uint32(f.data[paramsOff:])
242+
listEnd := uint64(paramsOff) + 4 + uint64(size)*2
243+
if listEnd > uint64(dataLen) {
244+
return nil, fmt.Errorf("type_list entries at offset 0x%x extend past file end", paramsOff)
245+
}
246+
247+
typeIndices := make([]uint32, size)
248+
pos := paramsOff + 4
249+
for i := uint32(0); i < size; i++ {
250+
typeIndices[i] = uint32(binary.LittleEndian.Uint16(f.data[pos:]))
251+
pos += 2
252+
}
253+
return typeIndices, nil
254+
}
255+
170256
// stubDescriptorToInterface converts a $Stub class descriptor to the
171257
// AIDL interface's dot-separated name.
172258
//
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
package dex
2+
3+
import (
4+
"encoding/binary"
5+
"fmt"
6+
"unsafe"
7+
)
8+
9+
// MethodSignatures maps method names to their parameter type descriptor lists.
10+
// Example: {"registerClient": ["Landroid/os/ParcelUuid;", "Landroid/bluetooth/IBluetoothGattCallback;", "Z", "Landroid/content/AttributionSource;"]}
11+
type MethodSignatures map[string][]string
12+
13+
// objectMethodNames contains method names inherited from java.lang.Object
14+
// that should be skipped when extracting proxy method signatures.
15+
var objectMethodNames = map[string]struct{}{
16+
"hashCode": {},
17+
"equals": {},
18+
"toString": {},
19+
"getClass": {},
20+
"notify": {},
21+
"notifyAll": {},
22+
"wait": {},
23+
"finalize": {},
24+
"clone": {},
25+
"asBinder": {},
26+
"getInterfaceDescriptor": {},
27+
}
28+
29+
// extractProxyMethodSignatures reads virtual methods from a $Stub$Proxy
30+
// class_def and returns the method name to parameter type descriptors mapping.
31+
//
32+
// classDefOff is the offset of the class_def_item within data.
33+
func extractProxyMethodSignatures(
34+
f *dexFile,
35+
data []byte,
36+
classDefOff uint32,
37+
) (MethodSignatures, error) {
38+
classDataOff := binary.LittleEndian.Uint32(data[classDefOff+0x18:])
39+
if classDataOff == 0 {
40+
return nil, nil
41+
}
42+
43+
dataLen := uint32(len(data))
44+
if classDataOff >= dataLen {
45+
return nil, fmt.Errorf("class_data_off 0x%x out of bounds (data len %d)", classDataOff, dataLen)
46+
}
47+
48+
// Parse class_data_item header:
49+
// static_fields_size, instance_fields_size, direct_methods_size, virtual_methods_size
50+
pos := classDataOff
51+
staticFieldsSize, pos, err := readULEB128(data, pos)
52+
if err != nil {
53+
return nil, fmt.Errorf("reading static_fields_size: %w", err)
54+
}
55+
56+
instanceFieldsSize, pos, err := readULEB128(data, pos)
57+
if err != nil {
58+
return nil, fmt.Errorf("reading instance_fields_size: %w", err)
59+
}
60+
61+
directMethodsSize, pos, err := readULEB128(data, pos)
62+
if err != nil {
63+
return nil, fmt.Errorf("reading direct_methods_size: %w", err)
64+
}
65+
66+
virtualMethodsSize, pos, err := readULEB128(data, pos)
67+
if err != nil {
68+
return nil, fmt.Errorf("reading virtual_methods_size: %w", err)
69+
}
70+
71+
if virtualMethodsSize == 0 {
72+
return nil, nil
73+
}
74+
75+
// Skip static fields.
76+
for i := uint32(0); i < staticFieldsSize; i++ {
77+
_, pos, err = readULEB128(data, pos) // field_idx_diff
78+
if err != nil {
79+
return nil, fmt.Errorf("skipping static_field[%d] idx_diff: %w", i, err)
80+
}
81+
_, pos, err = readULEB128(data, pos) // access_flags
82+
if err != nil {
83+
return nil, fmt.Errorf("skipping static_field[%d] access_flags: %w", i, err)
84+
}
85+
}
86+
87+
// Skip instance fields.
88+
for i := uint32(0); i < instanceFieldsSize; i++ {
89+
_, pos, err = readULEB128(data, pos) // field_idx_diff
90+
if err != nil {
91+
return nil, fmt.Errorf("skipping instance_field[%d] idx_diff: %w", i, err)
92+
}
93+
_, pos, err = readULEB128(data, pos) // access_flags
94+
if err != nil {
95+
return nil, fmt.Errorf("skipping instance_field[%d] access_flags: %w", i, err)
96+
}
97+
}
98+
99+
// Skip direct methods.
100+
for i := uint32(0); i < directMethodsSize; i++ {
101+
_, pos, err = readULEB128(data, pos) // method_idx_diff
102+
if err != nil {
103+
return nil, fmt.Errorf("skipping direct_method[%d] idx_diff: %w", i, err)
104+
}
105+
_, pos, err = readULEB128(data, pos) // access_flags
106+
if err != nil {
107+
return nil, fmt.Errorf("skipping direct_method[%d] access_flags: %w", i, err)
108+
}
109+
_, pos, err = readULEB128(data, pos) // code_off
110+
if err != nil {
111+
return nil, fmt.Errorf("skipping direct_method[%d] code_off: %w", i, err)
112+
}
113+
}
114+
115+
// Iterate virtual methods — these are the proxy methods.
116+
sigs := MethodSignatures{}
117+
var methodIdx uint32
118+
for i := uint32(0); i < virtualMethodsSize; i++ {
119+
diff, newPos, err := readULEB128(data, pos)
120+
if err != nil {
121+
return nil, fmt.Errorf("reading virtual_method[%d] idx_diff: %w", i, err)
122+
}
123+
pos = newPos
124+
125+
_, pos, err = readULEB128(data, pos) // access_flags
126+
if err != nil {
127+
return nil, fmt.Errorf("reading virtual_method[%d] access_flags: %w", i, err)
128+
}
129+
130+
_, pos, err = readULEB128(data, pos) // code_off
131+
if err != nil {
132+
return nil, fmt.Errorf("reading virtual_method[%d] code_off: %w", i, err)
133+
}
134+
135+
methodIdx += diff
136+
137+
_, protoIdx, nameIdx, err := f.readMethodID(methodIdx)
138+
if err != nil {
139+
return nil, fmt.Errorf("reading method_id[%d]: %w", methodIdx, err)
140+
}
141+
142+
nameBytes, err := f.readStringBytes(nameIdx)
143+
if err != nil {
144+
return nil, fmt.Errorf("reading method name for method_id[%d]: %w", methodIdx, err)
145+
}
146+
147+
// Skip Object-inherited and binder-internal methods.
148+
nameStr := unsafe.String(&nameBytes[0], len(nameBytes))
149+
if _, skip := objectMethodNames[nameStr]; skip {
150+
continue
151+
}
152+
153+
paramTypeIndices, err := f.readProtoParams(uint32(protoIdx))
154+
if err != nil {
155+
return nil, fmt.Errorf("reading proto params for method_id[%d]: %w", methodIdx, err)
156+
}
157+
158+
paramDescs := make([]string, len(paramTypeIndices))
159+
for pi, typeIdx := range paramTypeIndices {
160+
desc, err := f.readTypeDescriptor(typeIdx)
161+
if err != nil {
162+
return nil, fmt.Errorf("reading param type[%d] for method %s: %w", pi, nameBytes, err)
163+
}
164+
paramDescs[pi] = desc
165+
}
166+
167+
// Allocate a proper string for the map key (nameBytes points
168+
// into f.data which may be reused).
169+
sigs[string(nameBytes)] = paramDescs
170+
}
171+
172+
return sigs, nil
173+
}

0 commit comments

Comments
 (0)