Merge "In ShortcutService, save data of each package in a separate file" into rvc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
bc62377b95
@@ -22,20 +22,29 @@ import android.content.pm.PackageInfo;
|
|||||||
import android.content.pm.ShortcutInfo;
|
import android.content.pm.ShortcutInfo;
|
||||||
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 com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.server.pm.ShortcutService.DumpFilter;
|
import com.android.server.pm.ShortcutService.DumpFilter;
|
||||||
import com.android.server.pm.ShortcutUser.PackageWithUser;
|
import com.android.server.pm.ShortcutUser.PackageWithUser;
|
||||||
|
|
||||||
|
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;
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
import org.xmlpull.v1.XmlSerializer;
|
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.IOException;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -254,6 +263,53 @@ class ShortcutLauncher extends ShortcutPackageItem {
|
|||||||
out.endTag(null, TAG_ROOT);
|
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.
|
* Load.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -31,8 +31,10 @@ import android.os.PersistableBundle;
|
|||||||
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 com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.internal.util.ArrayUtils;
|
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.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;
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
import org.xmlpull.v1.XmlSerializer;
|
import org.xmlpull.v1.XmlSerializer;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
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.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
@@ -1716,6 +1724,53 @@ class ShortcutPackage extends ShortcutPackageItem {
|
|||||||
out.endTag(null, TAG_SHORTCUT);
|
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,
|
public static ShortcutPackage loadFromXml(ShortcutService s, ShortcutUser shortcutUser,
|
||||||
XmlPullParser parser, boolean fromBackup)
|
XmlPullParser parser, boolean fromBackup)
|
||||||
throws IOException, XmlPullParserException {
|
throws IOException, XmlPullParserException {
|
||||||
|
|||||||
@@ -18,8 +18,10 @@ package com.android.server.pm;
|
|||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
import android.content.pm.ShortcutInfo;
|
import android.content.pm.ShortcutInfo;
|
||||||
|
import android.util.AtomicFile;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
|
|
||||||
|
import com.android.internal.util.FastXmlSerializer;
|
||||||
import com.android.internal.util.Preconditions;
|
import com.android.internal.util.Preconditions;
|
||||||
|
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
@@ -27,7 +29,11 @@ import org.json.JSONObject;
|
|||||||
import org.xmlpull.v1.XmlPullParserException;
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
import org.xmlpull.v1.XmlSerializer;
|
import org.xmlpull.v1.XmlSerializer;
|
||||||
|
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -143,6 +149,31 @@ abstract class ShortcutPackageItem {
|
|||||||
public abstract void saveToXml(@NonNull XmlSerializer out, boolean forBackup)
|
public abstract void saveToXml(@NonNull XmlSerializer out, boolean forBackup)
|
||||||
throws IOException, XmlPullParserException;
|
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 {
|
public JSONObject dumpCheckin(boolean clear) throws JSONException {
|
||||||
final JSONObject result = new JSONObject();
|
final JSONObject result = new JSONObject();
|
||||||
result.put(KEY_NAME, mPackageName);
|
result.put(KEY_NAME, mPackageName);
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import android.annotation.UserIdInt;
|
|||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.pm.ShortcutManager;
|
import android.content.pm.ShortcutManager;
|
||||||
import android.metrics.LogMaker;
|
import android.metrics.LogMaker;
|
||||||
|
import android.os.FileUtils;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.format.Formatter;
|
import android.text.format.Formatter;
|
||||||
import android.util.ArrayMap;
|
import android.util.ArrayMap;
|
||||||
@@ -30,7 +31,6 @@ import android.util.Slog;
|
|||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.internal.logging.MetricsLogger;
|
import com.android.internal.logging.MetricsLogger;
|
||||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
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.DumpFilter;
|
||||||
import com.android.server.pm.ShortcutService.InvalidFileFormatException;
|
import com.android.server.pm.ShortcutService.InvalidFileFormatException;
|
||||||
|
|
||||||
@@ -55,6 +55,9 @@ import java.util.function.Consumer;
|
|||||||
class ShortcutUser {
|
class ShortcutUser {
|
||||||
private static final String TAG = ShortcutService.TAG;
|
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";
|
static final String TAG_ROOT = "user";
|
||||||
private static final String TAG_LAUNCHER = "launcher";
|
private static final String TAG_LAUNCHER = "launcher";
|
||||||
|
|
||||||
@@ -354,6 +357,13 @@ class ShortcutUser {
|
|||||||
mService.injectBuildFingerprint());
|
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.
|
// Can't use forEachPackageItem due to the checked exceptions.
|
||||||
{
|
{
|
||||||
final int size = mLaunchers.size();
|
final int size = mLaunchers.size();
|
||||||
@@ -371,20 +381,47 @@ class ShortcutUser {
|
|||||||
out.endTag(null, TAG_ROOT);
|
out.endTag(null, TAG_ROOT);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveShortcutPackageItem(XmlSerializer out,
|
private void saveShortcutPackageItem(XmlSerializer out, ShortcutPackageItem spi,
|
||||||
ShortcutPackageItem spi, boolean forBackup) throws IOException, XmlPullParserException {
|
boolean forBackup) throws IOException, XmlPullParserException {
|
||||||
if (forBackup) {
|
if (forBackup) {
|
||||||
if (spi.getPackageUserId() != spi.getOwnerUserId()) {
|
if (spi.getPackageUserId() != spi.getOwnerUserId()) {
|
||||||
return; // Don't save cross-user information.
|
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);
|
||||||
}
|
}
|
||||||
spi.saveToXml(out, 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,
|
public static ShortcutUser loadFromXml(ShortcutService s, XmlPullParser parser, int userId,
|
||||||
boolean fromBackup) throws IOException, XmlPullParserException, InvalidFileFormatException {
|
boolean fromBackup) throws IOException, XmlPullParserException, InvalidFileFormatException {
|
||||||
final ShortcutUser ret = new ShortcutUser(s, userId);
|
final ShortcutUser ret = new ShortcutUser(s, userId);
|
||||||
|
boolean readShortcutItems = false;
|
||||||
try {
|
try {
|
||||||
ret.mKnownLocales = ShortcutService.parseStringAttribute(parser,
|
ret.mKnownLocales = ShortcutService.parseStringAttribute(parser,
|
||||||
ATTR_KNOWN_LOCALES);
|
ATTR_KNOWN_LOCALES);
|
||||||
@@ -422,12 +459,14 @@ class ShortcutUser {
|
|||||||
|
|
||||||
// Don't use addShortcut(), we don't need to save the icon.
|
// Don't use addShortcut(), we don't need to save the icon.
|
||||||
ret.mPackages.put(shortcuts.getPackageName(), shortcuts);
|
ret.mPackages.put(shortcuts.getPackageName(), shortcuts);
|
||||||
|
readShortcutItems = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
case ShortcutLauncher.TAG_ROOT: {
|
case ShortcutLauncher.TAG_ROOT: {
|
||||||
ret.addLauncher(
|
ret.addLauncher(
|
||||||
ShortcutLauncher.loadFromXml(parser, ret, userId, fromBackup));
|
ShortcutLauncher.loadFromXml(parser, ret, userId, fromBackup));
|
||||||
|
readShortcutItems = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -438,9 +477,44 @@ class ShortcutUser {
|
|||||||
throw new ShortcutService.InvalidFileFormatException(
|
throw new ShortcutService.InvalidFileFormatException(
|
||||||
"Unable to parse file", e);
|
"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;
|
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() {
|
public ComponentName getLastKnownLauncher() {
|
||||||
return mLastKnownLauncher;
|
return mLastKnownLauncher;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user