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

Commit bc62377b authored by Mehdi Alizadeh's avatar Mehdi Alizadeh Committed by Android (Google) Code Review
Browse files

Merge "In ShortcutService, save data of each package in a separate file" into rvc-dev

parents a01520b3 a0869ba7
Loading
Loading
Loading
Loading
+56 −0
Original line number Diff line number Diff line
@@ -22,20 +22,29 @@ import android.content.pm.PackageInfo;
import android.content.pm.ShortcutInfo;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.Xml;

import com.android.internal.annotations.VisibleForTesting;
import com.android.server.pm.ShortcutService.DumpFilter;
import com.android.server.pm.ShortcutUser.PackageWithUser;

import libcore.io.IoUtils;

import org.json.JSONException;
import org.json.JSONObject;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

@@ -254,6 +263,53 @@ class ShortcutLauncher extends ShortcutPackageItem {
        out.endTag(null, TAG_ROOT);
    }

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

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

        try {
            final BufferedInputStream bis = new BufferedInputStream(in);

            ShortcutLauncher ret = null;
            XmlPullParser parser = Xml.newPullParser();
            parser.setInput(bis, StandardCharsets.UTF_8.name());

            int type;
            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
                if (type != XmlPullParser.START_TAG) {
                    continue;
                }
                final int depth = parser.getDepth();

                final String tag = parser.getName();
                if (ShortcutService.DEBUG_LOAD) {
                    Slog.d(TAG, String.format("depth=%d type=%d name=%s", depth, type, tag));
                }
                if ((depth == 1) && TAG_ROOT.equals(tag)) {
                    ret = loadFromXml(parser, shortcutUser, ownerUserId, fromBackup);
                    continue;
                }
                ShortcutService.throwForInvalidTag(depth, tag);
            }
            return ret;
        } catch (IOException | XmlPullParserException e) {
            Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e);
            return null;
        } finally {
            IoUtils.closeQuietly(in);
        }
    }

    /**
     * Load.
     */
+55 −0
Original line number Diff line number Diff line
@@ -31,8 +31,10 @@ import android.os.PersistableBundle;
import android.text.format.Formatter;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Log;
import android.util.Slog;
import android.util.Xml;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
@@ -43,15 +45,21 @@ import com.android.server.pm.ShortcutService.DumpFilter;
import com.android.server.pm.ShortcutService.ShortcutOperation;
import com.android.server.pm.ShortcutService.Stats;

import libcore.io.IoUtils;

import org.json.JSONException;
import org.json.JSONObject;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -1716,6 +1724,53 @@ class ShortcutPackage extends ShortcutPackageItem {
        out.endTag(null, TAG_SHORTCUT);
    }

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

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

        try {
            final BufferedInputStream bis = new BufferedInputStream(in);

            ShortcutPackage ret = null;
            XmlPullParser parser = Xml.newPullParser();
            parser.setInput(bis, StandardCharsets.UTF_8.name());

            int type;
            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
                if (type != XmlPullParser.START_TAG) {
                    continue;
                }
                final int depth = parser.getDepth();

                final String tag = parser.getName();
                if (ShortcutService.DEBUG_LOAD) {
                    Slog.d(TAG, String.format("depth=%d type=%d name=%s", depth, type, tag));
                }
                if ((depth == 1) && TAG_ROOT.equals(tag)) {
                    ret = loadFromXml(s, shortcutUser, parser, fromBackup);
                    continue;
                }
                ShortcutService.throwForInvalidTag(depth, tag);
            }
            return ret;
        } catch (IOException | XmlPullParserException e) {
            Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e);
            return null;
        } finally {
            IoUtils.closeQuietly(in);
        }
    }

    public static ShortcutPackage loadFromXml(ShortcutService s, ShortcutUser shortcutUser,
            XmlPullParser parser, boolean fromBackup)
            throws IOException, XmlPullParserException {
+31 −0
Original line number Diff line number Diff line
@@ -18,8 +18,10 @@ package com.android.server.pm;
import android.annotation.NonNull;
import android.content.pm.PackageInfo;
import android.content.pm.ShortcutInfo;
import android.util.AtomicFile;
import android.util.Slog;

import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.Preconditions;

import org.json.JSONException;
@@ -27,7 +29,11 @@ import org.json.JSONObject;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Objects;

/**
@@ -143,6 +149,31 @@ abstract class ShortcutPackageItem {
    public abstract void saveToXml(@NonNull XmlSerializer out, boolean forBackup)
            throws IOException, XmlPullParserException;

    public void saveToFile(File path, boolean forBackup) {
        final AtomicFile file = new AtomicFile(path);
        FileOutputStream os = null;
        try {
            os = file.startWrite();
            final BufferedOutputStream bos = new BufferedOutputStream(os);

            // Write to XML
            XmlSerializer itemOut = new FastXmlSerializer();
            itemOut.setOutput(bos, StandardCharsets.UTF_8.name());
            itemOut.startDocument(null, true);

            saveToXml(itemOut, forBackup);

            itemOut.endDocument();

            bos.flush();
            os.flush();
            file.finishWrite(os);
        } catch (XmlPullParserException | IOException e) {
            Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e);
            file.failWrite(os);
        }
    }

    public JSONObject dumpCheckin(boolean clear) throws JSONException {
        final JSONObject result = new JSONObject();
        result.put(KEY_NAME, mPackageName);
+79 −5
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.annotation.UserIdInt;
import android.content.ComponentName;
import android.content.pm.ShortcutManager;
import android.metrics.LogMaker;
import android.os.FileUtils;
import android.text.TextUtils;
import android.text.format.Formatter;
import android.util.ArrayMap;
@@ -30,7 +31,6 @@ import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.Preconditions;
import com.android.server.pm.ShortcutService.DumpFilter;
import com.android.server.pm.ShortcutService.InvalidFileFormatException;

@@ -55,6 +55,9 @@ import java.util.function.Consumer;
class ShortcutUser {
    private static final String TAG = ShortcutService.TAG;

    static final String DIRECTORY_PACKAGES = "packages";
    static final String DIRECTORY_LUANCHERS = "launchers";

    static final String TAG_ROOT = "user";
    private static final String TAG_LAUNCHER = "launcher";

@@ -354,6 +357,13 @@ class ShortcutUser {
                    mService.injectBuildFingerprint());
        }

        if (!forBackup) {
            // Since we are not handling package deletion yet, or any single package changes, just
            // clean the directory and rewrite all the ShortcutPackageItems.
            final File root = mService.injectUserDataPath(mUserId);
            FileUtils.deleteContents(new File(root, DIRECTORY_PACKAGES));
            FileUtils.deleteContents(new File(root, DIRECTORY_LUANCHERS));
        }
        // Can't use forEachPackageItem due to the checked exceptions.
        {
            final int size = mLaunchers.size();
@@ -371,20 +381,47 @@ class ShortcutUser {
        out.endTag(null, TAG_ROOT);
    }

    private void saveShortcutPackageItem(XmlSerializer out,
            ShortcutPackageItem spi, boolean forBackup) throws IOException, XmlPullParserException {
    private void saveShortcutPackageItem(XmlSerializer out, ShortcutPackageItem spi,
            boolean forBackup) throws IOException, XmlPullParserException {
        if (forBackup) {
            if (spi.getPackageUserId() != spi.getOwnerUserId()) {
                return; // Don't save cross-user information.
            }
        }
            spi.saveToXml(out, forBackup);
        } else {
            // Save each ShortcutPackageItem in a separate Xml file.
            final File path = getShortcutPackageItemFile(spi);
            if (ShortcutService.DEBUG) {
                Slog.d(TAG, "Saving package item " + spi.getPackageName() + " to " + path);
            }

            path.getParentFile().mkdirs();
            spi.saveToFile(path, forBackup);
        }
    }

    private File getShortcutPackageItemFile(ShortcutPackageItem spi) {
        boolean isShortcutLauncher = spi instanceof ShortcutLauncher;

        final File path = new File(mService.injectUserDataPath(mUserId),
                isShortcutLauncher ? DIRECTORY_LUANCHERS : DIRECTORY_PACKAGES);

        final String fileName;
        if (isShortcutLauncher) {
            // Package user id and owner id can have different values for ShortcutLaunchers. Adding
            // user Id to the file name to create a unique path. Owner id is used in the root path.
            fileName = spi.getPackageName() + spi.getPackageUserId() + ".xml";
        } else {
            fileName = spi.getPackageName() + ".xml";
        }

        return new File(path, fileName);
    }

    public static ShortcutUser loadFromXml(ShortcutService s, XmlPullParser parser, int userId,
            boolean fromBackup) throws IOException, XmlPullParserException, InvalidFileFormatException {
        final ShortcutUser ret = new ShortcutUser(s, userId);

        boolean readShortcutItems = false;
        try {
            ret.mKnownLocales = ShortcutService.parseStringAttribute(parser,
                    ATTR_KNOWN_LOCALES);
@@ -422,12 +459,14 @@ class ShortcutUser {

                            // Don't use addShortcut(), we don't need to save the icon.
                            ret.mPackages.put(shortcuts.getPackageName(), shortcuts);
                            readShortcutItems = true;
                            continue;
                        }

                        case ShortcutLauncher.TAG_ROOT: {
                            ret.addLauncher(
                                    ShortcutLauncher.loadFromXml(parser, ret, userId, fromBackup));
                            readShortcutItems = true;
                            continue;
                        }
                    }
@@ -438,9 +477,44 @@ class ShortcutUser {
            throw new ShortcutService.InvalidFileFormatException(
                    "Unable to parse file", e);
        }

        if (readShortcutItems) {
            // If the shortcuts info was read from the main Xml, skip reading from individual files.
            // Data will get stored in the new format during the next call to saveToXml().
            // TODO: ret.forAllPackageItems((ShortcutPackageItem item) -> item.markDirty());
            s.scheduleSaveUser(userId);
        } else {
            final File root = s.injectUserDataPath(userId);

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

            forAllFilesIn(new File(root, DIRECTORY_LUANCHERS), (File f) -> {
                final ShortcutLauncher sl =
                        ShortcutLauncher.loadFromFile(f, ret, userId, fromBackup);
                if (sl != null) {
                    ret.addLauncher(sl);
                }
            });
        }

        return ret;
    }

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

    public ComponentName getLastKnownLauncher() {
        return mLastKnownLauncher;
    }