diff options
author | 2020-05-20 21:01:46 +0000 | |
---|---|---|
committer | 2020-05-20 21:01:46 +0000 | |
commit | 930e61f46a94eebed136e49dff70c93e74efc7a5 (patch) | |
tree | a9e792272c2979051dab433fa9f4704c05ff2941 | |
parent | 5c1f74c5dece77859423e317f2dbf798cf821154 (diff) | |
parent | bc62377b95edcc403c1d2d6c648b8b1f68a6ae30 (diff) |
Merge "In ShortcutService, save data of each package in a separate file" into rvc-dev am: bc62377b95
Change-Id: I88d4a4bb4acbb0e348457e0ca95ab6781f70dc8c
4 files changed, 221 insertions, 5 deletions
diff --git a/services/core/java/com/android/server/pm/ShortcutLauncher.java b/services/core/java/com/android/server/pm/ShortcutLauncher.java index 0fecb631a182..0ebe596111f2 100644 --- a/services/core/java/com/android/server/pm/ShortcutLauncher.java +++ b/services/core/java/com/android/server/pm/ShortcutLauncher.java @@ -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. */ diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index 322763e36ab9..bdd0d3b071ef 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -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 { diff --git a/services/core/java/com/android/server/pm/ShortcutPackageItem.java b/services/core/java/com/android/server/pm/ShortcutPackageItem.java index 6d9d69e00877..801c6cbb8f46 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackageItem.java +++ b/services/core/java/com/android/server/pm/ShortcutPackageItem.java @@ -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); diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java index eab3f4d8c44f..df6d321fa33b 100644 --- a/services/core/java/com/android/server/pm/ShortcutUser.java +++ b/services/core/java/com/android/server/pm/ShortcutUser.java @@ -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"; } - spi.saveToXml(out, forBackup); + + 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; } |