diff options
-rw-r--r-- | core/java/android/os/ITradeInMode.aidl | 33 | ||||
-rw-r--r-- | data/etc/privapp-permissions-platform.xml | 1 | ||||
-rw-r--r-- | packages/Shell/AndroidManifest.xml | 3 | ||||
-rw-r--r-- | services/core/java/com/android/server/TradeInModeService.java | 115 |
4 files changed, 141 insertions, 11 deletions
diff --git a/core/java/android/os/ITradeInMode.aidl b/core/java/android/os/ITradeInMode.aidl index f15954d14d0e..d05f52cf6a90 100644 --- a/core/java/android/os/ITradeInMode.aidl +++ b/core/java/android/os/ITradeInMode.aidl @@ -59,4 +59,37 @@ interface ITradeInMode { * ENTER_TRADE_IN_MODE permission is required. */ boolean enterEvaluationMode(); + + /** + * Schedules a wipe to trigger SUW for trade-in mode testing. A reboot is + * required. After this, startTesting() can be called. + * + * ENTER_TRADE_IN_MODE permission is required and ro.debuggable must be 1. + */ + void scheduleWipeForTesting(); + + /** + * Enables testing. This only takes effect after the next reboot, and is + * only allowed in ro.debuggable builds. On the following boot, normal + * adbd will be disabled and trade-in mode adbd will be enabled instead. + * + * ENTER_TRADE_IN_MODE permission is required and ro.debuggable must be 1. + */ + void startTesting(); + + /** + * Disables testing. This disables trade-in mode and removes any scheduled + * trade-in mode wipe. + * + * ENTER_TRADE_IN_MODE permission is required, ro.debuggable must be 1, and + * startTesting() must have been called. + */ + void stopTesting(); + + /** + * Returns whether the device is testing trade-in mode. + * + * ENTER_TRADE_IN_MODE permission is required and ro.debuggable must be 1. + */ + boolean isTesting(); } diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 1edbffa9d572..1de8664231d7 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -259,6 +259,7 @@ applications that come with the platform <permission name="android.permission.MODIFY_DAY_NIGHT_MODE"/> <permission name="android.permission.ACCESS_LOWPAN_STATE"/> <permission name="android.permission.BACKUP"/> + <permission name="android.permission.ENTER_TRADE_IN_MODE"/> <!-- Needed for GMSCore Location API test only --> <permission name="android.permission.LOCATION_BYPASS"/> <!-- Needed for test only --> diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 8fe3a0c4b4ae..55f7317f25e4 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -1015,6 +1015,9 @@ <uses-permission android:name="android.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE" /> <uses-permission android:name="android.permission.READ_COLOR_ZONES" /> + <!-- Permission required for trade-in mode testing --> + <uses-permission android:name="android.permission.ENTER_TRADE_IN_MODE" /> + <application android:label="@string/app_label" android:theme="@android:style/Theme.DeviceDefault.DayNight" diff --git a/services/core/java/com/android/server/TradeInModeService.java b/services/core/java/com/android/server/TradeInModeService.java index a69667395ebd..1a9e02c86560 100644 --- a/services/core/java/com/android/server/TradeInModeService.java +++ b/services/core/java/com/android/server/TradeInModeService.java @@ -41,12 +41,15 @@ import android.util.Slog; import java.io.FileWriter; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; public final class TradeInModeService extends SystemService { private static final String TAG = "TradeInModeService"; private static final String TIM_PROP = "persist.adb.tradeinmode"; + private static final String TIM_TEST_PROP = "persist.adb.test_tradeinmode"; private static final int TIM_STATE_UNSET = 0; @@ -108,6 +111,10 @@ public final class TradeInModeService extends SystemService { // setup completion observer. if (isDeviceSetup()) { stopTradeInMode(); + } else if (isDebuggable() && !isForceEnabledForTesting()) { + // The device was made debuggable after entering TIM. This + // can happen while flashing. For convenience, leave test mode. + leaveTestMode(); } else { watchForSetupCompletion(); watchForNetworkChange(); @@ -171,12 +178,7 @@ public final class TradeInModeService extends SystemService { Slog.e(TAG, "Cannot enter evaluation mode, FRP lock is present."); return false; } - - try (FileWriter fw = new FileWriter(WIPE_INDICATOR_FILE, - StandardCharsets.US_ASCII)) { - fw.write("0"); - } catch (IOException e) { - Slog.e(TAG, "Failed to write " + WIPE_INDICATOR_FILE, e); + if (!scheduleTradeInModeWipe()) { return false; } @@ -189,7 +191,7 @@ public final class TradeInModeService extends SystemService { } SystemProperties.set(TIM_PROP, Integer.toString(TIM_STATE_EVALUATION_MODE)); - SystemProperties.set("ctl.restart", "adbd"); + restartAdbd(); return true; } @@ -200,6 +202,55 @@ public final class TradeInModeService extends SystemService { "Cannot test for trade-in evaluation mode allowed"); return !isFrpActive(); } + + @Override + @RequiresPermission(android.Manifest.permission.ENTER_TRADE_IN_MODE) + public void scheduleWipeForTesting() { + enforceTestingPermissions(); + + scheduleTradeInModeWipe(); + } + + @Override + @RequiresPermission(android.Manifest.permission.ENTER_TRADE_IN_MODE) + public void startTesting() { + enforceTestingPermissions(); + + enterTestMode(); + } + + @Override + @RequiresPermission(android.Manifest.permission.ENTER_TRADE_IN_MODE) + public void stopTesting() { + enforceTestingPermissions(); + + if (!isForceEnabledForTesting()) { + throw new IllegalStateException("testing must have been started"); + } + + final long callingId = Binder.clearCallingIdentity(); + try { + leaveTestMode(); + } finally { + Binder.restoreCallingIdentity(callingId); + } + } + + @Override + @RequiresPermission(android.Manifest.permission.ENTER_TRADE_IN_MODE) + public boolean isTesting() { + enforceTestingPermissions(); + + return isForceEnabledForTesting(); + } + + private void enforceTestingPermissions() { + mContext.enforceCallingOrSelfPermission("android.permission.ENTER_TRADE_IN_MODE", + "Caller must have ENTER_TRADE_IN_MODE permission"); + if (!isDebuggable()) { + throw new SecurityException("ro.debuggable must be set to 1"); + } + } } private void startTradeInMode() { @@ -207,8 +258,7 @@ public final class TradeInModeService extends SystemService { SystemProperties.set(TIM_PROP, Integer.toString(TIM_STATE_FOYER)); - final ContentResolver cr = mContext.getContentResolver(); - Settings.Global.putInt(cr, Settings.Global.ADB_ENABLED, 1); + setAdbEnabled(true); watchForSetupCompletion(); watchForNetworkChange(); @@ -223,8 +273,51 @@ public final class TradeInModeService extends SystemService { removeNetworkWatch(); removeAccountsWatch(); + if (isForceEnabledForTesting()) { + // If testing in a debug build, we need to re-enable ADB. + restartAdbd(); + } else { + // Otherwise, ADB must not be enabled. + setAdbEnabled(false); + } + } + + private void enterTestMode() { + SystemProperties.set(TIM_TEST_PROP, "1"); + } + + private void leaveTestMode() { + if (getTradeInModeState() == TIM_STATE_FOYER) { + stopTradeInMode(); + } + + SystemProperties.set(TIM_TEST_PROP, ""); + SystemProperties.set(TIM_PROP, ""); + try { + Files.deleteIfExists(Paths.get(WIPE_INDICATOR_FILE)); + } catch (IOException e) { + Slog.e(TAG, "Failed to remove wipe indicator", e); + } + } + + private boolean scheduleTradeInModeWipe() { + try (FileWriter fw = new FileWriter(WIPE_INDICATOR_FILE, + StandardCharsets.US_ASCII)) { + fw.write("0"); + } catch (IOException e) { + Slog.e(TAG, "Failed to write " + WIPE_INDICATOR_FILE, e); + return false; + } + return true; + } + + private void restartAdbd() { + SystemProperties.set("ctl.restart", "adbd"); + } + + private void setAdbEnabled(boolean enabled) { final ContentResolver cr = mContext.getContentResolver(); - Settings.Global.putInt(cr, Settings.Global.ADB_ENABLED, 0); + Settings.Global.putInt(cr, Settings.Global.ADB_ENABLED, enabled ? 1 : 0); } private int getTradeInModeState() { @@ -236,7 +329,7 @@ public final class TradeInModeService extends SystemService { } private boolean isForceEnabledForTesting() { - return SystemProperties.getInt("persist.adb.test_tradeinmode", 0) == 1; + return isDebuggable() && SystemProperties.getInt(TIM_TEST_PROP, 0) == 1; } private boolean isAdbEnabled() { |