Loading services/core/java/com/android/server/pm/ShortcutDumpFiles.java 0 → 100644 +105 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.pm; import android.util.Slog; import com.android.internal.util.ArrayUtils; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Comparator; import java.util.function.Consumer; public class ShortcutDumpFiles { private static final String TAG = ShortcutService.TAG; private static final boolean DEBUG = ShortcutService.DEBUG; private final ShortcutService mService; public ShortcutDumpFiles(ShortcutService service) { mService = service; } public boolean save(String filename, Consumer<PrintWriter> dumper) { try { final File directory = mService.getDumpPath(); directory.mkdirs(); if (!directory.exists()) { Slog.e(TAG, "Failed to create directory: " + directory); return false; } final File path = new File(directory, filename); if (DEBUG) { Slog.d(TAG, "Dumping to " + path); } try (PrintWriter pw = new PrintWriter(new BufferedOutputStream( new FileOutputStream(path)))) { dumper.accept(pw); } return true; } catch (RuntimeException|IOException e) { Slog.w(TAG, "Failed to create dump file: " + filename, e); return false; } } public boolean save(String filename, byte[] utf8bytes) { return save(filename, pw -> pw.println(StandardCharsets.UTF_8.decode( ByteBuffer.wrap(utf8bytes)).toString())); } public void dumpAll(PrintWriter pw) { try { final File directory = mService.getDumpPath(); final File[] files = directory.listFiles(f -> f.isFile()); if (!directory.exists() || ArrayUtils.isEmpty(files)) { pw.print(" No dump files found."); return; } Arrays.sort(files, Comparator.comparing(f -> f.getName())); for (File path : files) { pw.print("*** Dumping: "); pw.println(path.getName()); pw.print("mtime: "); pw.println(ShortcutService.formatTime(path.lastModified())); try (BufferedReader reader = new BufferedReader(new InputStreamReader( new FileInputStream(path)))) { String line = null; while ((line = reader.readLine()) != null) { pw.println(line); } } } } catch (RuntimeException|IOException e) { Slog.w(TAG, "Failed to print dump files", e); } } } services/core/java/com/android/server/pm/ShortcutPackage.java +4 −0 Original line number Diff line number Diff line Loading @@ -150,6 +150,10 @@ class ShortcutPackage extends ShortcutPackageItem { getPackageName(), getPackageUserId()); } public int getShortcutCount() { return mShortcuts.size(); } @Override protected void onRestoreBlocked() { // Can't restore due to version/signature mismatch. Remove all shortcuts. Loading services/core/java/com/android/server/pm/ShortcutService.java +79 −3 Original line number Diff line number Diff line Loading @@ -180,6 +180,9 @@ public class ShortcutService extends IShortcutService.Stub { @VisibleForTesting static final String DIRECTORY_PER_USER = "shortcut_service"; @VisibleForTesting static final String DIRECTORY_DUMP = "shortcut_dump"; @VisibleForTesting static final String FILENAME_USER_PACKAGES = "shortcuts.xml"; Loading Loading @@ -308,6 +311,7 @@ public class ShortcutService extends IShortcutService.Stub { private final ShortcutRequestPinProcessor mShortcutRequestPinProcessor; private final ShortcutBitmapSaver mShortcutBitmapSaver; private final ShortcutDumpFiles mShortcutDumpFiles; @GuardedBy("mLock") final SparseIntArray mUidState = new SparseIntArray(); Loading Loading @@ -429,6 +433,7 @@ public class ShortcutService extends IShortcutService.Stub { mShortcutRequestPinProcessor = new ShortcutRequestPinProcessor(this, mLock); mShortcutBitmapSaver = new ShortcutBitmapSaver(this); mShortcutDumpFiles = new ShortcutDumpFiles(this); if (onlyForPackageManagerApis) { return; // Don't do anything further. For unit tests only. Loading Loading @@ -3395,6 +3400,16 @@ public class ShortcutService extends IShortcutService.Stub { wtf("Can't restore: user " + userId + " is locked or not running"); return; } // Note we print the file timestamps in dumpsys too, but also printing the timestamp // in the files anyway. mShortcutDumpFiles.save("restore-0-start.txt", pw -> { pw.print("Start time: "); dumpCurrentTime(pw); pw.println(); }); mShortcutDumpFiles.save("restore-1-payload.xml", payload); // Actually do restore. final ShortcutUser restored; final ByteArrayInputStream is = new ByteArrayInputStream(payload); Loading @@ -3404,13 +3419,25 @@ public class ShortcutService extends IShortcutService.Stub { Slog.w(TAG, "Restoration failed.", e); return; } mShortcutDumpFiles.save("restore-2.txt", this::dumpInner); getUserShortcutsLocked(userId).mergeRestoredFile(restored); mShortcutDumpFiles.save("restore-3.txt", this::dumpInner); // Rescan all packages to re-publish manifest shortcuts and do other checks. rescanUpdatedPackagesLocked(userId, 0 // lastScanTime = 0; rescan all packages. ); mShortcutDumpFiles.save("restore-4.txt", this::dumpInner); mShortcutDumpFiles.save("restore-5-finish.txt", pw -> { pw.print("Finish time: "); dumpCurrentTime(pw); pw.println(); }); saveUserLocked(userId); } } Loading @@ -3425,23 +3452,54 @@ public class ShortcutService extends IShortcutService.Stub { @VisibleForTesting void dumpNoCheck(FileDescriptor fd, PrintWriter pw, String[] args) { boolean dumpMain = true; boolean checkin = false; boolean clear = false; boolean dumpUid = false; boolean dumpFiles = false; if (args != null) { for (String arg : args) { if ("-c".equals(arg)) { checkin = true; } else if ("--checkin".equals(arg)) { checkin = true; clear = true; } else if ("-a".equals(arg) || "--all".equals(arg)) { dumpUid = true; dumpFiles = true; } else if ("-u".equals(arg) || "--uid".equals(arg)) { dumpUid = true; } else if ("-f".equals(arg) || "--files".equals(arg)) { dumpFiles = true; } else if ("-n".equals(arg) || "--no-main".equals(arg)) { dumpMain = false; } } } if (checkin) { // Other flags are not supported for checkin. dumpCheckin(pw, clear); } else { if (dumpMain) { dumpInner(pw); pw.println(); } if (dumpUid) { dumpUid(pw); pw.println(); } if (dumpFiles) { dumpDumpFiles(pw); pw.println(); } } } Loading Loading @@ -3510,9 +3568,12 @@ public class ShortcutService extends IShortcutService.Stub { pw.println(); mUsers.valueAt(i).dump(pw, " "); } } } pw.println(); pw.println(" UID state:"); private void dumpUid(PrintWriter pw) { synchronized (mLock) { pw.println("** SHORTCUT MANAGER UID STATES (dumpsys shortcut -n -u)"); for (int i = 0; i < mUidState.size(); i++) { final int uid = mUidState.keyAt(i); Loading @@ -3537,6 +3598,10 @@ public class ShortcutService extends IShortcutService.Stub { return tobj.format("%Y-%m-%d %H:%M:%S"); } private void dumpCurrentTime(PrintWriter pw) { pw.print(formatTime(injectCurrentTimeMillis())); } private void dumpStatLS(PrintWriter pw, String prefix, int statId) { pw.print(prefix); final int count = mCountStats[statId]; Loading Loading @@ -3574,6 +3639,13 @@ public class ShortcutService extends IShortcutService.Stub { } } private void dumpDumpFiles(PrintWriter pw) { synchronized (mLock) { pw.println("** SHORTCUT MANAGER FILES (dumpsys shortcut -n -f)"); mShortcutDumpFiles.dumpAll(pw); } } // === Shell support === @Override Loading Loading @@ -3876,6 +3948,10 @@ public class ShortcutService extends IShortcutService.Stub { return new File(Environment.getDataSystemCeDirectory(userId), DIRECTORY_PER_USER); } public File getDumpPath() { return new File(injectUserDataPath(UserHandle.USER_SYSTEM), DIRECTORY_DUMP); } @VisibleForTesting boolean injectIsLowRamDevice() { return ActivityManager.isLowRamDeviceStatic(); Loading services/core/java/com/android/server/pm/ShortcutUser.java +11 −2 Original line number Diff line number Diff line Loading @@ -25,9 +25,7 @@ import android.text.format.Formatter; import android.util.ArrayMap; import android.util.Log; import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import com.android.server.pm.ShortcutService.InvalidFileFormatException; Loading Loading @@ -492,6 +490,10 @@ class ShortcutUser { // without users interaction it's really not a big deal, so we just clear existing // ShortcutLauncher instances in mLaunchers and add all the restored ones here. int[] restoredLaunchers = new int[1]; int[] restoredPackages = new int[1]; int[] restoredShortcuts = new int[1]; mLaunchers.clear(); restored.forAllLaunchers(sl -> { // If the app is already installed and allowbackup = false, then ignore the restored Loading @@ -501,6 +503,7 @@ class ShortcutUser { return; } addLauncher(sl); restoredLaunchers[0]++; }); restored.forAllPackages(sp -> { // If the app is already installed and allowbackup = false, then ignore the restored Loading @@ -516,10 +519,16 @@ class ShortcutUser { + " Existing non-manifeset shortcuts will be overwritten."); } addPackage(sp); restoredPackages[0]++; restoredShortcuts[0] += sp.getShortcutCount(); }); // Empty the launchers and packages in restored to avoid accidentally using them. restored.mLaunchers.clear(); restored.mPackages.clear(); Slog.i(TAG, "Restored: L=" + restoredLaunchers[0] + " P=" + restoredPackages[0] + " S=" + restoredShortcuts[0]); } public void dump(@NonNull PrintWriter pw, @NonNull String prefix) { Loading services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java +14 −3 Original line number Diff line number Diff line Loading @@ -1266,16 +1266,16 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { if (force || !ENABLE_DUMP) return; Log.v(TAG, "Dumping ShortcutService: " + message); for (String line : dumpsys(null).split("\n")) { for (String line : dumpsys("-u").split("\n")) { Log.v(TAG, line); } } protected String dumpCheckin() { return dumpsys(new String[]{"--checkin"}); return dumpsys("--checkin"); } private String dumpsys(String[] args) { protected String dumpsys(String... args) { final ArrayList<String> origPermissions = new ArrayList<>(mCallerPermissions); mCallerPermissions.add(android.Manifest.permission.DUMP); try { Loading Loading @@ -2139,4 +2139,15 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { mService.mPackageMonitor.onReceive(getTestContext(), genPackageAddIntent(getCallingPackage(), getCallingUserId())); } protected void assertFileNotExists(String path) { final File f = new File(mInjectedFilePathRoot, path); assertFalse("File shouldn't exist: " + f.getAbsolutePath(), f.exists()); } protected void assertFileExistsWithContent(String path) { final File f = new File(mInjectedFilePathRoot, path); assertTrue("File should exist: " + f.getAbsolutePath(), f.exists()); assertTrue("File should be larger than 0b: " + f.getAbsolutePath(), f.length() > 0); } } Loading
services/core/java/com/android/server/pm/ShortcutDumpFiles.java 0 → 100644 +105 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.pm; import android.util.Slog; import com.android.internal.util.ArrayUtils; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Comparator; import java.util.function.Consumer; public class ShortcutDumpFiles { private static final String TAG = ShortcutService.TAG; private static final boolean DEBUG = ShortcutService.DEBUG; private final ShortcutService mService; public ShortcutDumpFiles(ShortcutService service) { mService = service; } public boolean save(String filename, Consumer<PrintWriter> dumper) { try { final File directory = mService.getDumpPath(); directory.mkdirs(); if (!directory.exists()) { Slog.e(TAG, "Failed to create directory: " + directory); return false; } final File path = new File(directory, filename); if (DEBUG) { Slog.d(TAG, "Dumping to " + path); } try (PrintWriter pw = new PrintWriter(new BufferedOutputStream( new FileOutputStream(path)))) { dumper.accept(pw); } return true; } catch (RuntimeException|IOException e) { Slog.w(TAG, "Failed to create dump file: " + filename, e); return false; } } public boolean save(String filename, byte[] utf8bytes) { return save(filename, pw -> pw.println(StandardCharsets.UTF_8.decode( ByteBuffer.wrap(utf8bytes)).toString())); } public void dumpAll(PrintWriter pw) { try { final File directory = mService.getDumpPath(); final File[] files = directory.listFiles(f -> f.isFile()); if (!directory.exists() || ArrayUtils.isEmpty(files)) { pw.print(" No dump files found."); return; } Arrays.sort(files, Comparator.comparing(f -> f.getName())); for (File path : files) { pw.print("*** Dumping: "); pw.println(path.getName()); pw.print("mtime: "); pw.println(ShortcutService.formatTime(path.lastModified())); try (BufferedReader reader = new BufferedReader(new InputStreamReader( new FileInputStream(path)))) { String line = null; while ((line = reader.readLine()) != null) { pw.println(line); } } } } catch (RuntimeException|IOException e) { Slog.w(TAG, "Failed to print dump files", e); } } }
services/core/java/com/android/server/pm/ShortcutPackage.java +4 −0 Original line number Diff line number Diff line Loading @@ -150,6 +150,10 @@ class ShortcutPackage extends ShortcutPackageItem { getPackageName(), getPackageUserId()); } public int getShortcutCount() { return mShortcuts.size(); } @Override protected void onRestoreBlocked() { // Can't restore due to version/signature mismatch. Remove all shortcuts. Loading
services/core/java/com/android/server/pm/ShortcutService.java +79 −3 Original line number Diff line number Diff line Loading @@ -180,6 +180,9 @@ public class ShortcutService extends IShortcutService.Stub { @VisibleForTesting static final String DIRECTORY_PER_USER = "shortcut_service"; @VisibleForTesting static final String DIRECTORY_DUMP = "shortcut_dump"; @VisibleForTesting static final String FILENAME_USER_PACKAGES = "shortcuts.xml"; Loading Loading @@ -308,6 +311,7 @@ public class ShortcutService extends IShortcutService.Stub { private final ShortcutRequestPinProcessor mShortcutRequestPinProcessor; private final ShortcutBitmapSaver mShortcutBitmapSaver; private final ShortcutDumpFiles mShortcutDumpFiles; @GuardedBy("mLock") final SparseIntArray mUidState = new SparseIntArray(); Loading Loading @@ -429,6 +433,7 @@ public class ShortcutService extends IShortcutService.Stub { mShortcutRequestPinProcessor = new ShortcutRequestPinProcessor(this, mLock); mShortcutBitmapSaver = new ShortcutBitmapSaver(this); mShortcutDumpFiles = new ShortcutDumpFiles(this); if (onlyForPackageManagerApis) { return; // Don't do anything further. For unit tests only. Loading Loading @@ -3395,6 +3400,16 @@ public class ShortcutService extends IShortcutService.Stub { wtf("Can't restore: user " + userId + " is locked or not running"); return; } // Note we print the file timestamps in dumpsys too, but also printing the timestamp // in the files anyway. mShortcutDumpFiles.save("restore-0-start.txt", pw -> { pw.print("Start time: "); dumpCurrentTime(pw); pw.println(); }); mShortcutDumpFiles.save("restore-1-payload.xml", payload); // Actually do restore. final ShortcutUser restored; final ByteArrayInputStream is = new ByteArrayInputStream(payload); Loading @@ -3404,13 +3419,25 @@ public class ShortcutService extends IShortcutService.Stub { Slog.w(TAG, "Restoration failed.", e); return; } mShortcutDumpFiles.save("restore-2.txt", this::dumpInner); getUserShortcutsLocked(userId).mergeRestoredFile(restored); mShortcutDumpFiles.save("restore-3.txt", this::dumpInner); // Rescan all packages to re-publish manifest shortcuts and do other checks. rescanUpdatedPackagesLocked(userId, 0 // lastScanTime = 0; rescan all packages. ); mShortcutDumpFiles.save("restore-4.txt", this::dumpInner); mShortcutDumpFiles.save("restore-5-finish.txt", pw -> { pw.print("Finish time: "); dumpCurrentTime(pw); pw.println(); }); saveUserLocked(userId); } } Loading @@ -3425,23 +3452,54 @@ public class ShortcutService extends IShortcutService.Stub { @VisibleForTesting void dumpNoCheck(FileDescriptor fd, PrintWriter pw, String[] args) { boolean dumpMain = true; boolean checkin = false; boolean clear = false; boolean dumpUid = false; boolean dumpFiles = false; if (args != null) { for (String arg : args) { if ("-c".equals(arg)) { checkin = true; } else if ("--checkin".equals(arg)) { checkin = true; clear = true; } else if ("-a".equals(arg) || "--all".equals(arg)) { dumpUid = true; dumpFiles = true; } else if ("-u".equals(arg) || "--uid".equals(arg)) { dumpUid = true; } else if ("-f".equals(arg) || "--files".equals(arg)) { dumpFiles = true; } else if ("-n".equals(arg) || "--no-main".equals(arg)) { dumpMain = false; } } } if (checkin) { // Other flags are not supported for checkin. dumpCheckin(pw, clear); } else { if (dumpMain) { dumpInner(pw); pw.println(); } if (dumpUid) { dumpUid(pw); pw.println(); } if (dumpFiles) { dumpDumpFiles(pw); pw.println(); } } } Loading Loading @@ -3510,9 +3568,12 @@ public class ShortcutService extends IShortcutService.Stub { pw.println(); mUsers.valueAt(i).dump(pw, " "); } } } pw.println(); pw.println(" UID state:"); private void dumpUid(PrintWriter pw) { synchronized (mLock) { pw.println("** SHORTCUT MANAGER UID STATES (dumpsys shortcut -n -u)"); for (int i = 0; i < mUidState.size(); i++) { final int uid = mUidState.keyAt(i); Loading @@ -3537,6 +3598,10 @@ public class ShortcutService extends IShortcutService.Stub { return tobj.format("%Y-%m-%d %H:%M:%S"); } private void dumpCurrentTime(PrintWriter pw) { pw.print(formatTime(injectCurrentTimeMillis())); } private void dumpStatLS(PrintWriter pw, String prefix, int statId) { pw.print(prefix); final int count = mCountStats[statId]; Loading Loading @@ -3574,6 +3639,13 @@ public class ShortcutService extends IShortcutService.Stub { } } private void dumpDumpFiles(PrintWriter pw) { synchronized (mLock) { pw.println("** SHORTCUT MANAGER FILES (dumpsys shortcut -n -f)"); mShortcutDumpFiles.dumpAll(pw); } } // === Shell support === @Override Loading Loading @@ -3876,6 +3948,10 @@ public class ShortcutService extends IShortcutService.Stub { return new File(Environment.getDataSystemCeDirectory(userId), DIRECTORY_PER_USER); } public File getDumpPath() { return new File(injectUserDataPath(UserHandle.USER_SYSTEM), DIRECTORY_DUMP); } @VisibleForTesting boolean injectIsLowRamDevice() { return ActivityManager.isLowRamDeviceStatic(); Loading
services/core/java/com/android/server/pm/ShortcutUser.java +11 −2 Original line number Diff line number Diff line Loading @@ -25,9 +25,7 @@ import android.text.format.Formatter; import android.util.ArrayMap; import android.util.Log; import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import com.android.server.pm.ShortcutService.InvalidFileFormatException; Loading Loading @@ -492,6 +490,10 @@ class ShortcutUser { // without users interaction it's really not a big deal, so we just clear existing // ShortcutLauncher instances in mLaunchers and add all the restored ones here. int[] restoredLaunchers = new int[1]; int[] restoredPackages = new int[1]; int[] restoredShortcuts = new int[1]; mLaunchers.clear(); restored.forAllLaunchers(sl -> { // If the app is already installed and allowbackup = false, then ignore the restored Loading @@ -501,6 +503,7 @@ class ShortcutUser { return; } addLauncher(sl); restoredLaunchers[0]++; }); restored.forAllPackages(sp -> { // If the app is already installed and allowbackup = false, then ignore the restored Loading @@ -516,10 +519,16 @@ class ShortcutUser { + " Existing non-manifeset shortcuts will be overwritten."); } addPackage(sp); restoredPackages[0]++; restoredShortcuts[0] += sp.getShortcutCount(); }); // Empty the launchers and packages in restored to avoid accidentally using them. restored.mLaunchers.clear(); restored.mPackages.clear(); Slog.i(TAG, "Restored: L=" + restoredLaunchers[0] + " P=" + restoredPackages[0] + " S=" + restoredShortcuts[0]); } public void dump(@NonNull PrintWriter pw, @NonNull String prefix) { Loading
services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java +14 −3 Original line number Diff line number Diff line Loading @@ -1266,16 +1266,16 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { if (force || !ENABLE_DUMP) return; Log.v(TAG, "Dumping ShortcutService: " + message); for (String line : dumpsys(null).split("\n")) { for (String line : dumpsys("-u").split("\n")) { Log.v(TAG, line); } } protected String dumpCheckin() { return dumpsys(new String[]{"--checkin"}); return dumpsys("--checkin"); } private String dumpsys(String[] args) { protected String dumpsys(String... args) { final ArrayList<String> origPermissions = new ArrayList<>(mCallerPermissions); mCallerPermissions.add(android.Manifest.permission.DUMP); try { Loading Loading @@ -2139,4 +2139,15 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { mService.mPackageMonitor.onReceive(getTestContext(), genPackageAddIntent(getCallingPackage(), getCallingUserId())); } protected void assertFileNotExists(String path) { final File f = new File(mInjectedFilePathRoot, path); assertFalse("File shouldn't exist: " + f.getAbsolutePath(), f.exists()); } protected void assertFileExistsWithContent(String path) { final File f = new File(mInjectedFilePathRoot, path); assertTrue("File should exist: " + f.getAbsolutePath(), f.exists()); assertTrue("File should be larger than 0b: " + f.getAbsolutePath(), f.length() > 0); } }