blob: 3e44de989c7c002be265e6fd0429f54c2f75e040 [file] [log] [blame]
Makoto Onuki2d895c32016-12-02 15:48:40 -08001/*
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
Sunny Goyala6be88a2017-01-12 16:27:58 -080018import android.annotation.NonNull;
Makoto Onuki2d895c32016-12-02 15:48:40 -080019import android.annotation.Nullable;
Sunny Goyal87a563e2017-01-01 19:42:45 -080020import android.appwidget.AppWidgetProviderInfo;
Makoto Onuki2d895c32016-12-02 15:48:40 -080021import android.content.ComponentName;
22import android.content.Intent;
23import android.content.IntentSender;
24import android.content.pm.IPinItemRequest;
25import android.content.pm.LauncherApps;
26import android.content.pm.LauncherApps.PinItemRequest;
27import android.content.pm.ShortcutInfo;
28import android.os.Bundle;
Sunny Goyal4ad6b572017-02-28 11:11:51 -080029import android.os.RemoteException;
Makoto Onuki2d895c32016-12-02 15:48:40 -080030import android.os.UserHandle;
31import android.util.Log;
32import android.util.Pair;
33import android.util.Slog;
34
35import com.android.internal.annotations.GuardedBy;
36import com.android.internal.annotations.VisibleForTesting;
37import com.android.internal.util.Preconditions;
38
39/**
40 * Handles {@link android.content.pm.ShortcutManager#requestPinShortcut} related tasks.
41 */
42class ShortcutRequestPinProcessor {
43 private static final String TAG = ShortcutService.TAG;
44 private static final boolean DEBUG = ShortcutService.DEBUG;
45
46 private final ShortcutService mService;
47 private final Object mLock;
48
49 /**
50 * Internal for {@link android.content.pm.LauncherApps.PinItemRequest} which receives callbacks.
51 */
Makoto Onuki8abba3a2017-02-24 11:34:54 -080052 private abstract static class PinItemRequestInner extends IPinItemRequest.Stub {
Sunny Goyal87a563e2017-01-01 19:42:45 -080053 protected final ShortcutRequestPinProcessor mProcessor;
Makoto Onuki2d895c32016-12-02 15:48:40 -080054 private final IntentSender mResultIntent;
Sunny Goyala6be88a2017-01-12 16:27:58 -080055 private final int mLauncherUid;
Makoto Onuki2d895c32016-12-02 15:48:40 -080056
Makoto Onuki2d895c32016-12-02 15:48:40 -080057 @GuardedBy("this")
58 private boolean mAccepted;
59
Sunny Goyal87a563e2017-01-01 19:42:45 -080060 private PinItemRequestInner(ShortcutRequestPinProcessor processor,
Sunny Goyala6be88a2017-01-12 16:27:58 -080061 IntentSender resultIntent, int launcherUid) {
Makoto Onuki2d895c32016-12-02 15:48:40 -080062 mProcessor = processor;
Makoto Onuki2d895c32016-12-02 15:48:40 -080063 mResultIntent = resultIntent;
Sunny Goyala6be88a2017-01-12 16:27:58 -080064 mLauncherUid = launcherUid;
65 }
66
Sunny Goyal4ad6b572017-02-28 11:11:51 -080067 @Override
Makoto Onuki8abba3a2017-02-24 11:34:54 -080068 public ShortcutInfo getShortcutInfo() {
69 return null;
70 }
71
Sunny Goyal4ad6b572017-02-28 11:11:51 -080072 @Override
Makoto Onuki8abba3a2017-02-24 11:34:54 -080073 public AppWidgetProviderInfo getAppWidgetProviderInfo() {
74 return null;
75 }
76
Sunny Goyal4ad6b572017-02-28 11:11:51 -080077 @Override
78 public Bundle getExtras() {
79 return null;
80 }
81
Sunny Goyala6be88a2017-01-12 16:27:58 -080082 /**
83 * Returns true if the caller is same as the default launcher app when this request
84 * object was created.
85 */
86 private boolean isCallerValid() {
87 return mProcessor.isCallerUid(mLauncherUid);
Makoto Onuki2d895c32016-12-02 15:48:40 -080088 }
89
90 @Override
91 public boolean isValid() {
Sunny Goyala6be88a2017-01-12 16:27:58 -080092 if (!isCallerValid()) {
93 return false;
94 }
Makoto Onuki2d895c32016-12-02 15:48:40 -080095 // TODO When an app calls requestPinShortcut(), all pending requests should be
96 // invalidated.
97 synchronized (this) {
98 return !mAccepted;
99 }
100 }
101
102 /**
103 * Called when the launcher calls {@link PinItemRequest#accept}.
104 */
105 @Override
106 public boolean accept(Bundle options) {
107 // Make sure the options are unparcellable by the FW. (e.g. not containing unknown
108 // classes.)
Sunny Goyala6be88a2017-01-12 16:27:58 -0800109 if (!isCallerValid()) {
110 throw new SecurityException("Calling uid mismatch");
111 }
Sunny Goyal87a563e2017-01-01 19:42:45 -0800112 Intent extras = null;
Makoto Onuki2d895c32016-12-02 15:48:40 -0800113 if (options != null) {
114 try {
115 options.size();
Sunny Goyal87a563e2017-01-01 19:42:45 -0800116 extras = new Intent().putExtras(options);
Makoto Onuki2d895c32016-12-02 15:48:40 -0800117 } catch (RuntimeException e) {
118 throw new IllegalArgumentException("options cannot be unparceled", e);
119 }
120 }
121 synchronized (this) {
122 if (mAccepted) {
123 throw new IllegalStateException("accept() called already");
124 }
125 mAccepted = true;
126 }
Makoto Onuki2d895c32016-12-02 15:48:40 -0800127
128 // Pin it and send the result intent.
Sunny Goyal87a563e2017-01-01 19:42:45 -0800129 if (tryAccept()) {
130 mProcessor.sendResultIntent(mResultIntent, extras);
Makoto Onuki2d895c32016-12-02 15:48:40 -0800131 return true;
132 } else {
133 return false;
134 }
135 }
Sunny Goyal87a563e2017-01-01 19:42:45 -0800136
137 protected boolean tryAccept() {
138 return true;
139 }
140 }
141
142 /**
143 * Internal for {@link android.content.pm.LauncherApps.PinItemRequest} which receives callbacks.
144 */
Makoto Onuki8abba3a2017-02-24 11:34:54 -0800145 private static class PinAppWidgetRequestInner extends PinItemRequestInner {
146 final AppWidgetProviderInfo mAppWidgetProviderInfo;
Sunny Goyal4ad6b572017-02-28 11:11:51 -0800147 final Bundle mExtras;
Makoto Onuki8abba3a2017-02-24 11:34:54 -0800148
149 private PinAppWidgetRequestInner(ShortcutRequestPinProcessor processor,
150 IntentSender resultIntent, int launcherUid,
Sunny Goyal4ad6b572017-02-28 11:11:51 -0800151 AppWidgetProviderInfo appWidgetProviderInfo, Bundle extras) {
Makoto Onuki8abba3a2017-02-24 11:34:54 -0800152 super(processor, resultIntent, launcherUid);
153
154 mAppWidgetProviderInfo = appWidgetProviderInfo;
Sunny Goyal4ad6b572017-02-28 11:11:51 -0800155 mExtras = extras;
Makoto Onuki8abba3a2017-02-24 11:34:54 -0800156 }
157
158 @Override
159 public AppWidgetProviderInfo getAppWidgetProviderInfo() {
160 return mAppWidgetProviderInfo;
161 }
Sunny Goyal4ad6b572017-02-28 11:11:51 -0800162
163 @Override
164 public Bundle getExtras() {
165 return mExtras;
166 }
Makoto Onuki8abba3a2017-02-24 11:34:54 -0800167 }
168
169 /**
170 * Internal for {@link android.content.pm.LauncherApps.PinItemRequest} which receives callbacks.
171 */
Sunny Goyal87a563e2017-01-01 19:42:45 -0800172 private static class PinShortcutRequestInner extends PinItemRequestInner {
173 /** Original shortcut passed by the app. */
174 public final ShortcutInfo shortcutOriginal;
175
176 /**
177 * Cloned shortcut that's passed to the launcher. The notable difference from
178 * {@link #shortcutOriginal} is it must not have the intent.
179 */
180 public final ShortcutInfo shortcutForLauncher;
181
182 public final String launcherPackage;
183 public final int launcherUserId;
184 public final boolean preExisting;
185
186 private PinShortcutRequestInner(ShortcutRequestPinProcessor processor,
187 ShortcutInfo shortcutOriginal, ShortcutInfo shortcutForLauncher,
188 IntentSender resultIntent,
Sunny Goyala6be88a2017-01-12 16:27:58 -0800189 String launcherPackage, int launcherUserId, int launcherUid, boolean preExisting) {
190 super(processor, resultIntent, launcherUid);
Sunny Goyal87a563e2017-01-01 19:42:45 -0800191 this.shortcutOriginal = shortcutOriginal;
192 this.shortcutForLauncher = shortcutForLauncher;
193 this.launcherPackage = launcherPackage;
194 this.launcherUserId = launcherUserId;
195 this.preExisting = preExisting;
196 }
197
198 @Override
Makoto Onuki8abba3a2017-02-24 11:34:54 -0800199 public ShortcutInfo getShortcutInfo() {
200 return shortcutForLauncher;
201 }
202
203 @Override
Sunny Goyal87a563e2017-01-01 19:42:45 -0800204 protected boolean tryAccept() {
205 if (DEBUG) {
206 Slog.d(TAG, "Launcher accepted shortcut. ID=" + shortcutOriginal.getId()
207 + " package=" + shortcutOriginal.getPackage());
208 }
209 return mProcessor.directPinShortcut(this);
210 }
Makoto Onuki2d895c32016-12-02 15:48:40 -0800211 }
212
213 public ShortcutRequestPinProcessor(ShortcutService service, Object lock) {
214 mService = service;
215 mLock = lock;
216 }
217
Sunny Goyal7f7372a2017-01-24 11:53:54 -0800218 public boolean isRequestPinItemSupported(int callingUserId, int requestType) {
219 return getRequestPinConfirmationActivity(callingUserId, requestType) != null;
Makoto Onuki2d895c32016-12-02 15:48:40 -0800220 }
221
222 /**
Sunny Goyal87a563e2017-01-01 19:42:45 -0800223 * Handle {@link android.content.pm.ShortcutManager#requestPinShortcut)} and
224 * {@link android.appwidget.AppWidgetManager#requestPinAppWidget}.
Sunny Goyala6be88a2017-01-12 16:27:58 -0800225 * In this flow the PinItemRequest is delivered directly to the default launcher app.
Sunny Goyal87a563e2017-01-01 19:42:45 -0800226 * One of {@param inShortcut} and {@param inAppWidget} is always non-null and the other is
227 * always null.
Makoto Onuki2d895c32016-12-02 15:48:40 -0800228 */
Sunny Goyal87a563e2017-01-01 19:42:45 -0800229 public boolean requestPinItemLocked(ShortcutInfo inShortcut, AppWidgetProviderInfo inAppWidget,
Sunny Goyal4ad6b572017-02-28 11:11:51 -0800230 Bundle extras, int userId, IntentSender resultIntent) {
Makoto Onuki2d895c32016-12-02 15:48:40 -0800231
232 // First, make sure the launcher supports it.
233
234 // Find the confirmation activity in the default launcher.
Sunny Goyal7f7372a2017-01-24 11:53:54 -0800235 final int requestType = inShortcut != null ?
236 PinItemRequest.REQUEST_TYPE_SHORTCUT : PinItemRequest.REQUEST_TYPE_APPWIDGET;
Makoto Onuki2d895c32016-12-02 15:48:40 -0800237 final Pair<ComponentName, Integer> confirmActivity =
Sunny Goyal7f7372a2017-01-24 11:53:54 -0800238 getRequestPinConfirmationActivity(userId, requestType);
Makoto Onuki2d895c32016-12-02 15:48:40 -0800239
240 // If the launcher doesn't support it, just return a rejected result and finish.
241 if (confirmActivity == null) {
242 Log.w(TAG, "Launcher doesn't support requestPinnedShortcut(). Shortcut not created.");
243 return false;
244 }
245
Makoto Onuki2d895c32016-12-02 15:48:40 -0800246 final int launcherUserId = confirmActivity.second;
247
248 // Make sure the launcher user is unlocked. (it's always the parent profile, so should
249 // really be unlocked here though.)
250 mService.throwIfUserLockedL(launcherUserId);
251
252 // Next, validate the incoming shortcut, etc.
Sunny Goyal87a563e2017-01-01 19:42:45 -0800253 final PinItemRequest request;
254 if (inShortcut != null) {
Makoto Onuki0c280712017-01-19 15:14:27 -0800255 request = requestPinShortcutLocked(inShortcut, resultIntent, confirmActivity);
Sunny Goyal87a563e2017-01-01 19:42:45 -0800256 } else {
Sunny Goyala6be88a2017-01-12 16:27:58 -0800257 int launcherUid = mService.injectGetPackageUid(
258 confirmActivity.first.getPackageName(), launcherUserId);
Makoto Onuki8abba3a2017-02-24 11:34:54 -0800259 request = new PinItemRequest(
Sunny Goyal4ad6b572017-02-28 11:11:51 -0800260 new PinAppWidgetRequestInner(this, resultIntent, launcherUid, inAppWidget,
261 extras),
Makoto Onuki8abba3a2017-02-24 11:34:54 -0800262 PinItemRequest.REQUEST_TYPE_APPWIDGET);
Sunny Goyal87a563e2017-01-01 19:42:45 -0800263 }
Sunny Goyal7f7372a2017-01-24 11:53:54 -0800264 return startRequestConfirmActivity(confirmActivity.first, launcherUserId, request,
265 requestType);
Sunny Goyal87a563e2017-01-01 19:42:45 -0800266 }
267
268 /**
Sunny Goyala6be88a2017-01-12 16:27:58 -0800269 * Handle {@link android.content.pm.ShortcutManager#createShortcutResultIntent(ShortcutInfo)}.
270 * In this flow the PinItemRequest is delivered to the caller app. Its the app's responsibility
271 * to send it to the Launcher app (via {@link android.app.Activity#setResult(int, Intent)}).
272 */
273 public Intent createShortcutResultIntent(@NonNull ShortcutInfo inShortcut, int userId) {
274 // Find the default launcher activity
275 final int launcherUserId = mService.getParentOrSelfUserId(userId);
276 final ComponentName defaultLauncher = mService.getDefaultLauncher(launcherUserId);
277 if (defaultLauncher == null) {
278 Log.e(TAG, "Default launcher not found.");
279 return null;
280 }
281
282 // Make sure the launcher user is unlocked. (it's always the parent profile, so should
283 // really be unlocked here though.)
284 mService.throwIfUserLockedL(launcherUserId);
285
286 // Next, validate the incoming shortcut, etc.
Makoto Onuki0c280712017-01-19 15:14:27 -0800287 final PinItemRequest request = requestPinShortcutLocked(inShortcut, null,
288 Pair.create(defaultLauncher, launcherUserId));
Sunny Goyala6be88a2017-01-12 16:27:58 -0800289 return new Intent().putExtra(LauncherApps.EXTRA_PIN_ITEM_REQUEST, request);
290 }
291
292 /**
Sunny Goyal87a563e2017-01-01 19:42:45 -0800293 * Handle {@link android.content.pm.ShortcutManager#requestPinShortcut)}.
294 */
Makoto Onuki0c280712017-01-19 15:14:27 -0800295 @NonNull
Sunny Goyal87a563e2017-01-01 19:42:45 -0800296 private PinItemRequest requestPinShortcutLocked(ShortcutInfo inShortcut,
Makoto Onuki0c280712017-01-19 15:14:27 -0800297 IntentSender resultIntentOriginal, Pair<ComponentName, Integer> confirmActivity) {
Makoto Onuki2d895c32016-12-02 15:48:40 -0800298 final ShortcutPackage ps = mService.getPackageShortcutsForPublisherLocked(
299 inShortcut.getPackage(), inShortcut.getUserId());
300
301 final ShortcutInfo existing = ps.findShortcutById(inShortcut.getId());
302 final boolean existsAlready = existing != null;
Makoto Onukia4f89b12017-10-05 10:37:55 -0700303 final boolean existingIsVisible = existsAlready && existing.isVisibleToPublisher();
Makoto Onuki2d895c32016-12-02 15:48:40 -0800304
305 if (DEBUG) {
Makoto Onuki255461f2017-01-10 11:47:25 -0800306 Slog.d(TAG, "requestPinnedShortcut: package=" + inShortcut.getPackage()
Makoto Onuki2d895c32016-12-02 15:48:40 -0800307 + " existsAlready=" + existsAlready
Makoto Onukia4f89b12017-10-05 10:37:55 -0700308 + " existingIsVisible=" + existingIsVisible
Makoto Onuki2d895c32016-12-02 15:48:40 -0800309 + " shortcut=" + inShortcut.toInsecureString());
310 }
311
312 // This is the shortcut that'll be sent to the launcher.
Makoto Onukia01f4f02016-12-15 15:58:41 -0800313 final ShortcutInfo shortcutForLauncher;
Sunny Goyal87a563e2017-01-01 19:42:45 -0800314 final String launcherPackage = confirmActivity.first.getPackageName();
315 final int launcherUserId = confirmActivity.second;
Makoto Onuki2d895c32016-12-02 15:48:40 -0800316
Makoto Onuki0c280712017-01-19 15:14:27 -0800317 IntentSender resultIntentToSend = resultIntentOriginal;
318
Makoto Onuki2d895c32016-12-02 15:48:40 -0800319 if (existsAlready) {
320 validateExistingShortcut(existing);
321
Sunny Goyala6be88a2017-01-12 16:27:58 -0800322 final boolean isAlreadyPinned = mService.getLauncherShortcutsLocked(
323 launcherPackage, existing.getUserId(), launcherUserId).hasPinned(existing);
Makoto Onuki0c280712017-01-19 15:14:27 -0800324 if (isAlreadyPinned) {
325 // When the shortcut is already pinned by this launcher, the request will always
326 // succeed, so just send the result at this point.
327 sendResultIntent(resultIntentOriginal, null);
328
329 // So, do not send the intent again.
330 resultIntentToSend = null;
Makoto Onuki2d895c32016-12-02 15:48:40 -0800331 }
332
333 // Pass a clone, not the original.
334 // Note this will remove the intent and icons.
Makoto Onukia01f4f02016-12-15 15:58:41 -0800335 shortcutForLauncher = existing.clone(ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
336
Sunny Goyala6be88a2017-01-12 16:27:58 -0800337 if (!isAlreadyPinned) {
Makoto Onuki0c280712017-01-19 15:14:27 -0800338 // FLAG_PINNED may still be set, if it's pinned by other launchers.
Sunny Goyala6be88a2017-01-12 16:27:58 -0800339 shortcutForLauncher.clearFlags(ShortcutInfo.FLAG_PINNED);
340 }
Makoto Onuki2d895c32016-12-02 15:48:40 -0800341 } else {
Makoto Onuki255461f2017-01-10 11:47:25 -0800342 // If the shortcut has no default activity, try to set the main activity.
343 // But in the request-pin case, it's optional, so it's okay even if the caller
344 // has no default activity.
345 if (inShortcut.getActivity() == null) {
346 inShortcut.setActivity(mService.injectGetDefaultMainActivity(
347 inShortcut.getPackage(), inShortcut.getUserId()));
348 }
349
Makoto Onuki2d895c32016-12-02 15:48:40 -0800350 // It doesn't exist, so it must have all mandatory fields.
351 mService.validateShortcutForPinRequest(inShortcut);
352
353 // Initialize the ShortcutInfo for pending approval.
354 inShortcut.resolveResourceStrings(mService.injectGetResourcesForApplicationAsUser(
355 inShortcut.getPackage(), inShortcut.getUserId()));
356 if (DEBUG) {
Makoto Onuki255461f2017-01-10 11:47:25 -0800357 Slog.d(TAG, "Resolved shortcut=" + inShortcut.toInsecureString());
Makoto Onuki2d895c32016-12-02 15:48:40 -0800358 }
Makoto Onukia01f4f02016-12-15 15:58:41 -0800359 // We should strip out the intent, but should preserve the icon.
360 shortcutForLauncher = inShortcut.clone(
361 ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER_APPROVAL);
Makoto Onuki2d895c32016-12-02 15:48:40 -0800362 }
Makoto Onuki255461f2017-01-10 11:47:25 -0800363 if (DEBUG) {
364 Slog.d(TAG, "Sending to launcher=" + shortcutForLauncher.toInsecureString());
365 }
Makoto Onuki2d895c32016-12-02 15:48:40 -0800366
367 // Create a request object.
368 final PinShortcutRequestInner inner =
Makoto Onuki0c280712017-01-19 15:14:27 -0800369 new PinShortcutRequestInner(this, inShortcut, shortcutForLauncher,
370 resultIntentToSend, launcherPackage, launcherUserId,
Sunny Goyala6be88a2017-01-12 16:27:58 -0800371 mService.injectGetPackageUid(launcherPackage, launcherUserId),
372 existsAlready);
Makoto Onuki2d895c32016-12-02 15:48:40 -0800373
Makoto Onuki8abba3a2017-02-24 11:34:54 -0800374 return new PinItemRequest(inner, PinItemRequest.REQUEST_TYPE_SHORTCUT);
Makoto Onuki2d895c32016-12-02 15:48:40 -0800375 }
376
377 private void validateExistingShortcut(ShortcutInfo shortcutInfo) {
378 // Make sure it's enabled.
379 // (Because we can't always force enable it automatically as it may be a stale
380 // manifest shortcut.)
Makoto Onukia01f4f02016-12-15 15:58:41 -0800381 Preconditions.checkArgument(shortcutInfo.isEnabled(),
Makoto Onuki2d895c32016-12-02 15:48:40 -0800382 "Shortcut ID=" + shortcutInfo + " already exists but disabled.");
Makoto Onuki2d895c32016-12-02 15:48:40 -0800383 }
384
385 private boolean startRequestConfirmActivity(ComponentName activity, int launcherUserId,
Sunny Goyal7f7372a2017-01-24 11:53:54 -0800386 PinItemRequest request, int requestType) {
387 final String action = requestType == LauncherApps.PinItemRequest.REQUEST_TYPE_SHORTCUT ?
388 LauncherApps.ACTION_CONFIRM_PIN_SHORTCUT :
389 LauncherApps.ACTION_CONFIRM_PIN_APPWIDGET;
390
Makoto Onuki2d895c32016-12-02 15:48:40 -0800391 // Start the activity.
Sunny Goyal7f7372a2017-01-24 11:53:54 -0800392 final Intent confirmIntent = new Intent(action);
Makoto Onuki2d895c32016-12-02 15:48:40 -0800393 confirmIntent.setComponent(activity);
394 confirmIntent.putExtra(LauncherApps.EXTRA_PIN_ITEM_REQUEST, request);
395 confirmIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
396
397 final long token = mService.injectClearCallingIdentity();
398 try {
399 mService.mContext.startActivityAsUser(
400 confirmIntent, UserHandle.of(launcherUserId));
401 } catch (RuntimeException e) { // ActivityNotFoundException, etc.
402 Log.e(TAG, "Unable to start activity " + activity, e);
403 return false;
404 } finally {
405 mService.injectRestoreCallingIdentity(token);
406 }
407 return true;
408 }
409
410 /**
Sunny Goyal7f7372a2017-01-24 11:53:54 -0800411 * Find the activity that handles {@link LauncherApps#ACTION_CONFIRM_PIN_SHORTCUT} in the
Makoto Onuki2d895c32016-12-02 15:48:40 -0800412 * default launcher.
413 */
414 @Nullable
415 @VisibleForTesting
Sunny Goyal7f7372a2017-01-24 11:53:54 -0800416 Pair<ComponentName, Integer> getRequestPinConfirmationActivity(
417 int callingUserId, int requestType) {
Makoto Onuki2d895c32016-12-02 15:48:40 -0800418 // Find the default launcher.
419 final int launcherUserId = mService.getParentOrSelfUserId(callingUserId);
420 final ComponentName defaultLauncher = mService.getDefaultLauncher(launcherUserId);
421
422 if (defaultLauncher == null) {
423 Log.e(TAG, "Default launcher not found.");
424 return null;
425 }
426 final ComponentName activity = mService.injectGetPinConfirmationActivity(
Sunny Goyal7f7372a2017-01-24 11:53:54 -0800427 defaultLauncher.getPackageName(), launcherUserId, requestType);
Makoto Onuki2d895c32016-12-02 15:48:40 -0800428 return (activity == null) ? null : Pair.create(activity, launcherUserId);
429 }
430
Sunny Goyal87a563e2017-01-01 19:42:45 -0800431 public void sendResultIntent(@Nullable IntentSender intent, @Nullable Intent extras) {
Makoto Onuki2d895c32016-12-02 15:48:40 -0800432 if (DEBUG) {
433 Slog.d(TAG, "Sending result intent.");
434 }
Sunny Goyal87a563e2017-01-01 19:42:45 -0800435 mService.injectSendIntentSender(intent, extras);
Makoto Onuki2d895c32016-12-02 15:48:40 -0800436 }
437
Sunny Goyala6be88a2017-01-12 16:27:58 -0800438 public boolean isCallerUid(int uid) {
439 return uid == mService.injectBinderCallingUid();
440 }
441
Makoto Onuki2d895c32016-12-02 15:48:40 -0800442 /**
443 * The last step of the "request pin shortcut" flow. Called when the launcher accepted a
444 * request.
445 */
446 public boolean directPinShortcut(PinShortcutRequestInner request) {
447
Makoto Onukia01f4f02016-12-15 15:58:41 -0800448 final ShortcutInfo original = request.shortcutOriginal;
Makoto Onuki2d895c32016-12-02 15:48:40 -0800449 final int appUserId = original.getUserId();
450 final String appPackageName = original.getPackage();
451 final int launcherUserId = request.launcherUserId;
452 final String launcherPackage = request.launcherPackage;
453 final String shortcutId = original.getId();
454
455 synchronized (mLock) {
456 if (!(mService.isUserUnlockedL(appUserId)
457 && mService.isUserUnlockedL(request.launcherUserId))) {
458 Log.w(TAG, "User is locked now.");
459 return false;
460 }
461
Makoto Onuki0c280712017-01-19 15:14:27 -0800462 final ShortcutLauncher launcher = mService.getLauncherShortcutsLocked(
463 launcherPackage, appUserId, launcherUserId);
464 launcher.attemptToRestoreIfNeededAndSave();
465 if (launcher.hasPinned(original)) {
466 if (DEBUG) {
Makoto Onukia4f89b12017-10-05 10:37:55 -0700467 Slog.d(TAG, "Shortcut " + original + " already pinned."); // This too.
Makoto Onuki0c280712017-01-19 15:14:27 -0800468 }
469 return true;
470 }
471
Makoto Onuki2d895c32016-12-02 15:48:40 -0800472 final ShortcutPackage ps = mService.getPackageShortcutsForPublisherLocked(
473 appPackageName, appUserId);
474 final ShortcutInfo current = ps.findShortcutById(shortcutId);
475
476 // The shortcut might have been changed, so we need to do the same validation again.
477 try {
478 if (current == null) {
479 // It doesn't exist, so it must have all necessary fields.
Makoto Onukia01f4f02016-12-15 15:58:41 -0800480 mService.validateShortcutForPinRequest(original);
Makoto Onuki2d895c32016-12-02 15:48:40 -0800481 } else {
482 validateExistingShortcut(current);
483 }
484 } catch (RuntimeException e) {
485 Log.w(TAG, "Unable to pin shortcut: " + e.getMessage());
486 return false;
487 }
488
489 // If the shortcut doesn't exist, need to create it.
490 // First, create it as a dynamic shortcut.
491 if (current == null) {
492 if (DEBUG) {
493 Slog.d(TAG, "Temporarily adding " + shortcutId + " as dynamic");
494 }
Makoto Onuki255461f2017-01-10 11:47:25 -0800495 // Add as a dynamic shortcut. In order for a shortcut to be dynamic, it must
496 // have a target activity, so we set a dummy here. It's later removed
497 // in deleteDynamicWithId().
Makoto Onuki2d895c32016-12-02 15:48:40 -0800498 if (original.getActivity() == null) {
499 original.setActivity(mService.getDummyMainActivity(appPackageName));
500 }
Makoto Onukia4f89b12017-10-05 10:37:55 -0700501 ps.addOrReplaceDynamicShortcut(original);
Makoto Onuki2d895c32016-12-02 15:48:40 -0800502 }
503
504 // Pin the shortcut.
505 if (DEBUG) {
506 Slog.d(TAG, "Pinning " + shortcutId);
507 }
508
Makoto Onukia4f89b12017-10-05 10:37:55 -0700509 launcher.addPinnedShortcut(appPackageName, appUserId, shortcutId,
510 /*forPinRequest=*/ true);
Makoto Onuki2d895c32016-12-02 15:48:40 -0800511
512 if (current == null) {
513 if (DEBUG) {
514 Slog.d(TAG, "Removing " + shortcutId + " as dynamic");
515 }
Makoto Onukia4f89b12017-10-05 10:37:55 -0700516 ps.deleteDynamicWithId(shortcutId, /*ignoreInvisible=*/ false);
Makoto Onuki2d895c32016-12-02 15:48:40 -0800517 }
518
519 ps.adjustRanks(); // Shouldn't be needed, but just in case.
520 }
521
522 mService.verifyStates();
523 mService.packageShortcutsChanged(appPackageName, appUserId);
524
525 return true;
526 }
527}