Loading services/core/java/com/android/server/pm/ShortcutLauncher.java +1 −1 Original line number Diff line number Diff line Loading @@ -453,7 +453,7 @@ class ShortcutLauncher extends ShortcutPackageItem { @Override protected File getShortcutPackageItemFile() { final File path = new File(mShortcutUser.mService.injectUserDataPath( mShortcutUser.getUserId()), ShortcutUser.DIRECTORY_LUANCHERS); mShortcutUser.getUserId()), ShortcutUser.DIRECTORY_LAUNCHERS); // 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. final String fileName = getPackageName() + getPackageUserId() + ".xml"; Loading services/core/java/com/android/server/pm/ShortcutService.java +85 −60 Original line number Diff line number Diff line Loading @@ -16,7 +16,9 @@ package com.android.server.pm; import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED; import static android.provider.DeviceConfig.NAMESPACE_SYSTEMUI; import static com.android.server.pm.ShortcutUser.DIRECTORY_LAUNCHERS; import static com.android.server.pm.ShortcutUser.DIRECTORY_PACKAGES; import android.Manifest.permission; import android.annotation.IntDef; Loading Loading @@ -94,7 +96,6 @@ import android.os.ShellCommand; import android.os.SystemClock; import android.os.Trace; import android.os.UserHandle; import android.provider.DeviceConfig; import android.text.TextUtils; import android.text.format.TimeMigrationUtils; import android.util.ArraySet; Loading @@ -112,7 +113,6 @@ import android.view.IWindowManager; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.logging.MetricsLogger; import com.android.internal.util.CollectionUtils; import com.android.internal.util.DumpUtils; Loading Loading @@ -155,7 +155,6 @@ import java.util.concurrent.atomic.AtomicLong; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.regex.Pattern; import java.util.stream.Collectors; /** * TODO: Loading @@ -171,7 +170,7 @@ public class ShortcutService extends IShortcutService.Stub { static final boolean DEBUG = false; // STOPSHIP if true static final boolean DEBUG_LOAD = false; // STOPSHIP if true static final boolean DEBUG_PROCSTATE = false; // STOPSHIP if true static final boolean DEBUG_REBOOT = Build.IS_DEBUGGABLE; static final boolean DEBUG_REBOOT = false; // STOPSHIP if true @VisibleForTesting static final long DEFAULT_RESET_INTERVAL_SEC = 24 * 60 * 60; // 1 day Loading Loading @@ -292,7 +291,8 @@ public class ShortcutService extends IShortcutService.Stub { final Context mContext; private final Object mServiceLock = new Object(); @VisibleForTesting final Object mServiceLock = new Object(); private final Object mNonPersistentUsersLock = new Object(); private final Object mWtfLock = new Object(); Loading Loading @@ -982,7 +982,7 @@ public class ShortcutService extends IShortcutService.Stub { } @VisibleForTesting void saveBaseState() { void injectSaveBaseState() { try (ResilientAtomicFile file = getBaseStateFile()) { if (DEBUG || DEBUG_REBOOT) { Slog.d(TAG, "Saving to " + file.getBaseFile()); Loading @@ -994,6 +994,19 @@ public class ShortcutService extends IShortcutService.Stub { outs = file.startWrite(); } saveBaseStateAsXml(outs); // Close. injectFinishWrite(file, outs); } catch (IOException e) { Slog.w(TAG, "Failed to write to file " + file.getBaseFile(), e); file.failWrite(outs); } } } @VisibleForTesting protected void saveBaseStateAsXml(OutputStream outs) throws IOException { // Write to XML TypedXmlSerializer out = Xml.resolveSerializer(outs); out.startDocument(null, true); Loading @@ -1006,20 +1019,18 @@ public class ShortcutService extends IShortcutService.Stub { // Epilogue. out.endTag(null, TAG_ROOT); out.endDocument(); // Close. injectFinishWrite(file, outs); } catch (IOException e) { Slog.w(TAG, "Failed to write to file " + file.getBaseFile(), e); file.failWrite(outs); } } } @GuardedBy("mServiceLock") private void loadBaseStateLocked() { mRawLastResetTime.set(0); injectLoadBaseState(); // Adjust the last reset time. getLastResetTimeLocked(); } @VisibleForTesting protected void injectLoadBaseState() { try (ResilientAtomicFile file = getBaseStateFile()) { if (DEBUG || DEBUG_REBOOT) { Slog.d(TAG, "Loading from " + file.getBaseFile()); Loading @@ -1030,7 +1041,21 @@ public class ShortcutService extends IShortcutService.Stub { if (in == null) { throw new FileNotFoundException(file.getBaseFile().getAbsolutePath()); } loadBaseStateAsXml(in); } catch (FileNotFoundException e) { // Use the default } catch (IOException | XmlPullParserException e) { // Remove corrupted file and retry. file.failRead(in, e); loadBaseStateLocked(); return; } } } @VisibleForTesting protected void loadBaseStateAsXml(InputStream in) throws IOException, XmlPullParserException { TypedXmlPullParser parser = Xml.resolvePullParser(in); int type; Loading Loading @@ -1058,17 +1083,6 @@ public class ShortcutService extends IShortcutService.Stub { break; } } } catch (FileNotFoundException e) { // Use the default } catch (IOException | XmlPullParserException e) { // Remove corrupted file and retry. file.failRead(in, e); loadBaseStateLocked(); return; } } // Adjust the last reset time. getLastResetTimeLocked(); } @VisibleForTesting Loading @@ -1083,7 +1097,8 @@ public class ShortcutService extends IShortcutService.Stub { "user shortcut", null); } private void saveUser(@UserIdInt int userId) { @VisibleForTesting protected void injectSaveUser(@UserIdInt int userId) { try (ResilientAtomicFile file = getUserFile(userId)) { FileOutputStream os = null; try { Loading @@ -1092,8 +1107,14 @@ public class ShortcutService extends IShortcutService.Stub { } synchronized (mServiceLock) { // 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 = injectUserDataPath(userId); FileUtils.deleteContents(new File(root, DIRECTORY_PACKAGES)); FileUtils.deleteContents(new File(root, DIRECTORY_LAUNCHERS)); os = file.startWrite(); saveUserInternalLocked(userId, os, /* forBackup= */ false); getUserShortcutsLocked(userId).scheduleSaveAllLaunchersAndPackages(); } injectFinishWrite(file, os); Loading @@ -1109,8 +1130,9 @@ public class ShortcutService extends IShortcutService.Stub { getUserShortcutsLocked(userId).logSharingShortcutStats(mMetricsLogger); } @VisibleForTesting @GuardedBy("mServiceLock") private void saveUserInternalLocked(@UserIdInt int userId, OutputStream os, protected void saveUserInternalLocked(@UserIdInt int userId, OutputStream os, boolean forBackup) throws IOException, XmlPullParserException { // Write to XML Loading Loading @@ -1138,8 +1160,9 @@ public class ShortcutService extends IShortcutService.Stub { Slog.w(TAG, String.format("Invalid tag '%s' found at depth %d", tag, depth)); } @VisibleForTesting @Nullable private ShortcutUser loadUserLocked(@UserIdInt int userId) { protected ShortcutUser injectLoadUserLocked(@UserIdInt int userId) { try (ResilientAtomicFile file = getUserFile(userId)) { FileInputStream in = null; try { Loading @@ -1157,12 +1180,13 @@ public class ShortcutService extends IShortcutService.Stub { } catch (Exception e) { // Remove corrupted file and retry. file.failRead(in, e); return loadUserLocked(userId); return injectLoadUserLocked(userId); } } } private ShortcutUser loadUserInternal(@UserIdInt int userId, InputStream is, @VisibleForTesting protected ShortcutUser loadUserInternal(@UserIdInt int userId, InputStream is, boolean fromBackup) throws XmlPullParserException, IOException, InvalidFileFormatException { Loading Loading @@ -1240,9 +1264,9 @@ public class ShortcutService extends IShortcutService.Stub { for (int i = dirtyUserIds.size() - 1; i >= 0; i--) { final int userId = dirtyUserIds.get(i); if (userId == UserHandle.USER_NULL) { // USER_NULL for base state. saveBaseState(); injectSaveBaseState(); } else { saveUser(userId); injectSaveUser(userId); } } } catch (Exception e) { Loading Loading @@ -1349,7 +1373,7 @@ public class ShortcutService extends IShortcutService.Stub { ShortcutUser userPackages = mUsers.get(userId); if (userPackages == null) { userPackages = loadUserLocked(userId); userPackages = injectLoadUserLocked(userId); if (userPackages == null) { userPackages = new ShortcutUser(this, userId); } Loading Loading @@ -1430,8 +1454,9 @@ public class ShortcutService extends IShortcutService.Stub { * {@link ShortcutBitmapSaver#waitForAllSavesLocked()} to make sure there's no pending bitmap * saves are going on. */ @VisibleForTesting @GuardedBy("mServiceLock") private void cleanupDanglingBitmapDirectoriesLocked(@UserIdInt int userId) { void cleanupDanglingBitmapDirectoriesLocked(@UserIdInt int userId) { if (DEBUG) { Slog.d(TAG, "cleanupDanglingBitmaps: userId=" + userId); } Loading Loading @@ -2755,7 +2780,7 @@ public class ShortcutService extends IShortcutService.Stub { getPackageShortcutsLocked(packageName, userId) .resetRateLimitingForCommandLineNoSaving(); } saveUser(userId); injectSaveUser(userId); } // We override this method in unit tests to do a simpler check. Loading Loading @@ -4407,7 +4432,7 @@ public class ShortcutService extends IShortcutService.Stub { pw.println(); }); } saveUser(userId); injectSaveUser(userId); } // === Dump === Loading services/core/java/com/android/server/pm/ShortcutUser.java +25 −29 Original line number Diff line number Diff line Loading @@ -21,16 +21,12 @@ import android.annotation.UserIdInt; import android.content.pm.ShortcutManager; import android.content.pm.UserPackage; import android.metrics.LogMaker; import android.os.Binder; import android.os.FileUtils; import android.os.UserHandle; import android.text.TextUtils; import android.text.format.Formatter; import android.util.ArrayMap; import android.util.Log; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; Loading @@ -49,8 +45,6 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; import java.util.function.Consumer; Loading @@ -63,7 +57,7 @@ class ShortcutUser { private static final String TAG = ShortcutService.TAG; static final String DIRECTORY_PACKAGES = "packages"; static final String DIRECTORY_LUANCHERS = "launchers"; static final String DIRECTORY_LAUNCHERS = "launchers"; static final String TAG_ROOT = "user"; private static final String TAG_LAUNCHER = "launcher"; Loading Loading @@ -322,41 +316,43 @@ 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. if (forBackup) { int size = mLaunchers.size(); for (int i = 0; i < size; i++) { saveShortcutPackageItem(out, mLaunchers.valueAt(i)); } size = mPackages.size(); for (int i = 0; i < size; i++) { saveShortcutPackageItem(out, mPackages.valueAt(i)); } } out.endTag(null, TAG_ROOT); } void scheduleSaveAllLaunchersAndPackages() { { final int size = mLaunchers.size(); for (int i = 0; i < size; i++) { saveShortcutPackageItem(out, mLaunchers.valueAt(i), forBackup); mLaunchers.valueAt(i).scheduleSave(); } } { final int size = mPackages.size(); for (int i = 0; i < size; i++) { saveShortcutPackageItem(out, mPackages.valueAt(i), forBackup); mPackages.valueAt(i).scheduleSave(); } } out.endTag(null, TAG_ROOT); } private void saveShortcutPackageItem(TypedXmlSerializer out, ShortcutPackageItem spi, boolean forBackup) throws IOException, XmlPullParserException { if (forBackup) { private void saveShortcutPackageItem(TypedXmlSerializer out, ShortcutPackageItem spi) throws IOException, XmlPullParserException { if (spi.getPackageUserId() != spi.getOwnerUserId()) { return; // Don't save cross-user information. } spi.waitForBitmapSaves(); spi.saveToXml(out, forBackup); } else { spi.scheduleSave(); } spi.saveToXml(out, true /* forBackup */); } public static ShortcutUser loadFromXml(ShortcutService s, TypedXmlPullParser parser, int userId, Loading Loading @@ -429,7 +425,7 @@ class ShortcutUser { } }); forMainFilesIn(new File(root, DIRECTORY_LUANCHERS), (File f) -> { forMainFilesIn(new File(root, DIRECTORY_LAUNCHERS), (File f) -> { final ShortcutLauncher sl = ShortcutLauncher.loadFromFile(f, ret, userId, fromBackup); if (sl != null) { Loading services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java +58 −1 Original line number Diff line number Diff line Loading @@ -95,8 +95,8 @@ import android.test.mock.MockContext; import android.util.ArrayMap; import android.util.Log; import android.util.Pair; import android.util.SparseArray; import com.android.internal.infra.AndroidFuture; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.pm.LauncherAppsService.LauncherAppsImpl; Loading @@ -110,6 +110,7 @@ import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; Loading Loading @@ -149,6 +150,9 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { protected static final String MAIN_ACTIVITY_CLASS = "MainActivity"; protected static final String PIN_CONFIRM_ACTIVITY_CLASS = "PinConfirmActivity"; private byte[] mBaseState; protected final SparseArray<byte[]> mUserStates = new SparseArray<>(); // public for mockito public class BaseContext extends MockContext { @Override Loading Loading @@ -287,6 +291,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { final ServiceContext mContext; IUidObserver mUidObserver; public ShortcutServiceTestable(ServiceContext context, Looper looper) { super(context, looper, /* onyForPackageManagerApis */ false); mContext = context; Loading Loading @@ -567,6 +572,58 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { // During tests, WTF is fatal. fail(message + " exception: " + th + "\n" + Log.getStackTraceString(th)); } @Override void injectSaveBaseState() { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { saveBaseStateAsXml(baos); } catch (Exception e) { throw new RuntimeException(e); } mBaseState = baos.toByteArray(); } @Override protected void injectLoadBaseState() { if (mBaseState == null) { return; } ByteArrayInputStream bais = new ByteArrayInputStream(mBaseState); try { loadBaseStateAsXml(bais); } catch (Exception e) { throw new RuntimeException(e); } } @Override protected void injectSaveUser(@UserIdInt int userId) { synchronized (mServiceLock) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { saveUserInternalLocked(userId, baos, /* forBackup= */ false); cleanupDanglingBitmapDirectoriesLocked(userId); } catch (Exception e) { throw new RuntimeException(e); } mUserStates.put(userId, baos.toByteArray()); } } @Override protected ShortcutUser injectLoadUserLocked(@UserIdInt int userId) { final byte[] userState = mUserStates.get(userId); if (userState == null) { return null; } ByteArrayInputStream bais = new ByteArrayInputStream(userState); try { return loadUserInternal(userId, bais, /* forBackup= */ false); } catch (Exception e) { throw new RuntimeException(e); } } } /** ShortcutManager with injection override methods. */ Loading services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java +1 −1 Original line number Diff line number Diff line Loading @@ -201,7 +201,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { mInjectedCurrentTimeMillis = START_TIME + 4 * INTERVAL + 50; assertResetTimes(START_TIME + 4 * INTERVAL, START_TIME + 5 * INTERVAL); mService.saveBaseState(); mService.injectSaveBaseState(); dumpBaseStateFile(); Loading Loading
services/core/java/com/android/server/pm/ShortcutLauncher.java +1 −1 Original line number Diff line number Diff line Loading @@ -453,7 +453,7 @@ class ShortcutLauncher extends ShortcutPackageItem { @Override protected File getShortcutPackageItemFile() { final File path = new File(mShortcutUser.mService.injectUserDataPath( mShortcutUser.getUserId()), ShortcutUser.DIRECTORY_LUANCHERS); mShortcutUser.getUserId()), ShortcutUser.DIRECTORY_LAUNCHERS); // 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. final String fileName = getPackageName() + getPackageUserId() + ".xml"; Loading
services/core/java/com/android/server/pm/ShortcutService.java +85 −60 Original line number Diff line number Diff line Loading @@ -16,7 +16,9 @@ package com.android.server.pm; import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED; import static android.provider.DeviceConfig.NAMESPACE_SYSTEMUI; import static com.android.server.pm.ShortcutUser.DIRECTORY_LAUNCHERS; import static com.android.server.pm.ShortcutUser.DIRECTORY_PACKAGES; import android.Manifest.permission; import android.annotation.IntDef; Loading Loading @@ -94,7 +96,6 @@ import android.os.ShellCommand; import android.os.SystemClock; import android.os.Trace; import android.os.UserHandle; import android.provider.DeviceConfig; import android.text.TextUtils; import android.text.format.TimeMigrationUtils; import android.util.ArraySet; Loading @@ -112,7 +113,6 @@ import android.view.IWindowManager; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.logging.MetricsLogger; import com.android.internal.util.CollectionUtils; import com.android.internal.util.DumpUtils; Loading Loading @@ -155,7 +155,6 @@ import java.util.concurrent.atomic.AtomicLong; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.regex.Pattern; import java.util.stream.Collectors; /** * TODO: Loading @@ -171,7 +170,7 @@ public class ShortcutService extends IShortcutService.Stub { static final boolean DEBUG = false; // STOPSHIP if true static final boolean DEBUG_LOAD = false; // STOPSHIP if true static final boolean DEBUG_PROCSTATE = false; // STOPSHIP if true static final boolean DEBUG_REBOOT = Build.IS_DEBUGGABLE; static final boolean DEBUG_REBOOT = false; // STOPSHIP if true @VisibleForTesting static final long DEFAULT_RESET_INTERVAL_SEC = 24 * 60 * 60; // 1 day Loading Loading @@ -292,7 +291,8 @@ public class ShortcutService extends IShortcutService.Stub { final Context mContext; private final Object mServiceLock = new Object(); @VisibleForTesting final Object mServiceLock = new Object(); private final Object mNonPersistentUsersLock = new Object(); private final Object mWtfLock = new Object(); Loading Loading @@ -982,7 +982,7 @@ public class ShortcutService extends IShortcutService.Stub { } @VisibleForTesting void saveBaseState() { void injectSaveBaseState() { try (ResilientAtomicFile file = getBaseStateFile()) { if (DEBUG || DEBUG_REBOOT) { Slog.d(TAG, "Saving to " + file.getBaseFile()); Loading @@ -994,6 +994,19 @@ public class ShortcutService extends IShortcutService.Stub { outs = file.startWrite(); } saveBaseStateAsXml(outs); // Close. injectFinishWrite(file, outs); } catch (IOException e) { Slog.w(TAG, "Failed to write to file " + file.getBaseFile(), e); file.failWrite(outs); } } } @VisibleForTesting protected void saveBaseStateAsXml(OutputStream outs) throws IOException { // Write to XML TypedXmlSerializer out = Xml.resolveSerializer(outs); out.startDocument(null, true); Loading @@ -1006,20 +1019,18 @@ public class ShortcutService extends IShortcutService.Stub { // Epilogue. out.endTag(null, TAG_ROOT); out.endDocument(); // Close. injectFinishWrite(file, outs); } catch (IOException e) { Slog.w(TAG, "Failed to write to file " + file.getBaseFile(), e); file.failWrite(outs); } } } @GuardedBy("mServiceLock") private void loadBaseStateLocked() { mRawLastResetTime.set(0); injectLoadBaseState(); // Adjust the last reset time. getLastResetTimeLocked(); } @VisibleForTesting protected void injectLoadBaseState() { try (ResilientAtomicFile file = getBaseStateFile()) { if (DEBUG || DEBUG_REBOOT) { Slog.d(TAG, "Loading from " + file.getBaseFile()); Loading @@ -1030,7 +1041,21 @@ public class ShortcutService extends IShortcutService.Stub { if (in == null) { throw new FileNotFoundException(file.getBaseFile().getAbsolutePath()); } loadBaseStateAsXml(in); } catch (FileNotFoundException e) { // Use the default } catch (IOException | XmlPullParserException e) { // Remove corrupted file and retry. file.failRead(in, e); loadBaseStateLocked(); return; } } } @VisibleForTesting protected void loadBaseStateAsXml(InputStream in) throws IOException, XmlPullParserException { TypedXmlPullParser parser = Xml.resolvePullParser(in); int type; Loading Loading @@ -1058,17 +1083,6 @@ public class ShortcutService extends IShortcutService.Stub { break; } } } catch (FileNotFoundException e) { // Use the default } catch (IOException | XmlPullParserException e) { // Remove corrupted file and retry. file.failRead(in, e); loadBaseStateLocked(); return; } } // Adjust the last reset time. getLastResetTimeLocked(); } @VisibleForTesting Loading @@ -1083,7 +1097,8 @@ public class ShortcutService extends IShortcutService.Stub { "user shortcut", null); } private void saveUser(@UserIdInt int userId) { @VisibleForTesting protected void injectSaveUser(@UserIdInt int userId) { try (ResilientAtomicFile file = getUserFile(userId)) { FileOutputStream os = null; try { Loading @@ -1092,8 +1107,14 @@ public class ShortcutService extends IShortcutService.Stub { } synchronized (mServiceLock) { // 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 = injectUserDataPath(userId); FileUtils.deleteContents(new File(root, DIRECTORY_PACKAGES)); FileUtils.deleteContents(new File(root, DIRECTORY_LAUNCHERS)); os = file.startWrite(); saveUserInternalLocked(userId, os, /* forBackup= */ false); getUserShortcutsLocked(userId).scheduleSaveAllLaunchersAndPackages(); } injectFinishWrite(file, os); Loading @@ -1109,8 +1130,9 @@ public class ShortcutService extends IShortcutService.Stub { getUserShortcutsLocked(userId).logSharingShortcutStats(mMetricsLogger); } @VisibleForTesting @GuardedBy("mServiceLock") private void saveUserInternalLocked(@UserIdInt int userId, OutputStream os, protected void saveUserInternalLocked(@UserIdInt int userId, OutputStream os, boolean forBackup) throws IOException, XmlPullParserException { // Write to XML Loading Loading @@ -1138,8 +1160,9 @@ public class ShortcutService extends IShortcutService.Stub { Slog.w(TAG, String.format("Invalid tag '%s' found at depth %d", tag, depth)); } @VisibleForTesting @Nullable private ShortcutUser loadUserLocked(@UserIdInt int userId) { protected ShortcutUser injectLoadUserLocked(@UserIdInt int userId) { try (ResilientAtomicFile file = getUserFile(userId)) { FileInputStream in = null; try { Loading @@ -1157,12 +1180,13 @@ public class ShortcutService extends IShortcutService.Stub { } catch (Exception e) { // Remove corrupted file and retry. file.failRead(in, e); return loadUserLocked(userId); return injectLoadUserLocked(userId); } } } private ShortcutUser loadUserInternal(@UserIdInt int userId, InputStream is, @VisibleForTesting protected ShortcutUser loadUserInternal(@UserIdInt int userId, InputStream is, boolean fromBackup) throws XmlPullParserException, IOException, InvalidFileFormatException { Loading Loading @@ -1240,9 +1264,9 @@ public class ShortcutService extends IShortcutService.Stub { for (int i = dirtyUserIds.size() - 1; i >= 0; i--) { final int userId = dirtyUserIds.get(i); if (userId == UserHandle.USER_NULL) { // USER_NULL for base state. saveBaseState(); injectSaveBaseState(); } else { saveUser(userId); injectSaveUser(userId); } } } catch (Exception e) { Loading Loading @@ -1349,7 +1373,7 @@ public class ShortcutService extends IShortcutService.Stub { ShortcutUser userPackages = mUsers.get(userId); if (userPackages == null) { userPackages = loadUserLocked(userId); userPackages = injectLoadUserLocked(userId); if (userPackages == null) { userPackages = new ShortcutUser(this, userId); } Loading Loading @@ -1430,8 +1454,9 @@ public class ShortcutService extends IShortcutService.Stub { * {@link ShortcutBitmapSaver#waitForAllSavesLocked()} to make sure there's no pending bitmap * saves are going on. */ @VisibleForTesting @GuardedBy("mServiceLock") private void cleanupDanglingBitmapDirectoriesLocked(@UserIdInt int userId) { void cleanupDanglingBitmapDirectoriesLocked(@UserIdInt int userId) { if (DEBUG) { Slog.d(TAG, "cleanupDanglingBitmaps: userId=" + userId); } Loading Loading @@ -2755,7 +2780,7 @@ public class ShortcutService extends IShortcutService.Stub { getPackageShortcutsLocked(packageName, userId) .resetRateLimitingForCommandLineNoSaving(); } saveUser(userId); injectSaveUser(userId); } // We override this method in unit tests to do a simpler check. Loading Loading @@ -4407,7 +4432,7 @@ public class ShortcutService extends IShortcutService.Stub { pw.println(); }); } saveUser(userId); injectSaveUser(userId); } // === Dump === Loading
services/core/java/com/android/server/pm/ShortcutUser.java +25 −29 Original line number Diff line number Diff line Loading @@ -21,16 +21,12 @@ import android.annotation.UserIdInt; import android.content.pm.ShortcutManager; import android.content.pm.UserPackage; import android.metrics.LogMaker; import android.os.Binder; import android.os.FileUtils; import android.os.UserHandle; import android.text.TextUtils; import android.text.format.Formatter; import android.util.ArrayMap; import android.util.Log; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; Loading @@ -49,8 +45,6 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; import java.util.function.Consumer; Loading @@ -63,7 +57,7 @@ class ShortcutUser { private static final String TAG = ShortcutService.TAG; static final String DIRECTORY_PACKAGES = "packages"; static final String DIRECTORY_LUANCHERS = "launchers"; static final String DIRECTORY_LAUNCHERS = "launchers"; static final String TAG_ROOT = "user"; private static final String TAG_LAUNCHER = "launcher"; Loading Loading @@ -322,41 +316,43 @@ 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. if (forBackup) { int size = mLaunchers.size(); for (int i = 0; i < size; i++) { saveShortcutPackageItem(out, mLaunchers.valueAt(i)); } size = mPackages.size(); for (int i = 0; i < size; i++) { saveShortcutPackageItem(out, mPackages.valueAt(i)); } } out.endTag(null, TAG_ROOT); } void scheduleSaveAllLaunchersAndPackages() { { final int size = mLaunchers.size(); for (int i = 0; i < size; i++) { saveShortcutPackageItem(out, mLaunchers.valueAt(i), forBackup); mLaunchers.valueAt(i).scheduleSave(); } } { final int size = mPackages.size(); for (int i = 0; i < size; i++) { saveShortcutPackageItem(out, mPackages.valueAt(i), forBackup); mPackages.valueAt(i).scheduleSave(); } } out.endTag(null, TAG_ROOT); } private void saveShortcutPackageItem(TypedXmlSerializer out, ShortcutPackageItem spi, boolean forBackup) throws IOException, XmlPullParserException { if (forBackup) { private void saveShortcutPackageItem(TypedXmlSerializer out, ShortcutPackageItem spi) throws IOException, XmlPullParserException { if (spi.getPackageUserId() != spi.getOwnerUserId()) { return; // Don't save cross-user information. } spi.waitForBitmapSaves(); spi.saveToXml(out, forBackup); } else { spi.scheduleSave(); } spi.saveToXml(out, true /* forBackup */); } public static ShortcutUser loadFromXml(ShortcutService s, TypedXmlPullParser parser, int userId, Loading Loading @@ -429,7 +425,7 @@ class ShortcutUser { } }); forMainFilesIn(new File(root, DIRECTORY_LUANCHERS), (File f) -> { forMainFilesIn(new File(root, DIRECTORY_LAUNCHERS), (File f) -> { final ShortcutLauncher sl = ShortcutLauncher.loadFromFile(f, ret, userId, fromBackup); if (sl != null) { Loading
services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java +58 −1 Original line number Diff line number Diff line Loading @@ -95,8 +95,8 @@ import android.test.mock.MockContext; import android.util.ArrayMap; import android.util.Log; import android.util.Pair; import android.util.SparseArray; import com.android.internal.infra.AndroidFuture; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.pm.LauncherAppsService.LauncherAppsImpl; Loading @@ -110,6 +110,7 @@ import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; Loading Loading @@ -149,6 +150,9 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { protected static final String MAIN_ACTIVITY_CLASS = "MainActivity"; protected static final String PIN_CONFIRM_ACTIVITY_CLASS = "PinConfirmActivity"; private byte[] mBaseState; protected final SparseArray<byte[]> mUserStates = new SparseArray<>(); // public for mockito public class BaseContext extends MockContext { @Override Loading Loading @@ -287,6 +291,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { final ServiceContext mContext; IUidObserver mUidObserver; public ShortcutServiceTestable(ServiceContext context, Looper looper) { super(context, looper, /* onyForPackageManagerApis */ false); mContext = context; Loading Loading @@ -567,6 +572,58 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { // During tests, WTF is fatal. fail(message + " exception: " + th + "\n" + Log.getStackTraceString(th)); } @Override void injectSaveBaseState() { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { saveBaseStateAsXml(baos); } catch (Exception e) { throw new RuntimeException(e); } mBaseState = baos.toByteArray(); } @Override protected void injectLoadBaseState() { if (mBaseState == null) { return; } ByteArrayInputStream bais = new ByteArrayInputStream(mBaseState); try { loadBaseStateAsXml(bais); } catch (Exception e) { throw new RuntimeException(e); } } @Override protected void injectSaveUser(@UserIdInt int userId) { synchronized (mServiceLock) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { saveUserInternalLocked(userId, baos, /* forBackup= */ false); cleanupDanglingBitmapDirectoriesLocked(userId); } catch (Exception e) { throw new RuntimeException(e); } mUserStates.put(userId, baos.toByteArray()); } } @Override protected ShortcutUser injectLoadUserLocked(@UserIdInt int userId) { final byte[] userState = mUserStates.get(userId); if (userState == null) { return null; } ByteArrayInputStream bais = new ByteArrayInputStream(userState); try { return loadUserInternal(userId, bais, /* forBackup= */ false); } catch (Exception e) { throw new RuntimeException(e); } } } /** ShortcutManager with injection override methods. */ Loading
services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java +1 −1 Original line number Diff line number Diff line Loading @@ -201,7 +201,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { mInjectedCurrentTimeMillis = START_TIME + 4 * INTERVAL + 50; assertResetTimes(START_TIME + 4 * INTERVAL, START_TIME + 5 * INTERVAL); mService.saveBaseState(); mService.injectSaveBaseState(); dumpBaseStateFile(); Loading