summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/os/ITradeInMode.aidl33
-rw-r--r--data/etc/privapp-permissions-platform.xml1
-rw-r--r--packages/Shell/AndroidManifest.xml3
-rw-r--r--services/core/java/com/android/server/TradeInModeService.java115
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() {