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