Skip to content

Commit ae333ed

Browse files
muhomorrthestinger
authored andcommitted
UsbPortSecurityHooks: add support for the standard USB data signal API
1 parent 2034dc2 commit ae333ed

File tree

5 files changed

+129
-27
lines changed

5 files changed

+129
-27
lines changed

core/java/android/ext/settings/UsbPortSecurity.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,17 @@
44

55
/** @hide */
66
public class UsbPortSecurity {
7-
public static final int MODE_DISABLED = 0;
7+
public static final int MODE_ALL_PORTS_DISABLED = 0;
88
public static final int MODE_CHARGING_ONLY = 1;
99
// doesn't apply to connections that were made before locking
1010
public static final int MODE_CHARGING_ONLY_WHEN_LOCKED = 2;
1111
// doesn't apply to connections that were made before locking or first unlock
1212
public static final int MODE_CHARGING_ONLY_WHEN_LOCKED_AFU = 3;
13-
public static final int MODE_ENABLED = 4;
13+
public static final int MODE_ALL_PORTS_ENABLED = 4;
1414

1515
// keep in sync with USB HAL implementations that check this sysprop during init
1616
public static final IntSysProperty MODE_SETTING = new IntSysProperty(
1717
"persist.security.usb_mode",
1818
// USB adb access is needed for debugging early boot failures
19-
Build.IS_DEBUGGABLE ? MODE_ENABLED : MODE_CHARGING_ONLY_WHEN_LOCKED);
19+
Build.IS_DEBUGGABLE ? MODE_ALL_PORTS_ENABLED : MODE_CHARGING_ONLY_WHEN_LOCKED);
2020
}

services/core/java/com/android/server/policy/keyguard/UsbPortSecurityHooks.java

Lines changed: 99 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.android.server.policy.keyguard;
22

33
import android.annotation.Nullable;
4+
import android.app.ActivityThread;
45
import android.content.BroadcastReceiver;
56
import android.content.Context;
67
import android.content.Intent;
@@ -15,6 +16,7 @@
1516
import android.os.Process;
1617
import android.os.SystemProperties;
1718
import android.os.UserHandle;
19+
import android.util.ArraySet;
1820
import android.util.Log;
1921
import android.util.Slog;
2022

@@ -23,6 +25,7 @@
2325
import com.android.server.utils.Slogf;
2426

2527
import java.util.ArrayList;
28+
import java.util.Arrays;
2629
import java.util.Objects;
2730

2831
public class UsbPortSecurityHooks {
@@ -45,6 +48,10 @@ private UsbPortSecurityHooks(Context ctx) {
4548

4649
private static volatile int isSupportedCached;
4750

51+
public static boolean isSupported() {
52+
return isSupported(ActivityThread.currentSystemContext());
53+
}
54+
4855
public static boolean isSupported(Context ctx) {
4956
int cache = isSupportedCached;
5057
if (cache != 0) {
@@ -67,11 +74,11 @@ public static void setInitialMode(Context ctx) {
6774
switch (initialMode) {
6875
case UsbPortSecurity.MODE_CHARGING_ONLY:
6976
case UsbPortSecurity.MODE_CHARGING_ONLY_WHEN_LOCKED:
70-
setSecurityStateForAllPorts(ctx, PortSecurityState.CHARGING_ONLY_IMMEDIATE);
77+
setSecurityStateForAllPortsInner(ctx, PortSecurityState.CHARGING_ONLY_IMMEDIATE);
7178
break;
7279
case UsbPortSecurity.MODE_CHARGING_ONLY_WHEN_LOCKED_AFU:
73-
case UsbPortSecurity.MODE_ENABLED:
74-
setSecurityStateForAllPorts(ctx, PortSecurityState.PORTS_ENABLED);
80+
case UsbPortSecurity.MODE_ALL_PORTS_ENABLED:
81+
setSecurityStateForAllPortsInner(ctx, PortSecurityState.PORTS_ENABLED);
7582
break;
7683
}
7784
}
@@ -124,6 +131,70 @@ public void onReceive(Context context, Intent intent) {
124131
context.registerReceiver(receiver, filter, null, handler);
125132
}
126133

134+
private ArraySet<String> halEnabledPorts = new ArraySet<>();
135+
private ArraySet<String> halDisabledPorts = new ArraySet<>();
136+
137+
// implementation of the standard android.hardware.usb.IUsb.enableUsbDataSignal() API
138+
public static boolean onHalEnableUsbDataSignal(String portName, boolean enable) {
139+
if (!isSupported()) {
140+
return false;
141+
}
142+
Slog.d(TAG, "onHalEnableUsbDataSignal: portName: " + portName + ", enable: " + enable);
143+
144+
if (INSTANCE == null) {
145+
throw new IllegalStateException("UsbPortSecurityHooks is not initialized");
146+
}
147+
148+
INSTANCE.handler.post(() -> INSTANCE.onHalEnableUsbDataSignalInner(portName, enable));
149+
return true;
150+
}
151+
152+
public void onHalEnableUsbDataSignalInner(String portName, boolean enable) {
153+
Slog.d(TAG, "onHalEnableUsbDataSignalInner: portName: " + portName + ", enable: " + enable);
154+
155+
if (enable) {
156+
if (!halDisabledPorts.remove(portName)) {
157+
Slog.d(TAG, "port not found in halDisabledPorts");
158+
}
159+
if (!halEnabledPorts.add(portName)) {
160+
Slog.d(TAG, "port already in halEnabledPorts");
161+
}
162+
} else {
163+
if (!halEnabledPorts.remove(portName)) {
164+
Slog.d(TAG, "port not found in halEnabledPorts");
165+
}
166+
if (!halDisabledPorts.add(portName)) {
167+
Slog.d(TAG, "port already in halDisabledPorts");
168+
}
169+
}
170+
171+
Slog.d(TAG, "halDisabledPorts: " + Arrays.toString(halDisabledPorts.toArray()) + ", halEnabledPorts: " + Arrays.toString(halEnabledPorts.toArray()));
172+
173+
int setting = UsbPortSecurity.MODE_SETTING.get();
174+
175+
if (!halDisabledPorts.isEmpty()) {
176+
switch (setting) {
177+
case UsbPortSecurity.MODE_CHARGING_ONLY_WHEN_LOCKED:
178+
case UsbPortSecurity.MODE_CHARGING_ONLY_WHEN_LOCKED_AFU:
179+
case UsbPortSecurity.MODE_ALL_PORTS_ENABLED:
180+
setSecurityStateForAllPorts(PortSecurityState.CHARGING_ONLY_IMMEDIATE);
181+
break;
182+
}
183+
} else {
184+
switch (setting) {
185+
case UsbPortSecurity.MODE_CHARGING_ONLY_WHEN_LOCKED:
186+
case UsbPortSecurity.MODE_CHARGING_ONLY_WHEN_LOCKED_AFU:
187+
if (prevKeyguardShowing != null && !prevKeyguardShowing.booleanValue()) {
188+
setSecurityStateForAllPorts(PortSecurityState.PORTS_ENABLED);
189+
}
190+
break;
191+
case UsbPortSecurity.MODE_ALL_PORTS_ENABLED:
192+
setSecurityStateForAllPorts(PortSecurityState.PORTS_ENABLED);
193+
break;
194+
}
195+
}
196+
}
197+
127198
private static final ArrayList<Runnable> pendingCallbacks = new ArrayList<>();
128199

129200
public static void onKeyguardShowingStateChanged(Context ctx, boolean showing, int userId) {
@@ -224,10 +295,19 @@ private interface PortSecurityState {
224295
}
225296

226297
private void setSecurityStateForAllPorts(String state) {
227-
setSecurityStateForAllPorts(context, state);
298+
if (!handler.getLooper().isCurrentThread()) {
299+
throw new IllegalStateException("setSecurityStateForAllPorts() must be called on the handler thread");
300+
}
301+
302+
if (PortSecurityState.PORTS_ENABLED.equals(state) && !halDisabledPorts.isEmpty()) {
303+
Slogf.d(TAG, "setSecurityStateForAllPorts: ignoring enable request since halDisabledPorts is %s", Arrays.toString(halDisabledPorts.toArray()));
304+
return;
305+
}
306+
307+
setSecurityStateForAllPortsInner(context, state);
228308
}
229309

230-
private static void setSecurityStateForAllPorts(Context ctx, String state) {
310+
private static void setSecurityStateForAllPortsInner(Context ctx, String state) {
231311
Slog.d(TAG, "setSecurityStateForAllPorts: " + state);
232312

233313
setDenyNewUsb2(ctx, !state.equals(PortSecurityState.PORTS_ENABLED));
@@ -263,11 +343,15 @@ public static void updateSetting(int newValue) {
263343
if (instance == null) {
264344
throw new IllegalStateException("no UsbPortSecurityHooks instance");
265345
}
346+
instance.handler.post(() -> instance.updateSettingInner(newValue));
347+
}
266348

267-
if (!Boolean.FALSE.equals(instance.prevKeyguardShowing)) {
349+
private void updateSettingInner(int newValue) {
350+
if (!Boolean.FALSE.equals(prevKeyguardShowing)) {
268351
// not strictly necessary, but allows to simplify the logic in code that changes port
269352
// security state below
270-
throw new SecurityException("keyguard has to be dismissed before calling this method");
353+
Slog.e(TAG, "keyguard has to be dismissed before calling updateSetting()");
354+
return;
271355
}
272356

273357
int prevValue = UsbPortSecurity.MODE_SETTING.get();
@@ -280,36 +364,36 @@ public static void updateSetting(int newValue) {
280364
// Turn USB ports off first to trigger reconnection of devices that were connected
281365
// in charging-only state. Simply enabling the data path is not enough in some
282366
// advanced scenarios, e.g. when port alt mode or port role switching are used.
283-
instance.setSecurityStateForAllPorts(PortSecurityState.PORTS_DISABLED);
367+
setSecurityStateForAllPorts(PortSecurityState.PORTS_DISABLED);
284368
delayStateUpdate = true;
285369
}
286370

287371
String state = switch (newValue) {
288-
case UsbPortSecurity.MODE_DISABLED ->
372+
case UsbPortSecurity.MODE_ALL_PORTS_DISABLED ->
289373
PortSecurityState.PORTS_DISABLED;
290374
case UsbPortSecurity.MODE_CHARGING_ONLY ->
291375
PortSecurityState.CHARGING_ONLY_IMMEDIATE;
292376
case UsbPortSecurity.MODE_CHARGING_ONLY_WHEN_LOCKED,
293377
UsbPortSecurity.MODE_CHARGING_ONLY_WHEN_LOCKED_AFU,
294-
UsbPortSecurity.MODE_ENABLED ->
378+
UsbPortSecurity.MODE_ALL_PORTS_ENABLED ->
295379
PortSecurityState.PORTS_ENABLED;
296380
default -> throw new IllegalArgumentException(Integer.toString(newValue));
297381
};
298382

299383
if (delayStateUpdate) {
300-
final long curShowingChangeCount = instance.keyguardShowingChangeCount;
384+
final long curShowingChangeCount = keyguardShowingChangeCount;
301385
// it's hard to setup a proper callback to avoid this hardcoded delay, would need to
302386
// modify init and kernel
303387
final long delayMs = 1500;
304-
instance.handler.postDelayed(() -> {
305-
if (instance.keyguardShowingChangeCount == curShowingChangeCount) {
306-
instance.setSecurityStateForAllPorts(state);
388+
handler.postDelayed(() -> {
389+
if (keyguardShowingChangeCount == curShowingChangeCount) {
390+
setSecurityStateForAllPorts(state);
307391
} else {
308392
Slog.d(TAG, "updateSetting: showingChangeCount changed, skipping delayed state change");
309393
}
310394
}, delayMs);
311395
} else {
312-
instance.setSecurityStateForAllPorts(state);
396+
setSecurityStateForAllPorts(state);
313397
}
314398
}
315399

services/usb/java/com/android/server/usb/UsbService.java

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,6 @@
8383
import com.android.server.LocalServices;
8484
import com.android.server.SystemServerInitThreadPool;
8585
import com.android.server.SystemService;
86-
import com.android.server.policy.keyguard.UsbPortSecurityHooks;
8786

8887
import dalvik.annotation.optimization.NeverCompile;
8988

@@ -1179,14 +1178,9 @@ boolean enableUsbDataInternal(String portId, boolean enable, int operationId,
11791178
*/
11801179
private boolean shouldUpdateUsbSignaling(String portId, boolean enable,
11811180
int requester, boolean isInternalRequest) {
1182-
1183-
// The upstream USB data protection feature conflicts with the broader GrapheneOS external
1184-
// port protection feature
1185-
if (isInternalRequest && (UsbPortSecurityHooks.isSupported(mContext) ||
1186-
!android.hardware.usb.flags.Flags.enableUsbDataSignalStakingInternal())) {
1187-
return false;
1188-
}
1189-
1181+
if(isInternalRequest &&
1182+
!android.hardware.usb.flags.Flags.enableUsbDataSignalStakingInternal())
1183+
return false;
11901184
synchronized (mUsbDisableRequesters) {
11911185
if (!mUsbDisableRequesters.containsKey(portId)) {
11921186
mUsbDisableRequesters.put(portId, new UsbDataSignalDisableRequesters());

services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,12 @@ public boolean enableUsbData(String portName, boolean enable, long operationID,
338338
long key = operationID;
339339
synchronized (mLock) {
340340
try {
341+
if (com.android.server.policy.keyguard.UsbPortSecurityHooks
342+
.onHalEnableUsbDataSignal(portName, enable)) {
343+
callback.onOperationComplete(USB_OPERATION_SUCCESS);
344+
return true;
345+
}
346+
341347
if (mProxy == null) {
342348
logAndPrint(Log.ERROR, mPw,
343349
"enableUsbData: Proxy is null. Retry !opID:"
@@ -422,6 +428,14 @@ public void enableUsbDataWhileDocked(String portName, long operationID,
422428
long key = operationID;
423429
synchronized (mLock) {
424430
try {
431+
if (com.android.server.policy.keyguard.UsbPortSecurityHooks.isSupported()) {
432+
// enableUsbDataWhileDocked() is not used by AOSP code as of 16 QPR2 and is not
433+
// supported by the GrapheneOS port security API
434+
callback.onOperationComplete(android.hardware.usb
435+
.UsbOperationInternal.USB_OPERATION_ERROR_NOT_SUPPORTED);
436+
return;
437+
}
438+
425439
if (mProxy == null) {
426440
logAndPrint(Log.ERROR, mPw,
427441
"enableUsbDataWhileDocked: Proxy is null. Retry !opID:"

services/usb/java/com/android/server/usb/hal/port/UsbPortHidl.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,11 @@ public void enableLimitPowerTransfer(String portName, boolean limit, long transa
290290
@Override
291291
public void enableUsbDataWhileDocked(String portName, long transactionId,
292292
IUsbOperationInternal callback) {
293+
if (com.android.server.policy.keyguard.UsbPortSecurityHooks.isSupported()) {
294+
// modern devices don't use a HIDL USB HAL
295+
throw new UnsupportedOperationException();
296+
}
297+
293298
/* Not supported in HIDL hals*/
294299
try {
295300
callback.onOperationComplete(USB_OPERATION_ERROR_NOT_SUPPORTED);
@@ -333,6 +338,11 @@ public void resetUsbPort(String portName, long transactionId,
333338
@Override
334339
public boolean enableUsbData(String portName, boolean enable, long transactionId,
335340
IUsbOperationInternal callback) {
341+
if (com.android.server.policy.keyguard.UsbPortSecurityHooks.isSupported()) {
342+
// modern devices don't use a HIDL USB HAL
343+
throw new UnsupportedOperationException();
344+
}
345+
336346
int halVersion;
337347

338348
try {

0 commit comments

Comments
 (0)