summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/pm/ShortcutDumpFiles.java105
-rw-r--r--services/core/java/com/android/server/pm/ShortcutPackage.java4
-rw-r--r--services/core/java/com/android/server/pm/ShortcutService.java82
-rw-r--r--services/core/java/com/android/server/pm/ShortcutUser.java13
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java17
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java15
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java28
-rw-r--r--services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java4
8 files changed, 259 insertions, 9 deletions
diff --git a/services/core/java/com/android/server/pm/ShortcutDumpFiles.java b/services/core/java/com/android/server/pm/ShortcutDumpFiles.java
new file mode 100644
index 000000000000..dc380cc2b195
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ShortcutDumpFiles.java
@@ -0,0 +1,105 @@
+/*
+ * 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);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 103b25d4cea0..6f70f4c89ad2 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -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.
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 6d48a05273cb..791197267ecb 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -181,6 +181,9 @@ public class ShortcutService extends IShortcutService.Stub {
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";
static final String DIRECTORY_BITMAPS = "bitmaps";
@@ -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();
@@ -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.
@@ -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);
@@ -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);
}
}
@@ -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 {
- dumpInner(pw);
+ if (dumpMain) {
+ dumpInner(pw);
+ pw.println();
+ }
+ if (dumpUid) {
+ dumpUid(pw);
+ pw.println();
+ }
+ if (dumpFiles) {
+ dumpDumpFiles(pw);
+ pw.println();
+ }
}
}
@@ -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);
@@ -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];
@@ -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
@@ -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();
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index 5d4bfa4dcb3e..2c388c4a44d1 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -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;
@@ -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
@@ -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
@@ -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) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index f1d592789c41..3c64582a8cd2 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -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 {
@@ -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);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index f4944f9ee83d..951f226676b0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -4732,8 +4732,23 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
* - Bitmaps
*/
public void testBackupAndRestore() {
+
+ assertFileNotExists("user-0/shortcut_dump/restore-0-start.txt");
+ assertFileNotExists("user-0/shortcut_dump/restore-1-payload.xml");
+ assertFileNotExists("user-0/shortcut_dump/restore-2.txt");
+ assertFileNotExists("user-0/shortcut_dump/restore-3.txt");
+ assertFileNotExists("user-0/shortcut_dump/restore-4.txt");
+ assertFileNotExists("user-0/shortcut_dump/restore-5-finish.txt");
+
prepareForBackupTest();
+ assertFileExistsWithContent("user-0/shortcut_dump/restore-0-start.txt");
+ assertFileExistsWithContent("user-0/shortcut_dump/restore-1-payload.xml");
+ assertFileExistsWithContent("user-0/shortcut_dump/restore-2.txt");
+ assertFileExistsWithContent("user-0/shortcut_dump/restore-3.txt");
+ assertFileExistsWithContent("user-0/shortcut_dump/restore-4.txt");
+ assertFileExistsWithContent("user-0/shortcut_dump/restore-5-finish.txt");
+
checkBackupAndRestore_success();
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
index 25f9100d9252..da641b973537 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
@@ -15,6 +15,7 @@
*/
package com.android.server.pm;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.array;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertContains;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertExpectException;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertSuccess;
@@ -40,7 +41,7 @@ import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
- * Unit test for "cmd shortcut"
+ * Unit test for "cmd shortcut" and "dumpsys shortcut".
*
* Launcher related commands are tested in
*/
@@ -336,4 +337,29 @@ public class ShortcutManagerTest7 extends BaseShortcutManagerTest {
.areAllNotPinned();
});
}
+
+ public void testDumpsysArgs() {
+ checkDumpsysArgs(null, true, false, false);
+ checkDumpsysArgs(array("-u"), true, true, false);
+ checkDumpsysArgs(array("--uid"), true, true, false);
+
+ checkDumpsysArgs(array("-f"), true, false, true);
+ checkDumpsysArgs(array("--files"), true, false, true);
+
+ checkDumpsysArgs(array("-a"), true, true, true);
+ checkDumpsysArgs(array("--all"), true, true, true);
+
+ checkDumpsysArgs(array("-a", "-n"), false, true, true);
+ checkDumpsysArgs(array("-a", "--no-main"), false, true, true);
+ }
+
+ private void checkDumpsysArgs(String[] args, boolean expectMain, boolean expectUid,
+ boolean expectDumpFiles) {
+ String dump = dumpsys(args);
+
+ assertEquals(expectMain, dump.contains("Icon format: PNG"));
+ assertEquals(expectUid, dump.contains("SHORTCUT MANAGER UID STATES"));
+ assertEquals(expectDumpFiles, dump.contains("SHORTCUT MANAGER FILES"));
+ }
+
}
diff --git a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
index 922f08da643f..926a606df294 100644
--- a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
+++ b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
@@ -339,6 +339,10 @@ public class ShortcutManagerTestUtils {
return ret;
}
+ public static <T> T[] array(T... array) {
+ return array;
+ }
+
public static <T> List<T> list(T... array) {
return Arrays.asList(array);
}