Loading services/core/java/com/android/server/pm/ShortcutLauncher.java +56 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. */ Loading services/core/java/com/android/server/pm/ShortcutPackage.java +55 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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 { Loading services/core/java/com/android/server/pm/ShortcutPackageItem.java +31 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; /** Loading Loading @@ -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); Loading services/core/java/com/android/server/pm/ShortcutUser.java +79 −5 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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"; Loading Loading @@ -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(); Loading @@ -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); Loading Loading @@ -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; } } Loading @@ -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; } Loading Loading
services/core/java/com/android/server/pm/ShortcutLauncher.java +56 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. */ Loading
services/core/java/com/android/server/pm/ShortcutPackage.java +55 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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 { Loading
services/core/java/com/android/server/pm/ShortcutPackageItem.java +31 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; /** Loading Loading @@ -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); Loading
services/core/java/com/android/server/pm/ShortcutUser.java +79 −5 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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"; Loading Loading @@ -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(); Loading @@ -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); Loading Loading @@ -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; } } Loading @@ -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; } Loading