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

Commit a3112cee authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add dumpsys option to print last restore status"

parents ca8a3f49 50a320e5
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