ShortcutLauncher.java revision c51b2876ec5c0af449469a0f76bb38c51cfcff04
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.pm.ShortcutInfo; 21import android.util.ArrayMap; 22import android.util.ArraySet; 23import android.util.Slog; 24 25import com.android.internal.annotations.VisibleForTesting; 26import com.android.server.pm.ShortcutUser.PackageWithUser; 27 28import org.xmlpull.v1.XmlPullParser; 29import org.xmlpull.v1.XmlPullParserException; 30import org.xmlpull.v1.XmlSerializer; 31 32import java.io.IOException; 33import java.io.PrintWriter; 34import java.util.ArrayList; 35import java.util.List; 36 37/** 38 * Launcher information used by {@link ShortcutService}. 39 */ 40class ShortcutLauncher extends ShortcutPackageItem { 41 private static final String TAG = ShortcutService.TAG; 42 43 static final String TAG_ROOT = "launcher-pins"; 44 45 private static final String TAG_PACKAGE = "package"; 46 private static final String TAG_PIN = "pin"; 47 48 private static final String ATTR_LAUNCHER_USER_ID = "launcher-user"; 49 private static final String ATTR_VALUE = "value"; 50 private static final String ATTR_PACKAGE_NAME = "package-name"; 51 private static final String ATTR_PACKAGE_USER_ID = "package-user"; 52 53 private final int mOwnerUserId; 54 55 /** 56 * Package name -> IDs. 57 */ 58 final private ArrayMap<PackageWithUser, ArraySet<String>> mPinnedShortcuts = new ArrayMap<>(); 59 60 private ShortcutLauncher(@NonNull ShortcutUser shortcutUser, 61 @UserIdInt int ownerUserId, @NonNull String packageName, 62 @UserIdInt int launcherUserId, ShortcutPackageInfo spi) { 63 super(shortcutUser, launcherUserId, packageName, 64 spi != null ? spi : ShortcutPackageInfo.newEmpty()); 65 mOwnerUserId = ownerUserId; 66 } 67 68 public ShortcutLauncher(@NonNull ShortcutUser shortcutUser, 69 @UserIdInt int ownerUserId, @NonNull String packageName, 70 @UserIdInt int launcherUserId) { 71 this(shortcutUser, ownerUserId, packageName, launcherUserId, null); 72 } 73 74 @Override 75 public int getOwnerUserId() { 76 return mOwnerUserId; 77 } 78 79 /** 80 * Called when the new package can't receive the backup, due to signature or version mismatch. 81 */ 82 @Override 83 protected void onRestoreBlocked() { 84 final ArrayList<PackageWithUser> pinnedPackages = 85 new ArrayList<>(mPinnedShortcuts.keySet()); 86 mPinnedShortcuts.clear(); 87 for (int i = pinnedPackages.size() - 1; i >= 0; i--) { 88 final PackageWithUser pu = pinnedPackages.get(i); 89 final ShortcutPackage p = mShortcutUser.getPackageShortcutsIfExists(pu.packageName); 90 if (p != null) { 91 p.refreshPinnedFlags(); 92 } 93 } 94 } 95 96 @Override 97 protected void onRestored() { 98 // Nothing to do. 99 } 100 101 public void pinShortcuts(@UserIdInt int packageUserId, 102 @NonNull String packageName, @NonNull List<String> ids) { 103 final ShortcutPackage packageShortcuts = 104 mShortcutUser.getPackageShortcutsIfExists(packageName); 105 if (packageShortcuts == null) { 106 return; // No need to instantiate. 107 } 108 109 final PackageWithUser pu = PackageWithUser.of(packageUserId, packageName); 110 111 final int idSize = ids.size(); 112 if (idSize == 0) { 113 mPinnedShortcuts.remove(pu); 114 } else { 115 final ArraySet<String> prevSet = mPinnedShortcuts.get(pu); 116 117 // Pin shortcuts. Make sure only pin the ones that were visible to the caller. 118 // i.e. a non-dynamic, pinned shortcut by *other launchers* shouldn't be pinned here. 119 120 final ArraySet<String> newSet = new ArraySet<>(); 121 122 for (int i = 0; i < idSize; i++) { 123 final String id = ids.get(i); 124 final ShortcutInfo si = packageShortcuts.findShortcutById(id); 125 if (si == null) { 126 continue; 127 } 128 if (si.isDynamic() || (prevSet != null && prevSet.contains(id))) { 129 newSet.add(id); 130 } 131 } 132 mPinnedShortcuts.put(pu, newSet); 133 } 134 packageShortcuts.refreshPinnedFlags(); 135 } 136 137 /** 138 * Return the pinned shortcut IDs for the publisher package. 139 */ 140 public ArraySet<String> getPinnedShortcutIds(@NonNull String packageName, 141 @UserIdInt int packageUserId) { 142 return mPinnedShortcuts.get(PackageWithUser.of(packageUserId, packageName)); 143 } 144 145 boolean cleanUpPackage(String packageName, @UserIdInt int packageUserId) { 146 return mPinnedShortcuts.remove(PackageWithUser.of(packageUserId, packageName)) != null; 147 } 148 149 /** 150 * Persist. 151 */ 152 @Override 153 public void saveToXml(XmlSerializer out, boolean forBackup) 154 throws IOException { 155 final int size = mPinnedShortcuts.size(); 156 if (size == 0) { 157 return; // Nothing to write. 158 } 159 160 out.startTag(null, TAG_ROOT); 161 ShortcutService.writeAttr(out, ATTR_PACKAGE_NAME, getPackageName()); 162 ShortcutService.writeAttr(out, ATTR_LAUNCHER_USER_ID, getPackageUserId()); 163 getPackageInfo().saveToXml(out); 164 165 for (int i = 0; i < size; i++) { 166 final PackageWithUser pu = mPinnedShortcuts.keyAt(i); 167 168 if (forBackup && (pu.userId != getOwnerUserId())) { 169 continue; // Target package on a different user, skip. (i.e. work profile) 170 } 171 172 out.startTag(null, TAG_PACKAGE); 173 ShortcutService.writeAttr(out, ATTR_PACKAGE_NAME, pu.packageName); 174 ShortcutService.writeAttr(out, ATTR_PACKAGE_USER_ID, pu.userId); 175 176 final ArraySet<String> ids = mPinnedShortcuts.valueAt(i); 177 final int idSize = ids.size(); 178 for (int j = 0; j < idSize; j++) { 179 ShortcutService.writeTagValue(out, TAG_PIN, ids.valueAt(j)); 180 } 181 out.endTag(null, TAG_PACKAGE); 182 } 183 184 out.endTag(null, TAG_ROOT); 185 } 186 187 /** 188 * Load. 189 */ 190 public static ShortcutLauncher loadFromXml(XmlPullParser parser, ShortcutUser shortcutUser, 191 int ownerUserId, boolean fromBackup) throws IOException, XmlPullParserException { 192 final String launcherPackageName = ShortcutService.parseStringAttribute(parser, 193 ATTR_PACKAGE_NAME); 194 195 // If restoring, just use the real user ID. 196 final int launcherUserId = 197 fromBackup ? ownerUserId 198 : ShortcutService.parseIntAttribute(parser, ATTR_LAUNCHER_USER_ID, ownerUserId); 199 200 final ShortcutLauncher ret = new ShortcutLauncher(shortcutUser, launcherUserId, 201 launcherPackageName, launcherUserId); 202 203 ArraySet<String> ids = null; 204 final int outerDepth = parser.getDepth(); 205 int type; 206 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 207 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 208 if (type != XmlPullParser.START_TAG) { 209 continue; 210 } 211 final int depth = parser.getDepth(); 212 final String tag = parser.getName(); 213 if (depth == outerDepth + 1) { 214 switch (tag) { 215 case ShortcutPackageInfo.TAG_ROOT: 216 ret.getPackageInfo().loadFromXml(parser, fromBackup); 217 continue; 218 case TAG_PACKAGE: { 219 final String packageName = ShortcutService.parseStringAttribute(parser, 220 ATTR_PACKAGE_NAME); 221 final int packageUserId = fromBackup ? ownerUserId 222 : ShortcutService.parseIntAttribute(parser, 223 ATTR_PACKAGE_USER_ID, ownerUserId); 224 ids = new ArraySet<>(); 225 ret.mPinnedShortcuts.put( 226 PackageWithUser.of(packageUserId, packageName), ids); 227 continue; 228 } 229 } 230 } 231 if (depth == outerDepth + 2) { 232 switch (tag) { 233 case TAG_PIN: { 234 if (ids == null) { 235 Slog.w(TAG, TAG_PIN + " in invalid place"); 236 } else { 237 ids.add(ShortcutService.parseStringAttribute(parser, ATTR_VALUE)); 238 } 239 continue; 240 } 241 } 242 } 243 ShortcutService.warnForInvalidTag(depth, tag); 244 } 245 return ret; 246 } 247 248 public void dump(@NonNull PrintWriter pw, @NonNull String prefix) { 249 pw.println(); 250 251 pw.print(prefix); 252 pw.print("Launcher: "); 253 pw.print(getPackageName()); 254 pw.print(" Package user: "); 255 pw.print(getPackageUserId()); 256 pw.print(" Owner user: "); 257 pw.print(getOwnerUserId()); 258 pw.println(); 259 260 getPackageInfo().dump(pw, prefix + " "); 261 pw.println(); 262 263 final int size = mPinnedShortcuts.size(); 264 for (int i = 0; i < size; i++) { 265 pw.println(); 266 267 final PackageWithUser pu = mPinnedShortcuts.keyAt(i); 268 269 pw.print(prefix); 270 pw.print(" "); 271 pw.print("Package: "); 272 pw.print(pu.packageName); 273 pw.print(" User: "); 274 pw.println(pu.userId); 275 276 final ArraySet<String> ids = mPinnedShortcuts.valueAt(i); 277 final int idSize = ids.size(); 278 279 for (int j = 0; j < idSize; j++) { 280 pw.print(prefix); 281 pw.print(" Pinned: "); 282 pw.print(ids.valueAt(j)); 283 pw.println(); 284 } 285 } 286 } 287 288 @VisibleForTesting 289 ArraySet<String> getAllPinnedShortcutsForTest(String packageName, int packageUserId) { 290 return new ArraySet<>(mPinnedShortcuts.get(PackageWithUser.of(packageUserId, packageName))); 291 } 292} 293