Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a13c950b authored by David Anderson's avatar David Anderson Committed by Android (Google) Code Review
Browse files

Merge "Add helper functions for testing trade-in mode." into main

parents ec28bbfb f17b79fa
Loading
Loading
Loading
Loading
+33 −0
Original line number Diff line number Diff line
@@ -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();
}
+1 −0
Original line number Diff line number Diff line
@@ -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 -->
+3 −0
Original line number Diff line number Diff line
@@ -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"
+104 −11
Original line number Diff line number Diff line
@@ -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() {