13145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki/*
23145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki * Copyright (C) 2016 The Android Open Source Project
33145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki *
43145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki * Licensed under the Apache License, Version 2.0 (the "License");
53145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki * you may not use this file except in compliance with the License.
63145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki * You may obtain a copy of the License at
73145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki *
83145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki *      http://www.apache.org/licenses/LICENSE-2.0
93145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki *
103145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki * Unless required by applicable law or agreed to in writing, software
113145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki * distributed under the License is distributed on an "AS IS" BASIS,
123145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki * See the License for the specific language governing permissions and
143145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki * limitations under the License.
153145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki */
163145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onukipackage com.android.server.pm;
173145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki
183145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onukiimport android.annotation.NonNull;
192d895c3efd625e09e9f2cc4d0c7131b34f52f154Makoto Onukiimport android.annotation.Nullable;
203145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onukiimport android.annotation.UserIdInt;
21c8c3329dd918b8ea16d6317d19ddee12325800f3Makoto Onukiimport android.content.pm.PackageInfo;
223145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onukiimport android.content.pm.ShortcutInfo;
233145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onukiimport android.util.ArrayMap;
243145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onukiimport android.util.ArraySet;
252e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onukiimport android.util.Slog;
262e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki
272e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onukiimport com.android.internal.annotations.VisibleForTesting;
282e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onukiimport com.android.server.pm.ShortcutUser.PackageWithUser;
293145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki
3076269928e677725e2d9b28e2e3aa79961a60a1d0Makoto Onukiimport org.json.JSONException;
3176269928e677725e2d9b28e2e3aa79961a60a1d0Makoto Onukiimport org.json.JSONObject;
323145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onukiimport org.xmlpull.v1.XmlPullParser;
333145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onukiimport org.xmlpull.v1.XmlPullParserException;
343145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onukiimport org.xmlpull.v1.XmlSerializer;
353145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki
363145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onukiimport java.io.IOException;
373145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onukiimport java.io.PrintWriter;
382e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onukiimport java.util.ArrayList;
393145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onukiimport java.util.List;
403145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki
413145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki/**
423145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki * Launcher information used by {@link ShortcutService}.
4322fcc68e6be0edaa98f3dacf79d580a5e5d50005Makoto Onuki *
4422fcc68e6be0edaa98f3dacf79d580a5e5d50005Makoto Onuki * All methods should be guarded by {@code #mShortcutUser.mService.mLock}.
453145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki */
469da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onukiclass ShortcutLauncher extends ShortcutPackageItem {
473145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki    private static final String TAG = ShortcutService.TAG;
483145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki
493145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki    static final String TAG_ROOT = "launcher-pins";
503145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki
513145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki    private static final String TAG_PACKAGE = "package";
523145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki    private static final String TAG_PIN = "pin";
533145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki
54d99c6f04bbb68f8be78f2c3ca625a3a8d5645275Makoto Onuki    private static final String ATTR_LAUNCHER_USER_ID = "launcher-user";
553145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki    private static final String ATTR_VALUE = "value";
563145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki    private static final String ATTR_PACKAGE_NAME = "package-name";
572e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki    private static final String ATTR_PACKAGE_USER_ID = "package-user";
583145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki
599da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki    private final int mOwnerUserId;
60d99c6f04bbb68f8be78f2c3ca625a3a8d5645275Makoto Onuki
613145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki    /**
623145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki     * Package name -> IDs.
633145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki     */
642e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki    final private ArrayMap<PackageWithUser, ArraySet<String>> mPinnedShortcuts = new ArrayMap<>();
653145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki
664d36b3a8c5ba1289d851ef337e46709bba333100Makoto Onuki    private ShortcutLauncher(@NonNull ShortcutUser shortcutUser,
674d36b3a8c5ba1289d851ef337e46709bba333100Makoto Onuki            @UserIdInt int ownerUserId, @NonNull String packageName,
689da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki            @UserIdInt int launcherUserId, ShortcutPackageInfo spi) {
694d36b3a8c5ba1289d851ef337e46709bba333100Makoto Onuki        super(shortcutUser, launcherUserId, packageName,
704d36b3a8c5ba1289d851ef337e46709bba333100Makoto Onuki                spi != null ? spi : ShortcutPackageInfo.newEmpty());
719da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki        mOwnerUserId = ownerUserId;
723145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki    }
733145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki
744d36b3a8c5ba1289d851ef337e46709bba333100Makoto Onuki    public ShortcutLauncher(@NonNull ShortcutUser shortcutUser,
754d36b3a8c5ba1289d851ef337e46709bba333100Makoto Onuki            @UserIdInt int ownerUserId, @NonNull String packageName,
769da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki            @UserIdInt int launcherUserId) {
774d36b3a8c5ba1289d851ef337e46709bba333100Makoto Onuki        this(shortcutUser, ownerUserId, packageName, launcherUserId, null);
780acbb14574d859b5f1cc0b7c6bbdfbeba38f3e55Makoto Onuki    }
790acbb14574d859b5f1cc0b7c6bbdfbeba38f3e55Makoto Onuki
809da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki    @Override
819da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki    public int getOwnerUserId() {
829da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki        return mOwnerUserId;
83d99c6f04bbb68f8be78f2c3ca625a3a8d5645275Makoto Onuki    }
84d99c6f04bbb68f8be78f2c3ca625a3a8d5645275Makoto Onuki
852e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki    /**
862e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki     * Called when the new package can't receive the backup, due to signature or version mismatch.
872e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki     */
882e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki    @Override
89c51b2876ec5c0af449469a0f76bb38c51cfcff04Makoto Onuki    protected void onRestoreBlocked() {
902e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki        final ArrayList<PackageWithUser> pinnedPackages =
912e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki                new ArrayList<>(mPinnedShortcuts.keySet());
922e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki        mPinnedShortcuts.clear();
932e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki        for (int i = pinnedPackages.size() - 1; i >= 0; i--) {
942e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki            final PackageWithUser pu = pinnedPackages.get(i);
95c51b2876ec5c0af449469a0f76bb38c51cfcff04Makoto Onuki            final ShortcutPackage p = mShortcutUser.getPackageShortcutsIfExists(pu.packageName);
96c51b2876ec5c0af449469a0f76bb38c51cfcff04Makoto Onuki            if (p != null) {
97c51b2876ec5c0af449469a0f76bb38c51cfcff04Makoto Onuki                p.refreshPinnedFlags();
98c51b2876ec5c0af449469a0f76bb38c51cfcff04Makoto Onuki            }
992e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki        }
1002e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki    }
1012e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki
1022e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki    @Override
103c51b2876ec5c0af449469a0f76bb38c51cfcff04Makoto Onuki    protected void onRestored() {
1042e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki        // Nothing to do.
1052e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki    }
1062e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki
1072d895c3efd625e09e9f2cc4d0c7131b34f52f154Makoto Onuki    /**
1082d895c3efd625e09e9f2cc4d0c7131b34f52f154Makoto Onuki     * Pin the given shortcuts, replacing the current pinned ones.
1092d895c3efd625e09e9f2cc4d0c7131b34f52f154Makoto Onuki     */
110c51b2876ec5c0af449469a0f76bb38c51cfcff04Makoto Onuki    public void pinShortcuts(@UserIdInt int packageUserId,
1119da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki            @NonNull String packageName, @NonNull List<String> ids) {
1129da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki        final ShortcutPackage packageShortcuts =
113c51b2876ec5c0af449469a0f76bb38c51cfcff04Makoto Onuki                mShortcutUser.getPackageShortcutsIfExists(packageName);
114c51b2876ec5c0af449469a0f76bb38c51cfcff04Makoto Onuki        if (packageShortcuts == null) {
115c51b2876ec5c0af449469a0f76bb38c51cfcff04Makoto Onuki            return; // No need to instantiate.
116c51b2876ec5c0af449469a0f76bb38c51cfcff04Makoto Onuki        }
1170acbb14574d859b5f1cc0b7c6bbdfbeba38f3e55Makoto Onuki
1182e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki        final PackageWithUser pu = PackageWithUser.of(packageUserId, packageName);
1192e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki
1203145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki        final int idSize = ids.size();
1213145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki        if (idSize == 0) {
1222e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki            mPinnedShortcuts.remove(pu);
1233145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki        } else {
1242e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki            final ArraySet<String> prevSet = mPinnedShortcuts.get(pu);
1253145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki
1263145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki            // Pin shortcuts.  Make sure only pin the ones that were visible to the caller.
1273145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki            // i.e. a non-dynamic, pinned shortcut by *other launchers* shouldn't be pinned here.
1283145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki
1293145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki            final ArraySet<String> newSet = new ArraySet<>();
1303145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki
1313145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki            for (int i = 0; i < idSize; i++) {
1323145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki                final String id = ids.get(i);
1333145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki                final ShortcutInfo si = packageShortcuts.findShortcutById(id);
1343145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki                if (si == null) {
1353145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki                    continue;
1363145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki                }
13722fcc68e6be0edaa98f3dacf79d580a5e5d50005Makoto Onuki                if (si.isDynamic() || si.isManifestShortcut()
13822fcc68e6be0edaa98f3dacf79d580a5e5d50005Makoto Onuki                        || (prevSet != null && prevSet.contains(id))) {
1393145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki                    newSet.add(id);
1403145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki                }
1413145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki            }
1422e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki            mPinnedShortcuts.put(pu, newSet);
1433145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki        }
144c51b2876ec5c0af449469a0f76bb38c51cfcff04Makoto Onuki        packageShortcuts.refreshPinnedFlags();
1453145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki    }
1463145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki
1473145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki    /**
1483145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki     * Return the pinned shortcut IDs for the publisher package.
1493145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki     */
1502d895c3efd625e09e9f2cc4d0c7131b34f52f154Makoto Onuki    @Nullable
1512e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki    public ArraySet<String> getPinnedShortcutIds(@NonNull String packageName,
1522e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki            @UserIdInt int packageUserId) {
1532e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki        return mPinnedShortcuts.get(PackageWithUser.of(packageUserId, packageName));
1543145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki    }
1553145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki
1562d895c3efd625e09e9f2cc4d0c7131b34f52f154Makoto Onuki    /**
1572d895c3efd625e09e9f2cc4d0c7131b34f52f154Makoto Onuki     * Return true if the given shortcut is pinned by this launcher.
1582d895c3efd625e09e9f2cc4d0c7131b34f52f154Makoto Onuki     */
1592d895c3efd625e09e9f2cc4d0c7131b34f52f154Makoto Onuki    public boolean hasPinned(ShortcutInfo shortcut) {
1602d895c3efd625e09e9f2cc4d0c7131b34f52f154Makoto Onuki        final ArraySet<String> pinned =
1612d895c3efd625e09e9f2cc4d0c7131b34f52f154Makoto Onuki                getPinnedShortcutIds(shortcut.getPackage(), shortcut.getUserId());
1622d895c3efd625e09e9f2cc4d0c7131b34f52f154Makoto Onuki        return (pinned != null) && pinned.contains(shortcut.getId());
1632d895c3efd625e09e9f2cc4d0c7131b34f52f154Makoto Onuki    }
1642d895c3efd625e09e9f2cc4d0c7131b34f52f154Makoto Onuki
1652d895c3efd625e09e9f2cc4d0c7131b34f52f154Makoto Onuki    /**
1662d895c3efd625e09e9f2cc4d0c7131b34f52f154Makoto Onuki     * Additionally pin a shortcut. c.f. {@link #pinShortcuts(int, String, List)}
1672d895c3efd625e09e9f2cc4d0c7131b34f52f154Makoto Onuki     */
1682d895c3efd625e09e9f2cc4d0c7131b34f52f154Makoto Onuki    public void addPinnedShortcut(@NonNull String packageName, @UserIdInt int packageUserId,
1692d895c3efd625e09e9f2cc4d0c7131b34f52f154Makoto Onuki            String id) {
1702d895c3efd625e09e9f2cc4d0c7131b34f52f154Makoto Onuki        final ArraySet<String> pinnedSet = getPinnedShortcutIds(packageName, packageUserId);
1712d895c3efd625e09e9f2cc4d0c7131b34f52f154Makoto Onuki        final ArrayList<String> pinnedList;
1722d895c3efd625e09e9f2cc4d0c7131b34f52f154Makoto Onuki        if (pinnedSet != null) {
1732d895c3efd625e09e9f2cc4d0c7131b34f52f154Makoto Onuki            pinnedList = new ArrayList<>(pinnedSet.size() + 1);
1742d895c3efd625e09e9f2cc4d0c7131b34f52f154Makoto Onuki            pinnedList.addAll(pinnedSet);
1752d895c3efd625e09e9f2cc4d0c7131b34f52f154Makoto Onuki        } else {
1762d895c3efd625e09e9f2cc4d0c7131b34f52f154Makoto Onuki            pinnedList = new ArrayList<>(1);
1772d895c3efd625e09e9f2cc4d0c7131b34f52f154Makoto Onuki        }
1782d895c3efd625e09e9f2cc4d0c7131b34f52f154Makoto Onuki        pinnedList.add(id);
1792d895c3efd625e09e9f2cc4d0c7131b34f52f154Makoto Onuki
1802d895c3efd625e09e9f2cc4d0c7131b34f52f154Makoto Onuki        pinShortcuts(packageUserId, packageName, pinnedList);
1812d895c3efd625e09e9f2cc4d0c7131b34f52f154Makoto Onuki    }
1822d895c3efd625e09e9f2cc4d0c7131b34f52f154Makoto Onuki
1832e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki    boolean cleanUpPackage(String packageName, @UserIdInt int packageUserId) {
1842e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki        return mPinnedShortcuts.remove(PackageWithUser.of(packageUserId, packageName)) != null;
1853145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki    }
1863145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki
187c8c3329dd918b8ea16d6317d19ddee12325800f3Makoto Onuki    public void ensureVersionInfo() {
188c8c3329dd918b8ea16d6317d19ddee12325800f3Makoto Onuki        final PackageInfo pi = mShortcutUser.mService.getPackageInfoWithSignatures(
189c8c3329dd918b8ea16d6317d19ddee12325800f3Makoto Onuki                getPackageName(), getPackageUserId());
190c8c3329dd918b8ea16d6317d19ddee12325800f3Makoto Onuki        if (pi == null) {
191c8c3329dd918b8ea16d6317d19ddee12325800f3Makoto Onuki            Slog.w(TAG, "Package not found: " + getPackageName());
192c8c3329dd918b8ea16d6317d19ddee12325800f3Makoto Onuki            return;
193c8c3329dd918b8ea16d6317d19ddee12325800f3Makoto Onuki        }
194c8c3329dd918b8ea16d6317d19ddee12325800f3Makoto Onuki        getPackageInfo().updateVersionInfo(pi);
195c8c3329dd918b8ea16d6317d19ddee12325800f3Makoto Onuki    }
196c8c3329dd918b8ea16d6317d19ddee12325800f3Makoto Onuki
1973145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki    /**
1983145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki     * Persist.
1993145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki     */
2009da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki    @Override
2019da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki    public void saveToXml(XmlSerializer out, boolean forBackup)
2029da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki            throws IOException {
2033145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki        final int size = mPinnedShortcuts.size();
2043145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki        if (size == 0) {
2053145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki            return; // Nothing to write.
2063145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki        }
2073145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki
2083145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki        out.startTag(null, TAG_ROOT);
2099da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki        ShortcutService.writeAttr(out, ATTR_PACKAGE_NAME, getPackageName());
2109da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki        ShortcutService.writeAttr(out, ATTR_LAUNCHER_USER_ID, getPackageUserId());
2119da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki        getPackageInfo().saveToXml(out);
2123145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki
2133145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki        for (int i = 0; i < size; i++) {
2142e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki            final PackageWithUser pu = mPinnedShortcuts.keyAt(i);
2152e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki
2162e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki            if (forBackup && (pu.userId != getOwnerUserId())) {
2172e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki                continue; // Target package on a different user, skip. (i.e. work profile)
2182e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki            }
2192e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki
2203145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki            out.startTag(null, TAG_PACKAGE);
2212e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki            ShortcutService.writeAttr(out, ATTR_PACKAGE_NAME, pu.packageName);
2222e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki            ShortcutService.writeAttr(out, ATTR_PACKAGE_USER_ID, pu.userId);
2233145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki
2243145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki            final ArraySet<String> ids = mPinnedShortcuts.valueAt(i);
2253145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki            final int idSize = ids.size();
2263145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki            for (int j = 0; j < idSize; j++) {
2273145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki                ShortcutService.writeTagValue(out, TAG_PIN, ids.valueAt(j));
2283145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki            }
2293145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki            out.endTag(null, TAG_PACKAGE);
2303145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki        }
2313145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki
2323145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki        out.endTag(null, TAG_ROOT);
2333145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki    }
2343145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki
2353145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki    /**
2363145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki     * Load.
2373145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki     */
2384d36b3a8c5ba1289d851ef337e46709bba333100Makoto Onuki    public static ShortcutLauncher loadFromXml(XmlPullParser parser, ShortcutUser shortcutUser,
2394d36b3a8c5ba1289d851ef337e46709bba333100Makoto Onuki            int ownerUserId, boolean fromBackup) throws IOException, XmlPullParserException {
2403145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki        final String launcherPackageName = ShortcutService.parseStringAttribute(parser,
2413145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki                ATTR_PACKAGE_NAME);
2429da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki
2439da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki        // If restoring, just use the real user ID.
2449da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki        final int launcherUserId =
2459da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki                fromBackup ? ownerUserId
2469da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki                : ShortcutService.parseIntAttribute(parser, ATTR_LAUNCHER_USER_ID, ownerUserId);
2473145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki
248c8c3329dd918b8ea16d6317d19ddee12325800f3Makoto Onuki        final ShortcutLauncher ret = new ShortcutLauncher(shortcutUser, ownerUserId,
2494d36b3a8c5ba1289d851ef337e46709bba333100Makoto Onuki                launcherPackageName, launcherUserId);
2503145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki
2513145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki        ArraySet<String> ids = null;
2523145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki        final int outerDepth = parser.getDepth();
2533145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki        int type;
2543145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
2553145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
2563145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki            if (type != XmlPullParser.START_TAG) {
2573145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki                continue;
2583145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki            }
2593145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki            final int depth = parser.getDepth();
2603145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki            final String tag = parser.getName();
2619da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki            if (depth == outerDepth + 1) {
2629da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki                switch (tag) {
2639da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki                    case ShortcutPackageInfo.TAG_ROOT:
2642e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki                        ret.getPackageInfo().loadFromXml(parser, fromBackup);
2659da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki                        continue;
2669da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki                    case TAG_PACKAGE: {
2679da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki                        final String packageName = ShortcutService.parseStringAttribute(parser,
2689da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki                                ATTR_PACKAGE_NAME);
2692e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki                        final int packageUserId = fromBackup ? ownerUserId
2702e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki                                : ShortcutService.parseIntAttribute(parser,
2712e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki                                ATTR_PACKAGE_USER_ID, ownerUserId);
2729da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki                        ids = new ArraySet<>();
2732e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki                        ret.mPinnedShortcuts.put(
2742e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki                                PackageWithUser.of(packageUserId, packageName), ids);
2759da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki                        continue;
2769da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki                    }
2773145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki                }
2789da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki            }
2799da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki            if (depth == outerDepth + 2) {
2809da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki                switch (tag) {
2819da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki                    case TAG_PIN: {
2822e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki                        if (ids == null) {
2832e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki                            Slog.w(TAG, TAG_PIN + " in invalid place");
2842e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki                        } else {
2852e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki                            ids.add(ShortcutService.parseStringAttribute(parser, ATTR_VALUE));
2862e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki                        }
2879da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki                        continue;
2889da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki                    }
2893145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki                }
2903145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki            }
2919da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki            ShortcutService.warnForInvalidTag(depth, tag);
2929da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki        }
2933145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki        return ret;
2943145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki    }
2953145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki
296c51b2876ec5c0af449469a0f76bb38c51cfcff04Makoto Onuki    public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
2973145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki        pw.println();
2983145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki
2993145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki        pw.print(prefix);
3003145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki        pw.print("Launcher: ");
3019da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki        pw.print(getPackageName());
3029da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki        pw.print("  Package user: ");
3039da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki        pw.print(getPackageUserId());
3042e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki        pw.print("  Owner user: ");
3052e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki        pw.print(getOwnerUserId());
3069da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki        pw.println();
3079da23fc6ac565b38129d52f4f8f174c833a9bd01Makoto Onuki
308c51b2876ec5c0af449469a0f76bb38c51cfcff04Makoto Onuki        getPackageInfo().dump(pw, prefix + "  ");
3093145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki        pw.println();
3103145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki
3113145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki        final int size = mPinnedShortcuts.size();
3123145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki        for (int i = 0; i < size; i++) {
3133145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki            pw.println();
3143145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki
3152e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki            final PackageWithUser pu = mPinnedShortcuts.keyAt(i);
3162e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki
3173145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki            pw.print(prefix);
3183145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki            pw.print("  ");
3193145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki            pw.print("Package: ");
3202e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki            pw.print(pu.packageName);
3212e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki            pw.print("  User: ");
3222e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki            pw.println(pu.userId);
3233145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki
3243145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki            final ArraySet<String> ids = mPinnedShortcuts.valueAt(i);
3253145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki            final int idSize = ids.size();
3263145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki
3273145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki            for (int j = 0; j < idSize; j++) {
3283145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki                pw.print(prefix);
3290acbb14574d859b5f1cc0b7c6bbdfbeba38f3e55Makoto Onuki                pw.print("    Pinned: ");
3303145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki                pw.print(ids.valueAt(j));
3313145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki                pw.println();
3323145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki            }
3333145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki        }
3343145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki    }
3352e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki
33676269928e677725e2d9b28e2e3aa79961a60a1d0Makoto Onuki    @Override
33776269928e677725e2d9b28e2e3aa79961a60a1d0Makoto Onuki    public JSONObject dumpCheckin(boolean clear) throws JSONException {
33876269928e677725e2d9b28e2e3aa79961a60a1d0Makoto Onuki        final JSONObject result = super.dumpCheckin(clear);
33976269928e677725e2d9b28e2e3aa79961a60a1d0Makoto Onuki
34076269928e677725e2d9b28e2e3aa79961a60a1d0Makoto Onuki        // Nothing really interesting to dump.
34176269928e677725e2d9b28e2e3aa79961a60a1d0Makoto Onuki
34276269928e677725e2d9b28e2e3aa79961a60a1d0Makoto Onuki        return result;
34376269928e677725e2d9b28e2e3aa79961a60a1d0Makoto Onuki    }
34476269928e677725e2d9b28e2e3aa79961a60a1d0Makoto Onuki
3452e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki    @VisibleForTesting
3462e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki    ArraySet<String> getAllPinnedShortcutsForTest(String packageName, int packageUserId) {
3472e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki        return new ArraySet<>(mPinnedShortcuts.get(PackageWithUser.of(packageUserId, packageName)));
3482e210c4d0f766e52ea4c087a1d54213c36a4e0eaMakoto Onuki    }
3493145924596ad0db9e8f1f5aead90fb50127243cbMakoto Onuki}
350