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