ShortcutService.java revision 6f7362d92573e4ae693bc513dca586d6a4eb087b
16f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki/*
26f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * Copyright (C) 2016 The Android Open Source Project
36f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki *
46f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * Licensed under the Apache License, Version 2.0 (the "License");
56f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * you may not use this file except in compliance with the License.
66f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * You may obtain a copy of the License at
76f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki *
86f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki *      http://www.apache.org/licenses/LICENSE-2.0
96f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki *
106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * Unless required by applicable law or agreed to in writing, software
116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * distributed under the License is distributed on an "AS IS" BASIS,
126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * See the License for the specific language governing permissions and
146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * limitations under the License.
156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki */
166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukipackage com.android.server.pm;
176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.annotation.NonNull;
196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.annotation.Nullable;
206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.annotation.UserIdInt;
216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.content.ComponentName;
226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.content.Context;
236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.content.Intent;
246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.content.pm.IShortcutService;
256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.content.pm.LauncherApps;
266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.content.pm.LauncherApps.ShortcutQuery;
276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.content.pm.PackageManager;
286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.content.pm.PackageManager.NameNotFoundException;
296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.content.pm.ParceledListSlice;
306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.content.pm.ShortcutInfo;
316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.content.pm.ShortcutServiceInternal;
326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.content.pm.ShortcutServiceInternal.ShortcutChangeListener;
336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.graphics.drawable.Icon;
346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.os.Binder;
356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.os.Bundle;
366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.os.Environment;
376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.os.Handler;
386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.os.PersistableBundle;
396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.os.Process;
406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.os.RemoteException;
416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.os.ResultReceiver;
426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.os.ShellCommand;
436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.os.UserHandle;
446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.text.TextUtils;
456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.text.format.Time;
466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.util.ArrayMap;
476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.util.ArraySet;
486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.util.AtomicFile;
496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.util.Slog;
506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.util.SparseArray;
516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport android.util.Xml;
526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport com.android.internal.annotations.GuardedBy;
546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport com.android.internal.annotations.VisibleForTesting;
556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport com.android.internal.os.BackgroundThread;
566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport com.android.internal.util.FastXmlSerializer;
576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport com.android.internal.util.Preconditions;
586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport com.android.server.LocalServices;
596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport com.android.server.SystemService;
606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport libcore.io.IoUtils;
626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport org.xmlpull.v1.XmlPullParser;
646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport org.xmlpull.v1.XmlPullParserException;
656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport org.xmlpull.v1.XmlSerializer;
666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.io.File;
686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.io.FileDescriptor;
696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.io.FileInputStream;
706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.io.FileNotFoundException;
716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.io.FileOutputStream;
726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.io.IOException;
736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.io.PrintWriter;
746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.net.URISyntaxException;
756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.nio.charset.StandardCharsets;
766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.util.ArrayList;
776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.util.List;
786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukiimport java.util.function.Predicate;
796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki/**
816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * TODO:
826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * - Make save async
836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki *
846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * - Add Bitmap support
856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki *
866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * - Implement updateShortcuts
876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki *
886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * - Listen to PACKAGE_*, remove orphan info, update timestamp for icon res
896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki *
906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * - Pinned per each launcher package (multiple launchers)
916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki *
926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * - Dev option to reset all counts for QA (for now use "adb shell cmd shortcut reset-throttling")
936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki *
946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki * - Load config from settings
956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki */
966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onukipublic class ShortcutService extends IShortcutService.Stub {
976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private static final String TAG = "ShortcutService";
986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private static final boolean DEBUG = true; // STOPSHIP if true
1006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private static final boolean DEBUG_LOAD = true; // STOPSHIP if true
1016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private static final int DEFAULT_RESET_INTERVAL_SEC = 24 * 60 * 60; // 1 day
1036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private static final int DEFAULT_MAX_DAILY_UPDATES = 10;
1046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private static final int DEFAULT_MAX_SHORTCUTS_PER_APP = 5;
1056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private static final int SAVE_DELAY_MS = 5000; // in milliseconds.
1076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
1096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    static final String FILENAME_BASE_STATE = "shortcut_service.xml";
1106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
1126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    static final String DIRECTORY_PER_USER = "shortcut_service";
1136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
1156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    static final String FILENAME_USER_PACKAGES = "shortcuts.xml";
1166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private static final String DIRECTORY_BITMAPS = "bitmaps";
1186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private static final String TAG_ROOT = "root";
1206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private static final String TAG_LAST_RESET_TIME = "last_reset_time";
1216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private static final String ATTR_VALUE = "value";
1226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private final Context mContext;
1246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private final Object mLock = new Object();
1266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private final Handler mHandler;
1286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @GuardedBy("mLock")
1306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private final ArrayList<ShortcutChangeListener> mListeners = new ArrayList<>(1);
1316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @GuardedBy("mLock")
1336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private long mRawLastResetTime;
1346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
1366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * All the information relevant to shortcuts from a single package (per-user).
1376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     *
1386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * TODO Move the persisting code to this class.
1396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
1406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private static class PackageShortcuts {
1416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        /**
1426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki         * All the shortcuts from the package, keyed on IDs.
1436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki         */
1446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final private ArrayMap<String, ShortcutInfo> mShortcuts = new ArrayMap<>();
1456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        /**
1476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki         * # of dynamic shortcuts.
1486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki         */
1496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        private int mDynamicShortcutCount = 0;
1506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        /**
1526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki         * # of times the package has called rate-limited APIs.
1536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki         */
1546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        private int mApiCallCountInner;
1556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        /**
1576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki         * When {@link #mApiCallCountInner} was reset last time.
1586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki         */
1596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        private long mLastResetTime;
1606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        /**
1626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki         * @return the all shortcuts.  Note DO NOT add/remove or touch the flags of the result
1636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki         * directly, which would cause {@link #mDynamicShortcutCount} to be out of sync.
1646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki         */
1656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @GuardedBy("mLock")
1666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public ArrayMap<String, ShortcutInfo> getShortcuts() {
1676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return mShortcuts;
1686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
1696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        /**
1716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki         * Add a shortcut, or update one with the same ID, with taking over existing flags.
1726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki         *
1736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki         * It checks the max number of dynamic shortcuts.
1746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki         */
1756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @GuardedBy("mLock")
1766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public void updateShortcutWithCapping(@NonNull ShortcutService s,
1776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                @NonNull ShortcutInfo newShortcut) {
1786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final ShortcutInfo oldShortcut = mShortcuts.get(newShortcut.getId());
1796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            int oldFlags = 0;
1816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            int newDynamicCount = mDynamicShortcutCount;
1826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            if (oldShortcut != null) {
1846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                oldFlags = oldShortcut.getFlags();
1856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                if (oldShortcut.isDynamic()) {
1866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    newDynamicCount--;
1876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                }
1886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
1896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            if (newShortcut.isDynamic()) {
1906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                newDynamicCount++;
1916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
1926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Make sure there's still room.
1936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            s.enforceMaxDynamicShortcuts(newDynamicCount);
1946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Okay, make it dynamic and add.
1966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            newShortcut.addFlags(oldFlags);
1976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
1986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            mShortcuts.put(newShortcut.getId(), newShortcut);
1996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            mDynamicShortcutCount = newDynamicCount;
2006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
2016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @GuardedBy("mLock")
2036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public void deleteAllDynamicShortcuts() {
2046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            ArrayList<String> removeList = null; // Lazily initialize.
2056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            for (int i = mShortcuts.size() - 1; i >= 0; i--) {
2076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                final ShortcutInfo si = mShortcuts.valueAt(i);
2086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                if (!si.isDynamic()) {
2106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    continue;
2116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                }
2126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                if (si.isPinned()) {
2136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    // Still pinned, so don't remove; just make it non-dynamic.
2146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    si.clearFlags(ShortcutInfo.FLAG_DYNAMIC);
2156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                } else {
2166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    if (removeList == null) {
2176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        removeList = new ArrayList<>();
2186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    }
2196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    removeList.add(si.getId());
2206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                }
2216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
2226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            if (removeList != null) {
2236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                for (int i = removeList.size() - 1 ; i >= 0; i--) {
2246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    mShortcuts.remove(removeList.get(i));
2256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                }
2266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
2276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            mDynamicShortcutCount = 0;
2286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
2296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @GuardedBy("mLock")
2316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public void deleteDynamicWithId(@NonNull String shortcutId) {
2326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final ShortcutInfo oldShortcut = mShortcuts.get(shortcutId);
2336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            if (oldShortcut == null) {
2356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                return;
2366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
2376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            if (oldShortcut.isDynamic()) {
2386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                mDynamicShortcutCount--;
2396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
2406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            if (oldShortcut.isPinned()) {
2416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                oldShortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC);
2426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            } else {
2436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                mShortcuts.remove(shortcutId);
2446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
2456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
2466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @GuardedBy("mLock")
2486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public void pinAll(List<String> shortcutIds) {
2496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            for (int i = shortcutIds.size() - 1; i >= 0; i--) {
2506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                final ShortcutInfo shortcut = mShortcuts.get(shortcutIds.get(i));
2516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                if (shortcut != null) {
2526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    shortcut.addFlags(ShortcutInfo.FLAG_PINNED);
2536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                }
2546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
2556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
2566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        /**
2586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki         * Number of calls that the caller has made, since the last reset.
2596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki         */
2606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @GuardedBy("mLock")
2616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public int getApiCallCount(@NonNull ShortcutService s) {
2626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final long last = s.getLastResetTimeLocked();
2636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // If not reset yet, then reset.
2656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            if (mLastResetTime < last) {
2666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                mApiCallCountInner = 0;
2676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                mLastResetTime = last;
2686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
2696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return mApiCallCountInner;
2706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
2716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        /**
2736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki         * If the caller app hasn't been throttled yet, increment {@link #mApiCallCountInner}
2746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki         * and return true.  Otherwise just return false.
2756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki         */
2766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @GuardedBy("mLock")
2776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public boolean tryApiCall(@NonNull ShortcutService s) {
2786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            if (getApiCallCount(s) >= s.mMaxDailyUpdates) {
2796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                return false;
2806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
2816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            mApiCallCountInner++;
2826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return true;
2836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
2846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @GuardedBy("mLock")
2866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public void resetRateLimitingForCommandLine() {
2876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            mApiCallCountInner = 0;
2886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            mLastResetTime = 0;
2896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
2906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
2916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        /**
2926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki         * Find all shortcuts that match {@code query}.
2936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki         */
2946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @GuardedBy("mLock")
2956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public void findAll(@NonNull List<ShortcutInfo> result,
2966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                @Nullable Predicate<ShortcutInfo> query, int cloneFlag) {
2976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            for (int i = 0; i < mShortcuts.size(); i++) {
2986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                final ShortcutInfo si = mShortcuts.valueAt(i);
2996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                if (query == null || query.test(si)) {
3006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    result.add(si.clone(cloneFlag));
3016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                }
3026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
3036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
3046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
3056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
3066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
3076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * User ID -> package name -> list of ShortcutInfos.
3086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
3096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @GuardedBy("mLock")
3106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private final SparseArray<ArrayMap<String, PackageShortcuts>> mShortcuts =
3116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            new SparseArray<>();
3126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
3136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
3146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Max number of dynamic shortcuts that each application can have at a time.
3156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
3166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @GuardedBy("mLock")
3176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private int mMaxDynamicShortcuts;
3186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
3196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
3206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Max number of updating API calls that each application can make a day.
3216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
3226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @GuardedBy("mLock")
3236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private int mMaxDailyUpdates;
3246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
3256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
3266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Actual throttling-reset interval.  By default it's a day.
3276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
3286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @GuardedBy("mLock")
3296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private long mResetInterval;
3306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
3316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public ShortcutService(Context context) {
3326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        mContext = Preconditions.checkNotNull(context);
3336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        LocalServices.addService(ShortcutServiceInternal.class, new LocalService());
3346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        mHandler = new Handler(BackgroundThread.get().getLooper());
3356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
3366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
3376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
3386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * System service lifecycle.
3396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
3406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public static final class Lifecycle extends SystemService {
3416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final ShortcutService mService;
3426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
3436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public Lifecycle(Context context) {
3446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            super(context);
3456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            mService = new ShortcutService(context);
3466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
3476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
3486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
3496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public void onStart() {
3506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            publishBinderService(Context.SHORTCUT_SERVICE, mService);
3516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
3526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
3536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
3546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public void onBootPhase(int phase) {
3556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            mService.onBootPhase(phase);
3566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
3576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
3586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
3596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public void onCleanupUser(int userHandle) {
3606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            synchronized (mService.mLock) {
3616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                mService.onCleanupUserInner(userHandle);
3626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
3636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
3646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
3656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
3666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public void onStartUser(int userId) {
3676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            synchronized (mService.mLock) {
3686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                mService.onStartUserLocked(userId);
3696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
3706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
3716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
3726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
3736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /** lifecycle event */
3746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    void onBootPhase(int phase) {
3756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (DEBUG) {
3766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.d(TAG, "onBootPhase: " + phase);
3776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
3786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        switch (phase) {
3796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            case SystemService.PHASE_LOCK_SETTINGS_READY:
3806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                initialize();
3816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                break;
3826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
3836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
3846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
3856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /** lifecycle event */
3866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    void onStartUserLocked(int userId) {
3876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        // Preload
3886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        getUserShortcutsLocked(userId);
3896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
3906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
3916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /** lifecycle event */
3926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    void onCleanupUserInner(int userId) {
3936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        // Unload
3946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        mShortcuts.delete(userId);
3956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
3966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
3976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /** Return the base state file name */
3986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private AtomicFile getBaseStateFile() {
3996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final File path = new File(injectSystemDataPath(), FILENAME_BASE_STATE);
4006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        path.mkdirs();
4016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return new AtomicFile(path);
4026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
4056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Init the instance. (load the state file, etc)
4066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
4076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void initialize() {
4086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
4096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            injectLoadConfigurationLocked();
4106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            loadBaseStateLocked();
4116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
4126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // Test overrides it to inject different values.
4156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
4166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    void injectLoadConfigurationLocked() {
4176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        mResetInterval = DEFAULT_RESET_INTERVAL_SEC * 1000L;
4186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        mMaxDailyUpdates = DEFAULT_MAX_DAILY_UPDATES;
4196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        mMaxDynamicShortcuts = DEFAULT_MAX_SHORTCUTS_PER_APP;
4206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // === Persistings ===
4236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Nullable
4256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private String parseStringAttribute(XmlPullParser parser, String attribute) {
4266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return parser.getAttributeValue(null, attribute);
4276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private long parseLongAttribute(XmlPullParser parser, String attribute) {
4306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final String value = parseStringAttribute(parser, attribute);
4316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (TextUtils.isEmpty(value)) {
4326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return 0;
4336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
4346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        try {
4356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return Long.parseLong(value);
4366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } catch (NumberFormatException e) {
4376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.e(TAG, "Error parsing long " + value);
4386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return 0;
4396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
4406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Nullable
4436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private ComponentName parseComponentNameAttribute(XmlPullParser parser, String attribute) {
4446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final String value = parseStringAttribute(parser, attribute);
4456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (TextUtils.isEmpty(value)) {
4466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return null;
4476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
4486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return ComponentName.unflattenFromString(value);
4496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Nullable
4526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private Intent parseIntentAttribute(XmlPullParser parser, String attribute) {
4536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final String value = parseStringAttribute(parser, attribute);
4546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (TextUtils.isEmpty(value)) {
4556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return null;
4566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
4576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        try {
4586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return Intent.parseUri(value, /* flags =*/ 0);
4596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } catch (URISyntaxException e) {
4606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.e(TAG, "Error parsing intent", e);
4616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return null;
4626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
4636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void writeTagValue(XmlSerializer out, String tag, String value) throws IOException {
4666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (TextUtils.isEmpty(value)) return;
4676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        out.startTag(null, tag);
4696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        out.attribute(null, ATTR_VALUE, value);
4706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        out.endTag(null, tag);
4716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void writeTagValue(XmlSerializer out, String tag, long value) throws IOException {
4746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        writeTagValue(out, tag, Long.toString(value));
4756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void writeTagExtra(XmlSerializer out, String tag, PersistableBundle bundle)
4786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            throws IOException, XmlPullParserException {
4796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (bundle == null) return;
4806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        out.startTag(null, tag);
4826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        bundle.saveToXml(out);
4836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        out.endTag(null, tag);
4846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void writeAttr(XmlSerializer out, String name, String value) throws IOException {
4876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (TextUtils.isEmpty(value)) return;
4886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        out.attribute(null, name, value);
4906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void writeAttr(XmlSerializer out, String name, long value) throws IOException {
4936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        writeAttr(out, name, String.valueOf(value));
4946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
4956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
4966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void writeAttr(XmlSerializer out, String name, ComponentName comp) throws IOException {
4976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (comp == null) return;
4986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        writeAttr(out, name, comp.flattenToString());
4996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
5006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void writeAttr(XmlSerializer out, String name, Intent intent) throws IOException {
5026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (intent == null) return;
5036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        writeAttr(out, name, intent.toUri(/* flags =*/ 0));
5056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
5066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
5086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    void saveBaseStateLocked() {
5096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final AtomicFile file = getBaseStateFile();
5106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (DEBUG) {
5116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.i(TAG, "Saving to " + file.getBaseFile());
5126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
5136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        FileOutputStream outs = null;
5156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        try {
5166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            outs = file.startWrite();
5176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Write to XML
5196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            XmlSerializer out = new FastXmlSerializer();
5206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            out.setOutput(outs, StandardCharsets.UTF_8.name());
5216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            out.startDocument(null, true);
5226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            out.startTag(null, TAG_ROOT);
5236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Body.
5256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            writeTagValue(out, TAG_LAST_RESET_TIME, mRawLastResetTime);
5266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Epilogue.
5286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            out.endTag(null, TAG_ROOT);
5296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            out.endDocument();
5306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Close.
5326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            file.finishWrite(outs);
5336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } catch (IOException e) {
5346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e);
5356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            file.failWrite(outs);
5366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
5376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
5386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void loadBaseStateLocked() {
5406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        mRawLastResetTime = 0;
5416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final AtomicFile file = getBaseStateFile();
5436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (DEBUG) {
5446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.i(TAG, "Loading from " + file.getBaseFile());
5456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
5466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        try (FileInputStream in = file.openRead()) {
5476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            XmlPullParser parser = Xml.newPullParser();
5486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            parser.setInput(in, StandardCharsets.UTF_8.name());
5496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            int type;
5516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
5526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                if (type != XmlPullParser.START_TAG) {
5536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    continue;
5546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                }
5556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                final int depth = parser.getDepth();
5566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                // Check the root tag
5576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                final String tag = parser.getName();
5586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                if (depth == 1) {
5596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    if (!TAG_ROOT.equals(tag)) {
5606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        Slog.e(TAG, "Invalid root tag: " + tag);
5616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        return;
5626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    }
5636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    continue;
5646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                }
5656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                // Assume depth == 2
5666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                switch (tag) {
5676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    case TAG_LAST_RESET_TIME:
5686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        mRawLastResetTime = parseLongAttribute(parser, ATTR_VALUE);
5696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        break;
5706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    default:
5716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        Slog.e(TAG, "Invalid tag: " + tag);
5726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        break;
5736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                }
5746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
5756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } catch (FileNotFoundException e) {
5766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Use the default
5776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } catch (IOException|XmlPullParserException e) {
5786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e);
5796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            mRawLastResetTime = 0;
5816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
5826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        // Adjust the last reset time.
5836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        getLastResetTimeLocked();
5846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
5856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void saveUserLocked(@UserIdInt int userId) {
5876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final File path = new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES);
5886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (DEBUG) {
5896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.i(TAG, "Saving to " + path);
5906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
5916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        path.mkdirs();
5926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final AtomicFile file = new AtomicFile(path);
5936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        FileOutputStream outs = null;
5946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        try {
5956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            outs = file.startWrite();
5966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
5976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Write to XML
5986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            XmlSerializer out = new FastXmlSerializer();
5996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            out.setOutput(outs, StandardCharsets.UTF_8.name());
6006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            out.startDocument(null, true);
6016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            out.startTag(null, TAG_ROOT);
6026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final ArrayMap<String, PackageShortcuts> packages = getUserShortcutsLocked(userId);
6046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Body.
6066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            for (int i = 0; i < packages.size(); i++) {
6076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                final String packageName = packages.keyAt(i);
6086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                final PackageShortcuts shortcuts = packages.valueAt(i);
6096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                // TODO Move this to PackageShortcuts.
6116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                out.startTag(null, "package");
6136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                writeAttr(out, "name", packageName);
6156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                writeAttr(out, "dynamic-count", shortcuts.mDynamicShortcutCount);
6166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                writeAttr(out, "call-count", shortcuts.mApiCallCountInner);
6176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                writeAttr(out, "last-reset", shortcuts.mLastResetTime);
6186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                final int size = shortcuts.getShortcuts().size();
6206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                for (int j = 0; j < size; j++) {
6216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    saveShortcut(out, shortcuts.getShortcuts().valueAt(j));
6226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                }
6236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                out.endTag(null, "package");
6256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
6266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Epilogue.
6286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            out.endTag(null, TAG_ROOT);
6296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            out.endDocument();
6306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Close.
6326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            file.finishWrite(outs);
6336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } catch (IOException|XmlPullParserException e) {
6346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e);
6356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            file.failWrite(outs);
6366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
6376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
6386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void saveShortcut(XmlSerializer out, ShortcutInfo si)
6406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            throws IOException, XmlPullParserException {
6416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        out.startTag(null, "shortcut");
6426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        writeAttr(out, "id", si.getId());
6436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        // writeAttr(out, "package", si.getPackageName()); // not needed
6446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        writeAttr(out, "activity", si.getActivityComponent());
6456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        // writeAttr(out, "icon", si.getIcon());  // We don't save it.
6466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        writeAttr(out, "title", si.getTitle());
6476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        writeAttr(out, "intent", si.getIntent());
6486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        writeAttr(out, "weight", si.getWeight());
6496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        writeAttr(out, "timestamp", si.getLastChangedTimestamp());
6506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        writeAttr(out, "flags", si.getFlags());
6516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        writeAttr(out, "icon-res", si.getIconResourceId());
6526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        writeAttr(out, "bitmap-path", si.getBitmapPath());
6536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        writeTagExtra(out, "intent-extras", si.getIntentPersistableExtras());
6556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        writeTagExtra(out, "extras", si.getExtras());
6566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        out.endTag(null, "shortcut");
6586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
6596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private static IOException throwForInvalidTag(int depth, String tag) throws IOException {
6616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        throw new IOException(String.format("Invalid tag '%s' found at depth %d", tag, depth));
6626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
6636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Nullable
6656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private ArrayMap<String, PackageShortcuts> loadUserLocked(@UserIdInt int userId) {
6666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final File path = new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES);
6676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (DEBUG) {
6686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.i(TAG, "Loading from " + path);
6696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
6706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        path.mkdirs();
6716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final AtomicFile file = new AtomicFile(path);
6726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final FileInputStream in;
6746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        try {
6756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            in = file.openRead();
6766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } catch (FileNotFoundException e) {
6776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            if (DEBUG) {
6786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                Slog.i(TAG, "Not found " + path);
6796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
6806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return null;
6816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
6826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final ArrayMap<String, PackageShortcuts> ret = new ArrayMap<String, PackageShortcuts>();
6836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        try {
6846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            XmlPullParser parser = Xml.newPullParser();
6856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            parser.setInput(in, StandardCharsets.UTF_8.name());
6866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            String packageName = null;
6886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            PackageShortcuts shortcuts = null;
6896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            int type;
6916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
6926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                if (type != XmlPullParser.START_TAG) {
6936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    continue;
6946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                }
6956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                final int depth = parser.getDepth();
6966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                // TODO Move some of this to PackageShortcuts.
6986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
6996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                final String tag = parser.getName();
7006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                if (DEBUG_LOAD) {
7016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    Slog.d(TAG, String.format("depth=%d type=%d name=%s",
7026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            depth, type, tag));
7036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                }
7046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                switch (depth) {
7056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    case 1: {
7066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        if (TAG_ROOT.equals(tag)) {
7076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            continue;
7086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        }
7096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        break;
7106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    }
7116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    case 2: {
7126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        switch (tag) {
7136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            case "package":
7146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                packageName = parseStringAttribute(parser, "name");
7156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                shortcuts = new PackageShortcuts();
7166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                ret.put(packageName, shortcuts);
7176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
7186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                shortcuts.mDynamicShortcutCount =
7196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                        (int) parseLongAttribute(parser, "dynamic-count");
7206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                shortcuts.mApiCallCountInner =
7216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                        (int) parseLongAttribute(parser, "call-count");
7226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                shortcuts.mLastResetTime = parseLongAttribute(parser, "last-reset");
7236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                continue;
7246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        }
7256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        break;
7266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    }
7276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    case 3: {
7286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        switch (tag) {
7296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            case "shortcut":
7306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                final ShortcutInfo si = parseShortcut(parser, packageName);
7316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                shortcuts.mShortcuts.put(si.getId(), si);
7326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                continue;
7336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        }
7346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        break;
7356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    }
7366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                }
7376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                throwForInvalidTag(depth, tag);
7386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
7396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return ret;
7406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } catch (IOException|XmlPullParserException e) {
7416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e);
7426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return null;
7436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } finally {
7446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            IoUtils.closeQuietly(in);
7456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
7466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
7476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
7486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private ShortcutInfo parseShortcut(XmlPullParser parser, String packgeName)
7496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            throws IOException, XmlPullParserException {
7506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        String id;
7516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        ComponentName activityComponent;
7526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        Icon icon;
7536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        String title;
7546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        Intent intent;
7556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        PersistableBundle intentPersistableExtras = null;
7566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        int weight;
7576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        PersistableBundle extras = null;
7586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        long lastChangedTimestamp;
7596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        int flags;
7606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        int iconRes;
7616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        String bitmapPath;
7626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
7636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        id = parseStringAttribute(parser, "id");
7646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        activityComponent = parseComponentNameAttribute(parser, "activity");
7656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        title = parseStringAttribute(parser, "title");
7666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        intent = parseIntentAttribute(parser, "intent");
7676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        weight = (int) parseLongAttribute(parser, "weight");
7686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        lastChangedTimestamp = (int) parseLongAttribute(parser, "timestamp");
7696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        flags = (int) parseLongAttribute(parser, "flags");
7706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        iconRes = (int) parseLongAttribute(parser, "icon-res");
7716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        bitmapPath = parseStringAttribute(parser, "bitmap-path");
7726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
7736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final int outerDepth = parser.getDepth();
7746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        int type;
7756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
7766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
7776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            if (type != XmlPullParser.START_TAG) {
7786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                continue;
7796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
7806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final int depth = parser.getDepth();
7816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final String tag = parser.getName();
7826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            if (DEBUG_LOAD) {
7836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                Slog.d(TAG, String.format("  depth=%d type=%d name=%s",
7846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        depth, type, tag));
7856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
7866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            switch (tag) {
7876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                case "intent-extras":
7886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    intentPersistableExtras = PersistableBundle.restoreFromXml(parser);
7896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    continue;
7906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                case "extras":
7916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    extras = PersistableBundle.restoreFromXml(parser);
7926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    continue;
7936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
7946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            throw throwForInvalidTag(depth, tag);
7956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
7966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return new ShortcutInfo(
7976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                id, packgeName, activityComponent, /* icon =*/ null, title, intent,
7986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                intentPersistableExtras, weight, extras, lastChangedTimestamp, flags,
7996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                iconRes, bitmapPath);
8006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
8016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
8026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // TODO Actually make it async.
8036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void scheduleSaveBaseState() {
8046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
8056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            saveBaseStateLocked();
8066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
8076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
8086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
8096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // TODO Actually make it async.
8106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void scheduleSaveUser(@UserIdInt int userId) {
8116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
8126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            saveUserLocked(userId);
8136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
8146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
8156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
8166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /** Return the last reset time. */
8176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    long getLastResetTimeLocked() {
8186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        updateTimes();
8196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return mRawLastResetTime;
8206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
8216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
8226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /** Return the next reset time. */
8236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    long getNextResetTimeLocked() {
8246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        updateTimes();
8256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return mRawLastResetTime + mResetInterval;
8266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
8276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
8286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
8296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Update the last reset time.
8306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
8316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void updateTimes() {
8326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
8336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final long now = injectCurrentTimeMillis();
8346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
8356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final long prevLastResetTime = mRawLastResetTime;
8366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
8376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (mRawLastResetTime == 0) { // first launch.
8386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // TODO Randomize??
8396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            mRawLastResetTime = now;
8406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } else if (now < mRawLastResetTime) {
8416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Clock rewound.
8426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // TODO Randomize??
8436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            mRawLastResetTime = now;
8446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } else {
8456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // TODO Do it properly.
8466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            while ((mRawLastResetTime + mResetInterval) <= now) {
8476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                mRawLastResetTime += mResetInterval;
8486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
8496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
8506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (prevLastResetTime != mRawLastResetTime) {
8516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            scheduleSaveBaseState();
8526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
8536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
8546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
8556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /** Return the per-user state. */
8566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @GuardedBy("mLock")
8576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @NonNull
8586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private ArrayMap<String, PackageShortcuts> getUserShortcutsLocked(@UserIdInt int userId) {
8596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        ArrayMap<String, PackageShortcuts> userPackages = mShortcuts.get(userId);
8606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (userPackages == null) {
8616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            userPackages = loadUserLocked(userId);
8626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            if (userPackages == null) {
8636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                userPackages = new ArrayMap<>();
8646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
8656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            mShortcuts.put(userId, userPackages);
8666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
8676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return userPackages;
8686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
8696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
8706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /** Return the per-user per-package state. */
8716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @GuardedBy("mLock")
8726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @NonNull
8736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private PackageShortcuts getPackageShortcutsLocked(
8746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            @NonNull String packageName, @UserIdInt int userId) {
8756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final ArrayMap<String, PackageShortcuts> userPackages = getUserShortcutsLocked(userId);
8766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        PackageShortcuts shortcuts = userPackages.get(packageName);
8776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (shortcuts == null) {
8786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            shortcuts = new PackageShortcuts();
8796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            userPackages.put(packageName, shortcuts);
8806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
8816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return shortcuts;
8826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
8836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
8846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // === Caller validation ===
8856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
8866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private boolean isCallerSystem() {
8876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final int callingUid = injectBinderCallingUid();
8886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki         return UserHandle.isSameApp(callingUid, Process.SYSTEM_UID);
8896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
8906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
8916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private boolean isCallerShell() {
8926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final int callingUid = injectBinderCallingUid();
8936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID;
8946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
8956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
8966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void enforceSystemOrShell() {
8976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        Preconditions.checkState(isCallerSystem() || isCallerShell(),
8986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                "Caller must be system or shell");
8996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
9006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void enforceShell() {
9026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        Preconditions.checkState(isCallerShell(), "Caller must be shell");
9036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
9046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void verifyCaller(@NonNull String packageName, @UserIdInt int userId) {
9066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        Preconditions.checkStringNotEmpty(packageName, "packageName");
9076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (isCallerSystem()) {
9096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return; // no check
9106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
9116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final int callingUid = injectBinderCallingUid();
9136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        // Otherwise, make sure the arguments are valid.
9156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (UserHandle.getUserId(callingUid) != userId) {
9166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            throw new SecurityException("Invalid user-ID");
9176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
9186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCallingPackage(packageName);
9196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
9206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void verifyCallingPackage(@NonNull String packageName) {
9226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        Preconditions.checkStringNotEmpty(packageName, "packageName");
9236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (isCallerSystem()) {
9256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return; // no check
9266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
9276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (injectGetPackageUid(packageName) == injectBinderCallingUid()) {
9296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return; // Caller is valid.
9306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
9316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        throw new SecurityException("Caller UID= doesn't own " + packageName);
9326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
9336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // Test overrides it.
9356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    int injectGetPackageUid(String packageName) {
9366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        try {
9376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // TODO Is MATCH_UNINSTALLED_PACKAGES correct to get SD card app info?
9396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return mContext.getPackageManager().getPackageUid(packageName,
9416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE
9426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            | PackageManager.MATCH_UNINSTALLED_PACKAGES);
9436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        } catch (NameNotFoundException e) {
9446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return -1;
9456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
9466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
9476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
9496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Throw if {@code numShortcuts} is bigger than {@link #mMaxDynamicShortcuts}.
9506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
9516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    void enforceMaxDynamicShortcuts(int numShortcuts) {
9526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (numShortcuts > mMaxDynamicShortcuts) {
9536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            throw new IllegalArgumentException("Max number of dynamic shortcuts exceeded");
9546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
9556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
9566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
9586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * - Sends a notification to LauncherApps
9596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * - Write to file
9606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
9616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void userPackageChanged(@NonNull String packageName, @UserIdInt int userId) {
9626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        notifyListeners(packageName, userId);
9636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        scheduleSaveUser(userId);
9646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
9656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void notifyListeners(@NonNull String packageName, @UserIdInt int userId) {
9676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final ArrayList<ShortcutChangeListener> copy;
9686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final List<ShortcutInfo> shortcuts = new ArrayList<>();
9696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
9706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            copy = new ArrayList<>(mListeners);
9716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            getPackageShortcutsLocked(packageName, userId)
9736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    .findAll(shortcuts, /* query =*/ null, ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO);
9746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
9756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        for (int i = copy.size() - 1; i >= 0; i--) {
9766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            copy.get(i).onShortcutChanged(packageName, shortcuts, userId);
9776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
9786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
9796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
9816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Clean up / validate an incoming shortcut.
9826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * - Make sure all mandatory fields are set.
9836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * - Make sure the intent's extras are persistable, and them to set
9846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     *  {@link ShortcutInfo#mIntentPersistableExtras}.  Also clear its extras.
9856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * - Clear flags.
9866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
9876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut) {
9886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        Preconditions.checkNotNull(shortcut, "Null shortcut detected");
9896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (shortcut.getActivityComponent() != null) {
9906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Preconditions.checkState(
9916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    shortcut.getPackageName().equals(
9926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            shortcut.getActivityComponent().getPackageName()),
9936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    "Activity package name mismatch");
9946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
9956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        shortcut.enforceMandatoryFields();
9976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
9986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final Intent intent = shortcut.getIntent();
9996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final Bundle intentExtras = intent.getExtras();
10006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (intentExtras != null && intentExtras.size() > 0) {
10016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            intent.replaceExtras((Bundle) null);
10026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // PersistableBundle's constructor will throw IllegalArgumentException if original
10046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // extras contain something not persistable.
10056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            shortcut.setIntentPersistableExtras(new PersistableBundle(intentExtras));
10066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
10076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        // TODO Save the icon
10096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        shortcut.setIcon(null);
10106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        shortcut.setFlags(0);
10126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
10136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // === APIs ===
10156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
10176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public boolean setDynamicShortcuts(String packageName, ParceledListSlice shortcutInfoList,
10186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            @UserIdInt int userId) {
10196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
10206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
10226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final int size = newShortcuts.size();
10236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
10256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final PackageShortcuts ps = getPackageShortcutsLocked(packageName, userId);
10266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Throttling.
10286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            if (!ps.tryApiCall(this)) {
10296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                return false;
10306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
10316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            enforceMaxDynamicShortcuts(size);
10326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Validate the shortcuts.
10346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            for (int i = 0; i < size; i++) {
10356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                fixUpIncomingShortcutInfo(newShortcuts.get(i));
10366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
10376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // First, remove all un-pinned; dynamic shortcuts
10396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            ps.deleteAllDynamicShortcuts();
10406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Then, add/update all.  We need to make sure to take over "pinned" flag.
10426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            for (int i = 0; i < size; i++) {
10436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                final ShortcutInfo newShortcut = newShortcuts.get(i);
10446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC);
10456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                ps.updateShortcutWithCapping(this, newShortcut);
10466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
10476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
10486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        userPackageChanged(packageName, userId);
10496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return true;
10516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
10526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
10546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public boolean updateShortcuts(String packageName, ParceledListSlice shortcutInfoList,
10556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            @UserIdInt int userId) {
10566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
10576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
10596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
10616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            if (true) {
10636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                throw new RuntimeException("not implemented yet");
10646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
10656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // TODO Similar to setDynamicShortcuts, but don't add new ones, and don't change flags.
10676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Update non-null fields only.
10686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
10696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        userPackageChanged(packageName, userId);
10706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return true;
10726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
10736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
10756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public boolean addDynamicShortcut(String packageName, ShortcutInfo newShortcut,
10766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            @UserIdInt int userId) {
10776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
10786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
10806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final PackageShortcuts ps = getPackageShortcutsLocked(packageName, userId);
10816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Throttling.
10836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            if (!ps.tryApiCall(this)) {
10846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                return false;
10856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
10866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Validate the shortcut.
10886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            fixUpIncomingShortcutInfo(newShortcut);
10896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Add it.
10916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC);
10926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            ps.updateShortcutWithCapping(this, newShortcut);
10936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
10946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        userPackageChanged(packageName, userId);
10956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return true;
10976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
10986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
10996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
11006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public void deleteDynamicShortcut(String packageName, String shortcutId,
11016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            @UserIdInt int userId) {
11026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
11036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        Preconditions.checkStringNotEmpty(shortcutId, "shortcutId must be provided");
11046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
11066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            getPackageShortcutsLocked(packageName, userId).deleteDynamicWithId(shortcutId);
11076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
11086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        userPackageChanged(packageName, userId);
11096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
11106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
11126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public void deleteAllDynamicShortcuts(String packageName, @UserIdInt int userId) {
11136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
11146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
11166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            getPackageShortcutsLocked(packageName, userId).deleteAllDynamicShortcuts();
11176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
11186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        userPackageChanged(packageName, userId);
11196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
11206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
11226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public ParceledListSlice<ShortcutInfo> getDynamicShortcuts(String packageName,
11236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            @UserIdInt int userId) {
11246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
11256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
11266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return getShortcutsWithQueryLocked(
11276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
11286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    ShortcutInfo::isDynamic);
11296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
11306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
11316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
11336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public ParceledListSlice<ShortcutInfo> getPinnedShortcuts(String packageName,
11346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            @UserIdInt int userId) {
11356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
11366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
11376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return getShortcutsWithQueryLocked(
11386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
11396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    ShortcutInfo::isPinned);
11406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
11416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
11426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private ParceledListSlice<ShortcutInfo> getShortcutsWithQueryLocked(@NonNull String packageName,
11446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            @UserIdInt int userId, int cloneFlags, @NonNull Predicate<ShortcutInfo> query) {
11456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final ArrayList<ShortcutInfo> ret = new ArrayList<>();
11476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        getPackageShortcutsLocked(packageName, userId).findAll(ret, query, cloneFlags);
11496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return new ParceledListSlice<>(ret);
11516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
11526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
11546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public int getMaxDynamicShortcutCount(String packageName, @UserIdInt int userId)
11556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            throws RemoteException {
11566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
11576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return mMaxDynamicShortcuts;
11596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
11606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
11626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public int getRemainingCallCount(String packageName, @UserIdInt int userId) {
11636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
11646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
11666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return mMaxDailyUpdates
11676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    - getPackageShortcutsLocked(packageName, userId).getApiCallCount(this);
11686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
11696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
11706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
11726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public long getRateLimitResetTime(String packageName, @UserIdInt int userId) {
11736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        verifyCaller(packageName, userId);
11746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
11766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return getNextResetTimeLocked();
11776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
11786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
11796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
11816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Reset all throttling, for developer options and command line.  Only system/shell can call it.
11826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
11836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
11846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public void resetThrottling() {
11856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        enforceSystemOrShell();
11866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        resetThrottlingInner();
11886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
11896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
11916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    void resetThrottlingInner() {
11926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
11936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            mRawLastResetTime = injectCurrentTimeMillis();
11946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
11956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        scheduleSaveBaseState();
11966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
11976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
11986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
11996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Entry point from {@link LauncherApps}.
12006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
12016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private class LocalService extends ShortcutServiceInternal {
12026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
12036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public List<ShortcutInfo> getShortcuts(
12046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                @NonNull String callingPackage, long changedSince,
12056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                @Nullable String packageName, @Nullable ComponentName componentName,
12066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                int queryFlags, int userId) {
12076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final ArrayList<ShortcutInfo> ret = new ArrayList<>();
12086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final int cloneFlag =
12096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    ((queryFlags & ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY) == 0)
12106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            ? ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER
12116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            : ShortcutInfo.CLONE_REMOVE_NON_KEY_INFO;
12126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            synchronized (mLock) {
12146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                if (packageName != null) {
12156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    getShortcutsInnerLocked(packageName, changedSince, componentName, queryFlags,
12166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            userId, ret, cloneFlag);
12176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                } else {
12186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    final ArrayMap<String, PackageShortcuts> packages =
12196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            getUserShortcutsLocked(userId);
12206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    for (int i = 0; i < packages.size(); i++) {
12216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        getShortcutsInnerLocked(
12226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                packages.keyAt(i),
12236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                changedSince, componentName, queryFlags, userId, ret, cloneFlag);
12246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    }
12256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                }
12266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
12276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return ret;
12286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
12296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        private void getShortcutsInnerLocked(@Nullable String packageName,long changedSince,
12316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                @Nullable ComponentName componentName, int queryFlags,
12326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                int userId, ArrayList<ShortcutInfo> ret, int cloneFlag) {
12336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            getPackageShortcutsLocked(packageName, userId).findAll(ret,
12346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    (ShortcutInfo si) -> {
12356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        if (si.getLastChangedTimestamp() < changedSince) {
12366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            return false;
12376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        }
12386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        if (componentName != null
12396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                && !componentName.equals(si.getActivityComponent())) {
12406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                            return false;
12416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        }
12426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        final boolean matchDynamic =
12436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                ((queryFlags & ShortcutQuery.FLAG_GET_DYNAMIC) != 0)
12446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                && si.isDynamic();
12456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        final boolean matchPinned =
12466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                ((queryFlags & ShortcutQuery.FLAG_GET_PINNED) != 0)
12476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                                        && si.isPinned();
12486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        return matchDynamic || matchPinned;
12496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    }, cloneFlag);
12506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
12516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
12536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public List<ShortcutInfo> getShortcutInfo(
12546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                @NonNull String callingPackage,
12556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                @NonNull String packageName, @Nullable List<String> ids, int userId) {
12566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Calling permission must be checked by LauncherAppsImpl.
12576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Preconditions.checkStringNotEmpty(packageName, "packageName");
12586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final ArrayList<ShortcutInfo> ret = new ArrayList<>(ids.size());
12606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final ArraySet<String> idSet = new ArraySet<>(ids);
12616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            synchronized (mLock) {
12626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                getPackageShortcutsLocked(packageName, userId).findAll(ret,
12636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        (ShortcutInfo si) -> idSet.contains(si.getId()),
12646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
12656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
12666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return ret;
12676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
12686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
12706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public void pinShortcuts(@NonNull String callingPackage, @NonNull String packageName,
12716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                @NonNull List<String> shortcutIds, int userId) {
12726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Calling permission must be checked by LauncherAppsImpl.
12736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Preconditions.checkStringNotEmpty(packageName, "packageName");
12746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Preconditions.checkNotNull(shortcutIds, "shortcutIds");
12756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            synchronized (mLock) {
12776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                getPackageShortcutsLocked(packageName, userId).pinAll(shortcutIds);
12786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
12796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            userPackageChanged(packageName, userId);
12806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
12816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
12836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public Intent createShortcutIntent(@NonNull String callingPackage,
12846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                @NonNull ShortcutInfo shortcut, int userId) {
12856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            // Calling permission must be checked by LauncherAppsImpl.
12866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            Preconditions.checkNotNull(shortcut, "shortcut");
12876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
12886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            synchronized (mLock) {
12896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                final ShortcutInfo fullShortcut =
12906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        getPackageShortcutsLocked(shortcut.getPackageName(), userId)
12916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        .getShortcuts().get(shortcut.getId());
12926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                if (fullShortcut == null) {
12936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    return null;
12946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                } else {
12956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    final Intent intent = fullShortcut.getIntent();
12966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    final PersistableBundle extras = fullShortcut.getIntentPersistableExtras();
12976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    if (extras != null) {
12986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        intent.replaceExtras(new Bundle(extras));
12996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    }
13006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
13016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    return intent;
13026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                }
13036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
13046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
13056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
13066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
13076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public void addListener(@NonNull ShortcutChangeListener listener) {
13086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            synchronized (mLock) {
13096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                mListeners.add(Preconditions.checkNotNull(listener));
13106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
13116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
13126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
13136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
13146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // === Dump ===
13156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
13166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
13176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
13186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
13196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                != PackageManager.PERMISSION_GRANTED) {
13206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println("Permission Denial: can't dump UserManager from from pid="
13216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    + Binder.getCallingPid()
13226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    + ", uid=" + Binder.getCallingUid()
13236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    + " without permission "
13246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    + android.Manifest.permission.DUMP);
13256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return;
13266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
13276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        dumpInner(pw);
13286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
13296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
13306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
13316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    void dumpInner(PrintWriter pw) {
13326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        synchronized (mLock) {
13336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final long now = injectCurrentTimeMillis();
13346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("Now: [");
13356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print(now);
13366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("] ");
13376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print(formatTime(now));
13386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("  Raw last reset: [");
13396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print(mRawLastResetTime);
13406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("] ");
13416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print(formatTime(mRawLastResetTime));
13426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
13436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final long last = getLastResetTimeLocked();
13446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final long next = getNextResetTimeLocked();
13456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("  Last reset: [");
13466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print(last);
13476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("] ");
13486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print(formatTime(last));
13496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
13506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("  Next reset: [");
13516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print(next);
13526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("] ");
13536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print(formatTime(next));
13546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println();
13556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
13566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println();
13576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
13586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            for (int i = 0; i < mShortcuts.size(); i++) {
13596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                dumpUserLocked(pw, mShortcuts.keyAt(i));
13606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
13616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
13626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
13636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
13646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
13656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void dumpUserLocked(PrintWriter pw, int userId) {
13666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        pw.print("  User: ");
13676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        pw.print(userId);
13686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        pw.println();
13696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
13706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final ArrayMap<String, PackageShortcuts> packages = mShortcuts.get(userId);
13716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (packages == null) {
13726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return;
13736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
13746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        for (int j = 0; j < packages.size(); j++) {
13756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            dumpPackageLocked(pw, userId, packages.keyAt(j));
13766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
13776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        pw.println();
13786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
13796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
13806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private void dumpPackageLocked(PrintWriter pw, int userId, String packageName) {
13816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final PackageShortcuts shortcuts = mShortcuts.get(userId).get(packageName);
13826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        if (shortcuts == null) {
13836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return;
13846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
13856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
13866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        pw.print("    Package: ");
13876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        pw.print(packageName);
13886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        pw.println();
13896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
13906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        pw.print("      Calls: ");
13916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        pw.print(shortcuts.getApiCallCount(this));
13926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        pw.println();
13936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
13946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        // This should be after getApiCallCount(), which may update it.
13956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        pw.print("      Last reset: [");
13966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        pw.print(shortcuts.mLastResetTime);
13976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        pw.print("] ");
13986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        pw.print(formatTime(shortcuts.mLastResetTime));
13996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        pw.println();
14006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        pw.println("      Shortcuts:");
14026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        final int size = shortcuts.getShortcuts().size();
14036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        for (int i = 0; i < size; i++) {
14046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.print("        ");
14056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println(shortcuts.getShortcuts().valueAt(i).toInsecureString());
14066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
14076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
14086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private static String formatTime(long time) {
14106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        Time tobj = new Time();
14116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        tobj.set(time);
14126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return tobj.format("%Y-%m-%d %H:%M:%S");
14136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
14146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // === Shell support ===
14166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @Override
14186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
14196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            String[] args, ResultReceiver resultReceiver) throws RemoteException {
14206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        enforceShell();
14226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        (new MyShellCommand()).exec(this, in, out, err, args, resultReceiver);
14246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
14256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    /**
14276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     * Handle "adb shell cmd".
14286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki     */
14296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    private class MyShellCommand extends ShellCommand {
14306f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
14316f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public int onCommand(String cmd) {
14326f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            if (cmd == null) {
14336f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                return handleDefaultCommands(cmd);
14346f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
14356f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final PrintWriter pw = getOutPrintWriter();
14366f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            switch(cmd) {
14376f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                case "reset-package-throttling":
14386f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    return handleResetPackageThrottling();
14396f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                case "reset-throttling":
14406f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    return handleResetThrottling();
14416f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                default:
14426f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    return handleDefaultCommands(cmd);
14436f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
14446f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
14456f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14466f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        @Override
14476f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        public void onHelp() {
14486f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final PrintWriter pw = getOutPrintWriter();
14496f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println("Usage: cmd shortcut COMMAND [options ...]");
14506f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println();
14516f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println("cmd shortcut reset-package-throttling [--user USER_ID] PACKAGE");
14526f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println("    Reset throttling for a package");
14536f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println();
14546f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println("cmd shortcut reset-throttling");
14556f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println("    Reset throttling for all packages and users");
14566f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            pw.println();
14576f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
14586f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14596f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        private int handleResetThrottling() {
14606f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            resetThrottling();
14616f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return 0;
14626f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
14636f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14646f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        private int handleResetPackageThrottling() {
14656f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final PrintWriter pw = getOutPrintWriter();
14666f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14676f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            int userId = UserHandle.USER_SYSTEM;
14686f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            String opt;
14696f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            while ((opt = getNextOption()) != null) {
14706f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                switch (opt) {
14716f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    case "--user":
14726f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        userId = UserHandle.parseUserArg(getNextArgRequired());
14736f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        break;
14746f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                    default:
14756f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        pw.println("Error: Unknown option: " + opt);
14766f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                        return 1;
14776f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                }
14786f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
14796f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            final String packageName = getNextArgRequired();
14806f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14816f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            synchronized (mLock) {
14826f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                getPackageShortcutsLocked(packageName, userId).resetRateLimitingForCommandLine();
14836f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki                saveUserLocked(userId);
14846f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            }
14856f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14866f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki            return 0;
14876f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        }
14886f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
14896f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14906f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // === Unit test support ===
14916f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14926f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // Injection point.
14936f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    long injectCurrentTimeMillis() {
14946f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return System.currentTimeMillis();
14956f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
14966f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
14976f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    // Injection point.
14986f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    int injectBinderCallingUid() {
14996f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return getCallingUid();
15006f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
15016f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15026f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    File injectSystemDataPath() {
15036f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return Environment.getDataSystemDirectory();
15046f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
15056f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15066f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    File injectUserDataPath(@UserIdInt int userId) {
15076f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return new File(Environment.getDataSystemDeDirectory(userId), DIRECTORY_PER_USER);
15086f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
15096f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15106f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
15116f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    SparseArray<ArrayMap<String, PackageShortcuts>> getShortcutsForTest() {
15126f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        return mShortcuts;
15136f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
15146f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15156f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
15166f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    void setMaxDynamicShortcutsForTest(int max) {
15176f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        mMaxDynamicShortcuts = max;
15186f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
15196f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15206f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
15216f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    void setMaxDailyUpdatesForTest(int max) {
15226f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        mMaxDailyUpdates = max;
15236f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
15246f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki
15256f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    @VisibleForTesting
15266f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    public void setResetIntervalForTest(long interval) {
15276f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki        mResetInterval = interval;
15286f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki    }
15296f7362d92573e4ae693bc513dca586d6a4eb087bMakoto Onuki}
1530