ShortcutUser.java revision 6c1dbd577bcf2b8bccb9a0d04d741ff7337898f2
13da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson/* 23da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson * Copyright (C) 2016 The Android Open Source Project 33da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson * 43da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson * Licensed under the Apache License, Version 2.0 (the "License"); 53da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson * you may not use this file except in compliance with the License. 63da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson * You may obtain a copy of the License at 73da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson * 83da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson * http://www.apache.org/licenses/LICENSE-2.0 93da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson * 103da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson * Unless required by applicable law or agreed to in writing, software 113da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson * distributed under the License is distributed on an "AS IS" BASIS, 123da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 133da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson * See the License for the specific language governing permissions and 143da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson * limitations under the License. 153da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson */ 163da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibsonpackage com.android.server.pm; 173da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson 183da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibsonimport android.annotation.NonNull; 193da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibsonimport android.annotation.UserIdInt; 203da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibsonimport android.content.ComponentName; 213da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibsonimport android.text.format.Formatter; 223da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibsonimport android.util.ArrayMap; 233da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibsonimport android.util.Slog; 243da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibsonimport android.util.SparseArray; 253da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson 263da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibsonimport com.android.internal.annotations.VisibleForTesting; 273da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibsonimport com.android.internal.util.Preconditions; 283da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson 293da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibsonimport libcore.util.Objects; 303da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson 313da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibsonimport org.xmlpull.v1.XmlPullParser; 323da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibsonimport org.xmlpull.v1.XmlPullParserException; 333da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibsonimport org.xmlpull.v1.XmlSerializer; 343da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson 353da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibsonimport java.io.File; 363da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibsonimport java.io.IOException; 37bae93e8992fef3cfaff681fe85b26fd35e432e31David Gibsonimport java.io.PrintWriter; 383da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibsonimport java.util.function.Consumer; 393da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson 403da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson/** 413da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson * User information used by {@link ShortcutService}. 423da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson */ 433da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibsonclass ShortcutUser { 443da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson private static final String TAG = ShortcutService.TAG; 453da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson 463da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson static final String TAG_ROOT = "user"; 473da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson private static final String TAG_LAUNCHER = "launcher"; 483da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson 493da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson private static final String ATTR_VALUE = "value"; 503da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson private static final String ATTR_KNOWN_LOCALE_CHANGE_SEQUENCE_NUMBER = "locale-seq-no"; 513da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson 523da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson static final class PackageWithUser { 533da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson final int userId; 543da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson final String packageName; 553da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson 563da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson private PackageWithUser(int userId, String packageName) { 573da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson this.userId = userId; 583da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson this.packageName = Preconditions.checkNotNull(packageName); 593da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson } 603da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson 613da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson public static PackageWithUser of(int userId, String packageName) { 623da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson return new PackageWithUser(userId, packageName); 633da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson } 643da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson 653da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson public static PackageWithUser of(ShortcutPackageItem spi) { 663da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson return new PackageWithUser(spi.getPackageUserId(), spi.getPackageName()); 673da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson } 683da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson 693da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson @Override 703da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson public int hashCode() { 713da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson return packageName.hashCode() ^ userId; 723da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson } 733da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson 743da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson @Override 753da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson public boolean equals(Object obj) { 763da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson if (!(obj instanceof PackageWithUser)) { 773da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson return false; 783da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson } 793da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson final PackageWithUser that = (PackageWithUser) obj; 803da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson 813da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson return userId == that.userId && packageName.equals(that.packageName); 823da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson } 833da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson 843da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson @Override 853da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson public String toString() { 863da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson return String.format("{Package: %d, %s}", userId, packageName); 873da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson } 883da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson } 893da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson 903da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson @UserIdInt 913da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson private final int mUserId; 923da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson 933da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson private final ArrayMap<String, ShortcutPackage> mPackages = new ArrayMap<>(); 943da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson 953da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson private final SparseArray<ShortcutPackage> mPackagesFromUid = new SparseArray<>(); 96fd1bf3a5ae46962528ef89a824261a88830758a2David Gibson 97fd1bf3a5ae46962528ef89a824261a88830758a2David Gibson private final ArrayMap<PackageWithUser, ShortcutLauncher> mLaunchers = new ArrayMap<>(); 9873d60926a05814b8864c86c435e272b386513b0eDavid Gibson 993da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson /** Default launcher that can access the launcher apps APIs. */ 1009521dc5ecc66c158cd6853cabba2c29f545780f6David Gibson private ComponentName mLauncherComponent; 1013da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson 102bad5b28049e5e0562a8ad91797fb77953a53fa20David Gibson private long mKnownLocaleChangeSequenceNumber; 1033da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson 1043da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson public ShortcutUser(int userId) { 1053da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson mUserId = userId; 1063da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson } 107a6c76f923dcc42102fac58375eaca28057811c20David Gibson 108a6c76f923dcc42102fac58375eaca28057811c20David Gibson public int getUserId() { 1099521dc5ecc66c158cd6853cabba2c29f545780f6David Gibson return mUserId; 1103da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson } 111bad5b28049e5e0562a8ad91797fb77953a53fa20David Gibson 1123da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson // We don't expose this directly to non-test code because only ShortcutUser should add to/ 1133da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson // remove from it. 114cbf1410eab4b7ce7be1b15f985ef71bfc1f5886dDavid Gibson @VisibleForTesting 115cbf1410eab4b7ce7be1b15f985ef71bfc1f5886dDavid Gibson ArrayMap<String, ShortcutPackage> getAllPackagesForTest() { 116bad5b28049e5e0562a8ad91797fb77953a53fa20David Gibson return mPackages; 117cbf1410eab4b7ce7be1b15f985ef71bfc1f5886dDavid Gibson } 118cbf1410eab4b7ce7be1b15f985ef71bfc1f5886dDavid Gibson 1197ba551f96629a5273370329658a6e6b3a87068aaDavid Gibson public boolean hasPackage(@NonNull String packageName) { 1207ba551f96629a5273370329658a6e6b3a87068aaDavid Gibson return mPackages.containsKey(packageName); 121d2a9da045897c37071597d9aa473964717b14735David Gibson } 1223c44c87bdeacc66f46c55090d765a9766475ee50David Gibson 1234e6221c171377324cc3e80a9c2260b9788335d87David Gibson public ShortcutPackage removePackage(@NonNull ShortcutService s, @NonNull String packageName) { 12473d60926a05814b8864c86c435e272b386513b0eDavid Gibson final ShortcutPackage removed = mPackages.remove(packageName); 125a041dcdc48453f26b76bccdb5e2a1ebb3a0ea987David Gibson 1263da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson s.cleanupBitmapsForPackage(mUserId, packageName); 12736204fdf742cabc074617648a5b2cf62409dc40bSimon Glass 12836204fdf742cabc074617648a5b2cf62409dc40bSimon Glass return removed; 1293da0f9a10dfa9b615d06c350c7b9fe29f360a6eDavid Gibson } 130 131 // We don't expose this directly to non-test code because only ShortcutUser should add to/ 132 // remove from it. 133 @VisibleForTesting 134 ArrayMap<PackageWithUser, ShortcutLauncher> getAllLaunchersForTest() { 135 return mLaunchers; 136 } 137 138 public void addLauncher(ShortcutLauncher launcher) { 139 mLaunchers.put(PackageWithUser.of(launcher.getPackageUserId(), 140 launcher.getPackageName()), launcher); 141 } 142 143 public ShortcutLauncher removeLauncher( 144 @UserIdInt int packageUserId, @NonNull String packageName) { 145 return mLaunchers.remove(PackageWithUser.of(packageUserId, packageName)); 146 } 147 148 public ShortcutPackage getPackageShortcuts(ShortcutService s, @NonNull String packageName) { 149 ShortcutPackage ret = mPackages.get(packageName); 150 if (ret == null) { 151 ret = new ShortcutPackage(s, this, mUserId, packageName); 152 mPackages.put(packageName, ret); 153 } else { 154 ret.attemptToRestoreIfNeededAndSave(s); 155 } 156 return ret; 157 } 158 159 public ShortcutLauncher getLauncherShortcuts(ShortcutService s, @NonNull String packageName, 160 @UserIdInt int launcherUserId) { 161 final PackageWithUser key = PackageWithUser.of(launcherUserId, packageName); 162 ShortcutLauncher ret = mLaunchers.get(key); 163 if (ret == null) { 164 ret = new ShortcutLauncher(this, mUserId, packageName, launcherUserId); 165 mLaunchers.put(key, ret); 166 } else { 167 ret.attemptToRestoreIfNeededAndSave(s); 168 } 169 return ret; 170 } 171 172 public void forAllPackages(Consumer<? super ShortcutPackage> callback) { 173 final int size = mPackages.size(); 174 for (int i = 0; i < size; i++) { 175 callback.accept(mPackages.valueAt(i)); 176 } 177 } 178 179 public void forAllLaunchers(Consumer<? super ShortcutLauncher> callback) { 180 final int size = mLaunchers.size(); 181 for (int i = 0; i < size; i++) { 182 callback.accept(mLaunchers.valueAt(i)); 183 } 184 } 185 186 public void forAllPackageItems(Consumer<? super ShortcutPackageItem> callback) { 187 forAllLaunchers(callback); 188 forAllPackages(callback); 189 } 190 191 public void forPackageItem(@NonNull String packageName, @UserIdInt int packageUserId, 192 Consumer<ShortcutPackageItem> callback) { 193 forAllPackageItems(spi -> { 194 if ((spi.getPackageUserId() == packageUserId) 195 && spi.getPackageName().equals(packageName)) { 196 callback.accept(spi); 197 } 198 }); 199 } 200 201 /** 202 * Reset all throttling counters for all packages, if there has been a system locale change. 203 */ 204 public void resetThrottlingIfNeeded(ShortcutService s) { 205 final long currentNo = s.getLocaleChangeSequenceNumber(); 206 if (mKnownLocaleChangeSequenceNumber < currentNo) { 207 if (ShortcutService.DEBUG) { 208 Slog.d(TAG, "LocaleChange detected for user " + mUserId); 209 } 210 211 mKnownLocaleChangeSequenceNumber = currentNo; 212 213 forAllPackages(p -> p.resetRateLimiting(s)); 214 215 s.scheduleSaveUser(mUserId); 216 } 217 } 218 219 /** 220 * Called when a package is updated. 221 */ 222 public void handlePackageUpdated(ShortcutService s, @NonNull String packageName, 223 int newVersionCode) { 224 if (!mPackages.containsKey(packageName)) { 225 return; 226 } 227 getPackageShortcuts(s, packageName).handlePackageUpdated(s, newVersionCode); 228 } 229 230 public void attemptToRestoreIfNeededAndSave(ShortcutService s, @NonNull String packageName, 231 @UserIdInt int packageUserId) { 232 forPackageItem(packageName, packageUserId, spi -> { 233 spi.attemptToRestoreIfNeededAndSave(s); 234 }); 235 } 236 237 public void saveToXml(ShortcutService s, XmlSerializer out, boolean forBackup) 238 throws IOException, XmlPullParserException { 239 out.startTag(null, TAG_ROOT); 240 241 ShortcutService.writeAttr(out, ATTR_KNOWN_LOCALE_CHANGE_SEQUENCE_NUMBER, 242 mKnownLocaleChangeSequenceNumber); 243 244 ShortcutService.writeTagValue(out, TAG_LAUNCHER, 245 mLauncherComponent); 246 247 // Can't use forEachPackageItem due to the checked exceptions. 248 { 249 final int size = mLaunchers.size(); 250 for (int i = 0; i < size; i++) { 251 saveShortcutPackageItem(s, out, mLaunchers.valueAt(i), forBackup); 252 } 253 } 254 { 255 final int size = mPackages.size(); 256 for (int i = 0; i < size; i++) { 257 saveShortcutPackageItem(s, out, mPackages.valueAt(i), forBackup); 258 } 259 } 260 261 out.endTag(null, TAG_ROOT); 262 } 263 264 private void saveShortcutPackageItem(ShortcutService s, XmlSerializer out, 265 ShortcutPackageItem spi, boolean forBackup) throws IOException, XmlPullParserException { 266 if (forBackup) { 267 if (!s.shouldBackupApp(spi.getPackageName(), spi.getPackageUserId())) { 268 return; // Don't save. 269 } 270 if (spi.getPackageUserId() != spi.getOwnerUserId()) { 271 return; // Don't save cross-user information. 272 } 273 } 274 spi.saveToXml(out, forBackup); 275 } 276 277 public static ShortcutUser loadFromXml(ShortcutService s, XmlPullParser parser, int userId, 278 boolean fromBackup) throws IOException, XmlPullParserException { 279 final ShortcutUser ret = new ShortcutUser(userId); 280 281 ret.mKnownLocaleChangeSequenceNumber = ShortcutService.parseLongAttribute(parser, 282 ATTR_KNOWN_LOCALE_CHANGE_SEQUENCE_NUMBER); 283 284 final int outerDepth = parser.getDepth(); 285 int type; 286 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 287 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 288 if (type != XmlPullParser.START_TAG) { 289 continue; 290 } 291 final int depth = parser.getDepth(); 292 final String tag = parser.getName(); 293 294 if (depth == outerDepth + 1) { 295 switch (tag) { 296 case TAG_LAUNCHER: { 297 ret.mLauncherComponent = ShortcutService.parseComponentNameAttribute( 298 parser, ATTR_VALUE); 299 continue; 300 } 301 case ShortcutPackage.TAG_ROOT: { 302 final ShortcutPackage shortcuts = ShortcutPackage.loadFromXml( 303 s, ret, parser, fromBackup); 304 305 // Don't use addShortcut(), we don't need to save the icon. 306 ret.mPackages.put(shortcuts.getPackageName(), shortcuts); 307 continue; 308 } 309 310 case ShortcutLauncher.TAG_ROOT: { 311 ret.addLauncher( 312 ShortcutLauncher.loadFromXml(parser, ret, userId, fromBackup)); 313 continue; 314 } 315 } 316 } 317 ShortcutService.warnForInvalidTag(depth, tag); 318 } 319 return ret; 320 } 321 322 public ComponentName getLauncherComponent() { 323 return mLauncherComponent; 324 } 325 326 public void setLauncherComponent(ShortcutService s, ComponentName launcherComponent) { 327 if (Objects.equal(mLauncherComponent, launcherComponent)) { 328 return; 329 } 330 mLauncherComponent = launcherComponent; 331 s.scheduleSaveUser(mUserId); 332 } 333 334 public void resetThrottling() { 335 for (int i = mPackages.size() - 1; i >= 0; i--) { 336 mPackages.valueAt(i).resetThrottling(); 337 } 338 } 339 340 public void dump(@NonNull ShortcutService s, @NonNull PrintWriter pw, @NonNull String prefix) { 341 pw.print(prefix); 342 pw.print("User: "); 343 pw.print(mUserId); 344 pw.print(" Known locale seq#: "); 345 pw.print(mKnownLocaleChangeSequenceNumber); 346 pw.println(); 347 348 prefix += prefix + " "; 349 350 pw.print(prefix); 351 pw.print("Default launcher: "); 352 pw.print(mLauncherComponent); 353 pw.println(); 354 355 for (int i = 0; i < mLaunchers.size(); i++) { 356 mLaunchers.valueAt(i).dump(s, pw, prefix); 357 } 358 359 for (int i = 0; i < mPackages.size(); i++) { 360 mPackages.valueAt(i).dump(s, pw, prefix); 361 } 362 363 pw.println(); 364 pw.print(prefix); 365 pw.println("Bitmap directories: "); 366 dumpDirectorySize(s, pw, prefix + " ", s.getUserBitmapFilePath(mUserId)); 367 } 368 369 private void dumpDirectorySize(@NonNull ShortcutService s, @NonNull PrintWriter pw, 370 @NonNull String prefix, File path) { 371 int numFiles = 0; 372 long size = 0; 373 final File[] children = path.listFiles(); 374 if (children != null) { 375 for (File child : path.listFiles()) { 376 if (child.isFile()) { 377 numFiles++; 378 size += child.length(); 379 } else if (child.isDirectory()) { 380 dumpDirectorySize(s, pw, prefix + " ", child); 381 } 382 } 383 } 384 pw.print(prefix); 385 pw.print("Path: "); 386 pw.print(path.getName()); 387 pw.print("/ has "); 388 pw.print(numFiles); 389 pw.print(" files, size="); 390 pw.print(size); 391 pw.print(" ("); 392 pw.print(Formatter.formatFileSize(s.mContext, size)); 393 pw.println(")"); 394 } 395} 396