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

Commit cf0b30b7 authored by Jeongeun Song's avatar Jeongeun Song Committed by Alex Buynytskyy
Browse files

Add a backup and restore logic to ShortcutPackageItems

If shortcut package or launcher xml file is corrupted, booting would be
failed. It makes a reservecopy file when writing the files and use it
if the original file is corrupted.

Bug: 269072872
Test: atest ShortcutManagerTest1
Change-Id: I91b50db46f3eadf98e6a9eb96165be2ad3c7646a
parent 011b5373
Loading
Loading
Loading
Loading
+30 −38
Original line number Original line Diff line number Diff line
@@ -23,7 +23,6 @@ import android.content.pm.ShortcutInfo;
import android.content.pm.UserPackage;
import android.content.pm.UserPackage;
import android.util.ArrayMap;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.Slog;
import android.util.Xml;
import android.util.Xml;


@@ -32,8 +31,6 @@ import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
import com.android.modules.utils.TypedXmlSerializer;
import com.android.server.pm.ShortcutService.DumpFilter;
import com.android.server.pm.ShortcutService.DumpFilter;


import libcore.io.IoUtils;

import org.json.JSONException;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONObject;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParser;
@@ -41,7 +38,6 @@ import org.xmlpull.v1.XmlPullParserException;


import java.io.File;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.ArrayList;
@@ -264,19 +260,15 @@ class ShortcutLauncher extends ShortcutPackageItem {


    public static ShortcutLauncher loadFromFile(File path, ShortcutUser shortcutUser,
    public static ShortcutLauncher loadFromFile(File path, ShortcutUser shortcutUser,
            int ownerUserId, boolean fromBackup) {
            int ownerUserId, boolean fromBackup) {

        try (ResilientAtomicFile file = getResilientFile(path)) {
        final AtomicFile file = new AtomicFile(path);
            FileInputStream in = null;
        final FileInputStream in;
            try {
            try {
                in = file.openRead();
                in = file.openRead();
        } catch (FileNotFoundException e) {
                if (in == null) {
            if (ShortcutService.DEBUG) {
                    Slog.d(TAG, "Not found " + path);
                    Slog.d(TAG, "Not found " + path);
            }
                    return null;
                    return null;
                }
                }


        try {
                ShortcutLauncher ret = null;
                ShortcutLauncher ret = null;
                TypedXmlPullParser parser = Xml.resolvePullParser(in);
                TypedXmlPullParser parser = Xml.resolvePullParser(in);


@@ -298,11 +290,11 @@ class ShortcutLauncher extends ShortcutPackageItem {
                    ShortcutService.throwForInvalidTag(depth, tag);
                    ShortcutService.throwForInvalidTag(depth, tag);
                }
                }
                return ret;
                return ret;
        } catch (IOException | XmlPullParserException e) {
            } catch (Exception e) {
                Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e);
                Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e);
            return null;
                file.failRead(in, e);
        } finally {
                return loadFromFile(path, shortcutUser, ownerUserId, fromBackup);
            IoUtils.closeQuietly(in);
            }
        }
        }
    }
    }


+30 −38
Original line number Original line Diff line number Diff line
@@ -50,7 +50,6 @@ import android.os.StrictMode;
import android.text.format.Formatter;
import android.text.format.Formatter;
import android.util.ArrayMap;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Log;
import android.util.Log;
import android.util.Slog;
import android.util.Slog;
import android.util.Xml;
import android.util.Xml;
@@ -69,8 +68,6 @@ import com.android.server.pm.ShortcutService.DumpFilter;
import com.android.server.pm.ShortcutService.ShortcutOperation;
import com.android.server.pm.ShortcutService.ShortcutOperation;
import com.android.server.pm.ShortcutService.Stats;
import com.android.server.pm.ShortcutService.Stats;


import libcore.io.IoUtils;

import org.json.JSONException;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONObject;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParser;
@@ -78,7 +75,6 @@ import org.xmlpull.v1.XmlPullParserException;


import java.io.File;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.ArrayList;
@@ -1969,19 +1965,15 @@ class ShortcutPackage extends ShortcutPackageItem {


    public static ShortcutPackage loadFromFile(ShortcutService s, ShortcutUser shortcutUser,
    public static ShortcutPackage loadFromFile(ShortcutService s, ShortcutUser shortcutUser,
            File path, boolean fromBackup) {
            File path, boolean fromBackup) {

        try (ResilientAtomicFile file = getResilientFile(path)) {
        final AtomicFile file = new AtomicFile(path);
            FileInputStream in = null;
        final FileInputStream in;
            try {
            try {
                in = file.openRead();
                in = file.openRead();
        } catch (FileNotFoundException e) {
                if (in == null) {
            if (ShortcutService.DEBUG) {
                    Slog.d(TAG, "Not found " + path);
                    Slog.d(TAG, "Not found " + path);
            }
                    return null;
                    return null;
                }
                }


        try {
                ShortcutPackage ret = null;
                ShortcutPackage ret = null;
                TypedXmlPullParser parser = Xml.resolvePullParser(in);
                TypedXmlPullParser parser = Xml.resolvePullParser(in);


@@ -2003,11 +1995,11 @@ class ShortcutPackage extends ShortcutPackageItem {
                    ShortcutService.throwForInvalidTag(depth, tag);
                    ShortcutService.throwForInvalidTag(depth, tag);
                }
                }
                return ret;
                return ret;
        } catch (IOException | XmlPullParserException e) {
            } catch (Exception e) {
                Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e);
                Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e);
            return null;
                file.failRead(in, e);
        } finally {
                return loadFromFile(s, shortcutUser, path, fromBackup);
            IoUtils.closeQuietly(in);
            }
        }
        }
    }
    }


+34 −31
Original line number Original line Diff line number Diff line
@@ -20,14 +20,13 @@ import android.annotation.Nullable;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInfo;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutInfo;
import android.graphics.Bitmap;
import android.graphics.Bitmap;
import android.util.AtomicFile;
import android.os.FileUtils;
import android.util.Slog;
import android.util.Slog;
import android.util.Xml;
import android.util.Xml;


import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
import com.android.internal.util.Preconditions;
import com.android.modules.utils.TypedXmlSerializer;
import com.android.modules.utils.TypedXmlSerializer;
import com.android.server.security.FileIntegrity;


import org.json.JSONException;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONObject;
@@ -160,7 +159,7 @@ abstract class ShortcutPackageItem {


    @GuardedBy("mLock")
    @GuardedBy("mLock")
    public void saveToFileLocked(File path, boolean forBackup) {
    public void saveToFileLocked(File path, boolean forBackup) {
        final AtomicFile file = new AtomicFile(path);
        try (ResilientAtomicFile file = getResilientFile(path)) {
            FileOutputStream os = null;
            FileOutputStream os = null;
            try {
            try {
                os = file.startWrite();
                os = file.startWrite();
@@ -181,17 +180,12 @@ abstract class ShortcutPackageItem {


                os.flush();
                os.flush();
                file.finishWrite(os);
                file.finishWrite(os);

            try {
                FileIntegrity.setUpFsVerity(path);
            } catch (IOException e) {
                Slog.e(TAG, "Failed to verity-protect " + path, e);
            }
            } catch (XmlPullParserException | IOException e) {
            } catch (XmlPullParserException | IOException e) {
                Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e);
                Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e);
                file.failWrite(os);
                file.failWrite(os);
            }
            }
        }
        }
    }


    @GuardedBy("mLock")
    @GuardedBy("mLock")
    void scheduleSaveToAppSearchLocked() {
    void scheduleSaveToAppSearchLocked() {
@@ -265,9 +259,18 @@ abstract class ShortcutPackageItem {


    void removeShortcutPackageItem() {
    void removeShortcutPackageItem() {
        synchronized (mLock) {
        synchronized (mLock) {
            getShortcutPackageItemFile().delete();
            getResilientFile(getShortcutPackageItemFile()).delete();
        }
        }
    }
    }


    protected abstract File getShortcutPackageItemFile();
    protected abstract File getShortcutPackageItemFile();

    protected static ResilientAtomicFile getResilientFile(File file) {
        String path = file.getPath();
        File temporaryBackup = new File(path + ".backup");
        File reserveCopy = new File(path + ".reservecopy");
        int fileMode = FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IXOTH;
        return new ResilientAtomicFile(file, temporaryBackup, reserveCopy, fileMode,
                "shortcut package item", null);
    }
}
}
+6 −4
Original line number Original line Diff line number Diff line
@@ -437,14 +437,14 @@ class ShortcutUser {
        } else {
        } else {
            final File root = s.injectUserDataPath(userId);
            final File root = s.injectUserDataPath(userId);


            forAllFilesIn(new File(root, DIRECTORY_PACKAGES), (File f) -> {
            forMainFilesIn(new File(root, DIRECTORY_PACKAGES), (File f) -> {
                final ShortcutPackage sp = ShortcutPackage.loadFromFile(s, ret, f, fromBackup);
                final ShortcutPackage sp = ShortcutPackage.loadFromFile(s, ret, f, fromBackup);
                if (sp != null) {
                if (sp != null) {
                    ret.mPackages.put(sp.getPackageName(), sp);
                    ret.mPackages.put(sp.getPackageName(), sp);
                }
                }
            });
            });


            forAllFilesIn(new File(root, DIRECTORY_LUANCHERS), (File f) -> {
            forMainFilesIn(new File(root, DIRECTORY_LUANCHERS), (File f) -> {
                final ShortcutLauncher sl =
                final ShortcutLauncher sl =
                        ShortcutLauncher.loadFromFile(f, ret, userId, fromBackup);
                        ShortcutLauncher.loadFromFile(f, ret, userId, fromBackup);
                if (sl != null) {
                if (sl != null) {
@@ -456,15 +456,17 @@ class ShortcutUser {
        return ret;
        return ret;
    }
    }


    private static void forAllFilesIn(File path, Consumer<File> callback) {
    private static void forMainFilesIn(File path, Consumer<File> callback) {
        if (!path.exists()) {
        if (!path.exists()) {
            return;
            return;
        }
        }
        File[] list = path.listFiles();
        File[] list = path.listFiles();
        for (File f : list) {
        for (File f : list) {
            if (!f.getName().endsWith(".reservecopy") && !f.getName().endsWith(".backup")) {
                callback.accept(f);
                callback.accept(f);
            }
            }
        }
        }
    }


    public void setCachedLauncher(String launcher) {
    public void setCachedLauncher(String launcher) {
        mCachedLauncher = launcher;
        mCachedLauncher = launcher;
+10 −1
Original line number Original line Diff line number Diff line
@@ -125,6 +125,8 @@ import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStream;
import java.io.Writer;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.List;
import java.util.List;
import java.util.Locale;
import java.util.Locale;
@@ -4014,8 +4016,10 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {


        ShortcutUser user = new ShortcutUser(mService, 0);
        ShortcutUser user = new ShortcutUser(mService, 0);


        File corruptedShortcutPackage = new File("/data/local/tmp/cts/content/",
        File corruptedShortcutPackage = new File(getTestContext().getFilesDir(),
                "broken_shortcut.xml");
                "broken_shortcut.xml");
        Files.copy(new File("/data/local/tmp/cts/content/", "broken_shortcut.xml").toPath(),
                corruptedShortcutPackage.toPath(), StandardCopyOption.REPLACE_EXISTING);
        assertNull(ShortcutPackage.loadFromFile(mService, user, corruptedShortcutPackage, false));
        assertNull(ShortcutPackage.loadFromFile(mService, user, corruptedShortcutPackage, false));
    }
    }


@@ -4116,6 +4120,11 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
        try (Writer os = new FileWriter(mService.getUserFile(USER_10).getBaseFile())) {
        try (Writer os = new FileWriter(mService.getUserFile(USER_10).getBaseFile())) {
            os.write("<?xml version='1.0' encoding='utf");
            os.write("<?xml version='1.0' encoding='utf");
        }
        }
        ShortcutPackage sp = mService.getUserShortcutsLocked(USER_0).getPackageShortcutsIfExists(
                CALLING_PACKAGE_1);
        try (Writer os = new FileWriter(sp.getShortcutPackageItemFile().getPath())) {
            os.write("<?xml version='1.0' encoding='utf");
        }


        // Restore.
        // Restore.
        initService();
        initService();