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