ShortcutUser.java revision 6c1dbd577bcf2b8bccb9a0d04d741ff7337898f2
1/* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16package com.android.server.pm; 17 18import android.annotation.NonNull; 19import android.annotation.UserIdInt; 20import android.content.ComponentName; 21import android.text.format.Formatter; 22import android.util.ArrayMap; 23import android.util.Slog; 24import android.util.SparseArray; 25 26import com.android.internal.annotations.VisibleForTesting; 27import com.android.internal.util.Preconditions; 28 29import libcore.util.Objects; 30 31import org.xmlpull.v1.XmlPullParser; 32import org.xmlpull.v1.XmlPullParserException; 33import org.xmlpull.v1.XmlSerializer; 34 35import java.io.File; 36import java.io.IOException; 37import java.io.PrintWriter; 38import java.util.function.Consumer; 39 40/** 41 * User information used by {@link ShortcutService}. 42 */ 43class ShortcutUser { 44 private static final String TAG = ShortcutService.TAG; 45 46 static final String TAG_ROOT = "user"; 47 private static final String TAG_LAUNCHER = "launcher"; 48 49 private static final String ATTR_VALUE = "value"; 50 private static final String ATTR_KNOWN_LOCALE_CHANGE_SEQUENCE_NUMBER = "locale-seq-no"; 51 52 static final class PackageWithUser { 53 final int userId; 54 final String packageName; 55 56 private PackageWithUser(int userId, String packageName) { 57 this.userId = userId; 58 this.packageName = Preconditions.checkNotNull(packageName); 59 } 60 61 public static PackageWithUser of(int userId, String packageName) { 62 return new PackageWithUser(userId, packageName); 63 } 64 65 public static PackageWithUser of(ShortcutPackageItem spi) { 66 return new PackageWithUser(spi.getPackageUserId(), spi.getPackageName()); 67 } 68 69 @Override 70 public int hashCode() { 71 return packageName.hashCode() ^ userId; 72 } 73 74 @Override 75 public boolean equals(Object obj) { 76 if (!(obj instanceof PackageWithUser)) { 77 return false; 78 } 79 final PackageWithUser that = (PackageWithUser) obj; 80 81 return userId == that.userId && packageName.equals(that.packageName); 82 } 83 84 @Override 85 public String toString() { 86 return String.format("{Package: %d, %s}", userId, packageName); 87 } 88 } 89 90 @UserIdInt 91 private final int mUserId; 92 93 private final ArrayMap<String, ShortcutPackage> mPackages = new ArrayMap<>(); 94 95 private final SparseArray<ShortcutPackage> mPackagesFromUid = new SparseArray<>(); 96 97 private final ArrayMap<PackageWithUser, ShortcutLauncher> mLaunchers = new ArrayMap<>(); 98 99 /** Default launcher that can access the launcher apps APIs. */ 100 private ComponentName mLauncherComponent; 101 102 private long mKnownLocaleChangeSequenceNumber; 103 104 public ShortcutUser(int userId) { 105 mUserId = userId; 106 } 107 108 public int getUserId() { 109 return mUserId; 110 } 111 112 // We don't expose this directly to non-test code because only ShortcutUser should add to/ 113 // remove from it. 114 @VisibleForTesting 115 ArrayMap<String, ShortcutPackage> getAllPackagesForTest() { 116 return mPackages; 117 } 118 119 public boolean hasPackage(@NonNull String packageName) { 120 return mPackages.containsKey(packageName); 121 } 122 123 public ShortcutPackage removePackage(@NonNull ShortcutService s, @NonNull String packageName) { 124 final ShortcutPackage removed = mPackages.remove(packageName); 125 126 s.cleanupBitmapsForPackage(mUserId, packageName); 127 128 return removed; 129 } 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