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

Commit d2110a76 authored by Pawan Wagh's avatar Pawan Wagh Committed by Automerger Merge Worker
Browse files

Call update engine to apply payload am: 008842ed

parents acd2de25 008842ed
Loading
Loading
Loading
Loading
+8 −2
Original line number Diff line number Diff line
@@ -11330,9 +11330,15 @@
    <!-- setting Checkbox summary whether to boot with 16K page size[CHAR_LIMIT=50] -->
    <string name="enable_16k_pages_summary">Boot device using 16K page size supported kernel</string>
    <!-- Confirmation dialog title to ensure user wishes to enable 16K page size -->
    <string name="confirm_enable_16k_pages_title">Reboot with 16K page compatible kernel?</string>
    <string name="confirm_enable_16k_pages_title">Reboot with 16KB pages compatible kernel?</string>
    <!-- Warning dialog message to confirm user wishes to enable 16K page size -->
    <string name="confirm_enable_16k_pages_text">WARNING: Some applications may not be compatible with this mode</string>
    <string name="confirm_enable_16k_pages_text">WARNING: Some applications may not be compatible with this mode. Device will reboot after confirmation.</string>
    <!-- dialog title to confirm user wishes to revert to 4k page size kernel -->
    <string name="confirm_enable_4k_pages_title">Reboot with 4KB pages compatible kernel?</string>
    <!-- dialog message to confirm user wishes to enable 4K page size -->
    <string name="confirm_enable_4k_pages_text">Device will reboot after confirmation.</string>
    <!-- Toast message when 16k OTA update fails -->
    <string name="toast_16k_update_failed_text">Failed to update kernel to 16KB pages compatible kernel.</string>
    <!-- DSU Loader. Do not translate. -->
    <string name="dsu_loader_title" translatable="false">DSU Loader</string>
+196 −8
Original line number Diff line number Diff line
@@ -17,29 +17,74 @@
package com.android.settings.development;

import android.content.Context;
import android.os.Handler;
import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.os.SystemProperties;
import android.os.UpdateEngine;
import android.os.UpdateEngineStable;
import android.os.UpdateEngineStableCallback;
import android.provider.Settings;
import android.util.Log;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.core.content.ContextCompat;
import androidx.preference.Preference;
import androidx.preference.SwitchPreference;

import com.android.settings.R;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.development.DeveloperOptionsPreferenceController;

import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/** Controller for 16K pages developer option */
public class Enable16kPagesPreferenceController extends DeveloperOptionsPreferenceController
        implements Preference.OnPreferenceChangeListener,
                PreferenceControllerMixin,
                Enable16kbPagesDialogHost {

    private static final String TAG = "Enable16kPages";
    private static final String REBOOT_REASON = "toggle16k";
    private static final String ENABLE_16K_PAGES = "enable_16k_pages";
    private static final String DEV_OPTION_PROPERTY = "ro.product.build.16k_page.enabled";

    @VisibleForTesting
    static final String DEV_OPTION_PROPERTY = "ro.product.build.16k_page.enabled";

    private static final int ENABLE_4K_PAGE_SIZE = 0;
    private static final int ENABLE_16K_PAGE_SIZE = 1;

    private static final String OTA_16K_PATH = "/system/boot_otas/boot_ota_16k.zip";
    private static final String OTA_4K_PATH = "/system/boot_otas/boot_ota_4k.zip";
    private static final String PAYLOAD_BINARY_FILE_NAME = "payload.bin";
    private static final String PAYLOAD_PROPERTIES_FILE_NAME = "payload_properties.txt";
    private static final int OFFSET_TO_FILE_NAME = 30;

    private @Nullable DevelopmentSettingsDashboardFragment mFragment = null;
    private boolean mEnable16k;

    private final ListeningExecutorService mExecutorService =
            MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor());

    public Enable16kPagesPreferenceController(
            @NonNull Context context, @Nullable DevelopmentSettingsDashboardFragment fragment) {
@@ -59,12 +104,8 @@ public class Enable16kPagesPreferenceController extends DeveloperOptionsPreferen

    @Override
    public boolean onPreferenceChange(Preference preference, Object newValue) {
        final boolean optionEnabled = (Boolean) newValue;
        if (optionEnabled) {
            Enable16kPagesWarningDialog.show(mFragment, this);
        } else {
            // TODO(b/295573133):Directly reboot into 4k
        }
        mEnable16k = (Boolean) newValue;
        Enable16kPagesWarningDialog.show(mFragment, this, mEnable16k);
        return true;
    }

@@ -93,10 +134,157 @@ public class Enable16kPagesPreferenceController extends DeveloperOptionsPreferen
    /** Called when user confirms reboot dialog */
    @Override
    public void on16kPagesDialogConfirmed() {
        // TODO(b/295573133) : integrate update engine
        // Apply update in background
        ListenableFuture future = mExecutorService.submit(() -> installUpdate());
        Futures.addCallback(
                future,
                new FutureCallback<>() {

                    @Override
                    public void onSuccess(@NonNull Object result) {
                        // This means UpdateEngineStable is working on applying update in
                        // background.
                        // Result of that operation will be provided by separate callback.
                        Log.i(TAG, "applyPayload call to UpdateEngineStable succeeded.");
                    }

                    @Override
                    public void onFailure(Throwable t) {
                        Log.e(TAG, "Failed to call applyPayload of UpdateEngineStable!");
                        displayToast(mContext.getString(R.string.toast_16k_update_failed_text));
                    }
                },
                ContextCompat.getMainExecutor(mContext));
    }

    /** Called when user dismisses to reboot dialog */
    @Override
    public void on16kPagesDialogDismissed() {}

    private void installUpdate() {
        String updateFilePath = mEnable16k ? OTA_16K_PATH : OTA_4K_PATH;
        try {
            File updateFile = new File(updateFilePath);
            applyUpdateFile(updateFile);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @VisibleForTesting
    void applyUpdateFile(@NonNull File updateFile) throws IOException, FileNotFoundException {
        boolean payloadFound = false;
        boolean propertiesFound = false;
        long payloadOffset = 0;
        long payloadSize = 0;

        List<String> properties = new ArrayList<>();
        try (ZipFile zip = new ZipFile(updateFile)) {
            Enumeration<? extends ZipEntry> entries = zip.entries();
            long offset = 0;
            while (entries.hasMoreElements()) {
                ZipEntry zipEntry = entries.nextElement();
                String fileName = zipEntry.getName();
                long extraSize = zipEntry.getExtra() == null ? 0 : zipEntry.getExtra().length;
                offset += OFFSET_TO_FILE_NAME + fileName.length() + extraSize;

                if (zipEntry.isDirectory()) {
                    continue;
                }

                long length = zipEntry.getCompressedSize();
                if (PAYLOAD_BINARY_FILE_NAME.equals(fileName)) {
                    if (zipEntry.getMethod() != ZipEntry.STORED) {
                        throw new IOException("Unknown compression method.");
                    }
                    payloadFound = true;
                    payloadOffset = offset;
                    payloadSize = length;
                } else if (PAYLOAD_PROPERTIES_FILE_NAME.equals(fileName)) {
                    propertiesFound = true;
                    InputStream inputStream = zip.getInputStream(zipEntry);
                    if (inputStream != null) {
                        BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
                        String line;
                        while ((line = br.readLine()) != null) {
                            properties.add(line);
                        }
                    }
                }
                offset += length;
            }
        }

        if (!payloadFound) {
            throw new FileNotFoundException(
                    "Failed to find payload in zip: " + updateFile.getAbsolutePath());
        }

        if (!propertiesFound) {
            throw new FileNotFoundException(
                    "Failed to find payload properties in zip: " + updateFile.getAbsolutePath());
        }

        if (payloadSize == 0) {
            throw new IOException("Found empty payload in zip: " + updateFile.getAbsolutePath());
        }

        applyPayload(updateFile, payloadOffset, payloadSize, properties);
    }

    @VisibleForTesting
    void applyPayload(
            @NonNull File updateFile,
            long payloadOffset,
            long payloadSize,
            @NonNull List<String> properties)
            throws FileNotFoundException {
        String[] header = properties.stream().toArray(String[]::new);
        UpdateEngineStable updateEngineStable = new UpdateEngineStable();
        try {
            ParcelFileDescriptor pfd =
                    ParcelFileDescriptor.open(updateFile, ParcelFileDescriptor.MODE_READ_ONLY);
            updateEngineStable.bind(
                    new OtaUpdateCallback(updateEngineStable),
                    new Handler(mContext.getMainLooper()));
            updateEngineStable.applyPayloadFd(pfd, payloadOffset, payloadSize, header);
        } finally {
            Log.e(TAG, "Failure while applying an update using update engine");
        }
    }

    private void displayToast(String message) {
        Toast.makeText(mContext, message, Toast.LENGTH_SHORT).show();
    }

    private class OtaUpdateCallback extends UpdateEngineStableCallback {
        UpdateEngineStable mUpdateEngineStable;

        OtaUpdateCallback(@NonNull UpdateEngineStable engine) {
            mUpdateEngineStable = engine;
        }

        @Override
        public void onStatusUpdate(int status, float percent) {}

        @Override
        public void onPayloadApplicationComplete(int errorCode) {
            // unbind the callback from update engine
            mUpdateEngineStable.unbind();

            if (errorCode == UpdateEngine.ErrorCodeConstants.SUCCESS) {
                Log.i(TAG, "applyPayload successful");
                Settings.Global.putInt(
                        mContext.getContentResolver(),
                        Settings.Global.ENABLE_16K_PAGES,
                        mEnable16k ? ENABLE_16K_PAGE_SIZE : ENABLE_4K_PAGE_SIZE);

                PowerManager pm = mContext.getSystemService(PowerManager.class);
                pm.reboot(REBOOT_REASON);
            } else {
                Log.e(TAG, "applyPayload failed, error code: " + errorCode);
                displayToast(mContext.getString(R.string.toast_16k_update_failed_text));
            }
        }
    }
}
+19 −5
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ public class Enable16kPagesWarningDialog extends InstrumentedDialogFragment
        implements DialogInterface.OnClickListener, DialogInterface.OnDismissListener {

    public static final String TAG = "Enable16KDialog";
    private static final String DIALOG_BUNDLE_KEY = "SHOW_16K_DIALOG";

    private Enable16kbPagesDialogHost mHost;

@@ -42,17 +43,22 @@ public class Enable16kPagesWarningDialog extends InstrumentedDialogFragment
        mHost = host;
    }

    /** Used to display warning dialog */
    /** This method is used to show warning dialog to apply 16K update and reboot */
    public static void show(
            @NonNull Fragment hostFragment, @NonNull Enable16kbPagesDialogHost dialogHost) {
            @NonNull Fragment hostFragment,
            @NonNull Enable16kbPagesDialogHost dialogHost,
            boolean enable16k) {
        final FragmentManager manager = hostFragment.getActivity().getSupportFragmentManager();
        Fragment existingFragment = manager.findFragmentByTag(TAG);
        if (existingFragment == null) {
            existingFragment = new Enable16kPagesWarningDialog();
            existingFragment.setTargetFragment(hostFragment, 0 /* requestCode */);
        }

        if (existingFragment instanceof Enable16kPagesWarningDialog) {
            Bundle bundle = new Bundle();
            bundle.putBoolean(DIALOG_BUNDLE_KEY, enable16k);
            existingFragment.setArguments(bundle);
            existingFragment.setTargetFragment(hostFragment, 0 /* requestCode */);
            ((Enable16kPagesWarningDialog) existingFragment).setHost(dialogHost);
            ((Enable16kPagesWarningDialog) existingFragment).show(manager, TAG);
        }
@@ -66,9 +72,17 @@ public class Enable16kPagesWarningDialog extends InstrumentedDialogFragment
    @NonNull
    @Override
    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
        final Bundle bundle = getArguments();
        boolean is16kDialog = bundle.getBoolean(DIALOG_BUNDLE_KEY);
        return new AlertDialog.Builder(getActivity())
                .setTitle(R.string.confirm_enable_16k_pages_title)
                .setMessage(R.string.confirm_enable_16k_pages_text)
                .setTitle(
                        is16kDialog
                                ? R.string.confirm_enable_16k_pages_title
                                : R.string.confirm_enable_4k_pages_title)
                .setMessage(
                        is16kDialog
                                ? R.string.confirm_enable_16k_pages_text
                                : R.string.confirm_enable_4k_pages_text)
                .setPositiveButton(android.R.string.ok, this /* onClickListener */)
                .setNegativeButton(android.R.string.cancel, this /* onClickListener */)
                .create();
+786 B

File added.

No diff preview for this file type.

+558 B

File added.

No diff preview for this file type.

Loading