Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 50a320e5 authored by Makoto Onuki's avatar Makoto Onuki
Browse files

Add dumpsys option to print last restore status

Now "dumpsys shortcut" will *not* dump UID states.  "dumpsys shortcut -u" will
do so.

"dumpsys shortcut -f" now dumps dump taken after the last restore.

Bug: 62108985
Test: adb shell am instrument -e class com.android.server.pm.ShortcutManagerTest1 -w com.android.frameworks.servicestests
Test: adb shell am instrument -e class com.android.server.pm.ShortcutManagerTest2 -w com.android.frameworks.servicestests
Test: adb shell am instrument -e class com.android.server.pm.ShortcutManagerTest3 -w com.android.frameworks.servicestests
Test: adb shell am instrument -e class com.android.server.pm.ShortcutManagerTest4 -w com.android.frameworks.servicestests
Test: adb shell am instrument -e class com.android.server.pm.ShortcutManagerTest5 -w com.android.frameworks.servicestests
Test: adb shell am instrument -e class com.android.server.pm.ShortcutManagerTest6 -w com.android.frameworks.servicestests
Test: adb shell am instrument -e class com.android.server.pm.ShortcutManagerTest7 -w com.android.frameworks.servicestests
Test: adb shell am instrument -e class com.android.server.pm.ShortcutManagerTest8 -w com.android.frameworks.servicestests
Test: adb shell am instrument -e class com.android.server.pm.ShortcutManagerTest9 -w com.android.frameworks.servicestests
Test: adb shell am instrument -e class com.android.server.pm.ShortcutManagerTest10 -w com.android.frameworks.servicestests
Test: cts-tradefed run cts-dev --skip-device-info --skip-preconditions --skip-system-status-check com.android.compatibility.common.tradefed.targetprep.NetworkConnectivityChecker -a armeabi-v7a -l INFO -m CtsShortcutManagerTestCases
Test: cts-tradefed run cts-dev --skip-device-info --skip-preconditions --skip-system-status-check com.android.compatibility.common.tradefed.targetprep.NetworkConnectivityChecker -a armeabi-v7a -l INFO -m CtsShortcutHostTestCases
Change-Id: I5e8451b576081328242b485850f5311258fe7779
parent 2c8438cb
Loading
Loading
Loading
Loading
+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);
        }
    }
}
+4 −0
Original line number Diff line number Diff line
@@ -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.
+79 −3
Original line number Diff line number Diff line
@@ -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";

@@ -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 {
            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();
+11 −2
Original line number Diff line number Diff line
@@ -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) {
+14 −3
Original line number Diff line number Diff line
@@ -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);
    }
}
Loading